From c6c549eae920eea3b97ef9d237f837299c398e2e Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Thu, 20 Aug 2026 16:40:05 +0500 Subject: [PATCH 1/6] =?UTF-8?q?=D1=82=D1=80=D0=B5=D0=B9=D1=81=20=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20=D0=BE=D0=BF=D1=82=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/myRequests/RequestsTableView.tsx | 3 +- .../features/myRequests/SearchView.tsx | 5 +- .../features/myRequests/myRequests.service.ts | 356 ++++++++++-------- .../shared/approval/approvalListLocator.ts | 207 ++++++++++ .../shared/approval/approvalRoute.service.ts | 2 +- .../requestPortal/shared/approval/index.ts | 4 + .../shared/approval/listPartitions.ts | 7 +- .../requestPortal/shared/approval/siteUser.ts | 65 ++++ .../requestPortal/shared/perfTrace.ts | 196 ++++++++++ 9 files changed, 690 insertions(+), 155 deletions(-) create mode 100644 src/webparts/requestPortal/shared/approval/approvalListLocator.ts create mode 100644 src/webparts/requestPortal/shared/approval/siteUser.ts create mode 100644 src/webparts/requestPortal/shared/perfTrace.ts diff --git a/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx b/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx index 9aa8f71..5630086 100644 --- a/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx +++ b/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx @@ -16,6 +16,7 @@ import { IMyRequest } from './models/IMyRequest'; import { DataTable, IDataTableColumn } from '@tnsu/ui-kit'; import { REQUEST_STATUS, REQUEST_STATUS_CHOICES, IRequestType, StatusBadge } from '../../shared/approval'; import { onRequestsChanged } from '../../shared/requestEvents'; +import { perfScope } from '../../shared/perfTrace'; import { useRoutedQuery } from '../../shared/usePersistedQuery'; import { IPage, pageKey, pageSignature, readPage, writePage } from './pageCache'; import { Pager } from './Pager'; @@ -398,7 +399,7 @@ export const RequestsTableView: React.FC = ({ const load = async (forceRender = false): Promise => { try { - const fresh = await fetchPage(); + const fresh = await perfScope(`таблица ${view} стр.${page}`, fetchPage); if (cancelled) return; // Перерисовываем, только если данные реально изменились — иначе при diff --git a/src/webparts/requestPortal/features/myRequests/SearchView.tsx b/src/webparts/requestPortal/features/myRequests/SearchView.tsx index 0411d1a..85bd51f 100644 --- a/src/webparts/requestPortal/features/myRequests/SearchView.tsx +++ b/src/webparts/requestPortal/features/myRequests/SearchView.tsx @@ -4,6 +4,7 @@ import { getLinkedTypes, hydratePendingAuthors, searchMyRequests } from './myReq import { IMyRequest } from './models/IMyRequest'; import { IRequestType, StatusBadge } from '../../shared/approval'; import { onRequestsChanged } from '../../shared/requestEvents'; +import { perfScope } from '../../shared/perfTrace'; import { useRoutedQuery } from '../../shared/usePersistedQuery'; import { pageKey, pageSignature, readPage, writePage } from './pageCache'; import { Pager } from './Pager'; @@ -185,7 +186,9 @@ export const SearchView: React.FC = ({ const load = async (forceRender = false): Promise => { try { - const fresh = await searchMyRequests(context, types, debouncedQuery); + const fresh = await perfScope('вкладка «Поиск»', () => + searchMyRequests(context, types, debouncedQuery) + ); if (cancelled) return; applyKnownAuthors(fresh.rows, authors.current); const prev = readPage(cacheKey); diff --git a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts index 05c3d96..836eacb 100644 --- a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts +++ b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts @@ -11,15 +11,22 @@ import '@pnp/sp/search'; // но в рантайме не зарегистрирован. import '@pnp/sp/batching'; import { WebPartContext } from '@microsoft/sp-webpart-base'; -import { getRequestTypes, IRequestType, partitionsForTypeId, REQUEST_STATUS } from '../../shared/approval'; +import { + currentUserIdFor, + ensureUserIdFor, + getRequestTypesCached, + IRequestType, + approvalListKey, + locateApprovalLists, + partitionsForTypeId, + REQUEST_STATUS, + siteOrigin, +} from '../../shared/approval'; import { effectiveUserEmail } from '../../shared/impersonation'; import { IMyRequest } from './models/IMyRequest'; import { IMyRequestsTypeDebug } from './models/IMyRequestsDebug'; import { traceLinkedTypes, traceResultRows, traceSearchHits } from './searchTrace'; - -// Список данных «... — данные» → список согласования «... — согласование» -// (конвенция именования; см. forms/*/provisioning.json и forms/*/approval/provisioning.json). -const DATA_LIST_SUFFIX = /\s*—\s*данные\s*$/; +import { perfScope, traced } from '../../shared/perfTrace'; // Свежие заявки/строки (созданы или изменены недавно) поисковый индекс может // ещё не проиндексировать — такие добираем обычным REST-ом напрямую, в обход @@ -86,7 +93,11 @@ export function currentUserEmail(context: WebPartContext): string { // Типы заявок, у которых approval-route API уже отдал listId/siteUrl (готовы к чтению). export async function getLinkedTypes(context: WebPartContext): Promise { - const types = await getRequestTypes(context); + // Кешированный вариант: getLinkedTypes зовут и счётчики меню, и каждая + // таблица, и «Поиск» — на одно открытие портала это было 4-5 одинаковых + // AAD-запросов к /api/request-types. Ответ меняется только когда админ + // заводит тип заявки, то есть заведомо не в пределах загрузки страницы. + const types = await getRequestTypesCached(context); const linked = types.filter((t) => !!t.listId && !!t.siteUrl); traceLinkedTypes(types, linked); return linked; @@ -195,28 +206,38 @@ async function buildPendingIndex( [] ); - // Резолв Title/адреса списка согласования остаётся ПОПАРТИЦИОННЫМ REST-ом: - // approval-route не хранит на него ссылку (список согласования ищется только - // по имени, см. shared/listTitles.ts), значит его АДРЕС узнать больше неоткуда. - // Но это лёгкий $select без постраничного перебора — не то же самое, что - // полноценный Search, который эти вызовы как раз заменяют. - const resolved = await Promise.all( - flat.map(({ type, target }) => - resolveApprovalSearchTarget(context, type, target) + // Адреса списков согласования всех партиций разом: не больше ОДНОГО + // /_api/web/lists на сайт, а после первого раза — вообще без сети (адреса + // кешируются на сессию, см. shared/approval/approvalListLocator.ts). Раньше + // здесь был отдельный REST-запрос на КАЖДУЮ партицию, и он повторялся при + // каждой загрузке очереди — при 11 партициях это 11 запросов на проход, а + // проходов за одно открытие вкладки два (счётчики меню + сама таблица). + const located = await traced('адреса списков согласования', () => + locateApprovalLists( + context, + flat.map(({ target }) => ({ listId: target.listId, siteUrl: target.siteUrl, title: target.title })) ) ); const okEntries: IApprovalSearchTarget[] = []; - resolved.forEach((r) => { - const debugType = r.kind === 'ok' ? r.value.type : r.type; - const debug = debugByType.get(debugType.id); - if (!debug) return; - if (r.kind === 'ok') { - okEntries.push(r.value); - } else if (r.kind === 'skip') { - if (!debug.approvalSkippedReason) debug.approvalSkippedReason = r.reason; + flat.forEach(({ type, target }) => { + const debug = debugByType.get(type.id); + const lookup = located.get(approvalListKey(target.siteUrl, target.listId)); + if (!debug || !lookup) return; + + if (lookup.kind === 'ok') { + okEntries.push({ + type, + target, + // getByTitle — только построение url, отдельного запроса не стоит. + approvalList: spForSite(target.siteUrl, context).web.lists.getByTitle(lookup.value.approvalListTitle), + approvalListTitle: lookup.value.approvalListTitle, + approvalListAbsUrl: lookup.value.approvalListAbsUrl, + }); + } else if (lookup.kind === 'skip') { + if (!debug.approvalSkippedReason) debug.approvalSkippedReason = lookup.reason; } else { - if (!debug.approvalError) debug.approvalError = r.message; + if (!debug.approvalError) debug.approvalError = lookup.message; } }); @@ -225,22 +246,28 @@ async function buildPendingIndex( // поэтому идут параллельно, а не один за другим. const [hitsByTarget, recentByTarget] = await Promise.all([ okEntries.length > 0 - ? searchAcrossApprovalLists(context, okEntries, tokenEmail, statuses) + ? traced('Search: очередь согласования', () => + searchAcrossApprovalLists(context, okEntries, tokenEmail, statuses) + ) : Promise.resolve(new Map()), // Довесок нужен только режиму «чья очередь сейчас»: при текстовом поиске // важна полнота по архиву, а не мгновенная свежесть (см. scope='involved'). okEntries.length > 0 && scope === 'myTurn' - ? fetchRecentApprovalRows(context, okEntries, tokenEmail, statuses) + ? traced('REST-довесок по свежести (очередь)', () => + fetchRecentApprovalRows(context, okEntries, tokenEmail, statuses) + ) : Promise.resolve(new Map>>()), ]); - const parts = await Promise.all( - okEntries.map((entry) => - finalizeApprovalTarget( - context, - entry, - hitsByTarget.get(targetKey(entry)) ?? [], - recentByTarget.get(targetKey(entry)) ?? [] + const parts = await traced('дочитывание строк очереди (REST)', () => + Promise.all( + okEntries.map((entry) => + finalizeApprovalTarget( + context, + entry, + hitsByTarget.get(targetKey(entry)) ?? [], + recentByTarget.get(targetKey(entry)) ?? [] + ) ) ) ); @@ -281,6 +308,55 @@ async function buildPendingIndex( return { types, index, debug: types.map((t) => debugByType.get(t.id)!) }; } +/** Результат buildPendingIndex — вынесен в тип, потому что им теперь делятся вызывающие. */ +interface IPendingIndex { + types: IRequestType[]; + index: IMyRequest[]; + debug: IMyRequestsTypeDebug[]; +} + +// Уже идущие сборки индекса, по ключу «область + набор статусов». +// +// При открытии портала счётчики меню (getNavCounts) и сама таблица очереди +// стартуют практически одновременно и просят ОДИН И ТОТ ЖЕ набор — раньше это +// значило два полных прохода: два резолва адресов списков, два Search, два +// довеска по свежести. Второй вызов теперь подхватывает промис первого. +// +// Держим ТОЛЬКО пока запрос в полёте: как только он завершился, запись +// удаляется. Кешировать результат по времени нельзя — «Обновить» и событие +// rp-requests-changed обязаны читать данные заново. +const pendingIndexInFlight = new Map>(); + +function pendingIndexKey(scope: ApprovalScope, statuses: string[]): string { + return `${scope}|${[...statuses].sort().join(',')}`; +} + +async function sharedPendingIndex( + context: WebPartContext, + scope: ApprovalScope, + statuses: string[] +): Promise { + const key = pendingIndexKey(scope, statuses); + const running = pendingIndexInFlight.get(key); + if (running) return running; + + const forget = (): void => { + pendingIndexInFlight.delete(key); + }; + const promise = buildPendingIndex(context, scope, statuses).then( + (r) => { + forget(); + return r; + }, + (e) => { + forget(); + throw e; + } + ); + pendingIndexInFlight.set(key, promise); + return promise; +} + // Без текстового поиска: страница нарезается ЗДЕСЬ (сервер решает, какие 20 // заявок отдать), автор дочитывается только для них. export async function getApprovalQueue( @@ -288,12 +364,29 @@ export async function getApprovalQueue( kind: ApprovalQueueKind, page: number = 1 ): Promise { - const { types, index, debug } = await buildPendingIndex(context, 'myTurn', QUEUE_STATUSES[kind]); + // Очереди действий («На согласовании» и «На исполнении») собираются ОДНОЙ + // выборкой по обоим статусам, а разносятся уже по myStepStatus — ровно так же, + // как это делают счётчики меню (см. getNavCountsInner). Смысл: это тот же + // самый набор, и просить его двумя разными запросами значит дважды пройти по + // всем спискам согласования. История решений ('approved'/'rejected'/ + // 'executed') в эту пару не входит и берётся своим запросом. + const shared = MY_TURN_STATUSES.indexOf(QUEUE_STATUSES[kind][0]) >= 0; + const { types, index, debug } = await sharedPendingIndex( + context, + 'myTurn', + shared ? MY_TURN_STATUSES : QUEUE_STATUSES[kind] + ); - const pageRows = index.slice((page - 1) * PENDING_PAGE_SIZE, page * PENDING_PAGE_SIZE); + // Отбор по СВЕЖЕМУ статусу своей строки, а не по тому, что отдал индекс. + // Индекс мог отстать, и заявка, по которой решение уже принято, не должна + // висеть во вкладке «ждут вашего согласования» — по этой же причине + // finalizeApprovalTarget перечитывает статус REST-ом. + const rows = shared ? index.filter((r) => r.myStepStatus === QUEUE_STATUSES[kind][0]) : index; + + const pageRows = rows.slice((page - 1) * PENDING_PAGE_SIZE, page * PENDING_PAGE_SIZE); await hydrateAuthors(context, types, pageRows); - return { rows: pageRows, totalRows: index.length, debug }; + return { rows: pageRows, totalRows: rows.length, debug }; } // С текстовым поиском: полный индекс (без автора) — фильтрует его вызывающая @@ -308,7 +401,7 @@ export async function getApprovalQueueIndex( context: WebPartContext, kind: ApprovalQueueKind ): Promise<{ rows: IMyRequest[]; types: IRequestType[]; debug: IMyRequestsTypeDebug[] }> { - const { types, index, debug } = await buildPendingIndex(context, 'involved', QUEUE_STATUSES[kind]); + const { types, index, debug } = await sharedPendingIndex(context, 'involved', QUEUE_STATUSES[kind]); return { rows: index, types, debug }; } @@ -318,7 +411,7 @@ export async function getPendingApprovalIndex( context: WebPartContext, scope: ApprovalScope ): Promise<{ rows: IMyRequest[]; types: IRequestType[]; debug: IMyRequestsTypeDebug[] }> { - const { types, index, debug } = await buildPendingIndex(context, scope, []); + const { types, index, debug } = await sharedPendingIndex(context, scope, []); return { rows: index, types, debug }; } @@ -338,14 +431,14 @@ export interface INavCounts { // своим запросом — но параллельно, и ровно тем же вызовом, что наполняет саму // вкладку: иначе счётчик и список могли бы разойтись. export async function getNavCounts(context: WebPartContext): Promise { - return getNavCountsInner(context); + return perfScope('счётчики меню', () => getNavCountsInner(context)); } async function getNavCountsInner(context: WebPartContext): Promise { const types = await getLinkedTypes(context); const [turn, rework] = await Promise.all([ - buildPendingIndex(context, 'myTurn', MY_TURN_STATUSES), + sharedPendingIndex(context, 'myTurn', MY_TURN_STATUSES), getOwnRequestsByStatus(context, types, REQUEST_STATUS.returned, 1), ]); @@ -383,67 +476,6 @@ interface IApprovalSearchTarget { approvalListAbsUrl: string; } -type ApprovalTargetResolution = - | { kind: 'ok'; value: IApprovalSearchTarget } - | { kind: 'skip'; type: IRequestType; target: IListTarget; reason: string } - | { kind: 'error'; type: IRequestType; target: IListTarget; message: string }; - -/** - * Резолвит список согласования одной партиции: Title (по конвенции из Title - * списка данных) и его абсолютный адрес пути. - * - * Title списка данных — из target.title (бесплатно, из bulk-ответа - * /api/request-types/lists, см. IListTarget.title), REST — только если реестр - * партиций был недоступен. - * - * Адрес списка согласования — ВСЕГДА REST-ом: approval-route не хранит на него - * ссылку (список согласования ищется только по имени, см. shared/listTitles.ts), - * сам список создаётся SharePoint под произвольным номером, и его адрес нельзя - * вывести из адреса списка данных. Но это лёгкий $select без постраничного - * перебора — не то же самое, что полноценный Search, который эти вызовы - * заменяют (см. searchAcrossApprovalLists). - */ -async function resolveApprovalSearchTarget( - context: WebPartContext, - type: IRequestType, - target: IListTarget -): Promise { - try { - const sp = spForSite(target.siteUrl, context); - const list = sp.web.getList(target.listId); - - const dataTitle = - target.title ?? - ((await list.select('Title')()).Title as string) ?? - ''; - if (!DATA_LIST_SUFFIX.test(dataTitle)) { - return { - kind: 'skip', - type, - target, - reason: `«${dataTitle}» не оканчивается на «— данные» — список согласования не ищем`, - }; - } - const approvalListTitle = `${dataTitle.replace(DATA_LIST_SUFFIX, '')} — согласование`; - const approvalList = sp.web.lists.getByTitle(approvalListTitle); - - const rootFolder = (await approvalList.select('RootFolder/ServerRelativeUrl').expand('RootFolder')()) as { - RootFolder?: { ServerRelativeUrl?: string }; - }; - const relUrl = rootFolder.RootFolder?.ServerRelativeUrl; - if (!relUrl) { - return { kind: 'error', type, target, message: `Список согласования «${approvalListTitle}» не найден` }; - } - // Слеш на конце обязателен: без него префикс …/Lists/List1 совпадает и с - // путями элементов …/Lists/List10 (см. listPathPrefix). - const approvalListAbsUrl = `${siteOrigin(target.siteUrl)}${relUrl.replace(/\/+$/, '')}/`; - - return { kind: 'ok', value: { type, target, approvalList, approvalListTitle, approvalListAbsUrl } }; - } catch (e) { - return { kind: 'error', type, target, message: (e as Error).message }; - } -} - function targetKey(entry: IApprovalSearchTarget): string { return entry.approvalListAbsUrl.toLowerCase(); } @@ -570,16 +602,17 @@ async function fetchRecentApprovalRows( try { const sp = spForSite(siteUrl, context); - // ensureUser — ОДИН раз на сайт. Раньше он вызывался внутри обработки + // ensureUser — один раз на сайт И один раз на сессию: результат кешируется + // (см. shared/approval/siteUser.ts). Раньше он вызывался внутри обработки // каждой партиции, то есть повторялся N раз с тем же результатом, и это - // POST, а не дешёвый GET. + // POST, а не дешёвый GET; теперь его не платят даже повторные загрузки. // // TansuSiteApprover/EMail eq '...' в $filter по Person-полю ненадёжен в // SharePoint REST — поэтому резолвим email в числовой Id (как и везде в // проекте) и фильтруем по TansuSiteApproverId. const claimsLogin = tokenEmail.indexOf('|') >= 0 ? tokenEmail : `i:0#.f|membership|${tokenEmail}`; - const approver = await sp.web.ensureUser(claimsLogin); - const filter = `TansuSiteApproverId eq ${approver.Id} and Modified ge '${cutoffIso}'${statusFilter}`; + const approverId = await ensureUserIdFor(context, siteUrl, claimsLogin); + const filter = `TansuSiteApproverId eq ${approverId} and Modified ge '${cutoffIso}'${statusFilter}`; const [batchedSP, execute] = sp.batched(); // Обработчики навешиваем СРАЗУ при создании промиса, до execute(): иначе @@ -725,7 +758,14 @@ async function hydrateAuthors( rows: IMyRequest[] ): Promise { if (rows.length === 0) return; + return traced('дочитывание автора (REST)', () => hydrateAuthorsInner(context, types, rows)); +} +async function hydrateAuthorsInner( + context: WebPartContext, + types: IRequestType[], + rows: IMyRequest[] +): Promise { const typeById = new Map(); types.forEach((t) => typeById.set(t.id, t)); @@ -848,11 +888,6 @@ async function fetchAllSearchRows( return rows; } -function siteOrigin(siteUrl: string): string { - const match = /^https?:\/\/[^/]+/.exec(siteUrl); - return match ? match[0] : siteUrl; -} - function listAbsUrl(type: IRequestType): string { return `${siteOrigin(type.siteUrl as string)}${type.listId}`; } @@ -1106,11 +1141,14 @@ async function getOwnRowsForType(context: WebPartContext, type: IRequestType): P try { const sp = spForSite(target.siteUrl, context); - const me = await sp.web.currentUser.select('Id')(); + // Кешированный Id (см. shared/approval/siteUser.ts): эта функция зовётся в + // цикле по типам заявок, и без кеша тот же GET /_api/web/currentuser уходил + // бы столько раз, сколько у портала типов. + const myId = await currentUserIdFor(context, target.siteUrl); const rows = (await sp.web .getList(target.listId) - .items.filter(`AuthorId eq ${me.Id}`) + .items.filter(`AuthorId eq ${myId}`) .select('Id', 'RequestUniqueCode', 'Title', 'RequestStatus', 'Created', 'Author/EMail', 'Author/Title') .expand('Author') .orderBy('Created', false) @@ -1158,12 +1196,12 @@ async function getRecentlyChangedOwnRowsForType(context: WebPartContext, type: I try { const sp = spForSite(target.siteUrl, context); - const me = await sp.web.currentUser.select('Id')(); + const myId = await currentUserIdFor(context, target.siteUrl); const cutoffIso = new Date(Date.now() - RECENT_MS).toISOString(); const rows = (await sp.web .getList(target.listId) - .items.filter(`AuthorId eq ${me.Id} and Modified ge '${cutoffIso}'`) + .items.filter(`AuthorId eq ${myId} and Modified ge '${cutoffIso}'`) .select('Id', 'RequestUniqueCode', 'Title', 'RequestStatus', 'Created', 'Author/EMail', 'Author/Title') .expand('Author') .top(OWN_REST_TOP)()) as Array>; @@ -1235,21 +1273,23 @@ export async function searchOwnRequests( if (text) clauses.push(text); const kql = clauses.join(' AND '); - const results = await sp.search({ - Querytext: kql, - RowLimit: PAGE_SIZE, - StartRow: (page - 1) * PAGE_SIZE, - SelectProperties: ['ListItemID', 'Title', 'Path'], - TrimDuplicates: false, - // Свежие сверху (по умолчанию Search сортирует по релевантности, не по дате). - SortList: [{ Property: 'Created', Direction: 1 }], - }); + const results = await traced('Search: свои заявки', () => + sp.search({ + Querytext: kql, + RowLimit: PAGE_SIZE, + StartRow: (page - 1) * PAGE_SIZE, + SelectProperties: ['ListItemID', 'Title', 'Path'], + TrimDuplicates: false, + // Свежие сверху (по умолчанию Search сортирует по релевантности, не по дате). + SortList: [{ Property: 'Created', Direction: 1 }], + }) + ); const totalRows = results.TotalRows || 0; const entries = hitsToEntries(results, types); const byKey = entries.length > 0 - ? await loadByIds(context, entries, 'own') + ? await traced('дочитывание полей по Id (REST)', () => loadByIds(context, entries, 'own')) : new Map(); // Порядок задаёт релевантность поиска, а не порядок ответов REST. const rows = entries.map((e) => byKey.get(requestKey(e.target.listId, e.id))).filter((r): r is IMyRequest => !!r); @@ -1284,21 +1324,23 @@ export async function getOwnRequestsByStatus( if (text) clauses.push(text); const kql = clauses.join(' AND '); - const results = await sp.search({ - Querytext: kql, - RowLimit: PAGE_SIZE, - StartRow: (page - 1) * PAGE_SIZE, - SelectProperties: ['ListItemID', 'Title', 'Path'], - TrimDuplicates: false, - // Свежие сверху — как в searchOwnRequests. - SortList: [{ Property: 'Created', Direction: 1 }], - }); + const results = await traced('Search: свои заявки', () => + sp.search({ + Querytext: kql, + RowLimit: PAGE_SIZE, + StartRow: (page - 1) * PAGE_SIZE, + SelectProperties: ['ListItemID', 'Title', 'Path'], + TrimDuplicates: false, + // Свежие сверху — как в searchOwnRequests. + SortList: [{ Property: 'Created', Direction: 1 }], + }) + ); const totalRows = results.TotalRows || 0; const entries = hitsToEntries(results, types); const byKey = entries.length > 0 - ? await loadByIds(context, entries, 'own') + ? await traced('дочитывание полей по Id (REST)', () => loadByIds(context, entries, 'own')) : new Map(); const searchRows = entries.map((e) => byKey.get(requestKey(e.target.listId, e.id))).filter((r): r is IMyRequest => !!r); @@ -1326,20 +1368,6 @@ export async function getOwnRequestsByStatus( return { rows: merged, totalRows: totalRows + onlyFresh.length }; } -// Список согласования по типу заявки: Title списка данных → Title списка -// согласования по конвенции имён (та же логика, что в finalizeApprovalTarget). -// undefined — список заведён не по конвенции, маршрута для него не найти. -async function approvalListFor( - context: WebPartContext, - target: IListTarget -): Promise { - const sp = spForSite(target.siteUrl, context); - const info = (await sp.web.getList(target.listId).select('Title')()) as { Title?: string }; - const dataTitle = info.Title || ''; - if (!DATA_LIST_SUFFIX.test(dataTitle)) return undefined; - return sp.web.lists.getByTitle(`${dataTitle.replace(DATA_LIST_SUFFIX, '')} — согласование`); -} - // Сколько строк маршрута максимум забираем за раз при дочитывании комментариев: // на странице ≤20 заявок, у каждой обычно единицы шагов — запас многократный. const DECISION_COMMENT_ROW_LIMIT = 2000; @@ -1367,7 +1395,17 @@ export async function hydrateDecisionComments( decisionStatus: string ): Promise { if (rows.length === 0) return; + return traced('комментарии к решению (REST)', () => + hydrateDecisionCommentsInner(context, types, rows, decisionStatus) + ); +} +async function hydrateDecisionCommentsInner( + context: WebPartContext, + types: IRequestType[], + rows: IMyRequest[], + decisionStatus: string +): Promise { const typeById = new Map(); types.forEach((t) => typeById.set(t.id, t)); @@ -1387,11 +1425,26 @@ export async function hydrateDecisionComments( rowsByPartition.set(key, bucket); }); + // Адреса/имена списков согласования — одним резолвом на все партиции сразу и, + // как правило, вовсе без сети: их уже сложил в кеш buildPendingIndex + // (см. shared/approval/approvalListLocator.ts). Раньше здесь был отдельный + // REST за Title списка данных на КАЖДУЮ партицию страницы. + const located = await locateApprovalLists( + context, + Array.from(rowsByPartition.values()).map(({ target }) => ({ + listId: target.listId, + siteUrl: target.siteUrl, + })) + ); + await Promise.all( Array.from(rowsByPartition.values()).map(async ({ target, typeName, rows: typeRows }) => { try { - const approvalList = await approvalListFor(context, target); - if (!approvalList) return; + const lookup = located.get(approvalListKey(target.siteUrl, target.listId)); + if (!lookup || lookup.kind !== 'ok') return; + const approvalList = spForSite(target.siteUrl, context).web.lists.getByTitle( + lookup.value.approvalListTitle + ); // TicketId (OR-цепочка) в скобках обязателен: у OData `and` связывает // сильнее `or», без скобок фильтр разобрался бы как @@ -1523,7 +1576,10 @@ export async function searchOwnAll( traceSearchHits('Свои заявки', kql, types, results, listAbsUrl); const entries = hitsToEntries(results, types); - const byKey = entries.length > 0 ? await loadByIds(context, entries, 'own') : new Map(); + const byKey = + entries.length > 0 + ? await traced('дочитывание полей по Id (REST)', () => loadByIds(context, entries, 'own')) + : new Map(); const rows = entries.map((e) => byKey.get(requestKey(e.target.listId, e.id))).filter((r): r is IMyRequest => !!r); traceResultRows('Свои заявки', types, entries, rows); diff --git a/src/webparts/requestPortal/shared/approval/approvalListLocator.ts b/src/webparts/requestPortal/shared/approval/approvalListLocator.ts new file mode 100644 index 0000000..5435a49 --- /dev/null +++ b/src/webparts/requestPortal/shared/approval/approvalListLocator.ts @@ -0,0 +1,207 @@ +import { WebPartContext } from '@microsoft/sp-webpart-base'; +import { spForSite } from '../pnp/pnpjs'; +import '@pnp/sp/webs'; +import '@pnp/sp/lists'; + +/** + * Где лежит список согласования каждой партиции — Title и абсолютный адрес. + * + * Зачем отдельный модуль. Адрес списка «X — согласование» нельзя вывести из + * адреса списка «X — данные»: approval-route хранит ссылку только на список + * данных, а сам список согласования SharePoint создаёт под произвольным + * номером (…/Lists/List37). Раньше это означало ОТДЕЛЬНЫЙ REST-запрос на + * КАЖДУЮ партицию при каждой загрузке очереди согласования, а партиций с + * архивацией уже больше десятка (трассировка 2026-08-18: 11 партиций). + * + * Здесь то же самое стоит ОДИН запрос на сайт: /_api/web/lists отдаёт Title и + * RootFolder всех списков веба разом, дальше сопоставление идёт в памяти. И + * этот запрос делается один раз за сессию — адрес списка не меняется, пока + * список существует (партиции только добавляются, а переименование Title не + * трогает ServerRelativeUrl). + * + * Кешируются ТОЛЬКО успехи. Промах кешировать нельзя: новая партиция появляется + * асинхронно (см. getCurrentListPartition — он ставит создание следующей пары + * списков в очередь), и закешированное «не найдено» скрывало бы заявки нового + * списка до перезагрузки страницы. + */ + +// Конвенция именования пары списков — та же, что в shared/listTitles.ts и в +// админке approval-route. Меняя здесь, менять и там. +const DATA_LIST_SUFFIX = /\s*—\s*данные\s*$/; + +// Потолок на число списков, которое забираем с сайта за раз. Списков на сайте +// портала примерно два на партицию плюс системные — запас многократный. При +// упоре в потолок пишем предупреждение: молча «не найденная» партиция выглядела +// бы как пропавшие заявки. +const SITE_LISTS_TOP = 5000; + +/** Список данных партиции: откуда читаем и что о нём уже известно бесплатно. */ +export interface IDataListRef { + /** Серверный относительный url списка ДАННЫХ. */ + listId: string; + siteUrl: string; + /** Title списка данных из реестра партиций, если реестр загрузился. */ + title?: string; +} + +export interface IApprovalListLocation { + approvalListTitle: string; + /** Абсолютный префикс пути элементов списка согласования, СО слешем на конце. */ + approvalListAbsUrl: string; +} + +export type ApprovalListLookup = + | { kind: 'ok'; value: IApprovalListLocation } + | { kind: 'skip'; reason: string } + | { kind: 'error'; message: string }; + +/** Ключ и кеша, и возвращаемой карты: партиция однозначна парой сайт+список. */ +export function approvalListKey(siteUrl: string, listId: string): string { + return `${siteUrl}|${listId}`.toLowerCase(); +} + +const cache = new Map(); + +/** Забыть одну партицию — вызывать, когда чтение по закешированному адресу упало. */ +export function forgetApprovalList(siteUrl: string, listId: string): void { + cache.delete(approvalListKey(siteUrl, listId)); +} + +/** Полный сброс — на случай перезагрузки реестра партиций. */ +export function resetApprovalListCache(): void { + cache.clear(); +} + +/** Схема одного списка в ответе /_api/web/lists. */ +interface ISiteListRow { + Title?: string; + RootFolder?: { ServerRelativeUrl?: string }; +} + +/** Title и адреса всех списков одного веба — в обе стороны. */ +interface ISiteListMap { + urlByTitle: Map; + titleByUrl: Map; +} + +export function siteOrigin(siteUrl: string): string { + const match = /^https?:\/\/[^/]+/.exec(siteUrl); + return match ? match[0] : siteUrl; +} + +// Слеш на конце обязателен: без него префикс …/Lists/List1 совпадает и с +// путями элементов …/Lists/List10 (та же причина, что у listPathPrefix +// в features/myRequests/myRequests.service.ts). +function absPrefix(siteUrl: string, serverRelativeUrl: string): string { + return `${siteOrigin(siteUrl)}${serverRelativeUrl.replace(/\/+$/, '')}/`; +} + +async function loadSiteLists(context: WebPartContext, siteUrl: string): Promise { + const sp = spForSite(siteUrl, context); + const rows = (await sp.web.lists + .select('Title', 'RootFolder/ServerRelativeUrl') + .expand('RootFolder') + .top(SITE_LISTS_TOP)()) as ISiteListRow[]; + + if (rows.length === SITE_LISTS_TOP) { + console.warn( + `[Списки согласования] ${siteUrl}: сайт отдал ${SITE_LISTS_TOP} списков — часть партиций может не найтись` + ); + } + + const urlByTitle = new Map(); + const titleByUrl = new Map(); + rows.forEach((r) => { + const title = r.Title || ''; + const relUrl = r.RootFolder?.ServerRelativeUrl || ''; + if (!title || !relUrl) return; + urlByTitle.set(title.toLowerCase(), relUrl); + // По адресу — чтобы узнать Title списка ДАННЫХ, когда реестр партиций не + // загрузился и ref.title пуст. Раньше на это уходил ещё один REST на партицию. + titleByUrl.set(relUrl.replace(/\/+$/, '').toLowerCase(), title); + }); + + return { urlByTitle, titleByUrl }; +} + +function locate(ref: IDataListRef, site: ISiteListMap): ApprovalListLookup { + const dataTitle = ref.title ?? site.titleByUrl.get(ref.listId.replace(/\/+$/, '').toLowerCase()) ?? ''; + if (!dataTitle) { + return { + kind: 'error', + message: `Список данных «${ref.listId}» не найден среди списков сайта ${ref.siteUrl} (удалён или нет прав)` + }; + } + if (!DATA_LIST_SUFFIX.test(dataTitle)) { + return { kind: 'skip', reason: `«${dataTitle}» не оканчивается на «— данные» — список согласования не ищем` }; + } + + const approvalListTitle = `${dataTitle.replace(DATA_LIST_SUFFIX, '')} — согласование`; + const relUrl = site.urlByTitle.get(approvalListTitle.toLowerCase()); + if (!relUrl) { + // Коллекция /_api/web/lists секьюрити-тримится, поэтому «списка нет» и + // «нет прав» здесь неотличимы — говорим про оба случая сразу. + return { + kind: 'error', + message: `Список согласования «${approvalListTitle}» не найден на ${ref.siteUrl} (нет такого списка или нет прав на него)` + }; + } + + return { + kind: 'ok', + value: { approvalListTitle, approvalListAbsUrl: absPrefix(ref.siteUrl, relUrl) } + }; +} + +/** + * Найти списки согласования сразу для всех переданных партиций. + * + * Сеть — не больше одного запроса на САЙТ, и только за теми партициями, которых + * ещё нет в кеше. Отказ по одному сайту не лишает результата партиции других + * сайтов: они резолвятся независимо. + */ +export async function locateApprovalLists( + context: WebPartContext, + refs: IDataListRef[] +): Promise> { + const result = new Map(); + + const missingBySite = new Map(); + refs.forEach((ref) => { + const key = approvalListKey(ref.siteUrl, ref.listId); + const hit = cache.get(key); + if (hit) { + result.set(key, { kind: 'ok', value: hit }); + return; + } + const bucket = missingBySite.get(ref.siteUrl.toLowerCase()) ?? []; + bucket.push(ref); + missingBySite.set(ref.siteUrl.toLowerCase(), bucket); + }); + + await Promise.all( + Array.from(missingBySite.values()).map(async (siteRefs) => { + const siteUrl = siteRefs[0].siteUrl; + let site: ISiteListMap; + try { + site = await loadSiteLists(context, siteUrl); + } catch (e) { + const message = `Не удалось прочитать списки сайта ${siteUrl}: ${(e as Error).message}`; + siteRefs.forEach((ref) => result.set(approvalListKey(ref.siteUrl, ref.listId), { kind: 'error', message })); + return; + } + + siteRefs.forEach((ref) => { + const key = approvalListKey(ref.siteUrl, ref.listId); + const lookup = locate(ref, site); + // Кешируем только успехи: партиция могла быть создана секунду назад и + // ещё не попасть в ответ — закешированный промах спрятал бы её заявки + // до перезагрузки страницы. + if (lookup.kind === 'ok') cache.set(key, lookup.value); + result.set(key, lookup); + }); + }) + ); + + return result; +} diff --git a/src/webparts/requestPortal/shared/approval/approvalRoute.service.ts b/src/webparts/requestPortal/shared/approval/approvalRoute.service.ts index cd44e43..6321ca7 100644 --- a/src/webparts/requestPortal/shared/approval/approvalRoute.service.ts +++ b/src/webparts/requestPortal/shared/approval/approvalRoute.service.ts @@ -87,7 +87,7 @@ export async function getCurrentListPartition( // а не молча повторять один и тот же провал до конца сессии. let requestTypesPromise: Promise | undefined; -async function getRequestTypesCached(context: WebPartContext): Promise { +export async function getRequestTypesCached(context: WebPartContext): Promise { if (!requestTypesPromise) { requestTypesPromise = getRequestTypes(context).catch((e) => { requestTypesPromise = undefined; diff --git a/src/webparts/requestPortal/shared/approval/index.ts b/src/webparts/requestPortal/shared/approval/index.ts index ebb26d8..3fc9d9f 100644 --- a/src/webparts/requestPortal/shared/approval/index.ts +++ b/src/webparts/requestPortal/shared/approval/index.ts @@ -24,6 +24,7 @@ export type { export { getRequestTypes, + getRequestTypesCached, getRequestTypeIdByPageUrl, getListPartitions, getCurrentListPartition, @@ -40,6 +41,9 @@ export { registerCurrentPartition, } from './listPartitions'; export { resolveWriteListId } from './writeListId'; +export { locateApprovalLists, approvalListKey, forgetApprovalList, siteOrigin } from './approvalListLocator'; +export type { IDataListRef, IApprovalListLocation, ApprovalListLookup } from './approvalListLocator'; +export { currentUserIdFor, ensureUserIdFor } from './siteUser'; export { useRequestTypeId } from './useRequestTypeId'; export type { IRequestTypeIdState } from './useRequestTypeId'; export { ApprovalListService } from './approvalList.service'; diff --git a/src/webparts/requestPortal/shared/approval/listPartitions.ts b/src/webparts/requestPortal/shared/approval/listPartitions.ts index f2107f5..4114f95 100644 --- a/src/webparts/requestPortal/shared/approval/listPartitions.ts +++ b/src/webparts/requestPortal/shared/approval/listPartitions.ts @@ -1,5 +1,5 @@ import { WebPartContext } from '@microsoft/sp-webpart-base'; -import { getListPartitions, getRequestTypes } from './approvalRoute.service'; +import { getListPartitions, getRequestTypesCached } from './approvalRoute.service'; import { ICurrentListPartition, IListPartition } from './models/IApprovalRouteApi'; /** @@ -36,7 +36,10 @@ export async function initListPartitions(context: WebPartContext): Promise try { // Серии — по числовому id, а имена списков в формах — по слагу (pageUrl), // поэтому нужен и справочник типов, чтобы связать одно с другим. - const [series, types] = await Promise.all([getListPartitions(context), getRequestTypes(context)]); + // Кешированный вариант: тот же ответ нужен потом и getLinkedTypes («Мои + // заявки»), и useRequestTypeId в каждой форме — здесь он заодно прогревает + // общий кеш, чтобы позже за ним никто не ходил повторно. + const [series, types] = await Promise.all([getListPartitions(context), getRequestTypesCached(context)]); partitionsByTypeId.clear(); typeIdByPageUrl.clear(); diff --git a/src/webparts/requestPortal/shared/approval/siteUser.ts b/src/webparts/requestPortal/shared/approval/siteUser.ts new file mode 100644 index 0000000..3cafabe --- /dev/null +++ b/src/webparts/requestPortal/shared/approval/siteUser.ts @@ -0,0 +1,65 @@ +import { WebPartContext } from '@microsoft/sp-webpart-base'; +import { spForSite } from '../pnp/pnpjs'; +import '@pnp/sp/webs'; +import '@pnp/sp/site-users/web'; + +/** + * Числовой Id пользователя НА КОНКРЕТНОМ САЙТЕ — с кешом на сессию. + * + * Зачем: почти все фильтры SharePoint REST по Person-полям строятся по числовому + * Id (`AuthorId eq N`, `TansuSiteApproverId eq N`) — фильтр по EMail у Person-поля + * ненадёжен. Id свой на каждом сайте, поэтому его приходится резолвить, но + * в пределах сессии он не меняется. + * + * Раньше резолв стоял ВНУТРИ функций, которые вызываются в цикле по типам заявок + * (getOwnRowsForType / getRecentlyChangedOwnRowsForType): при восьми типах это + * восемь одинаковых GET /_api/web/currentuser подряд, и каждый — лишний + * round-trip ПЕРЕД основным запросом. ensureUser в очереди согласования — то же + * самое, только POST. + * + * Ошибку НЕ кешируем: упавший на холодном старте запрос должен повториться, + * а не залипнуть до конца сессии (тот же принцип, что у requestTypesPromise + * в approvalRoute.service.ts). + */ + +const byKey = new Map>(); + +function remember(key: string, load: () => Promise): Promise { + const pending = byKey.get(key); + if (pending) return pending; + + const promise = load().catch((e) => { + byKey.delete(key); + throw e; + }); + byKey.set(key, promise); + return promise; +} + +/** Полный сброс — на случай смены личности (отладочная подмена) или реинициализации. */ +export function resetSiteUserCache(): void { + byKey.clear(); +} + +/** Id ТЕКУЩЕГО (реального) пользователя на сайте. */ +export async function currentUserIdFor(context: WebPartContext, siteUrl: string): Promise { + return remember(`current|${siteUrl.toLowerCase()}`, async () => { + const me = await spForSite(siteUrl, context).web.currentUser.select('Id')(); + return me.Id as number; + }); +} + +/** + * Id пользователя по claims-логину. ensureUser — это POST (он при необходимости + * заводит пользователя в коллекции сайта), поэтому повторять его особенно жаль. + */ +export async function ensureUserIdFor( + context: WebPartContext, + siteUrl: string, + claimsLogin: string +): Promise { + return remember(`ensure|${siteUrl.toLowerCase()}|${claimsLogin.toLowerCase()}`, async () => { + const user = await spForSite(siteUrl, context).web.ensureUser(claimsLogin); + return user.Id; + }); +} diff --git a/src/webparts/requestPortal/shared/perfTrace.ts b/src/webparts/requestPortal/shared/perfTrace.ts new file mode 100644 index 0000000..daca7c6 --- /dev/null +++ b/src/webparts/requestPortal/shared/perfTrace.ts @@ -0,0 +1,196 @@ +/** + * Замер времени ЭТАПОВ загрузки — «на что именно ушло ожидание», а не просто + * «страница долго грузится». + * + * Зачем отдельно от вкладки Network: браузер показывает запросы, но не + * логические этапы. Из списка URL не видно, что десяток одинаковых + * `.../rootfolder` — это резолв адресов списков согласования по партициям, а + * два одинаковых Search-запроса подряд — это один и тот же индекс, собранный + * дважды (счётчиками меню и самой таблицей). Метки дают этот срез. + * + * Включение/выключение БЕЗ пересборки — из консоли браузера: + * window.rpPerf = true // включить + * window.rpPerf = false // выключить + * + * Флаг ЗАПОМИНАЕТСЯ в localStorage: выставил один раз — работает и после + * перезагрузки страницы, и завтра. Разбор производительности обычно идёт + * несколькими заходами с F5 между ними, и переписывать флаг в консоль каждый + * раз — ровно та мелочь, из-за которой замером перестают пользоваться. + * + * Сделано через свойство-аксессор на window, а не через отдельную функцию + * setPerf(): в консоли естественно написать `window.rpPerf = true`, и это + * должно работать. Сеттер кладёт значение в localStorage, геттер отдаёт + * текущее — снаружи флаг выглядит обычным полем. + * + * По умолчанию выключено: предыдущая версия этого модуля (коммит 7bd540e) + * писала в консоль всегда, без флага, и её пришлось снимать целиком вместе со + * всеми обёртками. С флагом обёртки могут жить в коде постоянно — выключенный + * traced() не делает ничего, кроме вызова переданной функции. + */ + +const DEFAULT_PERF: boolean = false; + +const PREFIX = '[Перф]'; + +// Ключ в localStorage совпадает с именем флага — чтобы в DevTools → Application +// его можно было найти и снять, не зная про этот модуль. +const STORAGE_KEY = 'rpPerf'; + +/** Накопленное по одной метке за текущую верхнеуровневую операцию. */ +interface IStat { + calls: number; + totalMs: number; + maxMs: number; +} + +const stats = new Map(); + +// Глубина открытых perfScope. Сводка печатается, когда закрылась ПОСЛЕДНЯЯ +// внешняя операция: этапы почти всегда идут параллельно (Promise.all), и +// печатать таблицу на каждом вложенном завершении значило бы печатать её +// десятками кусков. Если две верхнеуровневые операции наложились по времени +// (типичный случай: счётчики меню и загрузка таблицы стартуют вместе), их +// метки попадут в одну таблицу — это и нужно, там как раз видны дубли. +let openScopes = 0; +let scopeStart = 0; +let scopeLabels: string[] = []; + +// localStorage может быть недоступен (приватный режим, политика браузера, +// iframe с ограничениями) — отладочный флаг не повод падать, поэтому обе +// операции обёрнуты и молча откатываются к значению в памяти. +function readStoredFlag(): boolean | undefined { + try { + const raw = window.localStorage.getItem(STORAGE_KEY); + if (raw === null) return undefined; + return raw === 'true'; + } catch { + return undefined; + } +} + +function writeStoredFlag(value: boolean): void { + try { + window.localStorage.setItem(STORAGE_KEY, value ? 'true' : 'false'); + } catch { + // Значение всё равно применится — просто не переживёт перезагрузку. + } +} + +let enabled: boolean = readStoredFlag() ?? DEFAULT_PERF; + +function perfEnabled(): boolean { + return enabled; +} + +/** + * Вешает window.rpPerf как свойство с геттером и сеттером. + * + * Вызывается один раз при загрузке модуля. Значение, которое кто-то успел + * присвоить ДО этого (например, в снипете, выполненном раньше), не теряем — + * подхватываем и запоминаем. + */ +function installFlag(): void { + const w = window as unknown as { rpPerf?: boolean }; + + const existing = Object.getOwnPropertyDescriptor(w, 'rpPerf'); + if (existing && existing.get) return; + + const preset = typeof w.rpPerf === 'boolean' ? w.rpPerf : undefined; + + Object.defineProperty(w, 'rpPerf', { + configurable: true, + get: (): boolean => enabled, + set: (value: unknown): void => { + enabled = !!value; + writeStoredFlag(enabled); + console.log(`${PREFIX} замер ${enabled ? 'включён' : 'выключен'} (запомнено до отмены)`); + } + }); + + if (preset !== undefined) { + enabled = preset; + writeStoredFlag(preset); + } else if (enabled) { + // Иначе легко забыть, что флаг остался включённым с прошлого раза, и + // потом гадать, откуда в консоли таблицы. + console.log(`${PREFIX} замер включён (запомнен ранее). Выключить: window.rpPerf = false`); + } +} + +installFlag(); + +function record(label: string, ms: number): void { + const stat = stats.get(label) ?? { calls: 0, totalMs: 0, maxMs: 0 }; + stat.calls += 1; + stat.totalMs += ms; + stat.maxMs = Math.max(stat.maxMs, ms); + stats.set(label, stat); +} + +/** + * Обернуть один этап замером. Метку делай осмысленной и повторяемой: одинаковые + * этапы разных партиций должны попадать в ОДНУ строку сводки (число вызовов + * важнее, чем какая именно партиция), поэтому в метку не надо подставлять + * listId — только имя этапа и, при необходимости, тип заявки. + */ +export async function traced(label: string, fn: () => Promise): Promise { + if (!perfEnabled()) return fn(); + + const start = performance.now(); + try { + return await fn(); + } finally { + record(label, performance.now() - start); + } +} + +/** + * Верхнеуровневая операция (открытие вкладки, счётчики меню, открытие заявки). + * Когда она завершается, печатается сводная таблица по всем меткам внутри. + * + * Смотреть надо на ДВА числа сразу: «сумма» показывает, куда ушла работа, а + * «стена» — сколько на самом деле ждал пользователь. Этапы идут параллельно, + * поэтому сумма бывает в разы больше стены, и оптимизировать по одной только + * сумме — значит чинить не то. + */ +export async function perfScope(label: string, fn: () => Promise): Promise { + if (!perfEnabled()) return fn(); + + if (openScopes === 0) { + stats.clear(); + scopeStart = performance.now(); + scopeLabels = []; + } + openScopes += 1; + scopeLabels.push(label); + + try { + return await fn(); + } finally { + openScopes -= 1; + if (openScopes === 0) print(performance.now() - scopeStart); + } +} + +function print(wallMs: number): void { + if (stats.size === 0) return; + + const rows = Array.from(stats.entries()) + .map(([label, s]) => ({ + 'этап': label, + 'вызовов': s.calls, + 'сумма, мс': Math.round(s.totalMs), + 'макс, мс': Math.round(s.maxMs) + })) + .sort((a, b) => b['сумма, мс'] - a['сумма, мс']); + + const total = rows.reduce((acc, r) => acc + r['сумма, мс'], 0); + + console.log( + `${PREFIX} ${scopeLabels.join(' + ')}: стена ${Math.round(wallMs)} мс, сумма по этапам ${total} мс` + ); + console.table(rows); + + stats.clear(); + scopeLabels = []; +} -- 2.34.1 From a4e6e097f3e93c92c52c0db7cee3cc834fb370b9 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Thu, 20 Aug 2026 17:10:54 +0500 Subject: [PATCH 2/6] =?UTF-8?q?=D1=82=D1=80=D0=B5=D0=B9=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/myRequests/myRequests.service.ts | 75 +++- .../requestPortal/shared/perfTrace.ts | 382 ++++++++++++++---- .../requestPortal/shared/pnp/pnpjs.ts | 11 +- 3 files changed, 385 insertions(+), 83 deletions(-) diff --git a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts index 836eacb..58d06d3 100644 --- a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts +++ b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts @@ -439,13 +439,13 @@ async function getNavCountsInner(context: WebPartContext): Promise { const [turn, rework] = await Promise.all([ sharedPendingIndex(context, 'myTurn', MY_TURN_STATUSES), - getOwnRequestsByStatus(context, types, REQUEST_STATUS.returned, 1), + countOwnRequestsByStatus(context, types, REQUEST_STATUS.returned), ]); return { approvals: turn.index.filter((r) => r.myStepStatus === REQUEST_STATUS.onApproval).length, execution: turn.index.filter((r) => r.myStepStatus === REQUEST_STATUS.onExecution).length, - rework: rework.totalRows, + rework, }; } @@ -727,14 +727,23 @@ async function finalizeApprovalTarget( // Заголовок/статус/дата заявки — отдельным запросом к списку ДАННЫХ (см. // комментарий у функции: через Lookup-expand это не отдаётся). + // + // Автор берётся ЗДЕСЬ ЖЕ через expand. Раньше он дочитывался отдельным + // проходом (hydrateAuthors) и только по строкам видимой страницы — чтобы не + // тянуть его на весь индекс. Но экономилась при этом не сеть, а payload: + // запрос к списку данных всё равно делается, и expand не добавляет к нему + // ни одного round-trip'а, тогда как отдельный проход стоил целый — замер + // 2026-08-20 показал 262 мс ровно на критическом пути вкладки + // «На согласовании» (Search → строки очереди → автор). const ticketFilter = ticketIds.map((id) => `Id eq ${id}`).join(' or '); const ticketRows = (await list.items .filter(ticketFilter) - .select('Id', 'RequestUniqueCode', 'Title', 'RequestStatus', 'Created') + .select('Id', 'RequestUniqueCode', 'Title', 'RequestStatus', 'Created', 'Author/EMail', 'Author/Title') + .expand('Author') .top(ticketIds.length)()) as Array>; debug.rows = ticketRows.map((r) => ({ - ...toMyRequest(r, undefined, type, 'approval', target), + ...toMyRequest(r, r.Author as { EMail?: string; Title?: string } | undefined, type, 'approval', target), myStepStatus: stepStatusByTicket.get(r.Id as number), })); @@ -749,16 +758,23 @@ async function finalizeApprovalTarget( } } -// Дочитывает Author/EMail + Author/Title в списке ДАННЫХ каждого типа — -// только по переданным строкам (обычно уже нарезанная страница из ≤20), не по -// всему найденному срезу. Мутирует переданные объекты (проставляет authorEmail/authorName). +// Дочитывает Author/EMail + Author/Title в списке ДАННЫХ каждого типа — только +// по переданным строкам. Мутирует переданные объекты (проставляет +// authorEmail/authorName). +// +// Строки, у которых автор УЖЕ известен, пропускаются: и 'own' (loadByIds), и +// 'approval' (finalizeApprovalTarget) теперь получают его тем же запросом, что +// и остальные поля, поэтому на обычных путях сюда доходить нечему и запрос не +// уходит вовсе. Функция оставлена страховкой на случай источника строк без +// автора — молча показывать пустого инициатора хуже, чем один лишний запрос. async function hydrateAuthors( context: WebPartContext, types: IRequestType[], rows: IMyRequest[] ): Promise { - if (rows.length === 0) return; - return traced('дочитывание автора (REST)', () => hydrateAuthorsInner(context, types, rows)); + const missing = rows.filter((r) => !r.authorName && !r.authorEmail); + if (missing.length === 0) return; + return traced('дочитывание автора (REST)', () => hydrateAuthorsInner(context, types, missing)); } async function hydrateAuthorsInner( @@ -862,6 +878,10 @@ async function fetchAllSearchRows( let startRow = 0; for (;;) { + // Постраничной метки здесь больше нет: замер 2026-08-20 показал, что запрос + // ровно один (847 мс у вызывающего против 846 мс у страницы), то есть + // медленный сам Search, а не перебор StartRow. Вопрос закрыт, а метка + // занимала место в строке сводки, дублируя метку вызывающего. const results = await sp.search({ Querytext: kql, RowLimit: SEARCH_PAGE_SIZE, @@ -1310,6 +1330,36 @@ export async function getOwnRequestsByStatus( status: string, page: number, query: string = '' +): Promise { + return ownRequestsByStatus(context, types, status, page, query, true); +} + +/** + * Только СКОЛЬКО таких заявок — для счётчика «Доработка» в боковом меню. + * + * Отличие от getOwnRequestsByStatus одно: не дочитываются поля найденных заявок + * (loadByIds). Счётчику они не нужны — ему нужен totalRows, — а замер 2026-08-20 + * показал, что этот шаг стоит ~200-300 мс и идёт СТРОГО ПОСЛЕ Search, то есть + * целиком лежит на критическом пути открытия портала. + * + * Условия отбора и довесок по свежести — те же самые, поэтому счётчик и вкладка + * по-прежнему не расходятся (ради чего они и считаются одним кодом). + */ +export async function countOwnRequestsByStatus( + context: WebPartContext, + types: IRequestType[], + status: string +): Promise { + return (await ownRequestsByStatus(context, types, status, 1, '', false)).totalRows; +} + +async function ownRequestsByStatus( + context: WebPartContext, + types: IRequestType[], + status: string, + page: number, + query: string, + withRows: boolean ): Promise { if (types.length === 0) return { rows: [], totalRows: 0 }; @@ -1339,7 +1389,7 @@ export async function getOwnRequestsByStatus( const totalRows = results.TotalRows || 0; const entries = hitsToEntries(results, types); const byKey = - entries.length > 0 + withRows && entries.length > 0 ? await traced('дочитывание полей по Id (REST)', () => loadByIds(context, entries, 'own')) : new Map(); const searchRows = entries.map((e) => byKey.get(requestKey(e.target.listId, e.id))).filter((r): r is IMyRequest => !!r); @@ -1357,7 +1407,10 @@ export async function getOwnRequestsByStatus( .reduce((acc, r) => acc.concat(r), [] as IMyRequest[]) .filter((r) => r.status === status); - const searchKeys = new Set(searchRows.map((r) => rowKey(r))); + // Ключи для дедупа берём из ХИТОВ индекса, а не из дочитанных строк: пара + // «партиция + Id» известна уже по ответу Search, и режиму счётчика (withRows + // = false) не приходится ради этого дочитывать поля. + const searchKeys = new Set(entries.map((e) => requestKey(e.target.listId, e.id))); const onlyFresh = freshRows.filter((r) => !searchKeys.has(rowKey(r))); const merged = onlyFresh diff --git a/src/webparts/requestPortal/shared/perfTrace.ts b/src/webparts/requestPortal/shared/perfTrace.ts index daca7c6..b8f1792 100644 --- a/src/webparts/requestPortal/shared/perfTrace.ts +++ b/src/webparts/requestPortal/shared/perfTrace.ts @@ -1,31 +1,44 @@ +import { TimelinePipe } from '@pnp/core'; +import { Queryable } from '@pnp/queryable'; + /** - * Замер времени ЭТАПОВ загрузки — «на что именно ушло ожидание», а не просто - * «страница долго грузится». + * Замер загрузки: «на что именно ушло ожидание», а не просто «страница долго + * грузится». * - * Зачем отдельно от вкладки Network: браузер показывает запросы, но не - * логические этапы. Из списка URL не видно, что десяток одинаковых - * `.../rootfolder` — это резолв адресов списков согласования по партициям, а - * два одинаковых Search-запроса подряд — это один и тот же индекс, собранный - * дважды (счётчиками меню и самой таблицей). Метки дают этот срез. + * Пишет ДВА среза сразу, и они дополняют друг друга: + * • ЭТАПЫ — логические шаги, размеченные вручную через traced()/perfScope(). + * Из списка URL не видно, что десяток одинаковых `.../rootfolder` — + * это резолв адресов списков согласования по партициям. + * • ЗАПРОСЫ — КАЖДЫЙ HTTP-вызов PnPjs, сгруппированный по виду операции. + * Снимается автоматически (см. PerfObserver ниже, подключён в + * shared/pnp/pnpjs.ts), поэтому покрывает ВСЕ разделы портала — + * включая формы и панель заявки, где ручной разметки нет. + * + * Складывать эти два среза между собой нельзя: запросы лежат ВНУТРИ этапов, + * сумма получилась бы двойной. Поэтому и таблицы две, с отдельными итогами. + * + * Как читать: «ОЖИДАНИЕ» — сколько реально прошло от начала операции до конца, + * то есть ровно то, что чувствует пользователь. Сумма времён отдельных шагов + * всегда БОЛЬШЕ: шаги идут параллельно (Promise.all) и их времена накладываются. + * Поэтому большая сумма у шага сама по себе ещё ничего не значит — важно, лежит + * ли он на последовательной цепочке. Признак: сложил несколько шагов и получил + * примерно ожидание — значит они идут друг за другом, и сокращать надо их. * * Включение/выключение БЕЗ пересборки — из консоли браузера: * window.rpPerf = true // включить * window.rpPerf = false // выключить + * window.rpPerfLast // последняя сводка объектом (скопировать/отдать) * * Флаг ЗАПОМИНАЕТСЯ в localStorage: выставил один раз — работает и после - * перезагрузки страницы, и завтра. Разбор производительности обычно идёт - * несколькими заходами с F5 между ними, и переписывать флаг в консоль каждый - * раз — ровно та мелочь, из-за которой замером перестают пользоваться. + * перезагрузки. Разбор производительности идёт несколькими заходами с F5 между + * ними, и переписывать флаг каждый раз — ровно та мелочь, из-за которой замером + * перестают пользоваться. Сделано свойством-аксессором на window, а не функцией + * setPerf(): в консоли естественно написать `window.rpPerf = true`. * - * Сделано через свойство-аксессор на window, а не через отдельную функцию - * setPerf(): в консоли естественно написать `window.rpPerf = true`, и это - * должно работать. Сеттер кладёт значение в localStorage, геттер отдаёт - * текущее — снаружи флаг выглядит обычным полем. - * - * По умолчанию выключено: предыдущая версия этого модуля (коммит 7bd540e) - * писала в консоль всегда, без флага, и её пришлось снимать целиком вместе со - * всеми обёртками. С флагом обёртки могут жить в коде постоянно — выключенный - * traced() не делает ничего, кроме вызова переданной функции. + * По умолчанию выключено: предыдущая версия модуля (коммит 7bd540e) писала в + * консоль всегда, без флага, и её пришлось снимать целиком вместе со всеми + * обёртками. С флагом обёртки живут в коде постоянно — выключенный traced() не + * делает ничего, кроме вызова переданной функции. */ const DEFAULT_PERF: boolean = false; @@ -36,29 +49,61 @@ const PREFIX = '[Перф]'; // его можно было найти и снять, не зная про этот модуль. const STORAGE_KEY = 'rpPerf'; -/** Накопленное по одной метке за текущую верхнеуровневую операцию. */ +// Через сколько тишины считать операцию законченной и печатать сводку, если её +// никто не обрамил perfScope. Нужно для разделов без ручной разметки (панель +// заявки, формы): там просто идут запросы, и «загрузка закончилась» видно только +// по тому, что новых запросов больше нет. +const IDLE_FLUSH_MS = 900; + +// Сколько самых тяжёлых меток показывать прямо в строке. Больше трёх — и строка +// перестаёт читаться с одного взгляда, а ради этого всё и затевалось; +// остальное смотрится через window.rpPerfDetail(). +const TOP_IN_LINE = 3; + +/** Накопленное по одной метке. */ interface IStat { calls: number; totalMs: number; maxMs: number; } -const stats = new Map(); +/** Накопленное по одной операции за ВСЕ её прогоны — база для среднего. */ +interface IRunHistory { + runs: number; + wallTotalMs: number; + stages: Map; + requests: Map; +} + +// Текущий (ещё не напечатанный) прогон. +const stages = new Map(); +const requests = new Map(); + +// История по операциям — живёт до перезагрузки страницы или window.rpPerfReset(). +const history = new Map(); // Глубина открытых perfScope. Сводка печатается, когда закрылась ПОСЛЕДНЯЯ -// внешняя операция: этапы почти всегда идут параллельно (Promise.all), и -// печатать таблицу на каждом вложенном завершении значило бы печатать её -// десятками кусков. Если две верхнеуровневые операции наложились по времени -// (типичный случай: счётчики меню и загрузка таблицы стартуют вместе), их -// метки попадут в одну таблицу — это и нужно, там как раз видны дубли. +// внешняя операция: шаги почти всегда идут параллельно, и печатать таблицу на +// каждом вложенном завершении значило бы печатать её десятками кусков. Если две +// верхнеуровневые операции наложились (типичный случай: счётчики меню и загрузка +// таблицы стартуют вместе), их метки попадут в одну таблицу — это и нужно, там +// как раз видны дубли. let openScopes = 0; -let scopeStart = 0; let scopeLabels: string[] = []; -// localStorage может быть недоступен (приватный режим, политика браузера, -// iframe с ограничениями) — отладочный флаг не повод падать, поэтому обе -// операции обёрнуты и молча откатываются к значению в памяти. +// Границы текущего окна накопления — для «ОЖИДАНИЯ». Начало ставит открытие +// scope либо первая запись, конец двигает каждая следующая. +let windowStart = 0; +let windowEnd = 0; + +let idleTimer: number | undefined; + +// --------------------------------------------------------------------------- +// Флаг + function readStoredFlag(): boolean | undefined { + // localStorage может быть недоступен (приватный режим, политика браузера, + // ограниченный iframe) — отладочный флаг не повод падать. try { const raw = window.localStorage.getItem(STORAGE_KEY); if (raw === null) return undefined; @@ -82,19 +127,13 @@ function perfEnabled(): boolean { return enabled; } -/** - * Вешает window.rpPerf как свойство с геттером и сеттером. - * - * Вызывается один раз при загрузке модуля. Значение, которое кто-то успел - * присвоить ДО этого (например, в снипете, выполненном раньше), не теряем — - * подхватываем и запоминаем. - */ function installFlag(): void { const w = window as unknown as { rpPerf?: boolean }; const existing = Object.getOwnPropertyDescriptor(w, 'rpPerf'); if (existing && existing.get) return; + // Значение, присвоенное ДО загрузки этого чанка, не теряем. const preset = typeof w.rpPerf === 'boolean' ? w.rpPerf : undefined; Object.defineProperty(w, 'rpPerf', { @@ -111,27 +150,49 @@ function installFlag(): void { enabled = preset; writeStoredFlag(preset); } else if (enabled) { - // Иначе легко забыть, что флаг остался включённым с прошлого раза, и - // потом гадать, откуда в консоли таблицы. + // Иначе легко забыть, что флаг остался включённым с прошлого раза, и потом + // гадать, откуда в консоли таблицы. console.log(`${PREFIX} замер включён (запомнен ранее). Выключить: window.rpPerf = false`); } } installFlag(); -function record(label: string, ms: number): void { - const stat = stats.get(label) ?? { calls: 0, totalMs: 0, maxMs: 0 }; +// --------------------------------------------------------------------------- +// Накопление + +function record(into: Map, label: string, ms: number): void { + const now = performance.now(); + if (windowStart === 0) windowStart = now - ms; + windowEnd = now; + + const stat = into.get(label) ?? { calls: 0, totalMs: 0, maxMs: 0 }; stat.calls += 1; stat.totalMs += ms; stat.maxMs = Math.max(stat.maxMs, ms); - stats.set(label, stat); + into.set(label, stat); + + scheduleIdleFlush(); } +// Разделы без ручной разметки (панель заявки, формы) сводку сами не закрывают — +// закрываем по тишине. Пока открыт perfScope, решает он. +function scheduleIdleFlush(): void { + if (idleTimer !== undefined) window.clearTimeout(idleTimer); + idleTimer = window.setTimeout(() => { + idleTimer = undefined; + if (openScopes === 0) print(); + }, IDLE_FLUSH_MS); +} + +// --------------------------------------------------------------------------- +// Разметка этапов + /** * Обернуть один этап замером. Метку делай осмысленной и повторяемой: одинаковые * этапы разных партиций должны попадать в ОДНУ строку сводки (число вызовов * важнее, чем какая именно партиция), поэтому в метку не надо подставлять - * listId — только имя этапа и, при необходимости, тип заявки. + * listId — только имя этапа. */ export async function traced(label: string, fn: () => Promise): Promise { if (!perfEnabled()) return fn(); @@ -140,26 +201,20 @@ export async function traced(label: string, fn: () => Promise): Promise try { return await fn(); } finally { - record(label, performance.now() - start); + record(stages, label, performance.now() - start); } } /** - * Верхнеуровневая операция (открытие вкладки, счётчики меню, открытие заявки). - * Когда она завершается, печатается сводная таблица по всем меткам внутри. - * - * Смотреть надо на ДВА числа сразу: «сумма» показывает, куда ушла работа, а - * «стена» — сколько на самом деле ждал пользователь. Этапы идут параллельно, - * поэтому сумма бывает в разы больше стены, и оптимизировать по одной только - * сумме — значит чинить не то. + * Верхнеуровневая операция (открытие вкладки, счётчики меню). Когда она + * завершается, печатается сводка по всему, что накопилось внутри. */ export async function perfScope(label: string, fn: () => Promise): Promise { if (!perfEnabled()) return fn(); if (openScopes === 0) { - stats.clear(); - scopeStart = performance.now(); - scopeLabels = []; + reset(); + windowStart = performance.now(); } openScopes += 1; scopeLabels.push(label); @@ -168,29 +223,216 @@ export async function perfScope(label: string, fn: () => Promise): Promise return await fn(); } finally { openScopes -= 1; - if (openScopes === 0) print(performance.now() - scopeStart); + if (openScopes === 0) print(); } } -function print(wallMs: number): void { - if (stats.size === 0) return; +// --------------------------------------------------------------------------- +// Автоматический учёт КАЖДОГО запроса PnPjs - const rows = Array.from(stats.entries()) - .map(([label, s]) => ({ - 'этап': label, - 'вызовов': s.calls, - 'сумма, мс': Math.round(s.totalMs), - 'макс, мс': Math.round(s.maxMs) - })) - .sort((a, b) => b['сумма, мс'] - a['сумма, мс']); +// Вид операции по url — иначе каждая партиция со своим адресом и своим $filter +// даёт отдельную строку и таблица нечитаема. Нужна именно ОПЕРАЦИЯ, а не адрес. +function classify(rawUrl: string): string { + let path: string; + let search: string; + try { + const parsed = new URL(rawUrl); + path = parsed.pathname; + search = parsed.search; + } catch { + return rawUrl; + } - const total = rows.reduce((acc, r) => acc + r['сумма, мс'], 0); + if (/\/_api\/search\/(post)?query/i.test(path)) return 'search/query'; + if (/\/_api\/\$batch/i.test(path)) return '$batch (пакет)'; + if (/\/_api\/contextinfo/i.test(path)) return 'contextinfo (дайджест)'; + + const api = path.split(/\/_api\//i)[1]; + if (!api) return path; + + // Аргументы (getByTitle('…'), getList('/sites/…/Lists/List7')) и id элементов + // схлопываем — иначе каждая партиция даст свою строку. + const op = api.replace(/\((?:[^()]|\([^()]*\))*\)/g, '()').replace(/\/\d+(?=\/|$)/g, '/N'); + + // Резолв адреса списка согласования выделяем отдельно — ради него всё и + // затевалось, и в общей куче «web/lists()» его было бы не видно. + if (/rootfolder/i.test(op) || /rootfolder/i.test(search)) return `адрес списка (RootFolder) — ${op}`; + if (/^web\/lists$/i.test(op)) return 'web/lists (все списки веба)'; + + return op; +} + +interface ITimedQueryable { + __rpStart?: number; +} + +/** + * Behavior PnPjs: засекает время каждого запроса. + * + * Подключается один раз в shared/pnp/pnpjs.ts и за счёт этого покрывает ВСЕ + * разделы портала, а не только размеченные вручную. Когда замер выключен, + * наблюдатели всё равно висят, но не делают ничего дороже чтения флага. + */ +export function PerfObserver(): TimelinePipe { + return (instance: Queryable) => { + instance.on.pre(async function (url, init, result) { + if (perfEnabled()) (this as unknown as ITimedQueryable).__rpStart = performance.now(); + return [url, init, result]; + }); + + instance.on.post(async function (url, result) { + const start = (this as unknown as ITimedQueryable).__rpStart; + if (perfEnabled() && start !== undefined) { + record(requests, classify(url.toString()), performance.now() - start); + } + return [url, result]; + }); + + return instance; + }; +} + +// --------------------------------------------------------------------------- +// Печать + +function reset(): void { + stages.clear(); + requests.clear(); + scopeLabels = []; + windowStart = 0; + windowEnd = 0; +} + +/** + * Ключ, под которым прогоны копятся в историю. + * + * Номер страницы из метки убираем: пролистать вкладку по страницам — самый + * удобный способ набрать несколько замеров одной и той же операции, и усреднять + * их надо ВМЕСТЕ. Иначе каждая страница завела бы свою историю из одного + * прогона, и среднее никогда бы не набралось. + */ +function historyKey(title: string): string { + return title.replace(/\s*стр\.\d+/g, '').trim(); +} + +function mergeInto(target: Map, source: Map): void { + source.forEach((s, label) => { + const t = target.get(label) ?? { calls: 0, totalMs: 0, maxMs: 0 }; + t.calls += s.calls; + t.totalMs += s.totalMs; + t.maxMs = Math.max(t.maxMs, s.maxMs); + target.set(label, t); + }); +} + +function heaviest(source: Map, take: number): string { + return Array.from(source.entries()) + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .slice(0, take) + .map(([label, s]) => `${label} ${Math.round(s.totalMs)}`) + .join(', '); +} + +function totalCalls(source: Map): number { + return Array.from(source.values()).reduce((acc, s) => acc + s.calls, 0); +} + +/** + * Одна строка на загрузку — и всё. + * + * Раньше здесь печатались две таблицы. Их было невозможно ни окинуть взглядом + * при листании страниц, ни скопировать: console.table при переносе из консоли + * превращается в кашу из колонок. Полная раскладка никуда не делась — она за + * window.rpPerfDetail(), и её зовут тогда, когда строка показала, что смотреть + * есть на что. + * + * Среднее в строке важнее текущего значения: одиночный сетевой замер шумит + * настолько, что по нему нельзя сказать, стало лучше или просто повезло. + */ +function print(): void { + if (idleTimer !== undefined) { + window.clearTimeout(idleTimer); + idleTimer = undefined; + } + if (stages.size === 0 && requests.size === 0) return; + + const wallMs = (openScopes > 0 ? performance.now() : windowEnd) - windowStart; + const title = scopeLabels.length > 0 ? scopeLabels.join(' + ') : 'без разметки'; + const key = historyKey(title); + + const hist: IRunHistory = history.get(key) ?? { + runs: 0, + wallTotalMs: 0, + stages: new Map(), + requests: new Map() + }; + hist.runs += 1; + hist.wallTotalMs += wallMs; + mergeInto(hist.stages, stages); + mergeInto(hist.requests, requests); + history.set(key, hist); + + // Дольше всего — по этапам, если разметка есть; иначе по видам запросов + // (разделы без ручной разметки: панель заявки, формы). + const top = heaviest(stages.size > 0 ? stages : requests, TOP_IN_LINE); console.log( - `${PREFIX} ${scopeLabels.join(' + ')}: стена ${Math.round(wallMs)} мс, сумма по этапам ${total} мс` + `${PREFIX} ${key} | ожидание ${Math.round(wallMs)} мс` + + ` (сред. ${Math.round(hist.wallTotalMs / hist.runs)} за ${hist.runs})` + + ` | запросов ${totalCalls(requests)}` + + (top ? ` | дольше всего: ${top}` : '') ); - console.table(rows); - stats.clear(); - scopeLabels = []; + reset(); } + +/** Строки полной раскладки по одной операции — средние за все её прогоны. */ +function detailLines(key: string, hist: IRunHistory): string[] { + const lines = [`${PREFIX} ${key} - прогонов ${hist.runs}, ожидание в среднем ${Math.round(hist.wallTotalMs / hist.runs)} мс`]; + + const dump = (name: string, source: Map): void => { + if (source.size === 0) return; + lines.push(` ${name} (в среднем за прогон)`); + Array.from(source.entries()) + .sort((a, b) => b[1].totalMs - a[1].totalMs) + .forEach(([label, s]) => { + lines.push( + ` ${Math.round(s.totalMs / hist.runs)} мс, вызовов ${(s.calls / hist.runs).toFixed(1)}, макс ${Math.round(s.maxMs)} - ${label}` + ); + }); + }; + + dump('ЭТАПЫ', hist.stages); + dump('ЗАПРОСЫ', hist.requests); + return lines; +} + +/** + * Полная раскладка по всем измеренным операциям — window.rpPerfDetail(). + * + * Возвращает текст, а не только печатает: в консоли Chrome его удобно забрать + * целиком через copy(rpPerfDetail()). + */ +function installDetailApi(): void { + const w = window as unknown as { rpPerfDetail?: () => string; rpPerfReset?: () => void }; + + w.rpPerfDetail = (): string => { + if (history.size === 0) return `${PREFIX} замеров пока нет`; + const text = Array.from(history.entries()) + .reduce((acc, [key, hist]) => acc.concat(detailLines(key, hist)), []) + .join(String.fromCharCode(10)); + console.log(text); + return text; + }; + + // Старые прогоны тянут среднее назад, и «стало быстрее» проявится в нём + // только через несколько заходов — перед сравнением «до/после» серию надо + // обнулять. + w.rpPerfReset = (): void => { + history.clear(); + reset(); + console.log(`${PREFIX} история замеров очищена`); + }; +} + +installDetailApi(); diff --git a/src/webparts/requestPortal/shared/pnp/pnpjs.ts b/src/webparts/requestPortal/shared/pnp/pnpjs.ts index 4480b2b..03533ef 100644 --- a/src/webparts/requestPortal/shared/pnp/pnpjs.ts +++ b/src/webparts/requestPortal/shared/pnp/pnpjs.ts @@ -7,6 +7,13 @@ import '@pnp/sp/site-users/web'; import { BrowserFetchWithRetry } from '@pnp/queryable'; import { WebPartContext } from '@microsoft/sp-webpart-base'; import { DATA_SITE_URL } from '../config'; +import { PerfObserver } from '../perfTrace'; + +// PerfObserver подключён к ОБОИМ клиентам ниже: это единственное место, через +// которое проходит любой SharePoint-запрос портала, поэтому отладочный замер +// (window.rpPerf, см. shared/perfTrace.ts) покрывает сразу все разделы — и +// списки заявок, и формы, и панель заявки, — а не только те, где расставлены +// метки этапов вручную. Выключенный замер не делает ничего дороже чтения флага. // Мягкая ретрай-политика вместо стандартной PnPjs (retries: 3, interval: 200). // Дефолт слишком напорист: при отказе (429 «слишком много запросов», 503) он @@ -25,7 +32,7 @@ let _sp: SPFI | undefined; * списки живут там, где бы ни стоял сам web part. Авторизация берётся из context. */ export function initSP(context: WebPartContext): void { - _sp = spfi(DATA_SITE_URL).using(SPFx(context), BrowserFetchWithRetry(RETRY)); + _sp = spfi(DATA_SITE_URL).using(SPFx(context), BrowserFetchWithRetry(RETRY), PerfObserver()); } /** @@ -36,7 +43,7 @@ export function initSP(context: WebPartContext): void { * отправки (у behavior replace: true по умолчанию). */ export function spForSite(siteUrl: string, context: WebPartContext): SPFI { - return spfi(siteUrl).using(SPFx(context), BrowserFetchWithRetry(RETRY)); + return spfi(siteUrl).using(SPFx(context), BrowserFetchWithRetry(RETRY), PerfObserver()); } /** Получить инициализированный клиент. Бросает, если initSP не был вызван. */ -- 2.34.1 From 9b2ccafc17396e2c65299b8afbb950aad040e0ed Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 21 Aug 2026 09:34:28 +0500 Subject: [PATCH 3/6] =?UTF-8?q?=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/skills/sp-query/SKILL.md | 80 + tools/sp/README.md | 98 + tools/sp/package-lock.json | 7908 ++++++++++++++++++++++++++++++ tools/sp/package.json | 16 + tools/sp/sp.mjs | 251 + 5 files changed, 8353 insertions(+) create mode 100644 .claude/skills/sp-query/SKILL.md create mode 100644 tools/sp/README.md create mode 100644 tools/sp/package-lock.json create mode 100644 tools/sp/package.json create mode 100644 tools/sp/sp.mjs diff --git a/.claude/skills/sp-query/SKILL.md b/.claude/skills/sp-query/SKILL.md new file mode 100644 index 0000000..14a3643 --- /dev/null +++ b/.claude/skills/sp-query/SKILL.md @@ -0,0 +1,80 @@ +--- +name: sp-query +description: Спросить у SharePoint-тенанта реальные данные портала заявок через tools/sp — что отдаёт поисковый индекс (managed properties, Retrievable), сколько партиций и элементов в списках, произвольный KQL или REST. Использовать, когда ответ зависит от состояния тенанта, а не выводится из кода: «отдаётся ли поле через Search», «сколько на самом деле партиций», «почему запрос возвращает пусто», «проверь на живых данных». Не использовать для правки данных. +--- + +# Запросы к SharePoint портала заявок + +## Когда это нужно + +Рассуждать по коду можно долго и неправильно. Есть класс вопросов, где ответ живёт +только в тенанте: + +- отдаёт ли индекс конкретное поле (`Retrievable`) — от этого зависит, можно ли + заменить REST-дочитывание на `SelectProperties`; +- сколько на самом деле партиций у типа заявки и сколько в них элементов (это + переменная `P` во всех оценках числа запросов); +- почему KQL возвращает пусто — не то имя managed property, не тот `Path`, нет прав; +- как выглядит реальное значение поля (`AuthorOWSUSER` — не почта, а `почта | ФИО`). + +Если вопрос из этого списка — не гадай и не проси пользователя лезть в консоль +браузера. Спроси тенант. + +## Порядок действий + +```bash +cd tools/sp +node sp.mjs status # сначала: есть ли вход вообще +``` + +`Logged out` → нужен вход. Он **интерактивный**: команда печатает код, который +пользователь вводит в браузере сам. Запускать `login` в фоне бессмысленно — попроси +пользователя выполнить его и вернуться. + +```bash +node sp.mjs login +``` + +Если ругается на `SP_APP_ID` — регистрация приложения не сделана, отправь пользователя +в `tools/sp/README.md`, раздел «Разовая подготовка» (это разовая задача с admin +consent, сам её не закроешь). + +Дальше: + +| команда | что отвечает | +| --- | --- | +| `node sp.mjs props` | отдаёт ли индекс поля заявки; `<нет в ответе>` / `` = не Retrievable | +| `node sp.mjs lists` | портальные списки, их адреса и размеры — реальное число партиций | +| `node sp.mjs search "" --select a,b --rows N` | тот же запрос, что шлёт портал | +| `node sp.mjs get "/_api/web/..."` | произвольный GET | + +## Что нельзя + +- **Не вводить пароли и секреты.** Вход только device code: код вводит пользователь. + Если предлагаешь `--authType password` или `--secret` — ты делаешь неправильно. +- **Не писать данные.** Инструмент читающий. Любая правка списков — отдельный + разговор с пользователем, а не побочный эффект разбора. +- **Не заводить app-only доступ по сертификату ради Search.** Он для этого не годится: + `/_api/search/query` под app-only-токеном обычно не отдаёт ничего, поиску нужен + пользовательский контекст. + +## Грабли + +**`Queryable` ≠ `Retrievable`.** Разные флаги managed property. То, по чему получается +фильтровать, не обязано приходить в ответе — и наоборот. Доказав одно, не считай +доказанным другое: в этом проекте на этом уже спотыкались. + +**Имена managed properties нестандартные.** Суффикс по типу колонки: `OWSTEXT` (Text), +`OWSCHCS` (Choice), `OWSUSER` (Person/Group). У колонок из группы колонок сайта +добавляется префикс группы — поэтому существует `TansuSiteApproverOWSUSER`, а +«ожидаемого» `ApproverOWSUSER` в схеме нет вовсе. Прежде чем сочинять имя, посмотри +константы `MP_*` в `src/webparts/requestPortal/features/myRequests/myRequests.service.ts`. + +**Пустой результат ≠ свойство не работает.** Сначала проверь `Path` (адрес сайта, +слеш на конце, регистр) и права — в этом проекте вывод «managed property сломана» +однажды оказался артефактом задвоенного сегмента в тестовом `Path`. + +**Замер портала — это другое.** `tools/sp` отвечает на вопросы о данных. Чтобы понять, +куда уходит время в самом портале, есть встроенный замер: `window.rpPerf = true` в +консоли страницы (см. `src/webparts/requestPortal/shared/perfTrace.ts`), детали — +`window.rpPerfDetail()`. diff --git a/tools/sp/README.md b/tools/sp/README.md new file mode 100644 index 0000000..6e24608 --- /dev/null +++ b/tools/sp/README.md @@ -0,0 +1,98 @@ +# `tools/sp` — запросы к SharePoint портала заявок + +Инструмент для **разбора**, а не для правки данных: задать тенанту вопрос и получить +ответ текстом, который можно приложить к задаче. + +Появился из-за производительности: раз за разом нужно было выяснить «а что реально +отдаёт поисковый индекс?», «сколько на самом деле партиций у типа заявки?» — и каждый +раз это была ручная работа со снипетом в консоли браузера и копипастой результата. + +## Что важно понимать про доступ + +**Секреты через инструмент не проходят.** Вход — device code: команда печатает код, +пользователь вводит его в браузере сам, дальше CLI for Microsoft 365 держит токен у +себя. Ни пароля, ни client secret в репозитории и в командах нет. + +**Работаем от имени пользователя, а не приложения.** Это не удобство, а требование: +SharePoint Search REST (`/_api/search/query`) под app-only-токеном обычно не отдаёт +ничего, ему нужен пользовательский контекст. А Search — главное, что здесь измеряют. +Побочный плюс: видно ровно то же, что видит пользователь в портале. + +## Разовая подготовка + +CLI for Microsoft 365 начиная с 7-й версии **не возит собственное приложение** — нужен +свой Entra app. Регистрируется один раз на всю команду. + +1. Entra admin center → **App registrations** → **New registration** + - Name: `Portal Tools CLI` + - Supported account types: **Single tenant** + - Redirect URI: **Public client/native** → `http://localhost` +2. **Authentication** → Advanced settings → **Allow public client flows** → **Yes** + (без этого device code не работает) +3. **API permissions** → Add a permission → **SharePoint** → **Delegated**: + - `AllSites.Read` — чтение списков + - `Sites.Search.All` — поисковый индекс +4. **Grant admin consent** (нужен админ тенанта) +5. Скопировать **Application (client) ID** и **Directory (tenant) ID** + +Дальше — переменные окружения (PowerShell, на сессию): + +```powershell +$env:SP_APP_ID = "" +$env:SP_TENANT = "" +``` + +`SP_SITE` по умолчанию `https://tnsukz.sharepoint.com/sites/requests` — тот же адрес, +что в `src/webparts/requestPortal/shared/config.ts`. Задавать нужно, только если +списки типов заявок живут на другом сайте. + +## Использование + +```bash +cd tools/sp +npm install # один раз + +node sp.mjs login # покажет код — ввести его в браузере +node sp.mjs status +``` + +| команда | зачем | +| --- | --- | +| `node sp.mjs props` | отдаёт ли индекс поля заявки (`Retrievable`) — от этого зависит, можно ли убрать REST-дочитывание в `loadByIds` | +| `node sp.mjs lists` | все портальные списки с размерами и адресами — сколько реально партиций | +| `node sp.mjs search "" --select a,b --rows N` | тот же поисковый запрос, что делает портал | +| `node sp.mjs get "/_api/web/..."` | произвольный GET, когда отдельной команды нет | + +Примеры: + +```bash +# Свои заявки глазами индекса — ровно как их видит searchOwnRequests +node sp.mjs search 'AuthorOWSUSER:"i:0#.f|membership|user@tnsu.kz"' \ + --select ListItemID,Title,RequestStatusOWSCHCS,Created --rows 5 + +# Сколько элементов в каждой партиции +node sp.mjs lists +``` + +## Грабли, на которые уже наступали + +**`Queryable` ≠ `Retrievable`.** У managed property это разные флаги. То, по чему +получается фильтровать, не обязано приходить в ответе. Проверка от 2026-08-17 +подтвердила, что по `RequestStatusOWSCHCS` можно фильтровать, и это НЕ было +доказательством, что его можно получить (проверено отдельно 2026-08-20 — можно). +Команда `props` печатает `<нет в ответе>` и `` разными пометками именно +поэтому: схлопывать их в пустую строку — значит потерять ответ на вопрос. + +**Имена managed properties нестандартные.** Суффикс — по типу исходной колонки: +`OWSTEXT` (Text), `OWSCHCS` (Choice), `OWSUSER` (Person/Group). У колонок из группы +колонок сайта добавляется префикс группы — поэтому «ожидаемого» `ApproverOWSUSER` в +схеме нет, а есть `TansuSiteApproverOWSUSER` (разбирались 2026-08-06). Актуальные +имена — в константах `MP_*` в `features/myRequests/myRequests.service.ts`. + +**`AuthorOWSUSER` — это не почта.** Приходит строкой `почта | ФИО`, а при +claims-логине частей будет четыре. Разбирать сплитом по первому элементу нельзя. + +**POST `postquery`, а не GET `query`.** KQL портала перечисляет через `OR` адреса всех +партиций и в query-строке упирается в предел длины URL. Форма тела взята один в один +из PnPjs (`@pnp/sp/search/query.js`): `{ request: { ...query } }`, массивы плоские, +`odata=nometadata`. Иначе инструмент мерил бы не то, что портал. diff --git a/tools/sp/package-lock.json b/tools/sp/package-lock.json new file mode 100644 index 0000000..7cc6686 --- /dev/null +++ b/tools/sp/package-lock.json @@ -0,0 +1,7908 @@ +{ + "name": "sp", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sp", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@pnp/cli-microsoft365": "^11.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365": { + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/@pnp/cli-microsoft365/-/cli-microsoft365-11.10.0.tgz", + "integrity": "sha512-4qyZ7OZSVg4WDv2GfPCXaFcQtqk8rjsrwb0tYS3Bm+L+dAoiWbkpPcAPhGA7oCd4H0sB5dki7o5NqcP4a6yfTg==", + "hasShrinkwrap": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "^16.11.1", + "@azure/msal-node": "^5.4.0", + "@inquirer/confirm": "^6.1.1", + "@inquirer/input": "^5.1.2", + "@inquirer/select": "^5.2.1", + "@xmldom/xmldom": "^0.9.10", + "adaptive-expressions": "^4.23.3", + "adaptivecards": "^3.0.6", + "adaptivecards-templating": "^2.3.1", + "adm-zip": "^0.5.18", + "applicationinsights": "^3.15.1", + "axios": "^1.18.1", + "chalk": "^5.6.2", + "clipboardy": "^5.3.1", + "configstore": "^8.0.0", + "csv-parse": "^6.2.1", + "csv-stringify": "^6.8.1", + "easy-table": "^1.2.0", + "jmespath": "^0.16.0", + "json-to-ast": "^2.1.0", + "node-forge": "^1.4.0", + "omelette": "^0.4.17", + "open": "^11.0.0", + "semver": "^7.8.5", + "strip-json-comments": "^5.0.3", + "typescript": "^5.9.3", + "update-notifier": "^7.3.1", + "uuid": "^14.0.1", + "yaml": "^2.9.0", + "yargs-parser": "^22.0.0", + "zod": "^4.4.3" + }, + "bin": { + "m365": "dist/index.js", + "m365_chili": "dist/chili/index.js", + "m365_comp": "dist/autocomplete.js", + "microsoft365": "dist/index.js" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@actions/core": { + "version": "3.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@actions/exec": "^3.0.0", + "@actions/http-client": "^4.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@actions/exec": { + "version": "3.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@actions/io": "^3.0.2" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@actions/http-client": { + "version": "4.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^6.23.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@actions/io": { + "version": "3.0.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure-rest/core-client": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.8.0.tgz", + "integrity": "sha512-F1ybHeN+++QhyFCF/ehLUEvrOB6fehPdFBFtGdj0C3B2lpQ9zkPiO5JDgsqc6IfjuUe6b3dAbXK0a7+VgSGfhw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.24.0", + "@azure/core-tracing": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/abort-controller": { + "version": "2.1.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/core-auth": { + "version": "1.10.1", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/core-client": { + "version": "1.10.1", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/core-rest-pipeline": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz", + "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/core-tracing": { + "version": "1.3.1", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/core-util": { + "version": "1.13.1", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/functions": { + "version": "4.11.2", + "license": "MIT", + "dependencies": { + "@azure/functions-extensions-base": "0.2.0", + "cookie": "^0.7.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/functions-extensions-base": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/identity": { + "version": "4.13.1", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/identity/node_modules/open": { + "version": "10.2.0", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/identity/node_modules/wsl-utils": { + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/logger": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry/-/monitor-opentelemetry-1.18.2.tgz", + "integrity": "sha512-Alk3qvFew2HAq+PEe/TPlbIOMOX+VivEDF8U7JfBpkqYVACdw5Ae5TbhS5SnXvREnmO/FmkfGqf8ILgOD77Row==", + "license": "MIT", + "dependencies": { + "@azure-rest/core-client": "^2.5.2", + "@azure/core-auth": "^1.10.1", + "@azure/core-rest-pipeline": "^1.22.2", + "@azure/core-tracing": "^1.2.0", + "@azure/logger": "^1.3.0", + "@azure/monitor-opentelemetry-exporter": "1.0.0-beta.43", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.1.0-beta.1", + "@microsoft/applicationinsights-web-snippet": "^1.2.3", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.219.0", + "@opentelemetry/core": "^2.8.0", + "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation-bunyan": "^0.62.0", + "@opentelemetry/instrumentation-http": "^0.219.0", + "@opentelemetry/instrumentation-mongodb": "^0.70.0", + "@opentelemetry/instrumentation-mysql": "^0.63.0", + "@opentelemetry/instrumentation-pg": "^0.69.0", + "@opentelemetry/instrumentation-redis": "^0.65.0", + "@opentelemetry/instrumentation-winston": "^0.61.0", + "@opentelemetry/resource-detector-azure": "^0.25.0", + "@opentelemetry/resources": "^2.8.0", + "@opentelemetry/sdk-logs": "^0.219.0", + "@opentelemetry/sdk-metrics": "^2.8.0", + "@opentelemetry/sdk-node": "^0.219.0", + "@opentelemetry/sdk-trace-base": "^2.8.0", + "@opentelemetry/sdk-trace-node": "^2.8.0", + "@opentelemetry/semantic-conventions": "^1.40.0", + "@opentelemetry/winston-transport": "^0.27.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry-exporter": { + "version": "1.0.0-beta.43", + "resolved": "https://registry.npmjs.org/@azure/monitor-opentelemetry-exporter/-/monitor-opentelemetry-exporter-1.0.0-beta.43.tgz", + "integrity": "sha512-551lMY6gSf7TO+j15nmQ23+nG9fExb664TH9RDs9zUul1xHt3NUtfsZe90nNhOzMEeg+I1f7zn955dvH5o6yjA==", + "license": "MIT", + "dependencies": { + "@azure-rest/core-client": "^2.5.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.19.0", + "@azure/core-util": "^1.11.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.219.0", + "@opentelemetry/core": "^2.8.0", + "@opentelemetry/resources": "^2.8.0", + "@opentelemetry/sdk-logs": "^0.219.0", + "@opentelemetry/sdk-metrics": "^2.8.0", + "@opentelemetry/sdk-trace-base": "^2.8.0", + "@opentelemetry/semantic-conventions": "^1.40.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/api-logs": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz", + "integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/resources": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", + "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry-exporter/node_modules/@opentelemetry/sdk-logs": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.219.0.tgz", + "integrity": "sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/@azure/opentelemetry-instrumentation-azure-sdk": { + "version": "1.1.0-beta.1", + "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.1.0-beta.1.tgz", + "integrity": "sha512-FJCHWlMA//XMs5rxf1z/mN0gaWPbJhlAVw5l4IxKhEtZX2bgNERlqbdBWXNjjkok+nTh0UcKqV5G0rrCupo+4A==", + "license": "MIT", + "dependencies": { + "@azure/core-tracing": "^1.2.0", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/sdk-trace-web": "^2.0.0", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/api-logs": { + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", + "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/instrumentation": { + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", + "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.211.0", + "import-in-the-middle": "^2.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/@opentelemetry/api-logs": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz", + "integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/@opentelemetry/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/@opentelemetry/resources": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", + "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/@opentelemetry/sdk-logs": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.219.0.tgz", + "integrity": "sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/monitor-opentelemetry/node_modules/import-in-the-middle": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", + "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/msal-browser": { + "version": "5.11.0", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.6.2" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/msal-browser/node_modules/@azure/msal-common": { + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.6.2.tgz", + "integrity": "sha512-hQjjsekAjB00cM1EmatWJlzhEoK2Qhz7Rj5gvM6tYf8iL7RM3tkxlpU9fG0+ofkulzg9AEEA6dIEnSmDr5ZqUA==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/msal-common": { + "version": "16.11.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.11.1.tgz", + "integrity": "sha512-yPohvMwWLv1XnaWnIUyKUh8CvcVChCGqG/VluGwfGmaAfrZTNt5yQ+sIs462Sgw6+e2K83KGmMJ860p73ZSCrw==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/msal-node": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.4.0.tgz", + "integrity": "sha512-6EZEParwHRlnSSIikw8FNAnAzwmh71uhveUXdPNFeZFviJ9SH+rwFiurhjzXqICYTrpm3E+dj693QOwfPbJXAQ==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.11.1", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/opentelemetry-instrumentation-azure-sdk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0.tgz", + "integrity": "sha512-Y8rZOIMXQY/GwNRL+uLVuwIn9aEa/KnnggyYUmFxC1MigmRJCNH5NxMmxKSpddXF9SW6Z1ijRd6Pptd2A5OhGw==", + "license": "MIT", + "dependencies": { + "@azure/core-tracing": "^1.2.0", + "@azure/logger": "^1.0.0", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.211.0", + "@opentelemetry/sdk-trace-web": "^2.0.0", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/api-logs": { + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", + "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@opentelemetry/instrumentation": { + "version": "0.211.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", + "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.211.0", + "import-in-the-middle": "^2.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/import-in-the-middle": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", + "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", + "license": "Apache-2.0", + "dependencies": { + "acorn": "^8.15.0", + "acorn-import-attributes": "^1.9.5", + "cjs-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/config-array": { + "version": "0.23.5", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/config-array/node_modules/balanced-match": { + "version": "4.0.4", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "5.0.6", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "10.2.5", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/core": { + "version": "1.2.1", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/js": { + "version": "10.0.1", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/object-schema": { + "version": "3.0.5", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@grpc/grpc-js": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.4.tgz", + "integrity": "sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@humanfs/core": { + "version": "0.19.1", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@humanfs/node": { + "version": "0.16.7", + "extraneous": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@inquirer/ansi": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz", + "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@inquirer/confirm": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz", + "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@inquirer/core": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@inquirer/figures": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz", + "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@inquirer/input": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-5.1.2.tgz", + "integrity": "sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==", + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@inquirer/select": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.2.1.tgz", + "integrity": "sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==", + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.7", + "@inquirer/core": "^11.2.1", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@inquirer/type": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz", + "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==", + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "extraneous": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@microsoft/applicationinsights-web-snippet": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-snippet/-/applicationinsights-web-snippet-1.2.3.tgz", + "integrity": "sha512-59ex4x1/PabGQIg+o0GKG5olqAJYBvMOiXec/9HCD3hK2y36YMWT0ivq5mequvtS5+21kco3SOnMB6QyScLPIA==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@microsoft/microsoft-graph-types": { + "version": "2.43.1", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@microsoft/recognizers-text-data-types-timex-expression": { + "version": "1.3.1", + "license": "MIT", + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@nodable/entities": { + "version": "2.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/api": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/api-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.218.0.tgz", + "integrity": "sha512-fmEWp5kXlGEc3i/lR698Hz41DfGyN4Tbe4g7L1AxSc7fF8Xeh/FQ9Quqpa9dVA413Q1Ad43QOLzU4JoXgbFPWw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/configuration": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.218.0.tgz", + "integrity": "sha512-W8wIz7H2R1pufR5jfjb3gU2XkMpm2x/7b1RJcsuzvd70Il/rWWE+g5/Od7hQKrxRTSrTrOWlru101PWXz5I1EQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "yaml": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/configuration/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/context-async-hooks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.7.1.tgz", + "integrity": "sha512-OPFBYuXEn1E4ja3Y6eeA7O+ZnLBNcXTV5Cgsn1VaqBZ6hC5FnpZPLBNme1LJY8ZtF4aOujPKFoeWN4ik487KuQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/core": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.9.0.tgz", + "integrity": "sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-grpc": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.218.0.tgz", + "integrity": "sha512-hoxrNH1l/Xy6F9WTJ5IK+6j1r9nQFlPOmrnTlhYHTySdunfXLmUCPv3bQtKYntxag9h3wLYBZQ2HI6FOx+BT2g==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/sdk-logs": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.218.0.tgz", + "integrity": "sha512-Qx+4rpVHzgg89dawcWRHyt+XRXeLnhFz/qBtvggmjkcgPUdr+NAB0/u/eIPA8yAeJV0J80Vz43JZCh/XFvZFGw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/sdk-logs": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-proto": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.218.0.tgz", + "integrity": "sha512-1/noQNsp9gXD75HPzgjBrcF1+XTtry7pFAUfxVEJgg7mPv2AawKQuYkhMmJ8qjxz4Ubc3Y8bwvfxevXsKTq4cg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-logs-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.218.0.tgz", + "integrity": "sha512-YapQ9vNMX0NSZF6LK5pWAFfjpJleV2O9uYWfYGeb/5F1Kb9rPGK8tZDMJFa/sOksgdFuflDvYuA0B4qjDB4fjQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.218.0", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-grpc/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.218.0.tgz", + "integrity": "sha512-bV7d2OuMpZu2+gAaxUAhzfZ0h3WVZk8ETQUEE3DNSntbTaMpuITjtm8I0rNyHFdm7Ax57K6ty7SgFXlBmOLIvQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-proto": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.218.0.tgz", + "integrity": "sha512-ubLddKjWULhla9YZRCj/rTBeppjJYE4e9w0icx5mTu3eFhWjQzbV75NYjXuIlEG+NJsBl6d+sTFw5Qu+oej4oQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/exporter-metrics-otlp-http": "0.218.0", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-metrics-otlp-proto/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-prometheus": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.218.0.tgz", + "integrity": "sha512-RT5oEyu1kddZJ1vt7/BUo5wV+P7hpNAESsR3dUd3+8deHuX7gWNoCOZn+SfDT+hJHlIJ5h/AxiCLXIrutswDJg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-prometheus/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.218.0.tgz", + "integrity": "sha512-3fXxVQEj9TNAFaCi79JeFKfeLd0sDtInaR3gaZDVlzNSPHtz8PZuCV34JKWjD4XXzT20IdMe8IpX6mRVNDA4Tw==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-grpc/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.218.0.tgz", + "integrity": "sha512-8dqezsmPhtKitIK/eTipZhYl9EX2/gNQ5zUMhaz3uxEURwfkNf8IPvo6yNfrzbxdtpAOybS/+h7wmIWYqFSpiw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-proto": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.218.0.tgz", + "integrity": "sha512-r1Msf8SNLRmwh9J6XQ5uh82D7CdDWMNHnPB7LAVHjzut0TkSeKc5KcIvr4SvHvfk/xwN5gxC+VLKQ1k0o8PSPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-trace-otlp-proto/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-zipkin": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.7.1.tgz", + "integrity": "sha512-mfsD9bKAxcKrh5+y08TPodvClBO0CznBE3p79YAGnO81WI4LrdsGA65T53e4iTSbCalW4WaUpkbeJcbpyIUHfg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/exporter-zipkin/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.219.0.tgz", + "integrity": "sha512-X5t7I8GyIO9rmGHwoedZLREpQqrF1WW2nxzNNym6HOKpFiE+rvqV3ngC0xcZVO2YwIGf3KKmRdWrYwdwz3H9RQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.219.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-bunyan": { + "version": "0.62.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.62.0.tgz", + "integrity": "sha512-4PjSfgyzKzhnnczUkbh8ogDclQqInBsSK0J3BQX4wRueK7cCGUyQghLQYqPW1TbzrXUfM0kNtrnu1D5Xbk3LEA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.217.0", + "@opentelemetry/instrumentation": "^0.217.0", + "@types/bunyan": "1.8.11" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/api-logs": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", + "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/instrumentation": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.217.0.tgz", + "integrity": "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.217.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-http": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.219.0.tgz", + "integrity": "sha512-nNt1fqpyah/OKjNHdEOu8xLwISppRU2qJuF8aR+fCcftVwdFkPgtworBLA+TI1HU2iF508jcQBF2gerWczJAXg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.8.0", + "@opentelemetry/instrumentation": "0.219.0", + "@opentelemetry/semantic-conventions": "^1.29.0", + "forwarded-parse": "2.1.2" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-mongodb": { + "version": "0.70.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.70.0.tgz", + "integrity": "sha512-9DeBe6SiOkguL5uRyj20ma0I1lXgW0EYc9mUnABlMzbxYC3b7sczUFSj37eWaLzZNzTVQNr+U8UZZP2LvlZ+vw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.217.0", + "@opentelemetry/semantic-conventions": "^1.33.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/api-logs": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", + "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/instrumentation": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.217.0.tgz", + "integrity": "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.217.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-mysql": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.63.0.tgz", + "integrity": "sha512-IFzZiZtLgQEzK74OHT921n1ARRUyRWWe/5oa5nezuwQF4XzLEXIqTP+vthrZ4QJc1qIkRLgPROVDcjz20gEKew==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.217.0", + "@opentelemetry/semantic-conventions": "^1.33.0", + "@types/mysql": "2.15.27" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/api-logs": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", + "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/instrumentation": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.217.0.tgz", + "integrity": "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.217.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-pg": { + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.69.0.tgz", + "integrity": "sha512-5tHz2AOyuYaWJePxybooIHPlV8v1EPBwrgFlfuuGXBV/EFnnQME+KEPJ9u9e3oe2aKd0gi8CEkTzZ61VwuWo5g==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/instrumentation": "^0.217.0", + "@opentelemetry/semantic-conventions": "^1.34.0", + "@opentelemetry/sql-common": "^0.41.2", + "@types/pg": "8.15.6", + "@types/pg-pool": "2.0.7" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/api-logs": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", + "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/instrumentation": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.217.0.tgz", + "integrity": "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.217.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-redis": { + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.65.0.tgz", + "integrity": "sha512-NwEhtBXwIKvcPSEGyYqjWaUZB1vfda1aVbEOTjhtBSbNDsxSXnGF1/xlwBB2yYZtLC4oYzlc9FRS7wX//X4LTg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.217.0", + "@opentelemetry/redis-common": "^0.38.3", + "@opentelemetry/semantic-conventions": "^1.27.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/api-logs": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", + "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/instrumentation": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.217.0.tgz", + "integrity": "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.217.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-winston": { + "version": "0.61.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.61.0.tgz", + "integrity": "sha512-H600rWjFPFXK0UDGmWucEcbylXKI9i+7i2g8LGSkYqU44PCw09xFhKwv4VPTvyGmcEsCIlckbIKj/GvYy0UeEw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.217.0", + "@opentelemetry/instrumentation": "^0.217.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/api-logs": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", + "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/instrumentation": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.217.0.tgz", + "integrity": "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.217.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/instrumentation/node_modules/@opentelemetry/api-logs": { + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz", + "integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.218.0.tgz", + "integrity": "sha512-ZwqpkNL5W7RyGJPDZ9g06DvKp8KFTWPJPN12anpMQYSKpTSU0z3EIZuPq9vPGpS8siFyOqDYDAuCwlNO9FqgbA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-transformer": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.218.0.tgz", + "integrity": "sha512-H/lCGJ536N98VpYJOaWTQOkv4Dx6TnmStK6Rqfu1W7KkFbPAx04hjdYEMZF/YbnHzPUSIK4kM6OE2GKGBTpV9A==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.14.3", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-grpc-exporter-base/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.218.0.tgz", + "integrity": "sha512-CFaKH87WAzjuJ4awowTTLzUvMfaRfiOFG5+qm5S5ncyalRtN4ecQ+YmuANJSCrVPuvZFEkUgKhBPBndxi3rHsQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/propagator-b3": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.7.1.tgz", + "integrity": "sha512-RJid6E2CKyeGfKBzXKF21ejabGMHypFkPAh3qZ+NvI+SGjuIye79t3PmiqcDgtRzdKH6ynXzbfslQ8DfpRUg2A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/propagator-b3/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/propagator-jaeger": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.7.1.tgz", + "integrity": "sha512-KMjVBHzP4N60bOzxja76M1F1hZZ43lGPga5ix+mkv9+kk1nx9SbkxSvJsMbuVUxdPQmsPTqGShmhN8ulrMOg6Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/propagator-jaeger/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/redis-common": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.3.tgz", + "integrity": "sha512-VCghU1JYs/4gP6Gqf/xro9MEsZ7LrMv2uONVsaESKL38ZOB9BqnI98FfS23wjMnHlpuE+TTaWSoAVNpTwYXzjw==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/resource-detector-azure": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.25.0.tgz", + "integrity": "sha512-e/NRDC3W2NYfxXJIto38wF1qgEyWpelaKp6QPIHGn320EknobnHCI7gUuoOAB6J2EJaW9ewNTIoDWDhO3AbdKA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0", + "@opentelemetry/resources": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.37.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/resources": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.9.0.tgz", + "integrity": "sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.218.0.tgz", + "integrity": "sha512-QvnNdugatFTVCJXH0Mcu7GOOJSylA9j127kIezOE4YwTI4YbowRons2K4WZTv5FMS8T4q9P0NdaRHdkSmeAIag==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.9.0.tgz", + "integrity": "sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-node": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.218.0.tgz", + "integrity": "sha512-tPMjHrLV5gsfNdYqoRHjeGbCAZBXXD9c1Qo/2ut7VwnUABDNh76xNxrT0SEhkIIJuCN45bbN1vZnYL1gY0IkOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/configuration": "0.218.0", + "@opentelemetry/context-async-hooks": "2.7.1", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/exporter-logs-otlp-grpc": "0.218.0", + "@opentelemetry/exporter-logs-otlp-http": "0.218.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.218.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.218.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.218.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.218.0", + "@opentelemetry/exporter-prometheus": "0.218.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.218.0", + "@opentelemetry/exporter-trace-otlp-http": "0.218.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.218.0", + "@opentelemetry/exporter-zipkin": "2.7.1", + "@opentelemetry/instrumentation": "0.218.0", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/propagator-b3": "2.7.1", + "@opentelemetry/propagator-jaeger": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1", + "@opentelemetry/sdk-trace-node": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/core": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.218.0.tgz", + "integrity": "sha512-mIZil8Es+sYDK5m+DQiwAwF57F14TF2YlEqvIjZ/RQWcxDBwRGsKfdK2Tv65OU9meQKCMzSIFS9mxAcnAb6Bkg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/resources": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.7.1.tgz", + "integrity": "sha512-pCpQxU68lV+I9s9svqMyVu5iHdDDUnqUpSxqwyCU8A9ejEsSnMPCbearwsUO4yk08ZJzAIUCFuReMdVQvHrdvg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.7.1", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-trace": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.9.0.tgz", + "integrity": "sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.9.0.tgz", + "integrity": "sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/sdk-trace": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-trace-node": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.9.0.tgz", + "integrity": "sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/context-async-hooks": "2.9.0", + "@opentelemetry/core": "2.9.0", + "@opentelemetry/sdk-trace-base": "2.9.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-trace-node/node_modules/@opentelemetry/context-async-hooks": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.9.0.tgz", + "integrity": "sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w==", + "license": "Apache-2.0", + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sdk-trace-web": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-web/-/sdk-trace-web-2.9.0.tgz", + "integrity": "sha512-LS4XlzOK3e6YYdt84m15AmRR04121rKipmifi4XFZToH1h75f7F2bzHLbB1NMgIEYJ0jSKYm4VsK5mws/5kfTQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.9.0", + "@opentelemetry/sdk-trace-base": "2.9.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/sql-common": { + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", + "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "^2.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/winston-transport": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/winston-transport/-/winston-transport-0.27.0.tgz", + "integrity": "sha512-Imeoqm9CqxSJRo6INwtOn1Kx2XXYZzkyrH6q2teIDJ7ZypKx7DRf23l5ylQZ3vYibkcCLIANbzpeMqCNPp4CEw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "^0.217.0", + "winston-transport": "4.*" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@opentelemetry/winston-transport/node_modules/@opentelemetry/api-logs": { + "version": "0.217.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", + "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@pnpm/config.env-replace": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@pnpm/npm-conf": { + "version": "2.3.1", + "license": "MIT", + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/inquire": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.2.tgz", + "integrity": "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@sinonjs/commons": { + "version": "3.0.1", + "extraneous": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@sinonjs/fake-timers": { + "version": "15.4.0", + "extraneous": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@sinonjs/samsam": { + "version": "10.0.2", + "extraneous": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@stylistic/eslint-plugin": { + "version": "5.10.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.56.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": "^9.0.0 || ^10.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/adm-zip": { + "version": "0.5.8", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/atob-lite": { + "version": "2.0.2", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/btoa-lite": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/configstore": { + "version": "6.0.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/esrecurse": { + "version": "4.3.1", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/estree": { + "version": "1.0.8", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/jmespath": { + "version": "0.15.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/json-schema": { + "version": "7.0.15", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/json-to-ast": { + "version": "2.1.4", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/lru-cache": { + "version": "5.1.1", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/mocha": { + "version": "10.0.10", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/mysql": { + "version": "2.15.27", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", + "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/node-forge": { + "version": "1.3.14", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/omelette": { + "version": "0.4.5", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/pg": { + "version": "8.15.6", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", + "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^2.2.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/pg-pool": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", + "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", + "license": "MIT", + "dependencies": { + "@types/pg": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/semver": { + "version": "7.7.1", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/sinon": { + "version": "21.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@types/sinonjs__fake-timers": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/sinonjs__fake-timers": { + "version": "15.0.1", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/update-notifier": { + "version": "6.0.8", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@types/configstore": "*", + "boxen": "^7.1.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/uuid": { + "version": "10.0.0", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/xmldom": { + "version": "0.1.34", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@types/yargs-parser": { + "version": "21.0.3", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.63.0.tgz", + "integrity": "sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.63.0", + "@typescript-eslint/type-utils": "8.63.0", + "@typescript-eslint/utils": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.63.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/parser": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", + "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/scope-manager": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.63.0.tgz", + "integrity": "sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/type-utils": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.63.0.tgz", + "integrity": "sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/typescript-estree": "8.63.0", + "@typescript-eslint/utils": "8.63.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz", + "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.63.0", + "@typescript-eslint/types": "^8.63.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz", + "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz", + "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.63.0", + "@typescript-eslint/tsconfig-utils": "8.63.0", + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/types": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.63.0.tgz", + "integrity": "sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/typescript-estree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/utils": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.63.0.tgz", + "integrity": "sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.63.0", + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/typescript-estree": "8.63.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.63.0.tgz", + "integrity": "sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.63.0", + "@typescript-eslint/types": "^8.63.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz", + "integrity": "sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz", + "integrity": "sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.63.0", + "@typescript-eslint/tsconfig-utils": "8.63.0", + "@typescript-eslint/types": "8.63.0", + "@typescript-eslint/visitor-keys": "8.63.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.63.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.63.0.tgz", + "integrity": "sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.63.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@typespec/ts-http-runtime": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.7.tgz", + "integrity": "sha512-JVUD8X2tfDMWjcjLs4yVxxVrS8yR5vnh386GAXT9Qj79nBxxXSaHFQZg5FweLmT8HlPQ3kii6noUB+Z9RN7DvQ==", + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/acorn": { + "version": "8.16.0", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/acorn-jsx": { + "version": "5.3.2", + "extraneous": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/adaptive-expressions": { + "version": "4.23.3", + "license": "MIT", + "dependencies": { + "@microsoft/recognizers-text-data-types-timex-expression": "~1.3.1", + "@types/atob-lite": "^2.0.2", + "@types/btoa-lite": "^1.0.2", + "@types/lru-cache": "^5.1.1", + "@types/xmldom": "^0.1.34", + "@xmldom/xmldom": "^0.8.6", + "antlr4ts": "0.5.0-alpha.4", + "atob-lite": "^2.0.0", + "big-integer": "^1.6.52", + "btoa-lite": "^1.0.0", + "d3-format": "^3.1.0", + "dayjs": "^1.11.13", + "fast-xml-parser": "^4.4.1", + "jspath": "^0.4.0", + "lodash": "^4.17.21", + "lru-cache": "^5.1.1", + "uuid": "^10.0.0", + "xpath": "^0.0.34" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/adaptive-expressions/node_modules/@xmldom/xmldom": { + "version": "0.8.13", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/adaptive-expressions/node_modules/uuid": { + "version": "11.1.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/adaptivecards": { + "version": "3.0.6", + "license": "MIT", + "peerDependencies": { + "swiper": "^12.1.2" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/adaptivecards-templating": { + "version": "2.3.1", + "license": "MIT", + "peerDependencies": { + "adaptive-expressions": "^4.11.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/adm-zip": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz", + "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/agent-base": { + "version": "7.1.4", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ajv": { + "version": "6.14.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ansi-align": { + "version": "3.0.1", + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/applicationinsights": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-3.15.1.tgz", + "integrity": "sha512-uIk4ju/fmAtkiB9e5B1WG9+iTqExdX/XdcbNX3pcVDpTaIG96PAj0gsBU2nWxurPukxlKvKIwNvqlQymeqkv7A==", + "license": "MIT", + "dependencies": { + "@azure/core-auth": "^1.9.0", + "@azure/functions": "^4.11.2", + "@azure/identity": "^4.13.1", + "@azure/monitor-opentelemetry": "^1.18.1", + "@azure/monitor-opentelemetry-exporter": ">=1.0.0-beta.41 <1.0.0-c || ^1.0.0", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.7", + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.218.0", + "@opentelemetry/core": "^2.7.1", + "@opentelemetry/exporter-logs-otlp-http": "^0.218.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.218.0", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.218.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", + "@opentelemetry/otlp-exporter-base": "^0.218.0", + "@opentelemetry/resources": "^2.7.1", + "@opentelemetry/sdk-logs": "^0.218.0", + "@opentelemetry/sdk-metrics": "^2.7.1", + "@opentelemetry/sdk-trace-base": "^2.7.1", + "@opentelemetry/sdk-trace-node": "^2.7.1", + "@opentelemetry/semantic-conventions": "^1.38.0", + "diagnostic-channel": "1.1.1", + "diagnostic-channel-publishers": "1.0.8" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/argparse": { + "version": "2.0.1", + "extraneous": true, + "license": "Python-2.0" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/asynckit": { + "version": "0.4.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/atob-lite": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/atomically": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "stubborn-fs": "^2.0.0", + "when-exit": "^2.1.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/axios": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/balanced-match": { + "version": "1.0.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/big-integer": { + "version": "1.6.52", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/boxen": { + "version": "7.1.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.3", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/boxen/node_modules/wrap-ansi": { + "version": "8.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/brace-expansion": { + "version": "2.0.3", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/browser-stdout": { + "version": "1.3.1", + "extraneous": true, + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/btoa-lite": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "license": "BSD-3-Clause" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/buffer-from": { + "version": "1.1.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/bundle-name": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/c8": { + "version": "11.0.0", + "extraneous": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^8.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": "20 || >=22" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/c8/node_modules/yargs-parser": { + "version": "21.1.1", + "extraneous": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/camelcase": { + "version": "7.0.1", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/chalk": { + "version": "5.6.2", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/chokidar": { + "version": "4.0.3", + "extraneous": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cjs-module-lexer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cli-boxes": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cli-width": { + "version": "4.1.0", + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/clipboard-image": { + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "run-jxa": "^3.0.0" + }, + "bin": { + "clipboard-image": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/clipboardy": { + "version": "5.3.1", + "license": "MIT", + "dependencies": { + "clipboard-image": "^0.1.0", + "execa": "^9.6.1", + "is-wayland": "^0.1.0", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0", + "powershell-utils": "^0.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/clipboardy/node_modules/powershell-utils": { + "version": "0.2.0", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cliui": { + "version": "8.0.1", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/clone": { + "version": "1.0.4", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/code-error-fragment": { + "version": "0.0.230", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/combined-stream": { + "version": "1.0.8", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/config-chain": { + "version": "1.1.13", + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/config-chain/node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/configstore": { + "version": "8.0.0", + "license": "BSD-2-Clause", + "dependencies": { + "atomically": "^2.1.0", + "dot-prop": "^10.1.0", + "graceful-fs": "^4.2.11", + "is-safe-filename": "^0.1.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/convert-source-map": { + "version": "2.0.0", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cookie": { + "version": "0.7.2", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/cross-spawn": { + "version": "7.0.6", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/crypto-random-string": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/csv-parse": { + "version": "6.2.1", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/csv-stringify": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.8.1.tgz", + "integrity": "sha512-tZ6X6TKQyQgCo5OptXcyAbfN1pwmoxEqELPQ7KFazNErx7kiVsDK8o+VYRXhfMl4N9vvOOLXuioquR2MeP847A==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/d3-format": { + "version": "3.1.0", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/dayjs": { + "version": "1.11.19", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/decamelize": { + "version": "4.0.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/deep-extend": { + "version": "0.6.0", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/deep-is": { + "version": "0.1.4", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/default-browser": { + "version": "5.4.0", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/default-browser-id": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/defaults": { + "version": "1.0.4", + "license": "MIT", + "optional": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/define-lazy-prop": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/delayed-stream": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/diagnostic-channel": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/diagnostic-channel-publishers": { + "version": "1.0.8", + "license": "MIT", + "peerDependencies": { + "diagnostic-channel": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/diff": { + "version": "8.0.4", + "extraneous": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/dot-prop": { + "version": "10.1.0", + "license": "MIT", + "dependencies": { + "type-fest": "^5.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/dot-prop/node_modules/type-fest": { + "version": "5.4.4", + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/dunder-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/duplexer": { + "version": "0.1.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eastasianwidth": { + "version": "0.2.0", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/easy-table": { + "version": "1.2.0", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "optionalDependencies": { + "wcwidth": "^1.0.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/emoji-regex": { + "version": "9.2.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/es-define-property": { + "version": "1.0.1", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/es-errors": { + "version": "1.3.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/es-object-atoms": { + "version": "1.1.1", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/es-set-tostringtag": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/escape-goat": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/escape-string-regexp": { + "version": "4.0.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", + "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", + "extraneous": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint-plugin-mocha": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-11.3.0.tgz", + "integrity": "sha512-anENwrIwmdvunmmssjMn5a4nTd+mYMkqBlwjksxOECcIThLNhefWJIiTWY7pY/arMQFjNwHQjVOZb6pQ9PrLjg==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.1", + "globals": "^15.14.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint-plugin-mocha/node_modules/globals": { + "version": "15.15.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint-scope": { + "version": "9.1.2", + "extraneous": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint/node_modules/balanced-match": { + "version": "4.0.4", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint/node_modules/brace-expansion": { + "version": "5.0.6", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "extraneous": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint/node_modules/espree": { + "version": "11.2.0", + "extraneous": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/eslint/node_modules/minimatch": { + "version": "10.2.4", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/espree": { + "version": "10.4.0", + "extraneous": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/esquery": { + "version": "1.7.0", + "extraneous": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/esrecurse": { + "version": "4.3.0", + "extraneous": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/estraverse": { + "version": "5.3.0", + "extraneous": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/esutils": { + "version": "2.0.3", + "extraneous": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/event-stream": { + "version": "3.3.4", + "extraneous": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/execa": { + "version": "9.6.1", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-deep-equal": { + "version": "3.1.3", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-levenshtein": { + "version": "2.0.6", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-string-width": { + "version": "3.0.2", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-wrap-ansi": { + "version": "0.2.0", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-xml-builder": { + "version": "1.2.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fast-xml-parser": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.8.0.tgz", + "integrity": "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.2.0", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.3.0", + "xml-naming": "^0.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/figures": { + "version": "6.1.0", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/file-entry-cache": { + "version": "8.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/find-up": { + "version": "5.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/flat": { + "version": "5.0.2", + "extraneous": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/flat-cache": { + "version": "4.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/flatted": { + "version": "3.4.2", + "extraneous": true, + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/follow-redirects": { + "version": "1.16.0", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/foreground-child": { + "version": "3.3.1", + "extraneous": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/form-data": { + "version": "4.0.5", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/forwarded-parse": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", + "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/from": { + "version": "0.1.7", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/function-bind": { + "version": "1.1.2", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/get-caller-file": { + "version": "2.0.5", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/get-east-asian-width": { + "version": "1.4.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/get-intrinsic": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/get-proto": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/get-stream": { + "version": "9.0.1", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/glob": { + "version": "10.5.0", + "extraneous": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/glob-parent": { + "version": "6.0.2", + "extraneous": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/global-directory": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/globals": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/gopd": { + "version": "1.2.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/graceful-fs": { + "version": "4.2.11", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/grapheme-splitter": { + "version": "1.0.4", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/has-flag": { + "version": "4.0.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/has-symbols": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/has-tostringtag": { + "version": "1.0.2", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/hasown": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/he": { + "version": "1.2.0", + "extraneous": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/html-escaper": { + "version": "2.0.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/http-proxy-agent": { + "version": "7.0.2", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/https-proxy-agent": { + "version": "7.0.6", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/human-signals": { + "version": "8.0.1", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ignore": { + "version": "7.0.5", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/import-in-the-middle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.1.tgz", + "integrity": "sha512-0rymlHSFLwZ0ixx8DaQkoIyZojJPY2a0K2nEYslhKJ6jIYO/m0IcCb7iQsFPmS7WmKwISZiIrv5Icstrw/CmqA==", + "license": "Apache-2.0", + "dependencies": { + "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^2.2.0", + "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/imurmurhash": { + "version": "0.1.4", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ini": { + "version": "4.1.1", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-docker": { + "version": "3.0.0", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-extglob": { + "version": "2.1.1", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-glob": { + "version": "4.0.3", + "extraneous": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-in-ci": { + "version": "1.0.0", + "license": "MIT", + "bin": { + "is-in-ci": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-in-ssh": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-inside-container": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-installed-globally": { + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1", + "is-path-inside": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-installed-globally/node_modules/is-path-inside": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-npm": { + "version": "6.1.0", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-path-inside": { + "version": "3.0.3", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-plain-obj": { + "version": "4.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-safe-filename": { + "version": "0.1.1", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-stream": { + "version": "4.0.1", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-unicode-supported": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-wayland": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is-wsl": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/is64bit": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/isexe": { + "version": "2.0.0", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "extraneous": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/istanbul-lib-report": { + "version": "3.0.1", + "extraneous": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/istanbul-reports": { + "version": "3.2.0", + "extraneous": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/jackspeak": { + "version": "3.4.3", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/jmespath": { + "version": "0.16.0", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/js-yaml": { + "version": "4.1.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/json-buffer": { + "version": "3.0.1", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/json-schema-traverse": { + "version": "0.4.1", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/json-to-ast": { + "version": "2.1.0", + "license": "MIT", + "dependencies": { + "code-error-fragment": "0.0.230", + "grapheme-splitter": "^1.0.4" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/jsonwebtoken": { + "version": "9.0.2", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/jspath": { + "version": "0.4.0", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/jwa": { + "version": "1.4.2", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/jws": { + "version": "3.2.3", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.2", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/keyv": { + "version": "4.5.4", + "extraneous": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ky": { + "version": "1.14.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/latest-version": { + "version": "9.0.0", + "license": "MIT", + "dependencies": { + "package-json": "^10.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/levn": { + "version": "0.4.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/locate-path": { + "version": "6.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.includes": { + "version": "4.3.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.isboolean": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.isinteger": { + "version": "4.0.4", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.isnumber": { + "version": "3.0.3", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.isplainobject": { + "version": "4.0.6", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.isstring": { + "version": "4.0.1", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lodash.once": { + "version": "4.1.1", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/log-symbols": { + "version": "4.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/lru-cache": { + "version": "5.1.1", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/macos-version": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/make-dir": { + "version": "4.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/map-stream": { + "version": "0.1.0", + "extraneous": true + }, + "node_modules/@pnp/cli-microsoft365/node_modules/math-intrinsics": { + "version": "1.1.0", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/merge-stream": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mime-db": { + "version": "1.52.0", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mime-types": { + "version": "2.1.35", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mimic-fn": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/minimatch": { + "version": "9.0.9", + "extraneous": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/minimist": { + "version": "1.2.8", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/minipass": { + "version": "7.1.3", + "extraneous": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mocha": { + "version": "11.7.6", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.6.tgz", + "integrity": "sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "browser-stdout": "^1.3.1", + "chokidar": "^4.0.1", + "debug": "^4.3.5", + "diff": "^7.0.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^10.4.5", + "he": "^1.2.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^9.0.5", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mocha/node_modules/strip-json-comments": { + "version": "3.1.1", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mocha/node_modules/yargs-parser": { + "version": "21.1.1", + "extraneous": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/module-details-from-path": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", + "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/mute-stream": { + "version": "3.0.0", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/natural-compare": { + "version": "1.4.0", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/node-cleanup": { + "version": "2.1.2", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/node-forge": { + "version": "1.4.0", + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/npm-run-path": { + "version": "6.0.0", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/omelette": { + "version": "0.4.17", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/onetime": { + "version": "5.1.2", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/open": { + "version": "11.0.0", + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/optionator": { + "version": "0.9.4", + "extraneous": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/p-limit": { + "version": "3.1.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/p-locate": { + "version": "5.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/package-json": { + "version": "10.0.1", + "license": "MIT", + "dependencies": { + "ky": "^1.2.0", + "registry-auth-token": "^5.0.2", + "registry-url": "^6.0.1", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/package-json-from-dist": { + "version": "1.0.1", + "extraneous": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/parse-ms": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/path-exists": { + "version": "4.0.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/path-expression-matcher": { + "version": "1.5.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/path-key": { + "version": "3.1.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/path-scurry": { + "version": "1.11.1", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "extraneous": true, + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/pause-stream": { + "version": "0.0.11", + "extraneous": true, + "license": [ + "MIT", + "Apache2" + ], + "dependencies": { + "through": "~2.3" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/pg-protocol": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", + "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/picocolors": { + "version": "1.1.1", + "extraneous": true, + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/picomatch": { + "version": "4.0.4", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/postgres-bytea": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", + "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/powershell-utils": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/prelude-ls": { + "version": "1.2.1", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/pretty-ms": { + "version": "9.3.0", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/proto-list": { + "version": "1.2.4", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/protobufjs": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.0.tgz", + "integrity": "sha512-LtESOsMPTZgyYtwxhvdgdjGL0HmXEaRA/hVD6sol4zA60hVXXXP/SGmxnqDbgGE8gy7pYex7cym+5vYPcmaXBQ==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/proxy-from-env": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ps-tree": { + "version": "1.2.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "event-stream": "=3.3.4" + }, + "bin": { + "ps-tree": "bin/ps-tree.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/punycode": { + "version": "2.3.1", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/pupa": { + "version": "3.3.0", + "license": "MIT", + "dependencies": { + "escape-goat": "^4.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rc": { + "version": "1.2.8", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/readdirp": { + "version": "4.1.2", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/registry-auth-token": { + "version": "5.1.0", + "license": "MIT", + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/registry-url": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/require-directory": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/require-in-the-middle": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", + "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "module-details-from-path": "^1.0.3" + }, + "engines": { + "node": ">=9.3.0 || >=8.10.0 <9.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rimraf": { + "version": "6.1.3", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rimraf/node_modules/balanced-match": { + "version": "4.0.4", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rimraf/node_modules/brace-expansion": { + "version": "5.0.6", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rimraf/node_modules/glob": { + "version": "13.0.6", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rimraf/node_modules/lru-cache": { + "version": "11.2.6", + "extraneous": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rimraf/node_modules/minimatch": { + "version": "10.2.4", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.2", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-applescript": { + "version": "7.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "macos-version": "^6.0.0", + "subsume": "^4.0.0", + "type-fest": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa/node_modules/execa": { + "version": "5.1.1", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa/node_modules/get-stream": { + "version": "6.0.1", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa/node_modules/human-signals": { + "version": "2.1.0", + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa/node_modules/is-stream": { + "version": "2.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa/node_modules/npm-run-path": { + "version": "4.0.1", + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa/node_modules/signal-exit": { + "version": "3.0.7", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/run-jxa/node_modules/strip-final-newline": { + "version": "2.0.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/serialize-javascript": { + "version": "7.0.5", + "extraneous": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/shebang-command": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/shebang-regex": { + "version": "3.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/signal-exit": { + "version": "4.1.0", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/sinon": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.0.0.tgz", + "integrity": "sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==", + "extraneous": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^15.4.0", + "@sinonjs/samsam": "^10.0.2", + "diff": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/sinon/node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "extraneous": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/source-map": { + "version": "0.6.1", + "extraneous": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/source-map-support": { + "version": "0.5.21", + "extraneous": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/split": { + "version": "0.3.3", + "extraneous": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/stream-combiner": { + "version": "0.0.4", + "extraneous": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/string-argv": { + "version": "0.3.2", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/string-width": { + "version": "5.1.2", + "extraneous": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "extraneous": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/strip-ansi": { + "version": "7.1.2", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/strip-final-newline": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/strip-json-comments": { + "version": "5.0.3", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/strnum": { + "version": "2.3.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/stubborn-fs": { + "version": "2.0.0", + "license": "MIT", + "dependencies": { + "stubborn-utils": "^1.0.1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/stubborn-utils": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/subsume": { + "version": "4.0.0", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^5.0.0", + "unique-string": "^3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/subsume/node_modules/escape-string-regexp": { + "version": "5.0.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/supports-color": { + "version": "7.2.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/swiper": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-12.2.0.tgz", + "integrity": "sha512-K8uXsBZU6ME97Ia3xbBge8IRCnR1lOmIILzvY/jGVic7dSTQ530s3uO8RvXbPUtkkXLWIwmZLRPbtDxRWVAFdg==", + "funding": [ + { + "type": "custom", + "url": "https://sponsors.nolimits4web.com" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nolimits4web" + } + ], + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/system-architecture": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/tagged-tag": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/test-exclude": { + "version": "8.0.0", + "extraneous": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^13.0.6", + "minimatch": "^10.2.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/test-exclude/node_modules/balanced-match": { + "version": "4.0.4", + "extraneous": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/test-exclude/node_modules/brace-expansion": { + "version": "5.0.6", + "extraneous": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/test-exclude/node_modules/glob": { + "version": "13.0.6", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/test-exclude/node_modules/lru-cache": { + "version": "11.2.6", + "extraneous": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/test-exclude/node_modules/minimatch": { + "version": "10.2.4", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/test-exclude/node_modules/path-scurry": { + "version": "2.0.2", + "extraneous": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/through": { + "version": "2.3.8", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/ts-api-utils": { + "version": "2.5.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/tsc-watch": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/tsc-watch/-/tsc-watch-7.2.1.tgz", + "integrity": "sha512-FcntBnz5bXGQ/q265acfDzU7xi6R/sgwMokI+Iu293n14Wu8ovAtcwqNfU9vv/DM1THlnmmxatSSyGOvWsSblg==", + "extraneous": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6", + "node-cleanup": "^2.1.2", + "ps-tree": "^1.2.0", + "string-argv": "^0.3.2" + }, + "bin": { + "tsc-watch": "dist/lib/tsc-watch.js" + }, + "engines": { + "node": ">=12.12.0" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/tslib": { + "version": "2.8.1", + "license": "0BSD" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/tunnel": { + "version": "0.0.6", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/type-check": { + "version": "0.4.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/type-detect": { + "version": "4.0.8", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/type-fest": { + "version": "2.19.0", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/undici": { + "version": "6.24.1", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/unicorn-magic": { + "version": "0.3.0", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/unique-string": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier": { + "version": "7.3.1", + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^8.0.1", + "chalk": "^5.3.0", + "configstore": "^7.0.0", + "is-in-ci": "^1.0.0", + "is-installed-globally": "^1.0.0", + "is-npm": "^6.0.0", + "latest-version": "^9.0.0", + "pupa": "^3.1.0", + "semver": "^7.6.3", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/boxen": { + "version": "8.0.1", + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^8.0.0", + "chalk": "^5.3.0", + "cli-boxes": "^3.0.0", + "string-width": "^7.2.0", + "type-fest": "^4.21.0", + "widest-line": "^5.0.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/camelcase": { + "version": "8.0.0", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/configstore": { + "version": "7.1.0", + "license": "BSD-2-Clause", + "dependencies": { + "atomically": "^2.0.3", + "dot-prop": "^9.0.0", + "graceful-fs": "^4.2.11", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/dot-prop": { + "version": "9.0.0", + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/emoji-regex": { + "version": "10.6.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/string-width": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/type-fest": { + "version": "4.41.0", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/update-notifier/node_modules/widest-line": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/uri-js": { + "version": "4.4.1", + "extraneous": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/uuid": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.1.tgz", + "integrity": "sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/v8-to-istanbul": { + "version": "9.3.0", + "extraneous": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wcwidth": { + "version": "1.0.1", + "license": "MIT", + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/when-exit": { + "version": "2.1.5", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/which": { + "version": "2.0.2", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/widest-line": { + "version": "4.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/word-wrap": { + "version": "1.2.5", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/workerpool": { + "version": "9.3.4", + "extraneous": true, + "license": "Apache-2.0" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi": { + "version": "9.0.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "extraneous": true, + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "extraneous": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "extraneous": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "10.6.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/wsl-utils": { + "version": "0.3.1", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/xdg-basedir": { + "version": "5.1.0", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/xml-naming": { + "version": "0.1.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/xpath": { + "version": "0.0.34", + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/y18n": { + "version": "5.0.8", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yallist": { + "version": "3.1.1", + "license": "ISC" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs": { + "version": "17.7.2", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs-parser": { + "version": "22.0.0", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs-unparser": { + "version": "2.0.0", + "extraneous": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "license": "MIT" + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yocto-queue": { + "version": "0.1.0", + "extraneous": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/yoctocolors": { + "version": "2.1.2", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pnp/cli-microsoft365/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/tools/sp/package.json b/tools/sp/package.json new file mode 100644 index 0000000..7d9801f --- /dev/null +++ b/tools/sp/package.json @@ -0,0 +1,16 @@ +{ + "name": "sp-tools", + "version": "0.1.0", + "private": true, + "description": "Запросы к SharePoint портала заявок для разбора: Search, managed properties, партиции списков", + "type": "module", + "scripts": { + "login": "node sp.mjs login", + "status": "node sp.mjs status", + "props": "node sp.mjs props", + "lists": "node sp.mjs lists" + }, + "dependencies": { + "@pnp/cli-microsoft365": "^11.10.0" + } +} diff --git a/tools/sp/sp.mjs b/tools/sp/sp.mjs new file mode 100644 index 0000000..49bd5f4 --- /dev/null +++ b/tools/sp/sp.mjs @@ -0,0 +1,251 @@ +#!/usr/bin/env node +/** + * sp — тонкая обёртка над CLI for Microsoft 365 для РАЗБОРА, а не для правки данных. + * + * Зачем вообще: разбирая производительность портала, всё время нужно задать + * SharePoint один и тот же вопрос («а что реально отдаёт индекс?», «сколько + * партиций у типа?») и получить ответ текстом, который можно приложить к задаче. + * Через браузерную консоль это ручная работа с копипастой; здесь — команда. + * + * Почему device code, а не секрет приложения: пароль и секрет через этот + * инструмент не проходят вообще. `sp login` печатает код, пользователь вводит + * его в браузере сам, CLI дальше держит токен у себя. В репозитории не оседает + * ничего секретного. + * + * Почему сырой REST через токен, а не готовые команды `m365 spo`: группа + * `m365 search` — это Microsoft Search (Graph), совсем другой API. Нам нужен + * ИМЕННО SharePoint Search REST с SelectProperties — тот же, что дёргает портал + * (см. myRequests.service.ts). Тело запроса взято один в один из PnPjs + * (@pnp/sp/search/query.js): `{ request: { ...query } }`, массивы плоские, + * odata=nometadata. Иначе инструмент мерил бы не то, что портал. + */ +import { execFileSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const HERE = dirname(fileURLToPath(import.meta.url)); +const CLI = join(HERE, 'node_modules', '@pnp', 'cli-microsoft365', 'dist', 'index.js'); + +// Значения по умолчанию — те же, что в src/webparts/requestPortal/shared/config.ts. +// Переопределяются переменными окружения, чтобы не править файл ради другого стенда. +const SITE = process.env.SP_SITE || 'https://tnsukz.sharepoint.com/sites/requests'; +const APP_ID = process.env.SP_APP_ID || process.env.CLIMICROSOFT365_ENTRAAPPID || ''; +const TENANT = process.env.SP_TENANT || process.env.CLIMICROSOFT365_TENANT || 'common'; + +/** Хост тенанта — именно он, а не адрес сайта, является ресурсом для токена. */ +const RESOURCE = new URL(SITE).origin; + +function m365(args, { inherit = false } = {}) { + return execFileSync(process.execPath, [CLI, ...args], { + encoding: 'utf8', + stdio: inherit ? 'inherit' : ['ignore', 'pipe', 'pipe'], + maxBuffer: 64 * 1024 * 1024 + }); +} + +function fail(message) { + console.error(`[sp] ${message}`); + process.exit(1); +} + +let cachedToken; +function token() { + if (cachedToken) return cachedToken; + try { + cachedToken = m365(['util', 'accesstoken', 'get', '-r', RESOURCE, '-o', 'text']).trim(); + } catch (e) { + fail( + `не удалось получить токен для ${RESOURCE}.\n` + + ` Скорее всего нет входа — выполни: node sp.mjs login\n` + + ` ${String(e.stderr || e.message).trim().split('\n')[0]}` + ); + } + if (!cachedToken) fail(`пустой токен для ${RESOURCE} — выполни: node sp.mjs login`); + return cachedToken; +} + +async function spFetch(path, init = {}) { + const url = path.startsWith('http') ? path : `${SITE}${path.startsWith('/') ? '' : '/'}${path}`; + const res = await fetch(url, { + ...init, + headers: { + authorization: `Bearer ${token()}`, + accept: 'application/json;odata=nometadata', + ...(init.body ? { 'content-type': 'application/json;odata=nometadata' } : {}), + ...(init.headers || {}) + } + }); + const text = await res.text(); + if (!res.ok) fail(`${res.status} ${res.statusText}\n${text.slice(0, 800)}`); + try { + return JSON.parse(text); + } catch { + return text; + } +} + +// --------------------------------------------------------------------------- +// Команды + +async function cmdLogin() { + if (!APP_ID) { + fail( + 'не задан SP_APP_ID.\n' + + ' CLI for Microsoft 365 с 7-й версии не возит собственное приложение —\n' + + ' нужен свой Entra app (см. README.md, раздел «Разовая подготовка»).' + ); + } + console.log(`[sp] вход в ${TENANT} приложением ${APP_ID}. Код появится ниже — введи его в браузере.`); + m365(['login', '--appId', APP_ID, '--tenant', TENANT, '--authType', 'deviceCode'], { inherit: true }); +} + +function cmdStatus() { + console.log(m365(['status', '-o', 'text']).trim()); + console.log(`[sp] сайт: ${SITE}`); + console.log(`[sp] ресурс токена: ${RESOURCE}`); +} + +function cmdLogout() { + m365(['logout'], { inherit: true }); +} + +/** + * SharePoint Search — тот же вызов, что делает портал. + * + * POST /_api/search/postquery, а не GET /query: KQL портала перечисляет через OR + * адреса ВСЕХ партиций, и в query-строке это упирается в предел длины URL. + */ +async function cmdSearch(argv) { + const kql = argv._[0]; + if (!kql) fail('нужен KQL: node sp.mjs search "<запрос>" [--select a,b] [--rows N]'); + + const select = (argv.select || 'ListItemID,Path,Title').split(',').map((s) => s.trim()).filter(Boolean); + const rows = Number(argv.rows || 10); + + const data = await spFetch('/_api/search/postquery', { + method: 'POST', + body: JSON.stringify({ + request: { Querytext: kql, RowLimit: rows, SelectProperties: select, TrimDuplicates: false } + }) + }); + + const relevant = data?.PrimaryQueryResult?.RelevantResults; + const rawRows = relevant?.Table?.Rows ?? []; + const parsed = rawRows.map((r) => { + const cells = r.Cells?.results ?? r.Cells ?? []; + return select.reduce((acc, key) => { + const cell = cells.filter((c) => c.Key === key)[0]; + // Различаем «свойство не Retrievable» и «значение пустое» — ради этого + // различия инструмент и писался, схлопывать их в '' нельзя. + acc[key] = !cell ? '<нет в ответе>' : cell.Value === null ? '' : cell.Value; + return acc; + }, {}); + }); + + console.log(`[sp] в индексе ${relevant?.TotalRows ?? 0}, показано ${parsed.length}`); + console.log(JSON.stringify(parsed, null, 1)); +} + +/** Произвольный GET к REST — для всего, чего нет отдельной командой. */ +async function cmdGet(argv) { + const path = argv._[0]; + if (!path) fail('нужен путь: node sp.mjs get "/_api/web/lists?$select=Title"'); + console.log(JSON.stringify(await spFetch(path), null, 1)); +} + +/** + * Все списки сайта с адресами — тем же запросом, которым портал резолвит + * списки согласования (см. shared/approval/approvalListLocator.ts). Заодно + * показывает, сколько на самом деле партиций: это и есть P из оценок. + */ +async function cmdLists() { + const data = await spFetch( + "/_api/web/lists?$select=Title,ItemCount,Hidden,RootFolder/ServerRelativeUrl&$expand=RootFolder&$top=5000" + ); + const all = data.value ?? []; + const portal = all.filter((l) => !l.Hidden && /—\s*(данные|согласование)\s*$/.test(l.Title || '')); + + portal + .sort((a, b) => String(a.Title).localeCompare(String(b.Title))) + .forEach((l) => { + console.log(`${String(l.ItemCount).padStart(6)} ${l.RootFolder?.ServerRelativeUrl} ${l.Title}`); + }); + + const data_ = portal.filter((l) => /данные\s*$/.test(l.Title)).length; + console.log(`[sp] всего списков на сайте ${all.length}; портальных ${portal.length} (данные: ${data_})`); +} + +/** + * Отдаёт ли индекс поля заявки (Retrievable) — от этого зависит, можно ли + * заменить REST-дочитывание (loadByIds) на SelectProperties самого Search. + * + * Queryable и Retrievable у managed property — РАЗНЫЕ флаги: то, по чему можно + * фильтровать, не обязано приходить в ответе. Отбираем по самому + * RequestStatusOWSCHCS, поэтому найденное гарантированно является заявками + * портала, а не случайными элементами сайта. + */ +async function cmdProps(argv) { + const props = ( + argv.select || + 'ListItemID,Path,Title,Created,Author,AuthorOWSUSER,RequestStatusOWSCHCS,RequestUniqueCodeOWSTEXT,LastModifiedTime' + ) + .split(',') + .map((s) => s.trim()); + + const statuses = ['На согласовании', 'Черновик', 'Согласовано', 'Возвращена на доработку']; + const kql = `Path:"${SITE}/*" AND (${statuses.map((s) => `RequestStatusOWSCHCS:"${s}"`).join(' OR ')})`; + + await cmdSearch({ _: [kql], select: props.join(','), rows: argv.rows || 3 }); + console.log( + '[sp] <нет в ответе> или у свойства = оно НЕ Retrievable, для этого поля REST остаётся.' + ); +} + +// --------------------------------------------------------------------------- + +function parseArgv(args) { + const out = { _: [] }; + for (let i = 0; i < args.length; i += 1) { + const a = args[i]; + if (a.startsWith('--')) { + const [key, inline] = a.slice(2).split('='); + out[key] = inline ?? (args[i + 1] && !args[i + 1].startsWith('--') ? args[(i += 1)] : true); + } else { + out._.push(a); + } + } + return out; +} + +const USAGE = `sp — запросы к SharePoint портала заявок (только чтение) + + node sp.mjs login вход по device code (код вводишь в браузере сам) + node sp.mjs status кто залогинен и куда смотрим + node sp.mjs logout + + node sp.mjs search "" [--select a,b,c] [--rows N] + node sp.mjs props [--select a,b,c] [--rows N] отдаёт ли индекс поля заявки + node sp.mjs lists списки сайта и их размеры (партиции) + node sp.mjs get "/_api/web/..." произвольный GET + +Окружение: SP_SITE, SP_APP_ID, SP_TENANT (см. README.md)`; + +const [, , command, ...rest] = process.argv; +const argv = parseArgv(rest); + +const commands = { + login: cmdLogin, + status: cmdStatus, + logout: cmdLogout, + search: cmdSearch, + props: cmdProps, + lists: cmdLists, + get: cmdGet +}; + +if (!command || !commands[command]) { + console.log(USAGE); + process.exit(command ? 1 : 0); +} + +Promise.resolve(commands[command](argv)).catch((e) => fail(String(e.stack || e.message))); -- 2.34.1 From b75a0758e7bcb87c8ea6f466b9780deba75b7435 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 21 Aug 2026 12:04:47 +0500 Subject: [PATCH 4/6] =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D0=BB=20=D1=81?= =?UTF-8?q?=D0=BA=D0=B8=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/skills/sp-query/SKILL.md | 80 -------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 .claude/skills/sp-query/SKILL.md diff --git a/.claude/skills/sp-query/SKILL.md b/.claude/skills/sp-query/SKILL.md deleted file mode 100644 index 14a3643..0000000 --- a/.claude/skills/sp-query/SKILL.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: sp-query -description: Спросить у SharePoint-тенанта реальные данные портала заявок через tools/sp — что отдаёт поисковый индекс (managed properties, Retrievable), сколько партиций и элементов в списках, произвольный KQL или REST. Использовать, когда ответ зависит от состояния тенанта, а не выводится из кода: «отдаётся ли поле через Search», «сколько на самом деле партиций», «почему запрос возвращает пусто», «проверь на живых данных». Не использовать для правки данных. ---- - -# Запросы к SharePoint портала заявок - -## Когда это нужно - -Рассуждать по коду можно долго и неправильно. Есть класс вопросов, где ответ живёт -только в тенанте: - -- отдаёт ли индекс конкретное поле (`Retrievable`) — от этого зависит, можно ли - заменить REST-дочитывание на `SelectProperties`; -- сколько на самом деле партиций у типа заявки и сколько в них элементов (это - переменная `P` во всех оценках числа запросов); -- почему KQL возвращает пусто — не то имя managed property, не тот `Path`, нет прав; -- как выглядит реальное значение поля (`AuthorOWSUSER` — не почта, а `почта | ФИО`). - -Если вопрос из этого списка — не гадай и не проси пользователя лезть в консоль -браузера. Спроси тенант. - -## Порядок действий - -```bash -cd tools/sp -node sp.mjs status # сначала: есть ли вход вообще -``` - -`Logged out` → нужен вход. Он **интерактивный**: команда печатает код, который -пользователь вводит в браузере сам. Запускать `login` в фоне бессмысленно — попроси -пользователя выполнить его и вернуться. - -```bash -node sp.mjs login -``` - -Если ругается на `SP_APP_ID` — регистрация приложения не сделана, отправь пользователя -в `tools/sp/README.md`, раздел «Разовая подготовка» (это разовая задача с admin -consent, сам её не закроешь). - -Дальше: - -| команда | что отвечает | -| --- | --- | -| `node sp.mjs props` | отдаёт ли индекс поля заявки; `<нет в ответе>` / `` = не Retrievable | -| `node sp.mjs lists` | портальные списки, их адреса и размеры — реальное число партиций | -| `node sp.mjs search "" --select a,b --rows N` | тот же запрос, что шлёт портал | -| `node sp.mjs get "/_api/web/..."` | произвольный GET | - -## Что нельзя - -- **Не вводить пароли и секреты.** Вход только device code: код вводит пользователь. - Если предлагаешь `--authType password` или `--secret` — ты делаешь неправильно. -- **Не писать данные.** Инструмент читающий. Любая правка списков — отдельный - разговор с пользователем, а не побочный эффект разбора. -- **Не заводить app-only доступ по сертификату ради Search.** Он для этого не годится: - `/_api/search/query` под app-only-токеном обычно не отдаёт ничего, поиску нужен - пользовательский контекст. - -## Грабли - -**`Queryable` ≠ `Retrievable`.** Разные флаги managed property. То, по чему получается -фильтровать, не обязано приходить в ответе — и наоборот. Доказав одно, не считай -доказанным другое: в этом проекте на этом уже спотыкались. - -**Имена managed properties нестандартные.** Суффикс по типу колонки: `OWSTEXT` (Text), -`OWSCHCS` (Choice), `OWSUSER` (Person/Group). У колонок из группы колонок сайта -добавляется префикс группы — поэтому существует `TansuSiteApproverOWSUSER`, а -«ожидаемого» `ApproverOWSUSER` в схеме нет вовсе. Прежде чем сочинять имя, посмотри -константы `MP_*` в `src/webparts/requestPortal/features/myRequests/myRequests.service.ts`. - -**Пустой результат ≠ свойство не работает.** Сначала проверь `Path` (адрес сайта, -слеш на конце, регистр) и права — в этом проекте вывод «managed property сломана» -однажды оказался артефактом задвоенного сегмента в тестовом `Path`. - -**Замер портала — это другое.** `tools/sp` отвечает на вопросы о данных. Чтобы понять, -куда уходит время в самом портале, есть встроенный замер: `window.rpPerf = true` в -консоли страницы (см. `src/webparts/requestPortal/shared/perfTrace.ts`), детали — -`window.rpPerfDetail()`. -- 2.34.1 From 7f08a819a9598cfd8d3f2482aab6b65db4565537 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 21 Aug 2026 14:47:03 +0500 Subject: [PATCH 5/6] =?UTF-8?q?=D0=BE=D1=82=D0=BA=D0=B0=D0=B7=20=D0=BE?= =?UTF-8?q?=D1=82=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B5=D0=B3=D0=BE=20=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/myRequests/myRequests.service.ts | 215 +++++++++--------- .../requestPortal/shared/perfTrace.ts | 118 +++++++--- 2 files changed, 191 insertions(+), 142 deletions(-) diff --git a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts index 58d06d3..1c36bcd 100644 --- a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts +++ b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts @@ -49,10 +49,10 @@ const SEARCH_PAGE_SIZE = 500; const SEARCH_HARD_CAP = 5000; // Сколько Id максимум перечисляем в одном REST $filter (`Id eq 1 or Id eq 2…`) -// при дочитывании полей по найденным через Search элементам (см. loadByIds). -// SharePoint REST не резиновый по длине URL — несколько сотен ИЛИ уже рискуют -// упереться в лимит, поэтому набор Id одного типа режется на такие пачки, -// а не уходит одним запросом целиком. +// при дочитывании строк маршрута/заявки по найденным через Search Id (см. +// finalizeApprovalTarget). SharePoint REST не резиновый по длине URL — +// несколько сотен ИЛИ уже рискуют упереться в лимит, поэтому набор Id одного +// типа режется на такие пачки, а не уходит одним запросом целиком. const REST_ID_FILTER_CHUNK = 100; // Сколько последних заявок автора дочитываем через REST в довесок к Search @@ -83,6 +83,42 @@ const MP_STATUS = 'ApprovalStatusOWSCHCS'; // SharePoint — сама схема поиска исправна. const MP_REQUEST_STATUS = 'RequestStatusOWSCHCS'; +// Автор заявки (Person-поле Author списка ДАННЫХ) — суффикс OWSUSER, без +// префикса группы колонок (поле стандартное, не сайтовое, как и RequestStatus +// выше). Значение — «<логин> | <ФИО>» (см. parseAuthorOwsUser). Уже +// использовался в KQL (AuthorOWSUSER:"...") для отбора «своих» заявок; здесь +// то же свойство дочитывается из ответа Search вместо REST. +const MP_AUTHOR = 'AuthorOWSUSER'; + +// Уникальный код заявки (колонка RequestUniqueCode) — суффикс OWSTEXT, тип +// исходного поля Text. +const MP_CODE = 'RequestUniqueCodeOWSTEXT'; + +// Поля заявки, отдаваемые ПРЯМО индексом, без REST-дочитывания после Search — +// подтверждено вручную (tools/sp props, 2026-08-20): Queryable ≠ Retrievable у +// managed property, поэтому то, что раньше проверялось только на фильтрацию +// (см. комментарий у MP_REQUEST_STATUS), пришлось перепроверять отдельно на +// то, что значение вообще приходит в ответе. Пришло — все поля, что нужны для +// строки таблицы «Мои заявки» (title/created — стандартные MP 'Title'/'Created', +// их так же безусловно отдаёт любой список). 'Author' (без суффикса) отдаёт +// готовое отображаемое имя — короче и надёжнее выковыривать его из +// MP_AUTHOR/parseAuthorOwsUser, там он нужен только ради почты. +const OWN_SELECT_PROPERTIES = ['ListItemID', 'Path', 'Title', 'Created', 'Author', MP_AUTHOR, MP_REQUEST_STATUS, MP_CODE]; + +/** + * Managed property Person-поля хранит и логин, и ФИО одной строкой: + * «<логин> | <ФИО>» (проверено вручную, tools/sp props, 2026-08-20). Разделитель + * — именно " | " С ПРОБЕЛАМИ: сам логин при claims-аутентификации выглядит как + * `i:0#.f|membership|user@domain` и содержит "|" без пробелов вокруг, поэтому + * делить по ПЕРВОМУ "|" нельзя — часть логина отрезалась бы вместе с почтой. + */ +function parseAuthorOwsUser(value: unknown): { email: string; name: string } { + const text = typeof value === 'string' ? value : ''; + const sep = text.indexOf(' | '); + if (sep < 0) return { email: text.trim(), name: '' }; + return { email: text.slice(0, sep).trim(), name: text.slice(sep + 3).trim() }; +} + // effectiveUserEmail, а не pageContext напрямую: при отладочной подмене личности // (см. shared/impersonation.ts) «мои заявки» и очереди согласования должны // отбираться по подменённому сотруднику — иначе подмена показывала бы чужой @@ -687,8 +723,7 @@ async function finalizeApprovalTarget( ? [] : ( await Promise.all( - // Id режем на пачки — тот же предел длины URL REST $filter, что и в - // loadByIds (см. REST_ID_FILTER_CHUNK). + // Id режем на пачки — предел длины URL REST $filter (см. REST_ID_FILTER_CHUNK). chunk(searchHitIds, REST_ID_FILTER_CHUNK).map((idsChunk) => approvalList.items .filter(idsChunk.map((id) => `Id eq ${id}`).join(' or ')) @@ -762,11 +797,12 @@ async function finalizeApprovalTarget( // по переданным строкам. Мутирует переданные объекты (проставляет // authorEmail/authorName). // -// Строки, у которых автор УЖЕ известен, пропускаются: и 'own' (loadByIds), и -// 'approval' (finalizeApprovalTarget) теперь получают его тем же запросом, что -// и остальные поля, поэтому на обычных путях сюда доходить нечему и запрос не -// уходит вовсе. Функция оставлена страховкой на случай источника строк без -// автора — молча показывать пустого инициатора хуже, чем один лишний запрос. +// Строки, у которых автор УЖЕ известен, пропускаются: 'own' получает его прямо +// из ответа Search (см. toMyRequestFromSearch), 'approval' (finalizeApprovalTarget) +// — тем же REST-запросом, что и остальные поля, поэтому на обычных путях сюда +// доходить нечему и запрос не уходит вовсе. Функция оставлена страховкой на +// случай источника строк без автора — молча показывать пустого инициатора +// хуже, чем один лишний запрос. async function hydrateAuthors( context: WebPartContext, types: IRequestType[], @@ -1058,7 +1094,7 @@ function hitsToEntries( ): ISearchEntry[] { const seen = new Set(); return (results.PrimarySearchResults as Array>) - .map((r) => ({ id: Number(r.ListItemID), path: String(r.Path || '') })) + .map((r) => ({ id: Number(r.ListItemID), path: String(r.Path || ''), raw: r })) .filter((h) => !isNaN(h.id) && h.id > 0) .map((h) => { // Сравнение по границе сегмента (см. listPathPrefix): при «голом» @@ -1068,7 +1104,7 @@ function hitsToEntries( // из того же списка, где его нашёл Search (см. targetForPath). for (const type of types) { const target = targetForPath(h.path, type); - if (target) return { id: h.id, type, target }; + if (target) return { id: h.id, type, target, raw: h.raw }; } return undefined; }) @@ -1081,72 +1117,50 @@ function hitsToEntries( }); } -/** Найденный хит: Id, его тип и партиция, в которой он лежит. */ +/** + * Найденный хит: Id, его тип, партиция, в которой он лежит, и СЫРАЯ строка + * ответа Search — она же несёт остальные поля заявки (см. OWN_SELECT_PROPERTIES + * и toMyRequestFromSearch), дочитывать их REST-ом больше не нужно. + */ interface ISearchEntry { id: number; type: IRequestType; target: IListTarget; + raw: Record; } -// Дочитать поля заявок по найденным Id (Search отдаёт не все колонки, и для -// кастомных полей нет гарантии, что они проиндексированы как managed property). -// -// Id одного типа режем на пачки REST_ID_FILTER_CHUNK: теперь, когда Search -// собирает результат целиком (см. fetchAllSearchRows), их может набраться -// заметно больше сотни — один $filter из тысяч «Id eq N or …» упёрся бы в -// предел длины URL SharePoint REST. -async function loadByIds( - context: WebPartContext, - entries: ISearchEntry[], - kind: 'own' | 'approval' -): Promise> { - // Группируем по ПАРТИЦИИ, а не по типу: Id из архивной партиции в текущем - // списке либо не найдётся, либо укажет на чужую заявку с тем же Id. - const byPartition = new Map(); - entries.forEach((e) => { - const key = e.target.listId.toLowerCase(); - const bucket = byPartition.get(key) || { type: e.type, target: e.target, ids: [] }; - bucket.ids.push(e.id); - byPartition.set(key, bucket); - }); - - const byKey = new Map(); - await Promise.all( - Array.from(byPartition.values()).map(async ({ type, target, ids }) => { - // Ошибку ловим НА ПАРТИЦИЮ, а не на весь вызов: список серии может быть - // недоступен (удалён, переименован, остался в реестре после смены списка у - // типа заявки) — SharePoint отвечает 404 «Файл не найден». Без изоляции - // один такой список валил бы ВСЮ выдачу «Моих заявок», и пользователь не - // увидел бы ничего вместо «всё, кроме одного архива». - try { - const sp = spForSite(target.siteUrl, context); - const list = sp.web.getList(target.listId); - - await Promise.all( - chunk(ids, REST_ID_FILTER_CHUNK).map(async (idsChunk) => { - const rows = (await list.items - .filter(idsChunk.map((id) => `Id eq ${id}`).join(' or ')) - .select('Id', 'RequestUniqueCode', 'Title', 'RequestStatus', 'Created', 'Author/EMail', 'Author/Title') - .expand('Author') - .top(idsChunk.length)()) as Array>; - rows.forEach((r) => { - const author = r.Author as { EMail?: string; Title?: string } | undefined; - byKey.set( - requestKey(target.listId, r.Id as number), - toMyRequest(r, author, type, kind, target) - ); - }); - }) - ); - } catch (e) { - console.error( - `[Текущие заявки] «${type.name}»: не удалось прочитать список ${target.listId} — заявки из него в выдачу не попадут:`, - (e as Error).message - ); - } - }) - ); - return byKey; +/** + * Строка «Моих заявок» ПРЯМО из ответа Search — без второго прохода по REST. + * + * Раньше здесь был REST-запрос по найденным Id (loadByIds): считалось, что + * поисковый индекс отдаёт не все колонки. Проверено вручную (tools/sp props, + * 2026-08-20) — отдаёт все нужные, см. OWN_SELECT_PROPERTIES. Оставался вопрос + * СВЕЖЕСТИ: значения теперь из индекса, который переиндексируется с задержкой, + * а не «только что созданный элемент» напрямую. Для 'own' это уже было принятым + * компромиссом — searchOwnRequests и раньше не подмешивал REST-подстраховку + * (в отличие от getOwnRequests, где её явно добавляет getFreshOwnRowsForType). + */ +function toMyRequestFromSearch(entry: ISearchEntry): IMyRequest { + const r = entry.raw; + const author = parseAuthorOwsUser(r[MP_AUTHOR]); + return { + id: entry.id, + code: (r[MP_CODE] as string) || '', + title: (r.Title as string) || '', + status: (r[MP_REQUEST_STATUS] as string) || '', + authorEmail: author.email, + // 'Author' без суффикса — готовое отображаемое имя (см. OWN_SELECT_PROPERTIES); + // из MP_AUTHOR парсим только как запасной вариант, если 'Author' пуст. + authorName: ((r.Author as string) || author.name || '').trim(), + created: (r.Created as string) || '', + requestTypeId: entry.type.id, + requestTypeName: entry.type.name, + section: entry.type.section, + formKey: entry.type.pageUrl, + kind: 'own', + listId: entry.target.listId, + siteUrl: entry.target.siteUrl, + }; } // Последние OWN_REST_TOP заявок автора одного типа обычным REST-ом, в обход @@ -1287,7 +1301,7 @@ export async function searchOwnRequests( const clauses = [ pathClauseForTypes(types), - `AuthorOWSUSER:"${claimsLogin}"`, + `${MP_AUTHOR}:"${claimsLogin}"`, ]; const text = freeTextClause(query); if (text) clauses.push(text); @@ -1298,7 +1312,7 @@ export async function searchOwnRequests( Querytext: kql, RowLimit: PAGE_SIZE, StartRow: (page - 1) * PAGE_SIZE, - SelectProperties: ['ListItemID', 'Title', 'Path'], + SelectProperties: OWN_SELECT_PROPERTIES, TrimDuplicates: false, // Свежие сверху (по умолчанию Search сортирует по релевантности, не по дате). SortList: [{ Property: 'Created', Direction: 1 }], @@ -1306,13 +1320,9 @@ export async function searchOwnRequests( ); const totalRows = results.TotalRows || 0; - const entries = hitsToEntries(results, types); - const byKey = - entries.length > 0 - ? await traced('дочитывание полей по Id (REST)', () => loadByIds(context, entries, 'own')) - : new Map(); - // Порядок задаёт релевантность поиска, а не порядок ответов REST. - const rows = entries.map((e) => byKey.get(requestKey(e.target.listId, e.id))).filter((r): r is IMyRequest => !!r); + // Поля — прямо из ответа индекса (см. toMyRequestFromSearch), REST-дочитывания + // здесь больше нет. Порядок задаёт релевантность поиска. + const rows = hitsToEntries(results, types).map(toMyRequestFromSearch); return { rows, totalRows }; } @@ -1331,16 +1341,18 @@ export async function getOwnRequestsByStatus( page: number, query: string = '' ): Promise { - return ownRequestsByStatus(context, types, status, page, query, true); + return ownRequestsByStatus(context, types, status, page, query); } /** * Только СКОЛЬКО таких заявок — для счётчика «Доработка» в боковом меню. * - * Отличие от getOwnRequestsByStatus одно: не дочитываются поля найденных заявок - * (loadByIds). Счётчику они не нужны — ему нужен totalRows, — а замер 2026-08-20 - * показал, что этот шаг стоит ~200-300 мс и идёт СТРОГО ПОСЛЕ Search, то есть - * целиком лежит на критическом пути открытия портала. + * До 2026-08-20 у этого была отдельная REST-развилка (withRows): дочитывание + * полей найденных заявок стоило отдельного запроса, и счётчику, которому нужен + * только totalRows, было жаль на него тратиться. Теперь поля идут прямо из + * ответа Search (см. toMyRequestFromSearch) — построить rows настолько же + * дёшево, насколько и посчитать totalRows, отдельная ветка была бы одной лишь + * сложностью без выигрыша. * * Условия отбора и довесок по свежести — те же самые, поэтому счётчик и вкладка * по-прежнему не расходятся (ради чего они и считаются одним кодом). @@ -1350,7 +1362,7 @@ export async function countOwnRequestsByStatus( types: IRequestType[], status: string ): Promise { - return (await ownRequestsByStatus(context, types, status, 1, '', false)).totalRows; + return (await ownRequestsByStatus(context, types, status, 1, '')).totalRows; } async function ownRequestsByStatus( @@ -1358,8 +1370,7 @@ async function ownRequestsByStatus( types: IRequestType[], status: string, page: number, - query: string, - withRows: boolean + query: string ): Promise { if (types.length === 0) return { rows: [], totalRows: 0 }; @@ -1367,7 +1378,7 @@ async function ownRequestsByStatus( const claimsLogin = `i:0#.f|membership|${currentUserEmail(context)}`; const clauses = [ pathClauseForTypes(types), - `AuthorOWSUSER:"${claimsLogin}"`, + `${MP_AUTHOR}:"${claimsLogin}"`, `${MP_REQUEST_STATUS}:"${status}"`, ]; const text = freeTextClause(query); @@ -1379,7 +1390,7 @@ async function ownRequestsByStatus( Querytext: kql, RowLimit: PAGE_SIZE, StartRow: (page - 1) * PAGE_SIZE, - SelectProperties: ['ListItemID', 'Title', 'Path'], + SelectProperties: OWN_SELECT_PROPERTIES, TrimDuplicates: false, // Свежие сверху — как в searchOwnRequests. SortList: [{ Property: 'Created', Direction: 1 }], @@ -1388,11 +1399,7 @@ async function ownRequestsByStatus( const totalRows = results.TotalRows || 0; const entries = hitsToEntries(results, types); - const byKey = - withRows && entries.length > 0 - ? await traced('дочитывание полей по Id (REST)', () => loadByIds(context, entries, 'own')) - : new Map(); - const searchRows = entries.map((e) => byKey.get(requestKey(e.target.listId, e.id))).filter((r): r is IMyRequest => !!r); + const searchRows = entries.map(toMyRequestFromSearch); if (page !== 1 || query.trim()) return { rows: searchRows, totalRows }; @@ -1407,9 +1414,6 @@ async function ownRequestsByStatus( .reduce((acc, r) => acc.concat(r), [] as IMyRequest[]) .filter((r) => r.status === status); - // Ключи для дедупа берём из ХИТОВ индекса, а не из дочитанных строк: пара - // «партиция + Id» известна уже по ответу Search, и режиму счётчика (withRows - // = false) не приходится ради этого дочитывать поля. const searchKeys = new Set(entries.map((e) => requestKey(e.target.listId, e.id))); const onlyFresh = freshRows.filter((r) => !searchKeys.has(rowKey(r))); @@ -1616,24 +1620,19 @@ export async function searchOwnAll( const claimsLogin = `i:0#.f|membership|${currentUserEmail(context)}`; const clauses = [ pathClauseForTypes(types), - `AuthorOWSUSER:"${claimsLogin}"`, + `${MP_AUTHOR}:"${claimsLogin}"`, ]; const text = freeTextClause(query); if (text) clauses.push(text); const kql = clauses.join(' AND '); - const hits = await fetchAllSearchRows(sp, kql, ['ListItemID', 'Title', 'Path'], 'Свои заявки'); + const hits = await fetchAllSearchRows(sp, kql, OWN_SELECT_PROPERTIES, 'Свои заявки'); const results = { PrimarySearchResults: hits, TotalRows: hits.length }; - traceSearchHits('Свои заявки', kql, types, results, listAbsUrl); const entries = hitsToEntries(results, types); - const byKey = - entries.length > 0 - ? await traced('дочитывание полей по Id (REST)', () => loadByIds(context, entries, 'own')) - : new Map(); - const rows = entries.map((e) => byKey.get(requestKey(e.target.listId, e.id))).filter((r): r is IMyRequest => !!r); + const rows = entries.map(toMyRequestFromSearch); traceResultRows('Свои заявки', types, entries, rows); return rows; diff --git a/src/webparts/requestPortal/shared/perfTrace.ts b/src/webparts/requestPortal/shared/perfTrace.ts index b8f1792..fd35b09 100644 --- a/src/webparts/requestPortal/shared/perfTrace.ts +++ b/src/webparts/requestPortal/shared/perfTrace.ts @@ -67,12 +67,44 @@ interface IStat { maxMs: number; } -/** Накопленное по одной операции за ВСЕ её прогоны — база для среднего. */ +/** + * Сэмплы одной метки по всем прогонам — база для МЕДИАНЫ, а не среднего. + * + * Среднее одним выбросом (случайный 429 с ретраем, холодный кеш на первом + * заходе) утаскивает за собой всю картину надолго — а именно выбросы здесь + * основной случай, не редкость. Медиана его игнорирует: важнее «как обычно», + * а не «как в сумме с той единственной аномалией». + */ +interface ILabelSamples { + /** totalMs метки за каждый прогон, где она встретилась (метка не обязана быть в каждом). */ + totalMs: number[]; + calls: number[]; + /** Худший случай за всё время — в отличие от totalMs, это не медиана, а running max. */ + maxMs: number; +} + +// Сколько последних прогонов метки хранить ради медианы. Не безлимитный рост: +// в долгой сессии старые сэмплы устаревают быстрее, чем на них смотрят, а лимит +// защищает от утечки памяти на вкладке, оставленной открытой на весь день. +const MAX_SAMPLES = 50; + +function pushSample(list: number[], value: number): void { + list.push(value); + if (list.length > MAX_SAMPLES) list.shift(); +} + +function median(values: number[]): number { + if (values.length === 0) return 0; + const sorted = [...values].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + return sorted.length % 2 === 1 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2; +} + +/** Накопленное по одной операции за ВСЕ её прогоны — база для медианы. */ interface IRunHistory { - runs: number; - wallTotalMs: number; - stages: Map; - requests: Map; + wallSamples: number[]; + stages: Map; + requests: Map; } // Текущий (ещё не напечатанный) прогон. @@ -142,6 +174,18 @@ function installFlag(): void { set: (value: unknown): void => { enabled = !!value; writeStoredFlag(enabled); + if (!enabled) { + // Тишина СРАЗУ, а не после того, что уже накопилось. Без этого operация, + // стартовавшая ДО выключения (perfScope с сетевым шагом внутри) или + // ждущая debounce-таймера (см. scheduleIdleFlush), всё равно печатала + // итог ПОСЛЕ выключения — выглядело так, будто window.rpPerf = false + // не действует. + if (idleTimer !== undefined) { + window.clearTimeout(idleTimer); + idleTimer = undefined; + } + reset(); + } console.log(`${PREFIX} замер ${enabled ? 'включён' : 'выключен'} (запомнено до отмены)`); } }); @@ -181,7 +225,7 @@ function scheduleIdleFlush(): void { if (idleTimer !== undefined) window.clearTimeout(idleTimer); idleTimer = window.setTimeout(() => { idleTimer = undefined; - if (openScopes === 0) print(); + if (openScopes === 0 && perfEnabled()) print(); }, IDLE_FLUSH_MS); } @@ -223,7 +267,11 @@ export async function perfScope(label: string, fn: () => Promise): Promise return await fn(); } finally { openScopes -= 1; - if (openScopes === 0) print(); + // perfEnabled() — на выходе, а не только на входе: операцию могли выключить, + // пока она ещё выполнялась (см. setter window.rpPerf выше), и тогда печатать + // её итог уже не нужно — accumulated-данные к этому моменту сам setter уже + // сбросил. + if (openScopes === 0 && perfEnabled()) print(); } } @@ -307,19 +355,19 @@ function reset(): void { * Ключ, под которым прогоны копятся в историю. * * Номер страницы из метки убираем: пролистать вкладку по страницам — самый - * удобный способ набрать несколько замеров одной и той же операции, и усреднять - * их надо ВМЕСТЕ. Иначе каждая страница завела бы свою историю из одного - * прогона, и среднее никогда бы не набралось. + * удобный способ набрать несколько замеров одной и той же операции, и считать + * медиану надо по ним ВМЕСТЕ. Иначе каждая страница завела бы свою историю из + * одного прогона, и медиане не по чему было бы считаться. */ function historyKey(title: string): string { return title.replace(/\s*стр\.\d+/g, '').trim(); } -function mergeInto(target: Map, source: Map): void { +function recordSamples(target: Map, source: Map): void { source.forEach((s, label) => { - const t = target.get(label) ?? { calls: 0, totalMs: 0, maxMs: 0 }; - t.calls += s.calls; - t.totalMs += s.totalMs; + const t = target.get(label) ?? { totalMs: [], calls: [], maxMs: 0 }; + pushSample(t.totalMs, s.totalMs); + pushSample(t.calls, s.calls); t.maxMs = Math.max(t.maxMs, s.maxMs); target.set(label, t); }); @@ -346,8 +394,10 @@ function totalCalls(source: Map): number { * window.rpPerfDetail(), и её зовут тогда, когда строка показала, что смотреть * есть на что. * - * Среднее в строке важнее текущего значения: одиночный сетевой замер шумит - * настолько, что по нему нельзя сказать, стало лучше или просто повезло. + * Медиана в строке важнее текущего значения: одиночный сетевой замер шумит + * настолько, что по нему нельзя сказать, стало лучше или просто повезло, а + * среднее в этом же шуме утаскивает один выброс (холодный кеш, ретрай на 429) + * далеко за пределы того, что происходит обычно. */ function print(): void { if (idleTimer !== undefined) { @@ -361,15 +411,13 @@ function print(): void { const key = historyKey(title); const hist: IRunHistory = history.get(key) ?? { - runs: 0, - wallTotalMs: 0, - stages: new Map(), - requests: new Map() + wallSamples: [], + stages: new Map(), + requests: new Map() }; - hist.runs += 1; - hist.wallTotalMs += wallMs; - mergeInto(hist.stages, stages); - mergeInto(hist.requests, requests); + pushSample(hist.wallSamples, wallMs); + recordSamples(hist.stages, stages); + recordSamples(hist.requests, requests); history.set(key, hist); // Дольше всего — по этапам, если разметка есть; иначе по видам запросов @@ -378,7 +426,7 @@ function print(): void { console.log( `${PREFIX} ${key} | ожидание ${Math.round(wallMs)} мс` + - ` (сред. ${Math.round(hist.wallTotalMs / hist.runs)} за ${hist.runs})` + + ` (мед. ${Math.round(median(hist.wallSamples))} за ${hist.wallSamples.length})` + ` | запросов ${totalCalls(requests)}` + (top ? ` | дольше всего: ${top}` : '') ); @@ -386,18 +434,20 @@ function print(): void { reset(); } -/** Строки полной раскладки по одной операции — средние за все её прогоны. */ +/** Строки полной раскладки по одной операции — медианы за все её прогоны. */ function detailLines(key: string, hist: IRunHistory): string[] { - const lines = [`${PREFIX} ${key} - прогонов ${hist.runs}, ожидание в среднем ${Math.round(hist.wallTotalMs / hist.runs)} мс`]; + const lines = [ + `${PREFIX} ${key} - прогонов ${hist.wallSamples.length}, ожидание медиана ${Math.round(median(hist.wallSamples))} мс` + ]; - const dump = (name: string, source: Map): void => { + const dump = (name: string, source: Map): void => { if (source.size === 0) return; - lines.push(` ${name} (в среднем за прогон)`); + lines.push(` ${name} (медиана за прогон)`); Array.from(source.entries()) - .sort((a, b) => b[1].totalMs - a[1].totalMs) + .sort((a, b) => median(b[1].totalMs) - median(a[1].totalMs)) .forEach(([label, s]) => { lines.push( - ` ${Math.round(s.totalMs / hist.runs)} мс, вызовов ${(s.calls / hist.runs).toFixed(1)}, макс ${Math.round(s.maxMs)} - ${label}` + ` ${Math.round(median(s.totalMs))} мс, вызовов ${Math.round(median(s.calls))}, макс ${Math.round(s.maxMs)} - ${label}` ); }); }; @@ -425,9 +475,9 @@ function installDetailApi(): void { return text; }; - // Старые прогоны тянут среднее назад, и «стало быстрее» проявится в нём - // только через несколько заходов — перед сравнением «до/после» серию надо - // обнулять. + // Старые прогоны остаются в окне (см. MAX_SAMPLES) и тянут медиану назад — + // «стало быстрее» проявится в ней только после того, как их наберётся + // больше половины окна. Перед сравнением «до/после» серию надо обнулять. w.rpPerfReset = (): void => { history.clear(); reset(); -- 2.34.1 From a19444eb2891a8d97ada1c8dcce485bffd695554 Mon Sep 17 00:00:00 2001 From: "gorbenko.pavel" Date: Fri, 21 Aug 2026 15:00:33 +0500 Subject: [PATCH 6/6] =?UTF-8?q?=D1=80=D0=B5=D1=81=D1=82=20=D0=B8=20=D1=81?= =?UTF-8?q?=D0=B5=D1=80=D1=87=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=B2=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=BD=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/myRequests/RequestsTableView.tsx | 40 +++++- .../features/myRequests/myRequests.service.ts | 128 ++++++++++++------ 2 files changed, 117 insertions(+), 51 deletions(-) diff --git a/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx b/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx index 5630086..a8a139c 100644 --- a/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx +++ b/src/webparts/requestPortal/features/myRequests/RequestsTableView.tsx @@ -353,18 +353,26 @@ export const RequestsTableView: React.FC = ({ const q = debouncedQuery.trim(); - const fetchRows = async (): Promise => { + // onPartial — Search и REST-довесок по свежести («Мои заявки»/«Доработка», + // см. getOwnRequests/getOwnRequestsByStatus в myRequests.service.ts) теперь + // идут параллельно, и заранее не угадать, какой из двух ответит первым — + // сеть шумит то в одну, то в другую сторону. onPartial зовётся с тем, что + // пришло раньше, чтобы не держать список пустым ради заведомо более + // медленного источника. Остальные ветки (approvals/execution/поиск) его + // просто не принимают — там нет пары «Search + довесок» с независимо + // валидным промежуточным результатом. + const fetchRows = async (onPartial?: (p: IPage) => void): Promise => { // «Доработка» — тот же список, что «Инициированные», просто со статусом, // зашитым в раздел, а не выбранным в фильтре. if (view === 'rework' || (view === 'own:created' && statusFilter)) { const status = view === 'rework' ? REQUEST_STATUS.returned : statusFilter; - return getOwnRequestsByStatus(context, types, status, page, debouncedQuery); + return getOwnRequestsByStatus(context, types, status, page, debouncedQuery, onPartial); } if (view === 'own:created') { return q.length > 0 ? searchOwnRequests(context, types, debouncedQuery, page) - : getOwnRequests(context, types, page); + : getOwnRequests(context, types, page, onPartial); } const queue = QUEUE_BY_VIEW[view]; @@ -387,9 +395,11 @@ export const RequestsTableView: React.FC = ({ // Комментарий к решению дочитываем ОДНИМ местом на все ветки: «Доработка» и // «Отклонённые мной» приходят разными путями (по автору и по строкам // маршрута соответственно), а колонка у них одинаковая по смыслу. И только - // для видимой страницы — как и автор в hydratePendingAuthors. - const fetchPage = async (): Promise => { - const result = await fetchRows(); + // для видимой страницы — как и автор в hydratePendingAuthors. Промежуточный + // показ (onPartial) комментарий ещё не несёт — он дочитывается только для + // ИТОГОВОГО результата, полный показ его донесёт следом. + const fetchPage = async (onPartial?: (p: IPage) => void): Promise => { + const result = await fetchRows(onPartial); const decisionStatus = decisionStatusFor(view, statusFilter); if (decisionStatus) { await hydrateDecisionComments(context, types, result.rows, decisionStatus); @@ -399,7 +409,23 @@ export const RequestsTableView: React.FC = ({ const load = async (forceRender = false): Promise => { try { - const fresh = await perfScope(`таблица ${view} стр.${page}`, fetchPage); + // Промежуточный показ — ТОЛЬКО когда для этой страницы прямо СЕЙЧАС + // ничего не закешировано (первый заход, экран пуст). Проверяем на + // каждый вызов load(), а не один раз на весь эффект: тот же load() + // переиспользуется автообновлением по таймеру и событием + // rp-requests-changed все 10 минут жизни вкладки, и ко второму вызову + // кеш почти наверняка уже есть — без повторной проверки список бы на + // мгновение "усыхал" при КАЖДОМ фоновом обновлении, а не только при + // самой первой загрузке. + const onPartial = readPage(cacheKey) + ? undefined + : (partial: IPage): void => { + if (cancelled) return; + setRows(partial.rows); + setTotalRows(partial.totalRows); + }; + + const fresh = await perfScope(`таблица ${view} стр.${page}`, () => fetchPage(onPartial)); if (cancelled) return; // Перерисовываем, только если данные реально изменились — иначе при diff --git a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts index 1c36bcd..889eb2d 100644 --- a/src/webparts/requestPortal/features/myRequests/myRequests.service.ts +++ b/src/webparts/requestPortal/features/myRequests/myRequests.service.ts @@ -1252,37 +1252,85 @@ async function getRecentlyChangedOwnRowsForType(context: WebPartContext, type: I } } +// Итог слияния Search-страницы с REST-довеском по свежести — общая логика для +// getOwnRequests и ownRequestsByStatus. Довесок ДОПОЛНЯЕТ Search, а не заменяет: +// то, что Search уже нашёл, в приоритете (там могло появиться более точное +// совпадение по совместному запросу — статус/текст), довесок добавляет только +// то, чего в найденном ещё нет. +function mergeSearchAndFresh(searchPage: IOwnRequestsPage, freshRows: IMyRequest[]): IOwnRequestsPage { + const searchKeys = new Set(searchPage.rows.map((r) => rowKey(r))); + const onlyFresh = freshRows.filter((r) => !searchKeys.has(rowKey(r))); + const merged = onlyFresh + .concat(searchPage.rows) + .sort((a, b) => b.created.localeCompare(a.created)) + .slice(0, PAGE_SIZE); + return { rows: merged, totalRows: searchPage.totalRows + onlyFresh.length }; +} + +/** + * Гонит Search-страницу и REST-довесок по свежести ПАРАЛЛЕЛЬНО — то один + * отвечает быстрее, то другой, заранее не угадать, и ждать заведомо более + * медленный, держа экран пустым, незачем. + * + * Если задан onPartial — зовёт его РАЗ, как только придёт ПЕРВЫЙ из двух + * источников (та половина, что уже есть, без дедупа со вторым — его мы ещё не + * видели). Возвращает функция в любом случае ИТОГОВЫЙ слитый результат; + * onPartial — только для промежуточного показа, на итог не влияет. + * + * Без onPartial ведёт себя как раньше: ждёт оба источника и сразу отдаёт слитый + * результат — второй показ (полный) экономить незачем, если промежуточный + * всё равно никто не увидит. + */ +async function raceThenMerge( + searchPromise: Promise, + freshPromise: Promise, + onPartial: ((page: IOwnRequestsPage) => void) | undefined +): Promise { + if (onPartial) { + let searchDone = false; + let freshDone = false; + // Ошибку здесь не обрабатываем — её поймает Promise.all ниже; этот + // отдельный .then только подсматривает за тем, что пришло раньше. + searchPromise.then((r) => { + searchDone = true; + if (!freshDone) onPartial(r); + }, () => undefined); + freshPromise.then((r) => { + freshDone = true; + if (!searchDone) onPartial({ rows: r, totalRows: r.length }); + }, () => undefined); + } + + const [searchPage, freshRows] = await Promise.all([searchPromise, freshPromise]); + return mergeSearchAndFresh(searchPage, freshRows); +} + // «Мои заявки» БЕЗ поиска — гибрид для масштаба: Search с серверной пагинацией // (RowLimit/StartRow) находит страницу без сканирования всего архива, REST по // «последним N минутам» добирает то, что Search мог не успеть проиндексировать // (см. RECENT_MS). Довесок применяется только на первой странице — свежее по // дате там и оказалось бы, будь оно проиндексировано. +// +// Search и довесок идут ПАРАЛЛЕЛЬНО (см. raceThenMerge) — раньше довесок +// стартовал только ПОСЛЕ ответа Search, хотя они независимы друг от друга и +// читают разные источники; тем самым просто складывалось время обоих вместо +// времени более медленного из двух. export async function getOwnRequests( context: WebPartContext, types: IRequestType[], - page: number + page: number, + onPartial?: (page: IOwnRequestsPage) => void ): Promise { if (types.length === 0) return { rows: [], totalRows: 0 }; - const searchPage = await searchOwnRequests(context, types, '', page); + const searchPromise = searchOwnRequests(context, types, '', page); + if (page !== 1) return searchPromise; - if (page !== 1) return searchPage; + const freshPromise = Promise.all(types.map((t) => getFreshOwnRowsForType(context, t))).then((r) => + r.reduce((all, x) => all.concat(x), [] as IMyRequest[]) + ); - const freshRows = ( - await Promise.all( - types.map((t) => getFreshOwnRowsForType(context, t)) - ) - ).reduce((all, r) => all.concat(r), [] as IMyRequest[]); - - const searchKeys = new Set(searchPage.rows.map((r) => rowKey(r))); - const onlyFresh = freshRows.filter((r) => !searchKeys.has(rowKey(r))); - - const merged = onlyFresh - .concat(searchPage.rows) - .sort((a, b) => b.created.localeCompare(a.created)) - .slice(0, PAGE_SIZE); - - return { rows: merged, totalRows: searchPage.totalRows + onlyFresh.length }; + return raceThenMerge(searchPromise, freshPromise, onPartial); } // «Мои заявки» С поиском — только Search, без REST-подстраховки на свежесть: @@ -1339,9 +1387,10 @@ export async function getOwnRequestsByStatus( types: IRequestType[], status: string, page: number, - query: string = '' + query: string = '', + onPartial?: (page: IOwnRequestsPage) => void ): Promise { - return ownRequestsByStatus(context, types, status, page, query); + return ownRequestsByStatus(context, types, status, page, query, onPartial); } /** @@ -1370,7 +1419,8 @@ async function ownRequestsByStatus( types: IRequestType[], status: string, page: number, - query: string + query: string, + onPartial?: (page: IOwnRequestsPage) => void ): Promise { if (types.length === 0) return { rows: [], totalRows: 0 }; @@ -1385,7 +1435,7 @@ async function ownRequestsByStatus( if (text) clauses.push(text); const kql = clauses.join(' AND '); - const results = await traced('Search: свои заявки', () => + const searchPromise: Promise = traced('Search: свои заявки', () => sp.search({ Querytext: kql, RowLimit: PAGE_SIZE, @@ -1395,34 +1445,24 @@ async function ownRequestsByStatus( // Свежие сверху — как в searchOwnRequests. SortList: [{ Property: 'Created', Direction: 1 }], }) - ); - - const totalRows = results.TotalRows || 0; - const entries = hitsToEntries(results, types); - const searchRows = entries.map(toMyRequestFromSearch); - - if (page !== 1 || query.trim()) return { rows: searchRows, totalRows }; + ).then((results) => ({ + rows: hitsToEntries(results, types).map(toMyRequestFromSearch), + totalRows: results.TotalRows || 0, + })); // Довесок по недавним изменениям — только на первой странице и только без // текстового поиска (тот же принцип, что в getOwnRequests): заявку могли // вернуть на доработку только что, индекс мог не успеть её подхватить. - const freshRows = ( - await Promise.all( - types.map((t) => getRecentlyChangedOwnRowsForType(context, t)) - ) - ) - .reduce((acc, r) => acc.concat(r), [] as IMyRequest[]) - .filter((r) => r.status === status); + // Запускаем СРАЗУ, не дожидаясь Search: условие включения известно из + // параметров вызова, ждать ради него ответ Search незачем — раньше довесок + // стартовал только ПОСЛЕ Search, хотя независим от его результата. + if (page !== 1 || query.trim()) return searchPromise; - const searchKeys = new Set(entries.map((e) => requestKey(e.target.listId, e.id))); - const onlyFresh = freshRows.filter((r) => !searchKeys.has(rowKey(r))); + const freshPromise = Promise.all(types.map((t) => getRecentlyChangedOwnRowsForType(context, t))).then((r) => + r.reduce((acc, x) => acc.concat(x), [] as IMyRequest[]).filter((x) => x.status === status) + ); - const merged = onlyFresh - .concat(searchRows) - .sort((a, b) => b.created.localeCompare(a.created)) - .slice(0, PAGE_SIZE); - - return { rows: merged, totalRows: totalRows + onlyFresh.length }; + return raceThenMerge(searchPromise, freshPromise, onPartial); } // Сколько строк маршрута максимум забираем за раз при дочитывании комментариев: -- 2.34.1