Web: helpers live in lib/, generated files in lib/generated/; utils/ is gone

The web app kept helpers in two root folders (lib/ and utils/) and named
route-local helper folders either utils/ or lib/. Shared helpers now live in
lib/, route-only helpers in a lib/ next to their route (app/dashboard/lib,
app/sessions/lib). Generated wire types, manifests, guardrails, error codes and
the lastmod file move to lib/generated/, like the MCP server's src/generated/.
The session-search parity cases sit next to sessionSearchQuery.ts.

Pure move, no behaviour change. Imports, the generators (combine.js,
generate-shared-manifest-types.js, sync-error-codes.js, generate-lastmod.js),
the CI drift paths, the two backend tests and the MCP freshness test that read
these files, and the guard tests' registry path and scan list follow the new
locations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-10-10 16:29:57 +02:00
parent a6980a93f6
commit 224eb668a1
306 changed files with 382 additions and 382 deletions
+1 -1
View File
@@ -247,7 +247,7 @@ jobs:
- name: Fail if the generated files drifted
run: |
if ! git diff --quiet -- src/Web/autopilot-monitor-web/utils/shared-manifests.generated.ts src/Web/autopilot-monitor-web/utils/wire-types.generated.ts src/McpServer/autopilot-monitor-mcp/src/generated/wire-types.generated.ts src/McpServer/autopilot-monitor-mcp/src/generated/wire-vocabularies.generated.ts; then
if ! git diff --quiet -- src/Web/autopilot-monitor-web/lib/generated/shared-manifests.generated.ts src/Web/autopilot-monitor-web/lib/generated/wire-types.generated.ts src/McpServer/autopilot-monitor-mcp/src/generated/wire-types.generated.ts src/McpServer/autopilot-monitor-mcp/src/generated/wire-vocabularies.generated.ts; then
echo "::error::Generated wire types are out of date. Run 'npm run generate:manifests' in src/Web/autopilot-monitor-web and commit the result."
git diff --stat
exit 1
+1 -1
View File
@@ -60,7 +60,7 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add -- rules/dist/ \
src/Web/autopilot-monitor-web/utils/guardrails.generated.ts \
src/Web/autopilot-monitor-web/lib/generated/guardrails.generated.ts \
src/McpServer/autopilot-monitor-mcp/src/rule-authoring.generated.ts
if git diff --cached --quiet; then
echo "Generated outputs unchanged - nothing to commit"
+2 -2
View File
@@ -86,12 +86,12 @@ for (const config of configs) {
// ── Guardrails generation ───────────────────────────────────────────────────
const guardrailsPath = path.join(rulesRoot, 'guardrails.json');
// NOTE: consumers import from "@/utils/guardrails.generated" (see guardValidation.ts
// NOTE: consumers import from "@/lib/generated/guardrails.generated" (see guardValidation.ts
// and SectionGatherRules.tsx). The web re-org (commit a3e8e044) moved this file to
// utils/ but left this output path pointing at the now-orphaned lib/ copy, which
// silently froze the live allowlist. Keep this in sync with the import path.
const guardrailsOutput = path.resolve(
rulesRoot, '..', 'src', 'Web', 'autopilot-monitor-web', 'utils', 'guardrails.generated.ts'
rulesRoot, '..', 'src', 'Web', 'autopilot-monitor-web', 'lib', 'generated', 'guardrails.generated.ts'
);
if (fs.existsSync(guardrailsPath)) {
@@ -15,12 +15,12 @@ namespace AutopilotMonitor.Functions.Tests;
/// Server-side free-text search (<c>q=</c>) for the dashboard search box. The predicate is the
/// search grammar of <see cref="SessionSearchQuery"/> over the web dashboard's client-side
/// searchable field set (minus derived-only tokens). Parity with the web parser is pinned by
/// the shared case file <c>src/Web/autopilot-monitor-web/utils/session-search-syntax.cases.json</c>,
/// which vitest runs against <c>app/dashboard/utils/sessionSearchQuery.ts</c>.
/// the shared case file <c>src/Web/autopilot-monitor-web/app/dashboard/lib/session-search-syntax.cases.json</c>,
/// which vitest runs against <c>app/dashboard/lib/sessionSearchQuery.ts</c>.
/// </summary>
public class SessionFreeTextSearchTests
{
private const string CasesRepoPath = "src/Web/autopilot-monitor-web/utils/session-search-syntax.cases.json";
private const string CasesRepoPath = "src/Web/autopilot-monitor-web/app/dashboard/lib/session-search-syntax.cases.json";
private static SessionSummary Sample() => new()
{
@@ -12,7 +12,7 @@ namespace AutopilotMonitor.Functions.Tests;
/// <summary>
/// Cross-language parity anchor: reflects the wire-relevant catalogs out of
/// AutopilotMonitor.Shared (models, enums, string vocabularies) into a canonical JSON
/// manifest committed at <c>src/Web/autopilot-monitor-web/utils/shared-manifests.json</c>.
/// manifest committed at <c>src/Web/autopilot-monitor-web/lib/generated/shared-manifests.json</c>.
/// The web's vitest suite and compile-time checks compare their hand-written TS mirrors
/// against that manifest — so a C# change that would silently drift a TS mirror first
/// fails HERE, and the TS side fails until its mirror follows.
@@ -23,7 +23,7 @@ namespace AutopilotMonitor.Functions.Tests;
/// </summary>
public sealed class SharedManifestParityTests
{
private const string ManifestRepoPath = "src/Web/autopilot-monitor-web/utils/shared-manifests.json";
private const string ManifestRepoPath = "src/Web/autopilot-monitor-web/lib/generated/shared-manifests.json";
private const string WriteEnvVar = "AM_WRITE_SHARED_MANIFESTS";
[Fact]
@@ -155,7 +155,7 @@ public sealed class SharedManifestParityTests
// the portal's counters and upload budget read the same numbers the backend enforces.
["submissionLimits"] = ConstInts(typeof(Constants.SubmissionLimits)),
// Every IApiResponse + IApiRequest implementer + [WireContract] type, transitively closed —
// the source of utils/wire-types.generated.ts. See WireTypeManifestBuilder.
// the source of lib/generated/wire-types.generated.ts. See WireTypeManifestBuilder.
["types"] = WireTypeManifestBuilder.BuildTypesSection(),
};
@@ -9,8 +9,8 @@ namespace AutopilotMonitor.Functions.Helpers;
/// <summary>
/// The dashboard search-box grammar, parsed once per request for the server-side sweep
/// (<c>q=</c> on <c>/api/search/sessions</c> and the global twin). Mirrors the web's
/// <c>app/dashboard/utils/sessionSearchQuery.ts</c> exactly — both run
/// <c>utils/session-search-syntax.cases.json</c>, because a term that matches on one side
/// <c>app/dashboard/lib/sessionSearchQuery.ts</c> exactly — both run
/// <c>app/dashboard/lib/session-search-syntax.cases.json</c>, because a term that matches on one side
/// and not the other is a ghost result (found by the server, filtered out by the client, or
/// never fetched at all).
/// <para>
@@ -38,7 +38,7 @@ describe('generated wire types freshness (MCP copy)', () => {
const { buildMcpWireTypesSource } = require(
join(webRoot!, 'scripts', 'generate-shared-manifest-types.js'),
);
const json = readFileSync(join(webRoot!, 'utils', 'shared-manifests.json'), 'utf8');
const json = readFileSync(join(webRoot!, 'lib', 'generated', 'shared-manifests.json'), 'utf8');
const committed = readFileSync(
join(dirname(fileURLToPath(import.meta.url)), '..', 'generated', 'wire-types.generated.ts'),
'utf8',
@@ -59,7 +59,7 @@ describe('generated wire vocabularies freshness (MCP copy)', () => {
const { buildMcpVocabulariesSource } = require(
join(webRoot!, 'scripts', 'generate-shared-manifest-types.js'),
);
const json = readFileSync(join(webRoot!, 'utils', 'shared-manifests.json'), 'utf8');
const json = readFileSync(join(webRoot!, 'lib', 'generated', 'shared-manifests.json'), 'utf8');
const committed = readFileSync(
join(dirname(fileURLToPath(import.meta.url)), '..', 'generated', 'wire-vocabularies.generated.ts'),
'utf8',
@@ -1,5 +1,5 @@
// GENERATED — do not edit by hand. Second copy for the MCP server.
// Source: src/Web/autopilot-monitor-web/utils/shared-manifests.json.
// Source: src/Web/autopilot-monitor-web/lib/generated/shared-manifests.json.
// Regenerate: npm run generate:manifests in src/Web/autopilot-monitor-web.
//
// Wire types reflected from AutopilotMonitor.Shared (every IApiResponse and IApiRequest
@@ -1,5 +1,5 @@
// GENERATED — do not edit by hand. Second copy for the MCP server.
// Source: src/Web/autopilot-monitor-web/utils/shared-manifests.json.
// Source: src/Web/autopilot-monitor-web/lib/generated/shared-manifests.json.
// Regenerate: npm run generate:manifests in src/Web/autopilot-monitor-web.
//
// Vocabularies reflected from AutopilotMonitor.Shared. These are VALUES (not just types)
@@ -17,7 +17,7 @@ namespace AutopilotMonitor.Shared.Services
/// keyed by normalised code string (lowercase hex, e.g. <c>0x80070005</c>, or decimal MSI
/// exit code, e.g. <c>1603</c>), by symbol and by enforcement state.
/// <para>
/// Mirror of <c>src/Web/autopilot-monitor-web/utils/errorCodeMap.ts</c> and the MCP
/// Mirror of <c>src/Web/autopilot-monitor-web/lib/errorCodeMap.ts</c> and the MCP
/// server's <c>error-code-catalog.ts</c>; the JSON file is the authoritative copy.
/// </para>
/// </summary>
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { SITE_URL } from "@/utils/config";
import { SITE_URL } from "@/lib/config";
import { OPEN_GRAPH_BASE, TWITTER_BASE } from "@/lib/siteMetadata";
export const metadata: Metadata = {
@@ -1,7 +1,7 @@
import Link from "next/link";
import { LandingNavbar } from "../../components/landing/LandingNavbar";
import { SiteFooter } from "../../components/SiteFooter";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
import { ABOUT_FAQ } from "./faq";
// FAQPage structured data mirrors the visible "Common Questions" section
@@ -7,9 +7,9 @@ import { api } from "@/lib/api";
import { apiErrorText, fetchOk, jsonBody } from "@/lib/apiClient";
import { landingTarget } from "@/lib/landingTarget";
import { consumePostLoginReturnUrl } from "@/lib/postLoginReturn";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
import { BrandMark } from "../../components/BrandMark";
import type { SaveNotificationEmailRequest } from "@/utils/wire-types.generated";
import type { SaveNotificationEmailRequest } from "@/lib/generated/wire-types.generated";
// The URL query string is fixed for the lifetime of a page load — nothing to subscribe to.
const subscribeNever = () => () => {};
@@ -13,7 +13,7 @@ import type {
GetPreviewWhitelistResponse,
TestNotificationChannelRequest,
TestWebhookNotificationResponse,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
import type { NotificationChannel } from "@/app/settings/types";
// Re-export so existing `import { AdminConfiguration } from "../AdminConfigContext"` consumers keep working
@@ -8,7 +8,7 @@ import type {
RestoreRowPreviewResponse,
RestoreRowPropertyDiff,
RestoreRowPropertySnapshot,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
import { apiErrorText, fetchJson, jsonBody } from "@/lib/apiClient";
import { ModalPortal } from "@/components/ModalPortal";
@@ -8,7 +8,7 @@ import {
type BackupManifest,
type BackupTableEntry,
} from "@/lib/api";
import type { RestoreRowCommitResponse, RestoreRowPreviewResponse, RestoreRowRequest } from "@/utils/wire-types.generated";
import type { RestoreRowCommitResponse, RestoreRowPreviewResponse, RestoreRowRequest } from "@/lib/generated/wire-types.generated";
import { apiErrorText, fetchJson, jsonBody } from "@/lib/apiClient";
import { useAdminConfig } from "../../AdminConfigContext";
import { AdminNotifications } from "../../AdminNotifications";
@@ -4,7 +4,7 @@ import { backupUrl } from "@/lib/routes";
import { useCallback, useEffect, useState } from "react";
import Link from "next/link";
import { api } from "@/lib/api";
import type { BackupJobStatus, BackupTriggerResponse, ListBackupsResponse } from "@/utils/wire-types.generated";
import type { BackupJobStatus, BackupTriggerResponse, ListBackupsResponse } from "@/lib/generated/wire-types.generated";
import { apiErrorText, fetchJson } from "@/lib/apiClient";
import { useAdminConfig } from "../AdminConfigContext";
import { AdminNotifications } from "../AdminNotifications";
@@ -4,7 +4,7 @@ import { useState } from "react";
import { api } from "@/lib/api";
import { apiErrorText, fetchJson } from "@/lib/apiClient";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { ReseedFromGitHubResponse } from "@/utils/wire-types.generated";
import type { ReseedFromGitHubResponse } from "@/lib/generated/wire-types.generated";
interface ConfigReseedSectionProps {
getAccessToken: () => Promise<string | null>;
@@ -12,7 +12,7 @@ import { TenantSearchSelect } from "./TenantSearchSelect";
import { useCanMutatePlatform } from "@/hooks/useCanMutatePlatform";
import { firstBlockedSessionId, blockedSessionCount } from "./blockedDeviceHelpers";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { BlockDeviceRequest, BlockedDeviceEntry, BlockedDeviceListResponse, GetSessionResponse } from "@/utils/wire-types.generated";
import type { BlockDeviceRequest, BlockedDeviceEntry, BlockedDeviceListResponse, GetSessionResponse } from "@/lib/generated/wire-types.generated";
interface ResolvedDevice {
sessionId: string;
@@ -1,7 +1,7 @@
"use client";
import { useState, useMemo } from "react";
import { validateDiagnosticsPath } from "@/utils/guardValidation";
import { validateDiagnosticsPath } from "@/lib/guardValidation";
import { ValidationIndicator } from "@/components/ValidationIndicator";
import { BuiltInSectionsList } from "@/components/diagnostics/BuiltInSectionsList";
import { DiagnosticsPathRow } from "@/components/diagnostics/DiagnosticsPathRow";
@@ -4,7 +4,7 @@ import { useState, useEffect } from "react";
import { api } from "@/lib/api";
import { apiErrorText, fetchJson, nullOn404 } from "@/lib/apiClient";
import { TableSkeleton } from "@/components/skeletons/TableSkeleton";
import type { DistressReportEntry, DistressReportListResponse } from "@/utils/wire-types.generated";
import type { DistressReportEntry, DistressReportListResponse } from "@/lib/generated/wire-types.generated";
import { ModalPortal } from "@/components/ModalPortal";
/** One stored distress report — the wire shape (optional where the agent sent nothing). */
@@ -3,7 +3,7 @@
import { useCallback, useEffect, useState } from "react";
import { api } from "@/lib/api";
import { apiErrorText, fetchJson } from "@/lib/apiClient";
import type { FeedbackEntryWire, FeedbackListResponse } from "@/utils/wire-types.generated";
import type { FeedbackEntryWire, FeedbackListResponse } from "@/lib/generated/wire-types.generated";
import { avgRating, paginate, partitionFeedback, type FeedbackTab } from "@/lib/feedbackPartition";
/** One stored feedback entry — the wire shape (fields absent on dismissals). */
@@ -5,7 +5,7 @@ import { api } from "@/lib/api";
import { apiErrorText, fetchJson, fetchOk, jsonBody } from "@/lib/apiClient";
import { useCanMutatePlatform } from "@/hooks/useCanMutatePlatform";
import { isGuid, type IdentityBinding } from "@/lib/identityBinding";
import type { IdentityBindingListResponse, IdentityBindingRequest } from "@/utils/wire-types.generated";
import type { IdentityBindingListResponse, IdentityBindingRequest } from "@/lib/generated/wire-types.generated";
interface IdentityBindingsSectionProps {
tenantId: string;
@@ -7,7 +7,7 @@ import TruncatedLabel from "@/components/TruncatedLabel";
import { trackEvent } from "@/lib/appInsights";
import { TableSkeleton } from "@/components/skeletons/TableSkeleton";
import type { AutoResolveResult, IgnoredSoftwareEntry } from "./SoftwareMappingTypes";
import type { AutoResolveCpeMappingRequest, GetIgnoredSoftwareResponse, IgnoreSoftwareDeleteRequest } from "@/utils/wire-types.generated";
import type { AutoResolveCpeMappingRequest, GetIgnoredSoftwareResponse, IgnoreSoftwareDeleteRequest } from "@/lib/generated/wire-types.generated";
interface IgnoredSoftwareTabProps {
getAccessToken: () => Promise<string | null>;
@@ -6,7 +6,7 @@ import { ApiError, apiErrorText, fetchJson, fetchOk } from "@/lib/apiClient";
import { CONFIG_PATH_PREFIX, invalidateCachedAuthFetch } from "@/lib/cachedAuthFetch";
import { parseOpsDetails, useOpsRunStatus, type OpsRunLifecycle } from "@/hooks/useOpsRunStatus";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { GetLatestVersionsResponse } from "@/utils/wire-types.generated";
import type { GetLatestVersionsResponse } from "@/lib/generated/wire-types.generated";
const LIFECYCLE: OpsRunLifecycle = {
category: "Maintenance",
@@ -6,7 +6,7 @@ import { apiErrorText, fetchJson, fetchOk, jsonBody } from "@/lib/apiClient";
import { trackEvent } from "@/lib/appInsights";
import { CpeMappingEntry } from "./SoftwareMappingTypes";
import { TableSkeleton } from "@/components/skeletons/TableSkeleton";
import type { DeleteCustomCpeMappingRequest, GetCpeMappingsResponse, SaveCustomCpeMappingRequest } from "@/utils/wire-types.generated";
import type { DeleteCustomCpeMappingRequest, GetCpeMappingsResponse, SaveCustomCpeMappingRequest } from "@/lib/generated/wire-types.generated";
interface MappedSoftwareTabProps {
getAccessToken: () => Promise<string | null>;
@@ -10,7 +10,7 @@ import type { CursorPage } from "@/lib/cursorPager";
import { useCursorPager } from "@/hooks/useCursorPager";
import { extractSessionId, buildAutoReason } from "./opsEventSessionHelpers";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { OpsEventEntry, OpsEventListResponse } from "@/utils/wire-types.generated";
import type { OpsEventEntry, OpsEventListResponse } from "@/lib/generated/wire-types.generated";
import { ModalPortal } from "@/components/ModalPortal";
/** One ops event row (wire shape). */
@@ -7,7 +7,7 @@ import { apiErrorText, fetchJson, fetchOk, jsonBody, nullOn404 } from "@/lib/api
import TruncatedLabel from "@/components/TruncatedLabel";
import type { CursorPage } from "@/lib/cursorPager";
import { useCursorPager } from "@/hooks/useCursorPager";
import { isGuid } from "@/utils/inputValidation";
import { isGuid } from "@/lib/inputValidation";
import { trackEvent } from "@/lib/appInsights";
import { useCanMutatePlatform } from "@/hooks/useCanMutatePlatform";
import { TableSkeleton } from "@/components/skeletons/TableSkeleton";
@@ -22,7 +22,7 @@ import type {
RuleSubmissionFireStats,
RuleSubmissionItem,
RuleSubmissionListResponse,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
const PAGE_SIZE = 20;
const STATUS_FILTERS = ["pending", "approved", "declined", "withdrawn"] as const;
@@ -5,10 +5,10 @@ import { api } from "@/lib/api";
import { apiErrorText, fetchJson } from "@/lib/apiClient";
import { TenantConfiguration } from "./TenantManagementSection";
import { TenantSearchSelect } from "./TenantSearchSelect";
import { SessionExportEvent, generateCsvExport, generateUiExport } from "@/utils/sessionExportUtils";
import { SessionExportEvent, generateCsvExport, generateUiExport } from "@/lib/sessionExportUtils";
import { trackEvent } from "@/lib/appInsights";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { GetSessionEventsResponse } from "@/utils/wire-types.generated";
import type { GetSessionEventsResponse } from "@/lib/generated/wire-types.generated";
function downloadFile(content: string, filename: string, mimeType: string) {
const blob = new Blob([content], { type: mimeType });
@@ -7,12 +7,12 @@ import { apiErrorText, fetchJson, fetchOk, jsonBody, nullOn404 } from "@/lib/api
import TruncatedLabel from "@/components/TruncatedLabel";
import type { CursorPage } from "@/lib/cursorPager";
import { useCursorPager } from "@/hooks/useCursorPager";
import { isGuid } from "@/utils/inputValidation";
import { isGuid } from "@/lib/inputValidation";
import { trackEvent } from "@/lib/appInsights";
import { useCanMutatePlatform } from "@/hooks/useCanMutatePlatform";
import { TableSkeleton } from "@/components/skeletons/TableSkeleton";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { SessionReportDownloadUrlResponse, SessionReportListResponse, SessionReportMetadata, UpdateSessionReportNoteRequest } from "@/utils/wire-types.generated";
import type { SessionReportDownloadUrlResponse, SessionReportListResponse, SessionReportMetadata, UpdateSessionReportNoteRequest } from "@/lib/generated/wire-types.generated";
import { ModalPortal } from "@/components/ModalPortal";
const PAGE_SIZE = 20;
@@ -1,4 +1,4 @@
import type { UnmatchedSoftwareItem } from "@/utils/wire-types.generated";
import type { UnmatchedSoftwareItem } from "@/lib/generated/wire-types.generated";
/** One row of GET vulnerability/unmatched-software — the wire shape. */
export type UnmatchedSoftwareEntry = UnmatchedSoftwareItem;
@@ -6,8 +6,8 @@ import { apiErrorText, fetchJson, fetchOk, jsonBody } from "@/lib/apiClient";
import { useCanMutatePlatform } from "@/hooks/useCanMutatePlatform";
import { AddMemberForm } from "@/components/members/AddMemberForm";
import { MemberList } from "@/components/members/MemberList";
import type { AddTenantAdminRequest, TenantAdminRow, UpdateMemberPermissionsRequest } from "@/utils/wire-types.generated";
import { looksLikeGuid, type MemberKind } from "@/utils/principalKeys";
import type { AddTenantAdminRequest, TenantAdminRow, UpdateMemberPermissionsRequest } from "@/lib/generated/wire-types.generated";
import { looksLikeGuid, type MemberKind } from "@/lib/principalKeys";
// Wire type is generated from the backend DTO ("role" is absent for legacy pre-role rows).
type TenantAdmin = TenantAdminRow;
@@ -16,7 +16,7 @@ import type {
TenantConfiguration as WireTenantConfiguration,
TenantOffboardingStatusResponse,
UpdateTenantAppHomingResponse,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
import { useSearchParams } from "next/navigation";
import { api } from "@/lib/api";
import { ApiError, apiErrorText, fetchJson, fetchOk, jsonBody } from "@/lib/apiClient";
@@ -8,7 +8,7 @@ import TruncatedLabel from "@/components/TruncatedLabel";
import { TableSkeleton } from "@/components/skeletons/TableSkeleton";
import { trackEvent } from "@/lib/appInsights";
import type { UnmatchedSoftwareEntry, AutoResolveResult } from "./SoftwareMappingTypes";
import type { AutoResolveCpeMappingRequest, GetUnmatchedSoftwareResponse, IgnoreSoftwareRequest, SaveCustomCpeMappingRequest } from "@/utils/wire-types.generated";
import type { AutoResolveCpeMappingRequest, GetUnmatchedSoftwareResponse, IgnoreSoftwareRequest, SaveCustomCpeMappingRequest } from "@/lib/generated/wire-types.generated";
interface UnmappedSoftwareTabProps {
getAccessToken: () => Promise<string | null>;
@@ -5,7 +5,7 @@ import { api } from "@/lib/api";
import { apiErrorText, fetchJson, fetchOk, jsonBody } from "@/lib/apiClient";
import { useCanMutatePlatform } from "@/hooks/useCanMutatePlatform";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { BlockedVersionListResponse, BlockVersionRequest } from "@/utils/wire-types.generated";
import type { BlockedVersionListResponse, BlockVersionRequest } from "@/lib/generated/wire-types.generated";
interface BlockedVersion {
versionPattern: string;
@@ -18,7 +18,7 @@ import type {
TriggerMsrcSyncResponse,
TriggerNvdCacheRefreshResponse,
TriggerVulnerabilityDataSyncResponse,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
interface VulnerabilityDataSectionProps {
adminConfig: AdminConfiguration | null;
@@ -4,8 +4,8 @@
* list would 400 the save — and against the manifest.
*/
import { describe, expect, it } from "vitest";
import { SHARED_MANIFEST } from "@/utils/shared-manifests.generated";
import type { TenantConfiguration } from "@/utils/wire-types.generated";
import { SHARED_MANIFEST } from "@/lib/generated/shared-manifests.generated";
import type { TenantConfiguration } from "@/lib/generated/wire-types.generated";
import { TENANT_EDITOR_FIELDS, tenantEditorPatch } from "../tenantEditorFields";
/** BaseDeniedFields in TenantConfigPatchService, camelCased — independent copy. */
@@ -1,5 +1,5 @@
import { changedTenantConfigFields, type TenantConfigFieldName } from "@/lib/tenantConfigSave";
import type { TenantConfiguration } from "@/utils/wire-types.generated";
import type { TenantConfiguration } from "@/lib/generated/wire-types.generated";
/**
* The fields the Global Admin tenant editor saves with its generic Save. Plan, trial, delegated
@@ -8,7 +8,7 @@ import { apiErrorText, fetchJson, fetchOk } from "@/lib/apiClient";
import { useAdminConfig } from "../../AdminConfigContext";
import { AdminNotifications } from "../../AdminNotifications";
import { DeleteConfirmModal } from "../components/DeleteConfirmModal";
import type { CustomsArchiveEntryListResponse, CustomsArchiveEntryResponse, CustomsArchiveEntrySummary, TenantOffboardingCustomsArchiveEntry } from "@/utils/wire-types.generated";
import type { CustomsArchiveEntryListResponse, CustomsArchiveEntryResponse, CustomsArchiveEntrySummary, TenantOffboardingCustomsArchiveEntry } from "@/lib/generated/wire-types.generated";
export default function CustomsArchiveDetailPage() {
// useSearchParams() in CustomsArchiveDetailContent requires a Suspense boundary
@@ -8,7 +8,7 @@ import { apiErrorText, fetchJson } from "@/lib/apiClient";
import { useAdminConfig } from "../AdminConfigContext";
import { AdminNotifications } from "../AdminNotifications";
import { DeleteConfirmModal } from "./components/DeleteConfirmModal";
import type { CustomsArchiveDeleteRunResponse, CustomsArchiveRunListResponse, CustomsArchiveRunSummary } from "@/utils/wire-types.generated";
import type { CustomsArchiveDeleteRunResponse, CustomsArchiveRunListResponse, CustomsArchiveRunSummary } from "@/lib/generated/wire-types.generated";
export default function CustomsArchivePage() {
const { getAccessToken, setError, setSuccessMessage } = useAdminConfig();
@@ -6,8 +6,8 @@ import { useCallback, useEffect, useState } from "react";
import { useAuth } from "../../../../contexts/AuthContext";
import { apiErrorText, fetchJson } from "@/lib/apiClient";
import { api } from "@/lib/api";
import { isApplicationKey, principalLabel } from "@/utils/principalKeys";
import type { GetGlobalMcpUsageDailyResponse, GetGlobalMcpUsageResponse, UserUsageDailySummary } from "@/utils/wire-types.generated";
import { isApplicationKey, principalLabel } from "@/lib/principalKeys";
import type { GetGlobalMcpUsageDailyResponse, GetGlobalMcpUsageResponse, UserUsageDailySummary } from "@/lib/generated/wire-types.generated";
interface UsageRecord {
userId: string;
@@ -7,7 +7,7 @@ import { useAuth } from '../../../../contexts/AuthContext';
import { apiErrorText, fetchJson } from "@/lib/apiClient";
import { useFetchProgress } from "@/hooks/useFetchProgress";
import { CalculatingCard } from "@/components/CalculatingCard";
import type { PlatformUsageMetrics } from "@/utils/wire-types.generated";
import type { PlatformUsageMetrics } from "@/lib/generated/wire-types.generated";
// A fresh cross-tenant compute takes tens of seconds server-side; the default 30s fetch timeout
// aborted it client-side (HTTP 499 on the backend) while the server kept computing. Give this one
@@ -3,7 +3,7 @@
import { useState } from "react";
import { api } from "@/lib/api";
import { apiErrorText, fetchJson, jsonBody } from "@/lib/apiClient";
import type { RestoreSessionRequest, SessionRestoreResponse } from "@/utils/wire-types.generated";
import type { RestoreSessionRequest, SessionRestoreResponse } from "@/lib/generated/wire-types.generated";
import { ModalPortal } from "@/components/ModalPortal";
interface RestoreConfirmDialogProps {
@@ -9,7 +9,7 @@ import { HOME_TENANT_UNRESOLVED } from "@/lib/identityBinding";
import { parseSlotLimitError, type SlotLimitError } from "@/lib/delegatedSlots";
import { DelegatedSlotPrompt, raiseDelegatedSlotLimit } from "@/components/DelegatedSlotPrompt";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import type { DelegatedAdminListResponse, GrantDelegatedAdminRequest } from "@/utils/wire-types.generated";
import type { DelegatedAdminListResponse, GrantDelegatedAdminRequest } from "@/lib/generated/wire-types.generated";
/** One delegated-admin assignment as returned by /api/global/delegated-admins (camelCase JSON). */
interface DelegatedAssignment {
@@ -5,7 +5,7 @@ import { api } from "@/lib/api";
import { fetchJson, fetchOk, jsonBody } from "@/lib/apiClient";
import { useAdminConfig } from "../../AdminConfigContext";
import { AdminNotifications } from "../../AdminNotifications";
import type { EmailTemplateRequest, EmailTemplateTestSendResponse } from "@/utils/wire-types.generated";
import type { EmailTemplateRequest, EmailTemplateTestSendResponse } from "@/lib/generated/wire-types.generated";
import { ModalPortal } from "@/components/ModalPortal";
type TemplateKind = "welcome" | "farewell";
@@ -14,7 +14,7 @@ import type {
TenantGroup,
TenantGroupListResponse,
UpdateTenantGroupRequest,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
import { parseSlotLimitError, type SlotLimitError } from "@/lib/delegatedSlots";
import { DelegatedSlotPrompt, raiseDelegatedSlotLimit } from "@/components/DelegatedSlotPrompt";
@@ -10,7 +10,7 @@ import type {
PlanTierDefinitionsResponse,
SetPlanTierDefinitionsRequest,
UsagePlanCatalogDefaults,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
interface PlanTierDefinition {
name: string;
@@ -9,7 +9,7 @@ import type {
McpClientRegistrationListResponse,
TenantConfiguration,
TenantFeatureFlagsResponse,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
import {
isNonDefault,
RUNTIME_REPORT_SECTIONS,
@@ -9,8 +9,8 @@
* (AgentConfigResolverTests) — nothing is re-computed here.
*/
import { describe, expect, it } from "vitest";
import { SHARED_MANIFEST } from "@/utils/shared-manifests.generated";
import type { AgentConfigResponse } from "@/utils/wire-types.generated";
import { SHARED_MANIFEST } from "@/lib/generated/shared-manifests.generated";
import type { AgentConfigResponse } from "@/lib/generated/wire-types.generated";
import {
ANALYZER_FIELDS,
COLLECTOR_FIELDS,
@@ -14,13 +14,13 @@
* which the shared manifest carries as `defaults` (generated by the same test) — no
* hand-copied default anywhere in this file.
*/
import { SHARED_MANIFEST } from "@/utils/shared-manifests.generated";
import { SHARED_MANIFEST } from "@/lib/generated/shared-manifests.generated";
import type {
AgentConfigResponse,
AnalyzerConfiguration,
CollectorConfiguration,
TenantConfiguration,
} from "@/utils/wire-types.generated";
} from "@/lib/generated/wire-types.generated";
// ── Defaults from the manifest (compile-time complete over the wire types) ────
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { SITE_URL } from "@/utils/config";
import { SITE_URL } from "@/lib/config";
import { OPEN_GRAPH_BASE, TWITTER_BASE } from "@/lib/siteMetadata";
const SHARE_TITLE = "AI Analysis for Windows Autopilot – Bring Your Own AI";
@@ -7,7 +7,7 @@ import {
validateOnEventTypes,
} from "../types";
import { isReservedBuiltInRuleId, RESERVED_RULE_ID_MESSAGE } from "@/lib/ruleIdPolicy";
import { BLOCKED_INTERIM_TRIGGER_EVENT_TYPES } from "@/utils/guardrails.generated";
import { BLOCKED_INTERIM_TRIGGER_EVENT_TYPES } from "@/lib/generated/guardrails.generated";
interface AnalyzeRuleFormFieldsProps {
form: RuleForm;
@@ -19,7 +19,7 @@ import { groupRulesByCategory, categoryLabel } from "@/lib/ruleGroups";
import { RuleCategoryGroups } from "@/components/rules/RuleCategoryGroups";
import { useCollapsedGroups } from "@/hooks/useCollapsedGroups";
import { AnalyzeRule, RuleForm, PastedAnalyzeJson, EMPTY_FORM, EMPTY_CONDITION, ruleToForm, jsonToForm, formToEvaluateOn, validateOnEventTypes, getCategoryColor } from "./types";
import { BLOCKED_INTERIM_TRIGGER_EVENT_TYPES } from "@/utils/guardrails.generated";
import { BLOCKED_INTERIM_TRIGGER_EVENT_TYPES } from "@/lib/generated/guardrails.generated";
/** Pre-flight for the evaluation-trigger form fields; returns an error message or null. */
function validateEvaluateOnForm(form: RuleForm): string | null {
@@ -36,7 +36,7 @@ import TemplateConfigModal from "./components/TemplateConfigModal";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import { DOCS_PATHS } from "@/lib/docsPaths";
import { DocsLink } from "@/components/DocsLink";
import type { CreateAnalyzeRuleFromTemplateRequest, RuleStatsResponse, TenantConfiguration } from "@/utils/wire-types.generated";
import type { CreateAnalyzeRuleFromTemplateRequest, RuleStatsResponse, TenantConfiguration } from "@/lib/generated/wire-types.generated";
import { fetchJson, jsonBody } from "@/lib/apiClient";
import { useWindowDays } from "@/hooks/useWindowDays";
import { utcDateDaysAgo } from "@/lib/timeWindow";
@@ -5,7 +5,7 @@ import { useAuth } from "../../../contexts/AuthContext";
import { useNotifications } from "../../../contexts/NotificationContext";
import { scopedApi } from "@/lib/scopedApi";
import VulnerabilityExposurePanel from "@/components/VulnerabilityExposurePanel";
import type { CveExposureSummary } from "@/utils/wire-types.generated";
import type { CveExposureSummary } from "@/lib/generated/wire-types.generated";
import type { SoftwareTabScope } from "./types";
import { windowProgressKey } from "@/lib/timeWindow";
import { ApiError, fetchJson } from "@/lib/apiClient";
@@ -9,11 +9,11 @@ import { useTenant } from "../../../contexts/TenantContext";
import { useAuth } from "../../../contexts/AuthContext";
import { useNotifications } from "../../../contexts/NotificationContext";
import { scopedApi } from "@/lib/scopedApi";
import { getErrorCodeEntry, formatErrorCode, formatErrorCodeSource } from "@/utils/errorCodeMap";
import { getErrorCodeEntry, formatErrorCode, formatErrorCodeSource } from "@/lib/errorCodeMap";
import { trackEvent } from "@/lib/appInsights";
// Static import is safe: appReportData is pure data prep with no jsPDF dependency —
// jsPDF itself only loads with the dynamic import inside handleExportPdf.
import { scopeLabel } from "@/utils/pdf/appReportData";
import { scopeLabel } from "@/lib/pdf/appReportData";
import { useAggregatedAdminScope } from "@/hooks";
import { GlobalAdminBanner, globalAdminSubtitle } from "@/components/GlobalAdminBanner";
import { TenantScopeSelector } from "@/components/TenantScopeSelector";
@@ -374,7 +374,7 @@ function AppDetailContent() {
try {
// Lazy import keeps jsPDF out of the route bundle; a failed chunk load
// (deploy skew, offline) lands in the catch below and is tracked too.
const { generateAppReportPdf } = await import("@/utils/pdf/appReportPdf");
const { generateAppReportPdf } = await import("@/lib/pdf/appReportPdf");
const result = generateAppReportPdf({
analytics,
appName,
@@ -13,7 +13,7 @@ import { TenantScopeSelector } from "@/components/TenantScopeSelector";
import { GlobalAdminBanner, globalAdminSubtitle } from "@/components/GlobalAdminBanner";
import { DocsLink } from "@/components/DocsLink";
import { DOCS_PATHS } from "@/lib/docsPaths";
import type { AuditLogListResponse } from "@/utils/wire-types.generated";
import type { AuditLogListResponse } from "@/lib/generated/wire-types.generated";
import { fetchJson } from "@/lib/apiClient";
import { notifyApiError } from "@/contexts/NotificationContext";
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { SITE_URL } from "@/utils/config";
import { SITE_URL } from "@/lib/config";
import { OPEN_GRAPH_BASE, TWITTER_BASE } from "@/lib/siteMetadata";
export const metadata: Metadata = {
@@ -1,7 +1,7 @@
import Link from "next/link";
import { LandingNavbar } from "../../components/landing/LandingNavbar";
import { SiteFooter } from "../../components/SiteFooter";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
/**
* Purchase page for Pro: hands the buyer off to one of the two purchase channels. Linked from
@@ -1,7 +1,7 @@
"use client";
import { useSyncExternalStore } from "react";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
import { trackEvent } from "@/lib/appInsights";
import { useWhatsNew } from "@/hooks/useWhatsNew";
import { openFeedbackDialog } from "@/lib/feedbackDialogStore";
@@ -2,7 +2,7 @@
import { useEffect, useSyncExternalStore } from "react";
import Link from "next/link";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
import { DOCS_PATHS } from "@/lib/docsPaths";
import { trackEvent } from "@/lib/appInsights";
@@ -7,14 +7,14 @@ import type { Route } from "next";
import { useState, useEffect, useRef, useMemo, useDeferredValue } from "react";
import { Session } from "../types";
import { trackEvent } from "@/lib/appInsights";
import { fuzzyContains } from "@/utils/fuzzy";
import { fuzzyContains } from "@/lib/fuzzy";
import { buildUniqueValuesByField } from "./uniqueValuesByField";
import {
SESSION_SEARCH_FIELDS,
buildSessionSearchMatcher,
matchedSearchField,
parseSessionSearchQuery,
} from "../utils/sessionSearchQuery";
} from "../lib/sessionSearchQuery";
import { SessionStatusBadge } from "@/components/SessionStatusBadge";
import { TenantFilterBar } from "./TenantFilterBar";
import type { DeleteTarget } from "../hooks/useDeleteSession";
@@ -1,6 +1,6 @@
"use client";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
export function WelcomeMessage() {
return (
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { AppHomingBanner } from "../AppHomingBanner";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
describe("AppHomingBanner", () => {
it("renders the nudge with the settings CTA, the docs link and a per-tab dismiss control", () => {
@@ -5,7 +5,7 @@ import { TokenExpiredError } from "@/lib/authenticatedFetch";
import { NotificationType } from "@/contexts/NotificationContext";
import { BULK_CONCURRENCY, runWithConcurrency, summarizeBlockOutcomes, type BlockOutcome } from "./bulkActions";
import { ApiError, fetchOk, jsonBody } from "@/lib/apiClient";
import type { BlockDeviceRequest } from "@/utils/wire-types.generated";
import type { BlockDeviceRequest } from "@/lib/generated/wire-types.generated";
export interface BlockTarget {
serialNumber: string;
@@ -3,7 +3,7 @@
import { useDeferredValue, useEffect, useMemo, useState } from "react";
import { trackEvent } from "@/lib/appInsights";
import type { Session } from "../types";
import { buildSessionSearchMatcher, parseSessionSearchQuery } from "../utils/sessionSearchQuery";
import { buildSessionSearchMatcher, parseSessionSearchQuery } from "../lib/sessionSearchQuery";
const SESSIONS_PER_PAGE_KEY = "sessionsPerPage";
const DEFAULT_SESSIONS_PER_PAGE = 10;
@@ -119,7 +119,7 @@ export function useDashboardFilters({
const deferredSearchQuery = useDeferredValue(searchQuery);
// Search grammar (terms AND-ed, `-` excludes, quotes, `field=value`) — see
// utils/sessionSearchQuery. Null while the query carries no filter.
// lib/sessionSearchQuery. Null while the query carries no filter.
const searchMatcher = useMemo(() => buildSessionSearchMatcher(deferredSearchQuery), [deferredSearchQuery]);
// The client-derived `extra` text is read by free terms only (a qualified term reads its one
// field), so it is built only while the query has one.
@@ -4,16 +4,16 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { api } from "@/lib/api";
import { useLatest } from "@/hooks/useLatest";
import { extractContinuation } from "@/lib/paginationLink";
import { asGuidOrUndefined } from "@/utils/inputValidation";
import { boundTenantToDelegatedScope } from "@/utils/delegatedScope";
import { asGuidOrUndefined } from "@/lib/inputValidation";
import { boundTenantToDelegatedScope } from "@/lib/delegatedScope";
import { mergeSessionsById } from "@/lib/sessionSearchMerge";
import { isHomeTenantTarget } from "@/utils/homeTenantScope";
import { isHomeTenantTarget } from "@/lib/homeTenantScope";
import { hasTenantReadScope } from "@/lib/tenantScope";
import { useSignalRResync } from "@/contexts/SignalRContext";
import { type NotificationType, notifyApiError } from "@/contexts/NotificationContext";
import type { Session } from "../types";
import type { SignalRMessageName } from "@/lib/signalrMessages";
import type { BlockedDeviceListResponse, SearchSessionsResponse, SessionListResponse } from "@/utils/wire-types.generated";
import type { BlockedDeviceListResponse, SearchSessionsResponse, SessionListResponse } from "@/lib/generated/wire-types.generated";
import { ApiError, fetchJson, nullOnApiError } from "@/lib/apiClient";
import { getSessionsFetchPageSize } from "./sessionsPageSize";
@@ -4,11 +4,11 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { api } from "@/lib/api";
import { useLatest } from "@/hooks/useLatest";
import { useSignalRResync } from "@/contexts/SignalRContext";
import { asGuidOrUndefined } from "@/utils/inputValidation";
import { boundTenantToDelegatedScope } from "@/utils/delegatedScope";
import { isHomeTenantTarget } from "@/utils/homeTenantScope";
import { asGuidOrUndefined } from "@/lib/inputValidation";
import { boundTenantToDelegatedScope } from "@/lib/delegatedScope";
import { isHomeTenantTarget } from "@/lib/homeTenantScope";
import type { SignalRMessageName } from "@/lib/signalrMessages";
import type { SessionStatsResponse } from "@/utils/wire-types.generated";
import type { SessionStatsResponse } from "@/lib/generated/wire-types.generated";
import { ApiError, fetchJson } from "@/lib/apiClient";
import { DASHBOARD_STATS_DEFAULT_DAYS } from "./sessionsPageSize";
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import cases from "@/utils/session-search-syntax.cases.json";
import cases from "@/app/dashboard/lib/session-search-syntax.cases.json";
import {
SESSION_SEARCH_FIELDS,
buildSessionSearchMatcher,
@@ -1,5 +1,5 @@
{
"$comment": "Parity cases for the session search grammar. Run by the web (app/dashboard/utils/__tests__/sessionSearchQuery.test.ts) and the backend (SessionFreeTextSearchTests) against the SAME sample session: a query that matches on one side must match on the other, or the server sweep returns ghost results the client filters back out. Cases with `extra` are client-only (client-derived tokens the server never sees). All values are synthetic.",
"$comment": "Parity cases for the session search grammar. Run by the web (app/dashboard/lib/__tests__/sessionSearchQuery.test.ts) and the backend (SessionFreeTextSearchTests) against the SAME sample session: a query that matches on one side must match on the other, or the server sweep returns ghost results the client filters back out. Cases with `extra` are client-only (client-derived tokens the server never sees). All values are synthetic.",
"session": {
"deviceName": "DESKTOP-A1B2C3",
"serialNumber": "5CG1234XYZ",
@@ -16,7 +16,7 @@
// `foo=bar`). Qualified terms are substring matches like every other term — exact
// matching is the column filter's job. The backend parses the SAME grammar for the
// server-side sweep (`q=` on /api/search/sessions, SessionSearchQuery.cs); both sides
// run utils/session-search-syntax.cases.json, so a term that matches here matches there.
// run app/dashboard/lib/session-search-syntax.cases.json, so a term that matches here matches there.
import type { Session } from "../types";
import { literalText, tokenizeSearchQuery } from "@/lib/searchQueryTokens";
@@ -23,9 +23,9 @@ import { useDeleteSession } from "./hooks/useDeleteSession";
import { useBlockDevice } from "./hooks/useBlockDevice";
import { useTenantSecurityConfig } from "./hooks/useTenantSecurityConfig";
import { useTenantList } from "./hooks/useTenantList";
import { delegatedScopedTenantList, upnDomain } from "@/utils/homeTenantScope";
import { resolveTenantFilterInput } from "@/utils/tenantFilterResolve";
import { isGuid } from "@/utils/inputValidation";
import { delegatedScopedTenantList, upnDomain } from "@/lib/homeTenantScope";
import { resolveTenantFilterInput } from "@/lib/tenantFilterResolve";
import { isGuid } from "@/lib/inputValidation";
import { useDashboardFilters } from "./hooks/useDashboardFilters";
import { useDashboardSessions } from "./hooks/useDashboardSessions";
import { useDashboardStats } from "./hooks/useDashboardStats";
@@ -33,7 +33,7 @@ import { api } from "@/lib/api";
import { formatDuration } from "@/lib/formatting";
import { hasTenantReadScope } from "@/lib/tenantScope";
import { TableSkeleton } from "@/components/skeletons/TableSkeleton";
import type { GetRuleHitSessionsResponse } from "@/utils/wire-types.generated";
import type { GetRuleHitSessionsResponse } from "@/lib/generated/wire-types.generated";
import { fetchJson } from "@/lib/apiClient";
export default function Home() {
@@ -158,7 +158,7 @@ function HomeContent() {
const rawTenantList = useTenantList(crossTenant, getAccessToken);
// Delegated: bound the tenant filter's autocomplete to the managed subset (defense in depth on top of the
// backend-bounded config/all), plus the caller's own HOME tenant when they hold a member role there —
// home-tenant reads route via the member path (see utils/homeTenantScope.ts). GA/Reader: the full list.
// home-tenant reads route via the member path (see lib/homeTenantScope.ts). GA/Reader: the full list.
const tenantList = useMemo(() => {
if (!isDelegated || hasGlobalScope) return rawTenantList;
return delegatedScopedTenantList(
@@ -6,7 +6,7 @@ import { useSearchParams } from "next/navigation";
import { useAuth } from "@/contexts/AuthContext";
import { api } from "@/lib/api";
import { describeDelegationError, invitationStatusLabel } from "@/lib/delegations";
import type { AcceptDelegationInvitationRequest, AcceptDelegationInvitationResponse, DelegationAcceptPreviewResponse } from "@/utils/wire-types.generated";
import type { AcceptDelegationInvitationRequest, AcceptDelegationInvitationResponse, DelegationAcceptPreviewResponse } from "@/lib/generated/wire-types.generated";
import { ApiError, apiErrorText, fetchJson, jsonBody } from "@/lib/apiClient";
import { CONFIG_PATH_PREFIX, invalidateCachedAuthFetch } from "@/lib/cachedAuthFetch";
@@ -14,11 +14,11 @@ import { interpolateRuleTemplate } from "@/lib/interpolateRuleTemplate";
import { ConfidenceBadge, SeverityBadge } from "./components/DiagnosisBadges";
import { Session, EnrollmentEvent, RuleResult } from "@/types";
import { useAdminMode } from "@/hooks/useAdminMode";
import { isGuid } from "@/utils/inputValidation";
import { isGuid } from "@/lib/inputValidation";
import { safeHttpUrl } from "@/lib/safeDocUrl";
import { DocsLink } from "@/components/DocsLink";
import { DOCS_PATHS } from "@/lib/docsPaths";
import type { GetRuleResultsResponse, GetSessionEventsResponse, GetSessionResponse } from "@/utils/wire-types.generated";
import type { GetRuleResultsResponse, GetSessionEventsResponse, GetSessionResponse } from "@/lib/generated/wire-types.generated";
import { fetchJson } from "@/lib/apiClient";
import { notifyApiError } from "@/contexts/NotificationContext";
+2 -2
View File
@@ -2,7 +2,7 @@
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { isChunkLoadError, tryRecoverFromChunkError } from "@/utils/chunkReloadRecovery";
import { isChunkLoadError, tryRecoverFromChunkError } from "@/lib/chunkReloadRecovery";
import { trackEvent } from "@/lib/appInsights";
/**
@@ -21,7 +21,7 @@ export default function Error({
const router = useRouter();
// Stale-bundle chunk failure after a deploy: reload once instead of showing the
// error card (utils/chunkReloadRecovery.ts — the guard prevents reload loops; a
// error card (lib/chunkReloadRecovery.ts — the guard prevents reload loops; a
// repeat inside the guard window falls through to the card below).
useEffect(() => {
if (isChunkLoadError(error)) {
@@ -6,7 +6,7 @@ import { BrandMark } from "@/components/BrandMark";
import { GitHubStarLink } from "@/components/GitHubStarLink";
import { SiteFooter } from "@/components/SiteFooter";
import { CUSTOMER_QUOTE } from "@/components/landing/customerQuote";
import { DOCS_URL, sitePageUrl } from "@/utils/config";
import { DOCS_URL, sitePageUrl } from "@/lib/config";
import {
COMMUNITY_COMPANIONS,
EVENT_PAGE_ROBOTS,
@@ -7,7 +7,7 @@ import sitemap from "../../sitemap";
import EventFollowupPage, { generateMetadata, generateStaticParams } from "../[slug]/page";
import { COMMUNITY_COMPANIONS, FOLLOWUP_EVENTS, WALKTHROUGH_EMAIL, walkthroughMailto } from "../events";
import { trackIdOf } from "../eventClicks";
import { sitePageUrl } from "@/utils/config";
import { sitePageUrl } from "@/lib/config";
/**
* Event follow-up pages are reached only through the follow-up email: never indexed, never
@@ -10,7 +10,7 @@ import { ApiError, fetchJson } from "@/lib/apiClient";
// Wire shape of the server-aggregated Fleet Health payload: the generated mirror of the
// backend FleetHealthMetrics DTO. Presentation-only derivations (bar maxima, axis
// labels) stay in the page.
import type { FleetHealthMetrics } from "@/utils/wire-types.generated";
import type { FleetHealthMetrics } from "@/lib/generated/wire-types.generated";
export type FleetHealthData = FleetHealthMetrics;
@@ -4,8 +4,8 @@ import { Suspense, useEffect, useState, useRef, useMemo } from "react";
import Link from "next/link";
import type { Route } from "next";
import { dashboardUrl } from "@/lib/routes";
import { buildSessionSearchQuery } from "../dashboard/utils/sessionSearchQuery";
import type { FleetModelHealth } from "@/utils/wire-types.generated";
import { buildSessionSearchQuery } from "../dashboard/lib/sessionSearchQuery";
import type { FleetModelHealth } from "@/lib/generated/wire-types.generated";
import { ProtectedRoute } from "../../components/ProtectedRoute";
import { useSignalR } from "../../contexts/SignalRContext";
import { useTenant } from "../../contexts/TenantContext";
@@ -3,9 +3,9 @@
import { useEffect, useState } from "react";
import { useAuth } from "@/contexts/AuthContext";
import { api } from "@/lib/api";
import { isHomeTenantTarget } from "@/utils/homeTenantScope";
import { isHomeTenantTarget } from "@/lib/homeTenantScope";
import type { FleetSummary } from "../lib/fleetRollup";
import type { SessionStatsResponse } from "@/utils/wire-types.generated";
import type { SessionStatsResponse } from "@/lib/generated/wire-types.generated";
import { fetchJson, nullOnApiError } from "@/lib/apiClient";
export interface FleetSummariesState {
@@ -27,7 +27,7 @@ const CONCURRENCY = 6;
* @param days lookback window passed to each per-tenant stats call.
* @param homeTenantId the caller's OWN home tenant, if it is part of tenantIds: its summary is fetched via
* the JWT-bound member stats endpoint — home access is member-based, and the /global/ single-tenant path
* is bounded to the managed set (would return empty for it). See utils/homeTenantScope.ts.
* is bounded to the managed set (would return empty for it). See lib/homeTenantScope.ts.
*/
export function useFleetSummaries(tenantIds: string[], days: number, homeTenantId?: string): FleetSummariesState {
const { getAccessToken } = useAuth();
@@ -5,7 +5,7 @@ import Link from "next/link";
import { dashboardUrl } from "@/lib/routes";
import { useAuth } from "@/contexts/AuthContext";
import { useTenantList, type TenantInfo } from "@/hooks/useTenantList";
import { delegatedScopedTenantList, upnDomain } from "@/utils/homeTenantScope";
import { delegatedScopedTenantList, upnDomain } from "@/lib/homeTenantScope";
import { useFleetSummaries } from "./hooks/useFleetSummaries";
import { computeFleetRollup, type FleetSummary } from "./lib/fleetRollup";
@@ -26,7 +26,7 @@ export default function FleetPage() {
const tenants = useTenantList(isDelegated);
// Managed subset plus the caller's own HOME tenant when they hold a member role there — the home
// tenant is member-path access, not a delegated grant (see utils/homeTenantScope.ts), so its
// tenant is member-path access, not a delegated grant (see lib/homeTenantScope.ts), so its
// summary is fetched via the JWT-bound member stats endpoint below.
const myTenants = useMemo(
() => delegatedScopedTenantList(tenants, user?.delegatedTenantIds, user?.tenantId, upnDomain(user?.upn), !!user?.role),
@@ -4,7 +4,7 @@ import { useMemo, useState } from "react";
import { GatherRule, NewRuleForm, PastedGatherJson, getCategoryColor, COLLECTOR_TYPE_LABELS, formatTrigger, formatGatherPhase, gatherRuleToForm } from "../types";
import { GatherRuleFormFields } from "./GatherRuleFormFields";
import { FormJsonToggle, JsonModeToggleButtons, ReadOnlyJsonView } from "@/components/rules/FormJsonToggle";
import { validateGatherRuleTarget } from "@/utils/guardValidation";
import { validateGatherRuleTarget } from "@/lib/guardValidation";
import { ValidationIndicator } from "@/components/ValidationIndicator";
import { stripInternalFields } from "@/lib/rulePageHelpers";
@@ -20,7 +20,7 @@ import {
supportsEmitMode,
} from "../types";
import { KNOWN_EVENT_TYPES, findEventType } from "../eventTypes";
import { validateGatherRuleTarget } from "@/utils/guardValidation";
import { validateGatherRuleTarget } from "@/lib/guardValidation";
import { ValidationIndicator } from "@/components/ValidationIndicator";
import { isReservedBuiltInRuleId, RESERVED_RULE_ID_MESSAGE } from "@/lib/ruleIdPolicy";
@@ -22,7 +22,7 @@ import { GatherRuleCard } from "./components/GatherRuleCard";
import { SectionCardHeader } from "@/components/SectionCardHeader";
import { DOCS_PATHS } from "@/lib/docsPaths";
import { DocsLink } from "@/components/DocsLink";
import type { TenantFeatureFlagsResponse } from "@/utils/wire-types.generated";
import type { TenantFeatureFlagsResponse } from "@/lib/generated/wire-types.generated";
import { fetchJson } from "@/lib/apiClient";
import { CommunityContributionBox } from "@/components/rules/CommunityContributionBox";
import { MySubmissionsList } from "@/components/rules/MySubmissionsList";
@@ -1,4 +1,4 @@
import { validateGatherRuleTarget } from "@/utils/guardValidation";
import { validateGatherRuleTarget } from "@/lib/guardValidation";
export interface GatherRule {
ruleId: string;
@@ -4,7 +4,7 @@ import { useEffect, useMemo } from "react";
import { MapContainer, TileLayer, CircleMarker, Tooltip, useMap } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import { formatThroughput } from "@/lib/formatting";
import type { GlobalAverages, LocationMetrics } from "@/utils/wire-types.generated";
import type { GlobalAverages, LocationMetrics } from "@/lib/generated/wire-types.generated";
import { bucketFilterAllows, type BucketFilter, type MapColorMode } from "./mapColorModes";
interface GeoMapProps {
@@ -3,7 +3,7 @@ import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { LocationTableNote, LOCATION_TABLE_NOTE } from "../LocationTableNote";
import { DOCS_PATHS } from "@/lib/docsPaths";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
describe("LocationTableNote", () => {
const html = renderToStaticMarkup(createElement(LocationTableNote));
@@ -1,5 +1,5 @@
import { describe, it, expect } from "vitest";
import type { GlobalAverages, LocationMetrics } from "@/utils/wire-types.generated";
import type { GlobalAverages, LocationMetrics } from "@/lib/generated/wire-types.generated";
import {
DEFAULT_MAP_COLOR_MODE,
MAP_COLOR_MODES,
@@ -1,4 +1,4 @@
import type { GlobalAverages, LocationMetrics } from "@/utils/wire-types.generated";
import type { GlobalAverages, LocationMetrics } from "@/lib/generated/wire-types.generated";
/**
* Single source of truth for how a location is colour-coded on the Geographic Performance page.
@@ -19,7 +19,7 @@ import { CalculatingCard } from "@/components/CalculatingCard";
import { DocsLink } from "@/components/DocsLink";
import { DOCS_PATHS } from "@/lib/docsPaths";
import { formatThroughput } from "@/lib/formatting";
import type { GeographicMetricsResponse } from "@/utils/wire-types.generated";
import type { GeographicMetricsResponse } from "@/lib/generated/wire-types.generated";
import {
DEFAULT_MAP_COLOR_MODE,
MAP_COLOR_MODES,
@@ -10,14 +10,14 @@ import { api } from "@/lib/api";
import { useAdminMode } from "@/hooks/useAdminMode";
import { SessionStatusBadge } from "@/components/SessionStatusBadge";
import { GlobalAdminBanner } from "@/components/GlobalAdminBanner";
import { boundTenantToDelegatedScope } from "@/utils/delegatedScope";
import { isHomeTenantTarget } from "@/utils/homeTenantScope";
import { boundTenantToDelegatedScope } from "@/lib/delegatedScope";
import { isHomeTenantTarget } from "@/lib/homeTenantScope";
import { useFetchProgress } from "@/hooks/useFetchProgress";
import { useWindowDays } from "@/hooks/useWindowDays";
import { windowProgressKey } from "@/lib/timeWindow";
import { GEO_DEFAULT_WINDOW_DAYS } from "../geoWindow";
import { CalculatingCard } from "@/components/CalculatingCard";
import type { GeographicLocationSessionsResponse, LocationSessionRow } from "@/utils/wire-types.generated";
import type { GeographicLocationSessionsResponse, LocationSessionRow } from "@/lib/generated/wire-types.generated";
import { fetchJson } from "@/lib/apiClient";
// A cross-tenant drilldown can take tens of seconds server-side; the default 30s fetch
@@ -3,7 +3,7 @@ import { LandingNavbar } from "../../components/landing/LandingNavbar";
import { SignupConsentCta } from "../../components/landing/SignupConsentCta";
import { SiteFooter } from "../../components/SiteFooter";
import { MarketingTracker } from "../../components/MarketingTracker";
import { DOCS_URL, SITE_URL } from "@/utils/config";
import { DOCS_URL, SITE_URL } from "@/lib/config";
export const metadata: Metadata = {
title: "Get started",
@@ -17,7 +17,7 @@ import {
MCP_PROBE_TIMEOUT_MS,
} from '@/lib/healthCheckView';
import { useLatest } from '@/hooks/useLatest';
import type { DetailedHealthCheckResponse, HealthCheck, McpHealthCheckResponse } from '@/utils/wire-types.generated';
import type { DetailedHealthCheckResponse, HealthCheck, McpHealthCheckResponse } from '@/lib/generated/wire-types.generated';
import { DocsLink } from "@/components/DocsLink";
import { DOCS_PATHS } from "@/lib/docsPaths";
import { ApiError, fetchJson } from "@/lib/apiClient";
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { SITE_URL } from "@/utils/config";
import { SITE_URL } from "@/lib/config";
import { OPEN_GRAPH_BASE, TWITTER_BASE } from "@/lib/siteMetadata";
export const metadata: Metadata = {
@@ -1,7 +1,7 @@
import { LandingNavbar } from "../../components/landing/LandingNavbar";
import { WhatsNewTile } from "../../components/landing/WhatsNewTile";
import { SiteFooter } from "../../components/SiteFooter";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
const GITHUB_ISSUES = "https://github.com/okieselbach/AutopilotMonitor/issues";
const LINKEDIN_PROFILE = "https://www.linkedin.com/in/oliver-kieselbach/";
+1 -1
View File
@@ -19,7 +19,7 @@ import AppInsightsInit from "../components/AppInsightsInit";
import ChunkReloadRecovery from "../components/ChunkReloadRecovery";
import { HostRoutingGuard } from "../components/HostRoutingGuard";
import { LegacyPathRedirect } from "../components/LegacyPathRedirect";
import { API_BASE_URL, DOCS_URL, SITE_URL, sitePageUrl } from "@/utils/config";
import { API_BASE_URL, DOCS_URL, SITE_URL, sitePageUrl } from "@/lib/config";
import { AUTH_HINT_INLINE_SCRIPT } from "@/lib/authHint";
const inter = Inter({ subsets: ["latin"] });
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { SITE_URL } from "@/utils/config";
import { SITE_URL } from "@/lib/config";
import { OPEN_GRAPH_BASE, TWITTER_BASE } from "@/lib/siteMetadata";
export const metadata: Metadata = {
@@ -2,7 +2,7 @@ import Link from "next/link";
import { LandingNavbar } from "../../components/landing/LandingNavbar";
import { SiteFooter } from "../../components/SiteFooter";
import { PlanCards } from "../../components/plans/PlanCards";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
/**
* Public plans page. Deliberately named "Plans" (not "Pricing"): the message is
@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { SITE_URL } from "@/utils/config";
import { SITE_URL } from "@/lib/config";
import { OPEN_GRAPH_BASE } from "@/lib/siteMetadata";
export const metadata: Metadata = {
@@ -1,6 +1,6 @@
import { LandingNavbar } from "../../components/landing/LandingNavbar";
import { SiteFooter } from "../../components/SiteFooter";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
const LAST_UPDATED = "1 October 2026";
const DOCS_SECURITY_FAQ = `${DOCS_URL}/trust/security-faq`;
@@ -1,6 +1,6 @@
import { DOCS_PATHS } from "@/lib/docsPaths";
import { SUPPORT_EMAIL } from "@/lib/supportContact";
import { DOCS_URL } from "@/utils/config";
import { DOCS_URL } from "@/lib/config";
import type { ProgressAccessHint as Hint } from "../hooks/progressAccessHint";
export const ACCESS_HINT_DOCS_URL = `${DOCS_URL}${DOCS_PATHS.progressPortalOnly}`;

Some files were not shown because too many files have changed in this diff Show More