From 92e10a2b1758a3e0ef4d6641453a8d7c768843ed Mon Sep 17 00:00:00 2001 From: Maximilian Kaske Date: Wed, 8 Jul 2026 21:43:24 +0200 Subject: [PATCH] feat: custom-theme api --- .../status-page/__tests__/status-page.test.ts | 126 +++++++++++++++ .../rpc/handlers/status-page/converters.ts | 26 ++++ .../routes/rpc/handlers/status-page/index.ts | 143 +++++++++++++----- .../routes/rpc/handlers/status-page/limits.ts | 10 ++ .../openstatus/status_page/v1/service.proto | 8 + .../status_page/v1/status_page.proto | 14 ++ .../openstatus/status_page/v1/service_pb.ts | 20 ++- .../status_page/v1/status_page_pb.ts | 41 ++++- 8 files changed, 345 insertions(+), 43 deletions(-) diff --git a/apps/server/src/routes/rpc/handlers/status-page/__tests__/status-page.test.ts b/apps/server/src/routes/rpc/handlers/status-page/__tests__/status-page.test.ts index d153fd31..6217d0dc 100644 --- a/apps/server/src/routes/rpc/handlers/status-page/__tests__/status-page.test.ts +++ b/apps/server/src/routes/rpc/handlers/status-page/__tests__/status-page.test.ts @@ -67,6 +67,9 @@ beforeAll(async () => { await db.run( sql`UPDATE workspace SET limits = json_set(COALESCE(limits, '{}'), '$."status-subscribers"', json('true')) WHERE id = 1`, ); + await db.run( + sql`UPDATE workspace SET limits = json_set(COALESCE(limits, '{}'), '$."custom-theme"', json('true')) WHERE id = 1`, + ); // Clean up any existing test data await db @@ -427,6 +430,45 @@ describe("StatusPageService.CreateStatusPage", () => { await db.delete(page).where(eq(page.id, firstPage.id)); } }); + + test("creates a status page with a custom theme", async () => { + const res = await connectRequest( + "CreateStatusPage", + { + title: `${TEST_PREFIX}-custom-theme`, + slug: `${TEST_PREFIX}-custom-theme-slug`, + customTheme: { light: { "--primary": "hsl(24 94% 50%)" } }, + }, + { "x-openstatus-key": "1" }, + ); + + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.statusPage.customTheme?.light).toEqual({ + "--primary": "hsl(24 94% 50%)", + }); + + // Clean up + await db.delete(page).where(eq(page.id, Number(data.statusPage.id))); + }); + + test("returns 403 when creating with custom theme on free plan", async () => { + const res = await connectRequest( + "CreateStatusPage", + { + title: `${TEST_PREFIX}-custom-theme-denied`, + slug: `${TEST_PREFIX}-custom-theme-denied-slug`, + customTheme: { light: { "--primary": "red" } }, + }, + { "x-openstatus-key": "2" }, + ); + + expect(res.status).toBe(403); + + const data = await res.json(); + expect(data.message).toContain("Upgrade for custom theme"); + }); }); describe("StatusPageService.GetStatusPage", () => { @@ -705,6 +747,90 @@ describe("StatusPageService.UpdateStatusPage", () => { .set({ defaultLocale: "en", locales: null }) .where(eq(page.id, testPageToUpdateId)); }); + + test("updates and clears the custom theme", async () => { + const res = await connectRequest( + "UpdateStatusPage", + { + id: String(testPageToUpdateId), + customTheme: { + light: { "--primary": "hsl(24 94% 50%)" }, + dark: { "--background": "oklch(0.2 0 0)" }, + }, + }, + { "x-openstatus-key": "1" }, + ); + + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.statusPage.customTheme?.light).toEqual({ + "--primary": "hsl(24 94% 50%)", + }); + expect(data.statusPage.customTheme?.dark).toEqual({ + "--background": "oklch(0.2 0 0)", + }); + + // Empty message clears the stored overrides + const clearRes = await connectRequest( + "UpdateStatusPage", + { + id: String(testPageToUpdateId), + customTheme: {}, + }, + { "x-openstatus-key": "1" }, + ); + + expect(clearRes.status).toBe(200); + + const clearData = await clearRes.json(); + expect(clearData.statusPage.customTheme).toBeUndefined(); + }); + + test("rejects unknown custom theme variables", async () => { + const res = await connectRequest( + "UpdateStatusPage", + { + id: String(testPageToUpdateId), + customTheme: { light: { "--not-a-var": "red" } }, + }, + { "x-openstatus-key": "1" }, + ); + + expect(res.status).toBe(400); + }); + + test("returns 403 when updating custom theme on free plan", async () => { + const freePage = await db + .insert(page) + .values({ + workspaceId: 2, + title: `${TEST_PREFIX}-free-theme`, + slug: `${TEST_PREFIX}-free-theme-slug`, + description: "", + customDomain: "", + }) + .returning() + .get(); + + try { + const res = await connectRequest( + "UpdateStatusPage", + { + id: String(freePage.id), + customTheme: { light: { "--primary": "red" } }, + }, + { "x-openstatus-key": "2" }, + ); + + expect(res.status).toBe(403); + + const data = await res.json(); + expect(data.message).toContain("Upgrade for custom theme"); + } finally { + await db.delete(page).where(eq(page.id, freePage.id)); + } + }); }); // ========================================================================== diff --git a/apps/server/src/routes/rpc/handlers/status-page/converters.ts b/apps/server/src/routes/rpc/handlers/status-page/converters.ts index 5452c018..7c29b732 100644 --- a/apps/server/src/routes/rpc/handlers/status-page/converters.ts +++ b/apps/server/src/routes/rpc/handlers/status-page/converters.ts @@ -1,5 +1,6 @@ import type { Locale } from "@openstatus/locales"; import type { + CustomTheme, PageComponent, PageComponentGroup, PageSubscriber, @@ -18,6 +19,10 @@ import { Locale as ProtoLocale, SubscriberSource, } from "@openstatus/proto/status_page/v1"; +import { + type CustomTheme as DbCustomTheme, + hasCustomTheme, +} from "@openstatus/theme-store"; /** * Database types @@ -40,6 +45,7 @@ type DBPage = { defaultLocale: Locale; locales: Locale[] | null; allowIndex: boolean; + customTheme?: DbCustomTheme | null; createdAt: Date | null; updatedAt: Date | null; }; @@ -304,6 +310,26 @@ export function dbPageToProto(page: DBPage): StatusPage { authEmailDomains: page.authEmailDomains?.split(",").filter(Boolean) ?? [], allowIndex: page.allowIndex ?? true, allowedIpRanges: page.allowedIpRanges ?? "", + customTheme: dbCustomThemeToProto(page.customTheme), + }; +} + +export function dbCustomThemeToProto( + customTheme: DbCustomTheme | null | undefined, +): CustomTheme | undefined { + if (!hasCustomTheme(customTheme)) return undefined; + // ThemeVars values are `string | undefined`; proto maps require `string`. + const pick = (vars?: Record) => { + const out: Record = {}; + for (const [name, value] of Object.entries(vars ?? {})) { + if (typeof value === "string") out[name] = value; + } + return out; + }; + return { + $typeName: "openstatus.status_page.v1.CustomTheme" as const, + light: pick(customTheme.light), + dark: pick(customTheme.dark), }; } diff --git a/apps/server/src/routes/rpc/handlers/status-page/index.ts b/apps/server/src/routes/rpc/handlers/status-page/index.ts index f16f314e..5b704c2d 100644 --- a/apps/server/src/routes/rpc/handlers/status-page/index.ts +++ b/apps/server/src/routes/rpc/handlers/status-page/index.ts @@ -27,6 +27,7 @@ import { import type { ComponentDayBucket, ComponentEvent, + CustomTheme, GetPageComponentDailySummaryResponse, PageComponentDailySummary, StatusPageService, @@ -50,6 +51,7 @@ import { listPages, updatePageAppearance, updatePageCustomDomain, + updatePageCustomTheme, updatePageGeneral, updatePageLinks, updatePageLocales, @@ -64,7 +66,11 @@ import { upsertSelfSignupSubscriber, } from "@openstatus/services/page-subscriber"; import { getChannel } from "@openstatus/subscriptions"; -import { THEME_KEYS, type ThemeKey } from "@openstatus/theme-store"; +import { + THEME_KEYS, + type ThemeKey, + validateCustomTheme, +} from "@openstatus/theme-store"; import { toConnectError, toServiceCtx } from "../../adapter"; import { getRpcContext } from "../../interceptors"; @@ -107,6 +113,7 @@ import { } from "./errors"; import { checkCustomDomainLimit, + checkCustomThemeLimit, checkEmailDomainProtectionLimit, checkIpRestrictionLimit, checkNoIndexLimit, @@ -247,6 +254,24 @@ function validateAllowedIpRanges(ranges: string): string[] { /** * Helper to get a component by ID with workspace scope. */ +// Proto map values arrive unvalidated; check var names / safe values at the +// handler so callers get a readable InvalidArgument instead of the service's +// raw zod message. +function validateProtoCustomTheme(customTheme: CustomTheme): { + light: Record; + dark: Record; +} { + const input = { + light: { ...customTheme.light }, + dark: { ...customTheme.dark }, + }; + const result = validateCustomTheme(input); + if (!result.valid) { + throw new ConnectError(result.errors.join(" "), Code.InvalidArgument); + } + return input; +} + async function getComponentById(id: number, workspaceId: number) { return db .select() @@ -397,6 +422,14 @@ export const statusPageServiceImpl: ServiceImpl = { checkNoIndexLimit(limits); } + let customThemeInput: + | ReturnType + | undefined; + if (req.customTheme !== undefined) { + checkCustomThemeLimit(limits); + customThemeInput = validateProtoCustomTheme(req.customTheme); + } + // `published` relies on DB default (false). The service's // CreatePageInput type doesn't surface the column, and its behavior // matches the legacy `published: false` write on create. @@ -406,46 +439,63 @@ export const statusPageServiceImpl: ServiceImpl = { // parse time. The service destructures it and uses // `ctx.workspace.id` on insert (so the input value is ignored), // but the zod parse fires first and rejects without the field. - const created = await createPage({ - ctx: sCtx, - input: { - workspaceId: sCtx.workspace.id, - title: req.title, - description: req.description ?? "", - slug: req.slug, - customDomain, - icon, - forceTheme, - accessType, - password, - authEmailDomains, - allowedIpRanges, - homepageUrl: req.homepageUrl ?? null, - contactUrl: req.contactUrl ?? null, - defaultLocale, - locales, - allowIndex, - }, - }).catch((err) => { - // Same handler-layer remap as the `i18n` pre-check above — - // preserve `PermissionDenied` (403) for "plan quota reached" - // on `status-pages`. The service throws `LimitExceededError` - // which the Connect adapter maps to `ResourceExhausted` - // (429), but the gRPC contract here is 403 for "upgrade - // required". - if ( - err instanceof LimitExceededError && - err.message.startsWith("status-pages") - ) { - throw new ConnectError( - "Upgrade for more status pages.", - Code.PermissionDenied, - ); + // Wrap create + custom-theme write in one transaction so a rejected + // theme write can't leave a page created without it. + const created = await withTransaction(sCtx, async (tx) => { + const txCtx = { ...sCtx, db: tx }; + const row = await createPage({ + ctx: txCtx, + input: { + workspaceId: sCtx.workspace.id, + title: req.title, + description: req.description ?? "", + slug: req.slug, + customDomain, + icon, + forceTheme, + accessType, + password, + authEmailDomains, + allowedIpRanges, + homepageUrl: req.homepageUrl ?? null, + contactUrl: req.contactUrl ?? null, + defaultLocale, + locales, + allowIndex, + }, + }).catch((err) => { + // Same handler-layer remap as the `i18n` pre-check above — + // preserve `PermissionDenied` (403) for "plan quota reached" + // on `status-pages`. The service throws `LimitExceededError` + // which the Connect adapter maps to `ResourceExhausted` + // (429), but the gRPC contract here is 403 for "upgrade + // required". + if ( + err instanceof LimitExceededError && + err.message.startsWith("status-pages") + ) { + throw new ConnectError( + "Upgrade for more status pages.", + Code.PermissionDenied, + ); + } + throw err; + }); + + if (customThemeInput) { + await updatePageCustomTheme({ + ctx: txCtx, + input: { id: row.id, customTheme: customThemeInput }, + }); } - throw err; + + return row; }); - return { statusPage: dbPageToProto(serviceToConverterPage(created)) }; + const result = customThemeInput + ? await getPage({ ctx: sCtx, input: { id: created.id } }) + : created; + return { statusPage: dbPageToProto(serviceToConverterPage(result)) }; } catch (err) { toConnectError(err); } @@ -639,6 +689,16 @@ export const statusPageServiceImpl: ServiceImpl = { req.customDomain !== undefined ? req.customDomain : undefined; const accessChanged = hasAccessType || hasAllowIndex; + // Omitted = keep; empty message = clear (the service maps an empty + // var set to null). + let customThemeForUpdate: + | ReturnType + | undefined; + if (req.customTheme !== undefined) { + checkCustomThemeLimit(limits); + customThemeForUpdate = validateProtoCustomTheme(req.customTheme); + } + // Wrap all per-section updates in a single transaction so partial // failures don't leave the page in a half-updated state. Each // per-section service call's internal `withTransaction` detects @@ -734,6 +794,13 @@ export const statusPageServiceImpl: ServiceImpl = { }); } + if (customThemeForUpdate !== undefined) { + await updatePageCustomTheme({ + ctx: txCtx, + input: { id: pageId, customTheme: customThemeForUpdate }, + }); + } + if (localesChanged) { await updatePageLocales({ ctx: txCtx, diff --git a/apps/server/src/routes/rpc/handlers/status-page/limits.ts b/apps/server/src/routes/rpc/handlers/status-page/limits.ts index 2ff3b262..86c5f72a 100644 --- a/apps/server/src/routes/rpc/handlers/status-page/limits.ts +++ b/apps/server/src/routes/rpc/handlers/status-page/limits.ts @@ -86,6 +86,16 @@ export function checkNoIndexLimit(limits: Limits): void { } } +/** + * Check if the custom theme feature is available on the workspace plan. + * Throws ConnectError with PermissionDenied if not available. + */ +export function checkCustomThemeLimit(limits: Limits): void { + if (!limits["custom-theme"]) { + throw new ConnectError("Upgrade for custom theme", Code.PermissionDenied); + } +} + export function checkStatusSubscribersLimit(limits: Limits): void { if (!limits["status-subscribers"]) { throw new ConnectError( diff --git a/packages/proto/api/openstatus/status_page/v1/service.proto b/packages/proto/api/openstatus/status_page/v1/service.proto index bd5eb70d..1a03a3c1 100644 --- a/packages/proto/api/openstatus/status_page/v1/service.proto +++ b/packages/proto/api/openstatus/status_page/v1/service.proto @@ -186,6 +186,10 @@ message CreateStatusPageRequest { // Comma-separated IPv4 CIDR ranges (required when access_type is IP_RESTRICTED). optional string allowed_ip_ranges = 15; + + // Per-mode CSS variable overrides merged over the theme (optional). + // Only supported variable names are accepted. Requires the custom-theme plan feature. + optional CustomTheme custom_theme = 16; } // CreateStatusPageResponse is the response after creating a status page. @@ -286,6 +290,10 @@ message UpdateStatusPageRequest { // Comma-separated IPv4 CIDR ranges (required when access_type is IP_RESTRICTED). optional string allowed_ip_ranges = 16; + + // New per-mode CSS variable overrides (optional). Omit to keep the current + // value; send an empty message to clear. Requires the custom-theme plan feature. + optional CustomTheme custom_theme = 17; } // UpdateStatusPageResponse is the response after updating a status page. diff --git a/packages/proto/api/openstatus/status_page/v1/status_page.proto b/packages/proto/api/openstatus/status_page/v1/status_page.proto index 116609e3..447ba015 100644 --- a/packages/proto/api/openstatus/status_page/v1/status_page.proto +++ b/packages/proto/api/openstatus/status_page/v1/status_page.proto @@ -43,6 +43,17 @@ enum OverallStatus { OVERALL_STATUS_UNKNOWN = 6; } +// CustomTheme holds per-mode CSS variable overrides merged over the page theme. +message CustomTheme { + // CSS variable overrides applied in light mode, keyed by variable name. + map light = 1 [ + (gnostic.openapi.v3.property) = {example: {yaml: "{\"--primary\": \"hsl(24 94% 50%)\"}"}} + ]; + + // CSS variable overrides applied in dark mode, keyed by variable name. + map dark = 2; +} + // StatusPage represents a full status page with all details. message StatusPage { // Unique identifier for the status page. @@ -109,6 +120,9 @@ message StatusPage { // Comma-separated IPv4 CIDR ranges (only set when access_type is IP_RESTRICTED). string allowed_ip_ranges = 19; + + // Per-mode CSS variable overrides merged over the theme (only set when configured). + optional CustomTheme custom_theme = 20; } // StatusPageSummary represents metadata for a status page (used in list responses). diff --git a/packages/proto/gen/ts/openstatus/status_page/v1/service_pb.ts b/packages/proto/gen/ts/openstatus/status_page/v1/service_pb.ts index be926d2a..19ab4e6d 100644 --- a/packages/proto/gen/ts/openstatus/status_page/v1/service_pb.ts +++ b/packages/proto/gen/ts/openstatus/status_page/v1/service_pb.ts @@ -12,7 +12,7 @@ import type { PageComponent, PageComponentGroup, PageComponentType } from "./pag import { file_openstatus_status_page_v1_page_component } from "./page_component_pb.ts"; import type { PageSubscriber } from "./page_subscriber_pb.ts"; import { file_openstatus_status_page_v1_page_subscriber } from "./page_subscriber_pb.ts"; -import type { Locale, OverallStatus, PageAccessType, PageTheme, StatusPage, StatusPageSummary } from "./status_page_pb.ts"; +import type { CustomTheme, Locale, OverallStatus, PageAccessType, PageTheme, StatusPage, StatusPageSummary } from "./status_page_pb.ts"; import { file_openstatus_status_page_v1_status_page } from "./status_page_pb.ts"; import type { PageComponentImpact, StatusReport } from "../../status_report/v1/status_report_pb.ts"; import { file_openstatus_status_report_v1_status_report } from "../../status_report/v1/status_report_pb.ts"; @@ -22,7 +22,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file openstatus/status_page/v1/service.proto. */ export const file_openstatus_status_page_v1_service: GenFile = /*@__PURE__*/ - fileDesc("CidvcGVuc3RhdHVzL3N0YXR1c19wYWdlL3YxL3NlcnZpY2UucHJvdG8SGW9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEi6gYKF0NyZWF0ZVN0YXR1c1BhZ2VSZXF1ZXN0EjAKBXRpdGxlGAEgASgJQiG6RxQ6EhIQQWNtZSBDb3JwIFN0YXR1c7pIB3IFEAEYgAISIgoLZGVzY3JpcHRpb24YAiABKAlCCLpIBXIDGIAISACIAQESSQoEc2x1ZxgDIAEoCUI7ukcSOhASDm15LXN0YXR1cy1wYWdlukgjciEQARiAAjIaXlthLXowLTldKyg/Oi1bYS16MC05XSspKiQSOQoMaG9tZXBhZ2VfdXJsGAQgASgJQh66Rxs6GRIXaHR0cHM6Ly93d3cuZXhhbXBsZS5jb21IAYgBARIYCgtjb250YWN0X3VybBgFIAEoCUgCiAEBEj4KDmRlZmF1bHRfbG9jYWxlGAYgASgOMiEub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5Mb2NhbGVIA4gBARIyCgdsb2NhbGVzGAcgAygOMiEub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5Mb2NhbGUSGwoEaWNvbhgIIAEoCUIIukgFcgMYgAhIBIgBARIkCg1jdXN0b21fZG9tYWluGAkgASgJQgi6SAVyAxiAAkgFiAEBEjgKBXRoZW1lGAogASgOMiQub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlVGhlbWVIBogBARJDCgthY2Nlc3NfdHlwZRgLIAEoDjIpLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUFjY2Vzc1R5cGVIB4gBARIhCghwYXNzd29yZBgMIAEoCUIKukgHcgUQARiAAkgIiAEBEhoKEmF1dGhfZW1haWxfZG9tYWlucxgNIAMoCRIYCgthbGxvd19pbmRleBgOIAEoCEgJiAEBEh4KEWFsbG93ZWRfaXBfcmFuZ2VzGA8gASgJSAqIAQFCDgoMX2Rlc2NyaXB0aW9uQg8KDV9ob21lcGFnZV91cmxCDgoMX2NvbnRhY3RfdXJsQhEKD19kZWZhdWx0X2xvY2FsZUIHCgVfaWNvbkIQCg5fY3VzdG9tX2RvbWFpbkIICgZfdGhlbWVCDgoMX2FjY2Vzc190eXBlQgsKCV9wYXNzd29yZEIOCgxfYWxsb3dfaW5kZXhCFAoSX2FsbG93ZWRfaXBfcmFuZ2VzIlYKGENyZWF0ZVN0YXR1c1BhZ2VSZXNwb25zZRI6CgtzdGF0dXNfcGFnZRgBIAEoCzIlLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuU3RhdHVzUGFnZSIrChRHZXRTdGF0dXNQYWdlUmVxdWVzdBITCgJpZBgBIAEoCUIHukgEcgIQASJTChVHZXRTdGF0dXNQYWdlUmVzcG9uc2USOgoLc3RhdHVzX3BhZ2UYASABKAsyJS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN0YXR1c1BhZ2UiagoWTGlzdFN0YXR1c1BhZ2VzUmVxdWVzdBIdCgVsaW1pdBgBIAEoBUIJukgGGgQYZCgBSACIAQESHAoGb2Zmc2V0GAIgASgFQge6SAQaAigASAGIAQFCCAoGX2xpbWl0QgkKB19vZmZzZXQicQoXTGlzdFN0YXR1c1BhZ2VzUmVzcG9uc2USQgoMc3RhdHVzX3BhZ2VzGAEgAygLMiwub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5TdGF0dXNQYWdlU3VtbWFyeRISCgp0b3RhbF9zaXplGAIgASgFItAGChdVcGRhdGVTdGF0dXNQYWdlUmVxdWVzdBITCgJpZBgBIAEoCUIHukgEcgIQARIeCgV0aXRsZRgCIAEoCUIKukgHcgUQARiAAkgAiAEBEiIKC2Rlc2NyaXB0aW9uGAMgASgJQgi6SAVyAxiACEgBiAEBEjkKBHNsdWcYBCABKAlCJrpII3IhEAEYgAIyGl5bYS16MC05XSsoPzotW2EtejAtOV0rKSokSAKIAQESGQoMaG9tZXBhZ2VfdXJsGAUgASgJSAOIAQESGAoLY29udGFjdF91cmwYBiABKAlIBIgBARI+Cg5kZWZhdWx0X2xvY2FsZRgHIAEoDjIhLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTG9jYWxlSAWIAQESMgoHbG9jYWxlcxgIIAMoDjIhLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTG9jYWxlEhsKBGljb24YCSABKAlCCLpIBXIDGIAISAaIAQESJAoNY3VzdG9tX2RvbWFpbhgKIAEoCUIIukgFcgMYgAJIB4gBARI4CgV0aGVtZRgLIAEoDjIkLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZVRoZW1lSAiIAQESQwoLYWNjZXNzX3R5cGUYDCABKA4yKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VBY2Nlc3NUeXBlSAmIAQESIQoIcGFzc3dvcmQYDSABKAlCCrpIB3IFEAEYgAJICogBARIaChJhdXRoX2VtYWlsX2RvbWFpbnMYDiADKAkSGAoLYWxsb3dfaW5kZXgYDyABKAhIC4gBARIeChFhbGxvd2VkX2lwX3JhbmdlcxgQIAEoCUgMiAEBQggKBl90aXRsZUIOCgxfZGVzY3JpcHRpb25CBwoFX3NsdWdCDwoNX2hvbWVwYWdlX3VybEIOCgxfY29udGFjdF91cmxCEQoPX2RlZmF1bHRfbG9jYWxlQgcKBV9pY29uQhAKDl9jdXN0b21fZG9tYWluQggKBl90aGVtZUIOCgxfYWNjZXNzX3R5cGVCCwoJX3Bhc3N3b3JkQg4KDF9hbGxvd19pbmRleEIUChJfYWxsb3dlZF9pcF9yYW5nZXMiVgoYVXBkYXRlU3RhdHVzUGFnZVJlc3BvbnNlEjoKC3N0YXR1c19wYWdlGAEgASgLMiUub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5TdGF0dXNQYWdlIi4KF0RlbGV0ZVN0YXR1c1BhZ2VSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABIisKGERlbGV0ZVN0YXR1c1BhZ2VSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIIu8BChpBZGRNb25pdG9yQ29tcG9uZW50UmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhsKCm1vbml0b3JfaWQYAiABKAlCB7pIBHICEAESGwoEbmFtZRgDIAEoCUIIukgFcgMYgAJIAIgBARIiCgtkZXNjcmlwdGlvbhgEIAEoCUIIukgFcgMYgAhIAYgBARISCgVvcmRlchgFIAEoBUgCiAEBEhUKCGdyb3VwX2lkGAYgASgJSAOIAQFCBwoFX25hbWVCDgoMX2Rlc2NyaXB0aW9uQggKBl9vcmRlckILCglfZ3JvdXBfaWQiWgobQWRkTW9uaXRvckNvbXBvbmVudFJlc3BvbnNlEjsKCWNvbXBvbmVudBgBIAEoCzIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUNvbXBvbmVudCLFAQoZQWRkU3RhdGljQ29tcG9uZW50UmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhgKBG5hbWUYAiABKAlCCrpIB3IFEAEYgAISIgoLZGVzY3JpcHRpb24YAyABKAlCCLpIBXIDGIAISACIAQESEgoFb3JkZXIYBCABKAVIAYgBARIVCghncm91cF9pZBgFIAEoCUgCiAEBQg4KDF9kZXNjcmlwdGlvbkIICgZfb3JkZXJCCwoJX2dyb3VwX2lkIlkKGkFkZFN0YXRpY0NvbXBvbmVudFJlc3BvbnNlEjsKCWNvbXBvbmVudBgBIAEoCzIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUNvbXBvbmVudCItChZSZW1vdmVDb21wb25lbnRSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABIioKF1JlbW92ZUNvbXBvbmVudFJlc3BvbnNlEg8KB3N1Y2Nlc3MYASABKAgi8wEKFlVwZGF0ZUNvbXBvbmVudFJlcXVlc3QSEwoCaWQYASABKAlCB7pIBHICEAESGwoEbmFtZRgCIAEoCUIIukgFcgMYgAJIAIgBARIiCgtkZXNjcmlwdGlvbhgDIAEoCUIIukgFcgMYgAhIAYgBARISCgVvcmRlchgEIAEoBUgCiAEBEhUKCGdyb3VwX2lkGAUgASgJSAOIAQESGAoLZ3JvdXBfb3JkZXIYBiABKAVIBIgBAUIHCgVfbmFtZUIOCgxfZGVzY3JpcHRpb25CCAoGX29yZGVyQgsKCV9ncm91cF9pZEIOCgxfZ3JvdXBfb3JkZXIiVgoXVXBkYXRlQ29tcG9uZW50UmVzcG9uc2USOwoJY29tcG9uZW50GAEgASgLMigub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlQ29tcG9uZW50In0KG0NyZWF0ZUNvbXBvbmVudEdyb3VwUmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhgKBG5hbWUYAiABKAlCCrpIB3IFEAEYgAISGQoMZGVmYXVsdF9vcGVuGAMgASgISACIAQFCDwoNX2RlZmF1bHRfb3BlbiJcChxDcmVhdGVDb21wb25lbnRHcm91cFJlc3BvbnNlEjwKBWdyb3VwGAEgASgLMi0ub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlQ29tcG9uZW50R3JvdXAiMgobRGVsZXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABIi8KHERlbGV0ZUNvbXBvbmVudEdyb3VwUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCCKGAQobVXBkYXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABEh0KBG5hbWUYAiABKAlCCrpIB3IFEAEYgAJIAIgBARIZCgxkZWZhdWx0X29wZW4YAyABKAhIAYgBAUIHCgVfbmFtZUIPCg1fZGVmYXVsdF9vcGVuIlwKHFVwZGF0ZUNvbXBvbmVudEdyb3VwUmVzcG9uc2USPAoFZ3JvdXAYASABKAsyLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VDb21wb25lbnRHcm91cCJhChZTdWJzY3JpYmVUb1BhZ2VSZXF1ZXN0EhgKB3BhZ2VfaWQYASABKAlCB7pIBHICEAESLQoFZW1haWwYAiABKAlCHrpHFDoSEhB1c2VyQGV4YW1wbGUuY29tukgEcgJgASJYChdTdWJzY3JpYmVUb1BhZ2VSZXNwb25zZRI9CgpzdWJzY3JpYmVyGAEgASgLMikub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlU3Vic2NyaWJlciImCgxFbWFpbENoYW5uZWwSFgoFZW1haWwYASABKAlCB7pIBHICYAEiOwoUV2ViaG9va0NoYW5uZWxIZWFkZXISFAoDa2V5GAEgASgJQge6SARyAhABEg0KBXZhbHVlGAIgASgJInYKDldlYmhvb2tDaGFubmVsEiIKC3dlYmhvb2tfdXJsGAEgASgJQg26SApyCBABGIAQiAEBEkAKB2hlYWRlcnMYAiADKAsyLy5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLldlYmhvb2tDaGFubmVsSGVhZGVyIpACCh1DcmVhdGVQYWdlU3Vic2NyaXB0aW9uUmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhsKBG5hbWUYAiABKAlCCLpIBXIDGP8BSAGIAQESFQoNY29tcG9uZW50X2lkcxgDIAMoCRJACg1lbWFpbF9jaGFubmVsGAogASgLMicub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5FbWFpbENoYW5uZWxIABJECg93ZWJob29rX2NoYW5uZWwYCyABKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLldlYmhvb2tDaGFubmVsSABCEAoHY2hhbm5lbBIFukgCCAFCBwoFX25hbWUiXwoeQ3JlYXRlUGFnZVN1YnNjcmlwdGlvblJlc3BvbnNlEj0KCnN1YnNjcmliZXIYASABKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VTdWJzY3JpYmVyImMKGlVuc3Vic2NyaWJlRnJvbVBhZ2VSZXF1ZXN0EhgKB3BhZ2VfaWQYASABKAlCB7pIBHICEAESDwoFZW1haWwYAiABKAlIABIMCgJpZBgDIAEoCUgAQgwKCmlkZW50aWZpZXIiLgobVW5zdWJzY3JpYmVGcm9tUGFnZVJlc3BvbnNlEg8KB3N1Y2Nlc3MYASABKAgiwAEKFkxpc3RTdWJzY3JpYmVyc1JlcXVlc3QSGAoHcGFnZV9pZBgBIAEoCUIHukgEcgIQARIdCgVsaW1pdBgCIAEoBUIJukgGGgQYZCgBSACIAQESHAoGb2Zmc2V0GAMgASgFQge6SAQaAigASAGIAQESIQoUaW5jbHVkZV91bnN1YnNjcmliZWQYBCABKAhIAogBAUIICgZfbGltaXRCCQoHX29mZnNldEIXChVfaW5jbHVkZV91bnN1YnNjcmliZWQibQoXTGlzdFN1YnNjcmliZXJzUmVzcG9uc2USPgoLc3Vic2NyaWJlcnMYASADKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VTdWJzY3JpYmVyEhIKCnRvdGFsX3NpemUYAiABKAUiSQobR2V0U3RhdHVzUGFnZUNvbnRlbnRSZXF1ZXN0EgwKAmlkGAEgASgJSAASDgoEc2x1ZxgCIAEoCUgAQgwKCmlkZW50aWZpZXIi3wIKHEdldFN0YXR1c1BhZ2VDb250ZW50UmVzcG9uc2USOgoLc3RhdHVzX3BhZ2UYASABKAsyJS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN0YXR1c1BhZ2USPAoKY29tcG9uZW50cxgCIAMoCzIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUNvbXBvbmVudBI9CgZncm91cHMYAyADKAsyLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VDb21wb25lbnRHcm91cBJBCg5zdGF0dXNfcmVwb3J0cxgEIAMoCzIpLm9wZW5zdGF0dXMuc3RhdHVzX3JlcG9ydC52MS5TdGF0dXNSZXBvcnQSQwoMbWFpbnRlbmFuY2VzGAUgAygLMi0ub3BlbnN0YXR1cy5tYWludGVuYW5jZS52MS5NYWludGVuYW5jZVN1bW1hcnkiRQoXR2V0T3ZlcmFsbFN0YXR1c1JlcXVlc3QSDAoCaWQYASABKAlIABIOCgRzbHVnGAIgASgJSABCDAoKaWRlbnRpZmllciJhCg9Db21wb25lbnRTdGF0dXMSFAoMY29tcG9uZW50X2lkGAEgASgJEjgKBnN0YXR1cxgCIAEoDjIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuT3ZlcmFsbFN0YXR1cyKkAQoYR2V0T3ZlcmFsbFN0YXR1c1Jlc3BvbnNlEkAKDm92ZXJhbGxfc3RhdHVzGAEgASgOMigub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5PdmVyYWxsU3RhdHVzEkYKEmNvbXBvbmVudF9zdGF0dXNlcxgCIAMoCzIqLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ29tcG9uZW50U3RhdHVzIp0BCiNHZXRQYWdlQ29tcG9uZW50RGFpbHlTdW1tYXJ5UmVxdWVzdBIMCgJpZBgBIAEoCUgAEg4KBHNsdWcYAiABKAlIABIjCg1jb21wb25lbnRfaWRzGAMgAygJQgy6SAmSAQYiBHICEAESHAoEZGF5cxgEIAEoBUIJukgGGgQYLSgBSAGIAQFCDAoKaWRlbnRpZmllckIHCgVfZGF5cyLuAQoSQ29tcG9uZW50RGF5QnVja2V0EgsKA2RheRgBIAEoCRINCgVjb3VudBgCIAEoAxIKCgJvaxgDIAEoAxIQCghkZWdyYWRlZBgEIAEoAxINCgVlcnJvchgFIAEoAxI9CgZzdGF0dXMYBiABKA4yLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNvbXBvbmVudERheVN0YXR1cxJFCgZpbXBhY3QYByABKA4yMC5vcGVuc3RhdHVzLnN0YXR1c19yZXBvcnQudjEuUGFnZUNvbXBvbmVudEltcGFjdEgAiAEBQgkKB19pbXBhY3QioAIKDkNvbXBvbmVudEV2ZW50EgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSOwoEdHlwZRgDIAEoDjItLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ29tcG9uZW50RXZlbnRUeXBlEj8KBnN0YXR1cxgEIAEoDjIvLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ29tcG9uZW50RXZlbnRTdGF0dXMSDAoEZnJvbRgFIAEoCRIPCgJ0bxgGIAEoCUgAiAEBEkUKBmltcGFjdBgHIAEoDjIwLm9wZW5zdGF0dXMuc3RhdHVzX3JlcG9ydC52MS5QYWdlQ29tcG9uZW50SW1wYWN0SAGIAQFCBQoDX3RvQgkKB19pbXBhY3QingIKGVBhZ2VDb21wb25lbnREYWlseVN1bW1hcnkSFAoMY29tcG9uZW50X2lkGAEgASgJEjoKBHR5cGUYAiABKA4yLC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VDb21wb25lbnRUeXBlEhcKCm1vbml0b3JfaWQYAyABKAlIAIgBARIMCgRuYW1lGAQgASgJEj4KB2J1Y2tldHMYBSADKAsyLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNvbXBvbmVudERheUJ1Y2tldBI5CgZldmVudHMYBiADKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNvbXBvbmVudEV2ZW50Qg0KC19tb25pdG9yX2lkInAKJEdldFBhZ2VDb21wb25lbnREYWlseVN1bW1hcnlSZXNwb25zZRJICgpjb21wb25lbnRzGAEgAygLMjQub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlQ29tcG9uZW50RGFpbHlTdW1tYXJ5KugBChJDb21wb25lbnREYXlTdGF0dXMSJAogQ09NUE9ORU5UX0RBWV9TVEFUVVNfVU5TUEVDSUZJRUQQABIkCiBDT01QT05FTlRfREFZX1NUQVRVU19PUEVSQVRJT05BTBABEiEKHUNPTVBPTkVOVF9EQVlfU1RBVFVTX0RFR1JBREVEEAISHQoZQ09NUE9ORU5UX0RBWV9TVEFUVVNfRE9XThADEiQKIENPTVBPTkVOVF9EQVlfU1RBVFVTX01BSU5URU5BTkNFEAQSHgoaQ09NUE9ORU5UX0RBWV9TVEFUVVNfRU1QVFkQBSqkAQoSQ29tcG9uZW50RXZlbnRUeXBlEiQKIENPTVBPTkVOVF9FVkVOVF9UWVBFX1VOU1BFQ0lGSUVEEAASJAogQ09NUE9ORU5UX0VWRU5UX1RZUEVfTUFJTlRFTkFOQ0UQARIhCh1DT01QT05FTlRfRVZFTlRfVFlQRV9JTkNJREVOVBACEh8KG0NPTVBPTkVOVF9FVkVOVF9UWVBFX1JFUE9SVBADKtQBChRDb21wb25lbnRFdmVudFN0YXR1cxImCiJDT01QT05FTlRfRVZFTlRfU1RBVFVTX1VOU1BFQ0lGSUVEEAASJgoiQ09NUE9ORU5UX0VWRU5UX1NUQVRVU19PUEVSQVRJT05BTBABEiMKH0NPTVBPTkVOVF9FVkVOVF9TVEFUVVNfREVHUkFERUQQAhIfChtDT01QT05FTlRfRVZFTlRfU1RBVFVTX0RPV04QAxImCiJDT01QT05FTlRfRVZFTlRfU1RBVFVTX01BSU5URU5BTkNFEAQyqSMKEVN0YXR1c1BhZ2VTZXJ2aWNlEnsKEENyZWF0ZVN0YXR1c1BhZ2USMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNyZWF0ZVN0YXR1c1BhZ2VSZXF1ZXN0GjMub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5DcmVhdGVTdGF0dXNQYWdlUmVzcG9uc2USdwoNR2V0U3RhdHVzUGFnZRIvLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuR2V0U3RhdHVzUGFnZVJlcXVlc3QaMC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldFN0YXR1c1BhZ2VSZXNwb25zZSIDkAIBEn0KD0xpc3RTdGF0dXNQYWdlcxIxLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN0YXR1c1BhZ2VzUmVxdWVzdBoyLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN0YXR1c1BhZ2VzUmVzcG9uc2UiA5ACARJ7ChBVcGRhdGVTdGF0dXNQYWdlEjIub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5VcGRhdGVTdGF0dXNQYWdlUmVxdWVzdBozLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuVXBkYXRlU3RhdHVzUGFnZVJlc3BvbnNlEnsKEERlbGV0ZVN0YXR1c1BhZ2USMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkRlbGV0ZVN0YXR1c1BhZ2VSZXF1ZXN0GjMub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5EZWxldGVTdGF0dXNQYWdlUmVzcG9uc2UShAEKE0FkZE1vbml0b3JDb21wb25lbnQSNS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkFkZE1vbml0b3JDb21wb25lbnRSZXF1ZXN0GjYub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5BZGRNb25pdG9yQ29tcG9uZW50UmVzcG9uc2USgQEKEkFkZFN0YXRpY0NvbXBvbmVudBI0Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQWRkU3RhdGljQ29tcG9uZW50UmVxdWVzdBo1Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQWRkU3RhdGljQ29tcG9uZW50UmVzcG9uc2USeAoPUmVtb3ZlQ29tcG9uZW50EjEub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5SZW1vdmVDb21wb25lbnRSZXF1ZXN0GjIub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5SZW1vdmVDb21wb25lbnRSZXNwb25zZRJ4Cg9VcGRhdGVDb21wb25lbnQSMS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlVwZGF0ZUNvbXBvbmVudFJlcXVlc3QaMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlVwZGF0ZUNvbXBvbmVudFJlc3BvbnNlEocBChRDcmVhdGVDb21wb25lbnRHcm91cBI2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ3JlYXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0Gjcub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5DcmVhdGVDb21wb25lbnRHcm91cFJlc3BvbnNlEocBChREZWxldGVDb21wb25lbnRHcm91cBI2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuRGVsZXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0Gjcub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5EZWxldGVDb21wb25lbnRHcm91cFJlc3BvbnNlEocBChRVcGRhdGVDb21wb25lbnRHcm91cBI2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuVXBkYXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0Gjcub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5VcGRhdGVDb21wb25lbnRHcm91cFJlc3BvbnNlErkECg9TdWJzY3JpYmVUb1BhZ2USMS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN1YnNjcmliZVRvUGFnZVJlcXVlc3QaMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN1YnNjcmliZVRvUGFnZVJlc3BvbnNlIr4Duke6Axq3A0VuZC11c2VyIGVtYWlsIHNlbGYtc2lnbnVwIHdpdGggZG91YmxlIG9wdC1pbiB2ZXJpZmljYXRpb24uIEEgdmVyaWZpY2F0aW9uIGVtYWlsIGlzIHNlbnQgYW5kIHRoZSBzdWJzY3JpcHRpb24gYWN0aXZhdGVzIG9ubHkgYWZ0ZXIgdGhlIHJlY2lwaWVudCBjb25maXJtcy4gSWYgdGhlIGVtYWlsIHdhcyBwcmV2aW91c2x5IHVuc3Vic2NyaWJlZCwgdGhlIGV4aXN0aW5nIHJvdyBpcyByZWFjdGl2YXRlZCBpbnN0ZWFkIG9mIGEgZHVwbGljYXRlIGJlaW5nIGNyZWF0ZWQuIFVzZSB0aGlzIGZvciBzdWJzY3JpYmVyLWluaXRpYXRlZCBzaWdudXBzIG9uIHRoZSBwdWJsaWMgc3RhdHVzIHBhZ2U7IHVzZSBDcmVhdGVQYWdlU3Vic2NyaXB0aW9uIGZvciBvcGVyYXRvci1pbml0aWF0ZWQgKHZlbmRvcikgc3Vic2NyaXB0aW9ucyB0aGF0IHNob3VsZCBza2lwIHZlcmlmaWNhdGlvbi4S/QQKFkNyZWF0ZVBhZ2VTdWJzY3JpcHRpb24SOC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNyZWF0ZVBhZ2VTdWJzY3JpcHRpb25SZXF1ZXN0Gjkub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5DcmVhdGVQYWdlU3Vic2NyaXB0aW9uUmVzcG9uc2Ui7QO6R+kDGuYDT3BlcmF0b3ItYWRkZWQgc3Vic2NyaWJlciAoZW1haWwgb3Igd2ViaG9vaykgd2l0aCBubyB2ZXJpZmljYXRpb24gZmxvdyDigJQgdGhlIHBhcnRuZXIgc3RhcnRzIHJlY2VpdmluZyBub3RpZmljYXRpb25zIGltbWVkaWF0ZWx5LiBTdXBwb3J0cyBTbGFjayBhbmQgRGlzY29yZCB3ZWJob29rIFVSTHMgaW4gYWRkaXRpb24gdG8gZW1haWwuIEEgbWFuYWdlbWVudCB0b2tlbiBpcyBzdGlsbCBnZW5lcmF0ZWQgc28gdGhlIHN1YnNjcmliZXIgY2FuIHNlbGYtbWFuYWdlICh1cGRhdGUgc2NvcGUsIHVuc3Vic2NyaWJlKSB3aXRob3V0IG9wZXJhdG9yIGludm9sdmVtZW50LiBVc2UgdGhpcyBmb3IgdmVuZG9yL3BhcnRuZXIgaW50ZWdyYXRpb25zIHdoZXJlIGNvbnNlbnQgaXMgZXN0YWJsaXNoZWQgb3V0LW9mLWJhbmQ7IHVzZSBTdWJzY3JpYmVUb1BhZ2UgZm9yIHN1YnNjcmliZXItaW5pdGlhdGVkIHNpZ251cHMgdGhhdCByZXF1aXJlIGRvdWJsZSBvcHQtaW4uEoQBChNVbnN1YnNjcmliZUZyb21QYWdlEjUub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5VbnN1YnNjcmliZUZyb21QYWdlUmVxdWVzdBo2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuVW5zdWJzY3JpYmVGcm9tUGFnZVJlc3BvbnNlEn0KD0xpc3RTdWJzY3JpYmVycxIxLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN1YnNjcmliZXJzUmVxdWVzdBoyLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN1YnNjcmliZXJzUmVzcG9uc2UiA5ACARLAAwoUR2V0U3RhdHVzUGFnZUNvbnRlbnQSNi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldFN0YXR1c1BhZ2VDb250ZW50UmVxdWVzdBo3Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuR2V0U3RhdHVzUGFnZUNvbnRlbnRSZXNwb25zZSK2ApACAbpHrwIarAJSZXR1cm5zIHRoZSBmdWxsIGNvbnRlbnQgb2YgYSBzdGF0dXMgcGFnZSBpbmNsdWRpbmcgaXRzIGNvbXBvbmVudHMsIGNvbXBvbmVudCBncm91cHMsIGFjdGl2ZSBzdGF0dXMgcmVwb3J0cywgYW5kIHNjaGVkdWxlZCBtYWludGVuYW5jZXMuIFN1cHBvcnRzIHR3byBhY2Nlc3MgcGF0aHM6IGJ5IElEIChyZXF1aXJlcyBhdXRoZW50aWNhdGlvbiwgd29ya3NwYWNlLXNjb3BlZCkgb3IgYnkgc2x1ZyAocHVibGljIGFjY2VzcywgcmVxdWlyZXMgdGhlIHBhZ2UgdG8gYmUgcHVibGlzaGVkIHdpdGggYWNjZXNzX3R5cGU9UFVCTElDKS4SqgMKEEdldE92ZXJhbGxTdGF0dXMSMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldE92ZXJhbGxTdGF0dXNSZXF1ZXN0GjMub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5HZXRPdmVyYWxsU3RhdHVzUmVzcG9uc2UirAKQAgG6R6UCGqICUmV0dXJucyB0aGUgb3ZlcmFsbCBzdGF0dXMgb2YgYSBzdGF0dXMgcGFnZSBhbG9uZyB3aXRoIGluZGl2aWR1YWwgY29tcG9uZW50IHN0YXR1c2VzLiBUaGUgb3ZlcmFsbCBzdGF0dXMgaXMgY29tcHV0ZWQgZnJvbSBhY3RpdmUgc3RhdHVzIHJlcG9ydHMgYW5kIG1haW50ZW5hbmNlcyB3aXRoIHRoZSBmb2xsb3dpbmcgcHJpb3JpdHk6IGRlZ3JhZGVkIChmcm9tIGFjdGl2ZSBzdGF0dXMgcmVwb3J0cykgPiBtYWludGVuYW5jZSAoZnJvbSBhY3RpdmUgbWFpbnRlbmFuY2Ugd2luZG93cykgPiBvcGVyYXRpb25hbC4S1QQKHEdldFBhZ2VDb21wb25lbnREYWlseVN1bW1hcnkSPi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldFBhZ2VDb21wb25lbnREYWlseVN1bW1hcnlSZXF1ZXN0Gj8ub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5HZXRQYWdlQ29tcG9uZW50RGFpbHlTdW1tYXJ5UmVzcG9uc2UiswOQAgG6R6wDGqkDUmV0dXJucyBwZXItY29tcG9uZW50IGRhaWx5IHN0YXR1cyBidWNrZXRzIChvay9kZWdyYWRlZC9lcnJvci9jb3VudCBwbHVzIGEgcmVzb2x2ZWQgc3RhdHVzKSBtZXJnZWQgd2l0aCB0aGUgaW5jaWRlbnQsIG1haW50ZW5hbmNlLCBhbmQgc3RhdHVzLXJlcG9ydCB0aW1lbGluZSBvdmVyIHRoZSBsYXN0IE4gZGF5cyAobWF4IDQ1KS4gU3VpdGFibGUgYXMgYSBzaW5nbGUgc291cmNlIG9mIHRydXRoIGZvciBzdGF0dXMtYmFyIGFuZCB1cHRpbWUtY2FsZW5kYXIgcmVuZGVyaW5nLiBTdXBwb3J0cyB0d28gYWNjZXNzIHBhdGhzOiBieSBpZCAoYXV0aGVudGljYXRlZCwgd29ya3NwYWNlLXNjb3BlZCkgb3IgYnkgc2x1ZyAocHVibGljIGFjY2VzcywgcmVxdWlyZXMgdGhlIHBhZ2UgdG8gYmUgcHVibGlzaGVkIHdpdGggYWNjZXNzX3R5cGU9UFVCTElDKS5CWlpYZ2l0aHViLmNvbS9vcGVuc3RhdHVzaHEvb3BlbnN0YXR1cy9wYWNrYWdlcy9wcm90by9vcGVuc3RhdHVzL3N0YXR1c19wYWdlL3YxO3N0YXR1c3BhZ2V2MWIGcHJvdG8z", [file_buf_validate_validate, file_gnostic_openapi_v3_annotations, file_openstatus_maintenance_v1_maintenance, file_openstatus_status_page_v1_page_component, file_openstatus_status_page_v1_page_subscriber, file_openstatus_status_page_v1_status_page, file_openstatus_status_report_v1_status_report]); + fileDesc("CidvcGVuc3RhdHVzL3N0YXR1c19wYWdlL3YxL3NlcnZpY2UucHJvdG8SGW9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEivgcKF0NyZWF0ZVN0YXR1c1BhZ2VSZXF1ZXN0EjAKBXRpdGxlGAEgASgJQiG6RxQ6EhIQQWNtZSBDb3JwIFN0YXR1c7pIB3IFEAEYgAISIgoLZGVzY3JpcHRpb24YAiABKAlCCLpIBXIDGIAISACIAQESSQoEc2x1ZxgDIAEoCUI7ukcSOhASDm15LXN0YXR1cy1wYWdlukgjciEQARiAAjIaXlthLXowLTldKyg/Oi1bYS16MC05XSspKiQSOQoMaG9tZXBhZ2VfdXJsGAQgASgJQh66Rxs6GRIXaHR0cHM6Ly93d3cuZXhhbXBsZS5jb21IAYgBARIYCgtjb250YWN0X3VybBgFIAEoCUgCiAEBEj4KDmRlZmF1bHRfbG9jYWxlGAYgASgOMiEub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5Mb2NhbGVIA4gBARIyCgdsb2NhbGVzGAcgAygOMiEub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5Mb2NhbGUSGwoEaWNvbhgIIAEoCUIIukgFcgMYgAhIBIgBARIkCg1jdXN0b21fZG9tYWluGAkgASgJQgi6SAVyAxiAAkgFiAEBEjgKBXRoZW1lGAogASgOMiQub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlVGhlbWVIBogBARJDCgthY2Nlc3NfdHlwZRgLIAEoDjIpLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUFjY2Vzc1R5cGVIB4gBARIhCghwYXNzd29yZBgMIAEoCUIKukgHcgUQARiAAkgIiAEBEhoKEmF1dGhfZW1haWxfZG9tYWlucxgNIAMoCRIYCgthbGxvd19pbmRleBgOIAEoCEgJiAEBEh4KEWFsbG93ZWRfaXBfcmFuZ2VzGA8gASgJSAqIAQESQQoMY3VzdG9tX3RoZW1lGBAgASgLMiYub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5DdXN0b21UaGVtZUgLiAEBQg4KDF9kZXNjcmlwdGlvbkIPCg1faG9tZXBhZ2VfdXJsQg4KDF9jb250YWN0X3VybEIRCg9fZGVmYXVsdF9sb2NhbGVCBwoFX2ljb25CEAoOX2N1c3RvbV9kb21haW5CCAoGX3RoZW1lQg4KDF9hY2Nlc3NfdHlwZUILCglfcGFzc3dvcmRCDgoMX2FsbG93X2luZGV4QhQKEl9hbGxvd2VkX2lwX3Jhbmdlc0IPCg1fY3VzdG9tX3RoZW1lIlYKGENyZWF0ZVN0YXR1c1BhZ2VSZXNwb25zZRI6CgtzdGF0dXNfcGFnZRgBIAEoCzIlLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuU3RhdHVzUGFnZSIrChRHZXRTdGF0dXNQYWdlUmVxdWVzdBITCgJpZBgBIAEoCUIHukgEcgIQASJTChVHZXRTdGF0dXNQYWdlUmVzcG9uc2USOgoLc3RhdHVzX3BhZ2UYASABKAsyJS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN0YXR1c1BhZ2UiagoWTGlzdFN0YXR1c1BhZ2VzUmVxdWVzdBIdCgVsaW1pdBgBIAEoBUIJukgGGgQYZCgBSACIAQESHAoGb2Zmc2V0GAIgASgFQge6SAQaAigASAGIAQFCCAoGX2xpbWl0QgkKB19vZmZzZXQicQoXTGlzdFN0YXR1c1BhZ2VzUmVzcG9uc2USQgoMc3RhdHVzX3BhZ2VzGAEgAygLMiwub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5TdGF0dXNQYWdlU3VtbWFyeRISCgp0b3RhbF9zaXplGAIgASgFIqQHChdVcGRhdGVTdGF0dXNQYWdlUmVxdWVzdBITCgJpZBgBIAEoCUIHukgEcgIQARIeCgV0aXRsZRgCIAEoCUIKukgHcgUQARiAAkgAiAEBEiIKC2Rlc2NyaXB0aW9uGAMgASgJQgi6SAVyAxiACEgBiAEBEjkKBHNsdWcYBCABKAlCJrpII3IhEAEYgAIyGl5bYS16MC05XSsoPzotW2EtejAtOV0rKSokSAKIAQESGQoMaG9tZXBhZ2VfdXJsGAUgASgJSAOIAQESGAoLY29udGFjdF91cmwYBiABKAlIBIgBARI+Cg5kZWZhdWx0X2xvY2FsZRgHIAEoDjIhLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTG9jYWxlSAWIAQESMgoHbG9jYWxlcxgIIAMoDjIhLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTG9jYWxlEhsKBGljb24YCSABKAlCCLpIBXIDGIAISAaIAQESJAoNY3VzdG9tX2RvbWFpbhgKIAEoCUIIukgFcgMYgAJIB4gBARI4CgV0aGVtZRgLIAEoDjIkLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZVRoZW1lSAiIAQESQwoLYWNjZXNzX3R5cGUYDCABKA4yKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VBY2Nlc3NUeXBlSAmIAQESIQoIcGFzc3dvcmQYDSABKAlCCrpIB3IFEAEYgAJICogBARIaChJhdXRoX2VtYWlsX2RvbWFpbnMYDiADKAkSGAoLYWxsb3dfaW5kZXgYDyABKAhIC4gBARIeChFhbGxvd2VkX2lwX3JhbmdlcxgQIAEoCUgMiAEBEkEKDGN1c3RvbV90aGVtZRgRIAEoCzImLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ3VzdG9tVGhlbWVIDYgBAUIICgZfdGl0bGVCDgoMX2Rlc2NyaXB0aW9uQgcKBV9zbHVnQg8KDV9ob21lcGFnZV91cmxCDgoMX2NvbnRhY3RfdXJsQhEKD19kZWZhdWx0X2xvY2FsZUIHCgVfaWNvbkIQCg5fY3VzdG9tX2RvbWFpbkIICgZfdGhlbWVCDgoMX2FjY2Vzc190eXBlQgsKCV9wYXNzd29yZEIOCgxfYWxsb3dfaW5kZXhCFAoSX2FsbG93ZWRfaXBfcmFuZ2VzQg8KDV9jdXN0b21fdGhlbWUiVgoYVXBkYXRlU3RhdHVzUGFnZVJlc3BvbnNlEjoKC3N0YXR1c19wYWdlGAEgASgLMiUub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5TdGF0dXNQYWdlIi4KF0RlbGV0ZVN0YXR1c1BhZ2VSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABIisKGERlbGV0ZVN0YXR1c1BhZ2VSZXNwb25zZRIPCgdzdWNjZXNzGAEgASgIIu8BChpBZGRNb25pdG9yQ29tcG9uZW50UmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhsKCm1vbml0b3JfaWQYAiABKAlCB7pIBHICEAESGwoEbmFtZRgDIAEoCUIIukgFcgMYgAJIAIgBARIiCgtkZXNjcmlwdGlvbhgEIAEoCUIIukgFcgMYgAhIAYgBARISCgVvcmRlchgFIAEoBUgCiAEBEhUKCGdyb3VwX2lkGAYgASgJSAOIAQFCBwoFX25hbWVCDgoMX2Rlc2NyaXB0aW9uQggKBl9vcmRlckILCglfZ3JvdXBfaWQiWgobQWRkTW9uaXRvckNvbXBvbmVudFJlc3BvbnNlEjsKCWNvbXBvbmVudBgBIAEoCzIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUNvbXBvbmVudCLFAQoZQWRkU3RhdGljQ29tcG9uZW50UmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhgKBG5hbWUYAiABKAlCCrpIB3IFEAEYgAISIgoLZGVzY3JpcHRpb24YAyABKAlCCLpIBXIDGIAISACIAQESEgoFb3JkZXIYBCABKAVIAYgBARIVCghncm91cF9pZBgFIAEoCUgCiAEBQg4KDF9kZXNjcmlwdGlvbkIICgZfb3JkZXJCCwoJX2dyb3VwX2lkIlkKGkFkZFN0YXRpY0NvbXBvbmVudFJlc3BvbnNlEjsKCWNvbXBvbmVudBgBIAEoCzIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUNvbXBvbmVudCItChZSZW1vdmVDb21wb25lbnRSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABIioKF1JlbW92ZUNvbXBvbmVudFJlc3BvbnNlEg8KB3N1Y2Nlc3MYASABKAgi8wEKFlVwZGF0ZUNvbXBvbmVudFJlcXVlc3QSEwoCaWQYASABKAlCB7pIBHICEAESGwoEbmFtZRgCIAEoCUIIukgFcgMYgAJIAIgBARIiCgtkZXNjcmlwdGlvbhgDIAEoCUIIukgFcgMYgAhIAYgBARISCgVvcmRlchgEIAEoBUgCiAEBEhUKCGdyb3VwX2lkGAUgASgJSAOIAQESGAoLZ3JvdXBfb3JkZXIYBiABKAVIBIgBAUIHCgVfbmFtZUIOCgxfZGVzY3JpcHRpb25CCAoGX29yZGVyQgsKCV9ncm91cF9pZEIOCgxfZ3JvdXBfb3JkZXIiVgoXVXBkYXRlQ29tcG9uZW50UmVzcG9uc2USOwoJY29tcG9uZW50GAEgASgLMigub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlQ29tcG9uZW50In0KG0NyZWF0ZUNvbXBvbmVudEdyb3VwUmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhgKBG5hbWUYAiABKAlCCrpIB3IFEAEYgAISGQoMZGVmYXVsdF9vcGVuGAMgASgISACIAQFCDwoNX2RlZmF1bHRfb3BlbiJcChxDcmVhdGVDb21wb25lbnRHcm91cFJlc3BvbnNlEjwKBWdyb3VwGAEgASgLMi0ub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlQ29tcG9uZW50R3JvdXAiMgobRGVsZXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABIi8KHERlbGV0ZUNvbXBvbmVudEdyb3VwUmVzcG9uc2USDwoHc3VjY2VzcxgBIAEoCCKGAQobVXBkYXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0EhMKAmlkGAEgASgJQge6SARyAhABEh0KBG5hbWUYAiABKAlCCrpIB3IFEAEYgAJIAIgBARIZCgxkZWZhdWx0X29wZW4YAyABKAhIAYgBAUIHCgVfbmFtZUIPCg1fZGVmYXVsdF9vcGVuIlwKHFVwZGF0ZUNvbXBvbmVudEdyb3VwUmVzcG9uc2USPAoFZ3JvdXAYASABKAsyLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VDb21wb25lbnRHcm91cCJhChZTdWJzY3JpYmVUb1BhZ2VSZXF1ZXN0EhgKB3BhZ2VfaWQYASABKAlCB7pIBHICEAESLQoFZW1haWwYAiABKAlCHrpHFDoSEhB1c2VyQGV4YW1wbGUuY29tukgEcgJgASJYChdTdWJzY3JpYmVUb1BhZ2VSZXNwb25zZRI9CgpzdWJzY3JpYmVyGAEgASgLMikub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlU3Vic2NyaWJlciImCgxFbWFpbENoYW5uZWwSFgoFZW1haWwYASABKAlCB7pIBHICYAEiOwoUV2ViaG9va0NoYW5uZWxIZWFkZXISFAoDa2V5GAEgASgJQge6SARyAhABEg0KBXZhbHVlGAIgASgJInYKDldlYmhvb2tDaGFubmVsEiIKC3dlYmhvb2tfdXJsGAEgASgJQg26SApyCBABGIAQiAEBEkAKB2hlYWRlcnMYAiADKAsyLy5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLldlYmhvb2tDaGFubmVsSGVhZGVyIpACCh1DcmVhdGVQYWdlU3Vic2NyaXB0aW9uUmVxdWVzdBIYCgdwYWdlX2lkGAEgASgJQge6SARyAhABEhsKBG5hbWUYAiABKAlCCLpIBXIDGP8BSAGIAQESFQoNY29tcG9uZW50X2lkcxgDIAMoCRJACg1lbWFpbF9jaGFubmVsGAogASgLMicub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5FbWFpbENoYW5uZWxIABJECg93ZWJob29rX2NoYW5uZWwYCyABKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLldlYmhvb2tDaGFubmVsSABCEAoHY2hhbm5lbBIFukgCCAFCBwoFX25hbWUiXwoeQ3JlYXRlUGFnZVN1YnNjcmlwdGlvblJlc3BvbnNlEj0KCnN1YnNjcmliZXIYASABKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VTdWJzY3JpYmVyImMKGlVuc3Vic2NyaWJlRnJvbVBhZ2VSZXF1ZXN0EhgKB3BhZ2VfaWQYASABKAlCB7pIBHICEAESDwoFZW1haWwYAiABKAlIABIMCgJpZBgDIAEoCUgAQgwKCmlkZW50aWZpZXIiLgobVW5zdWJzY3JpYmVGcm9tUGFnZVJlc3BvbnNlEg8KB3N1Y2Nlc3MYASABKAgiwAEKFkxpc3RTdWJzY3JpYmVyc1JlcXVlc3QSGAoHcGFnZV9pZBgBIAEoCUIHukgEcgIQARIdCgVsaW1pdBgCIAEoBUIJukgGGgQYZCgBSACIAQESHAoGb2Zmc2V0GAMgASgFQge6SAQaAigASAGIAQESIQoUaW5jbHVkZV91bnN1YnNjcmliZWQYBCABKAhIAogBAUIICgZfbGltaXRCCQoHX29mZnNldEIXChVfaW5jbHVkZV91bnN1YnNjcmliZWQibQoXTGlzdFN1YnNjcmliZXJzUmVzcG9uc2USPgoLc3Vic2NyaWJlcnMYASADKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VTdWJzY3JpYmVyEhIKCnRvdGFsX3NpemUYAiABKAUiSQobR2V0U3RhdHVzUGFnZUNvbnRlbnRSZXF1ZXN0EgwKAmlkGAEgASgJSAASDgoEc2x1ZxgCIAEoCUgAQgwKCmlkZW50aWZpZXIi3wIKHEdldFN0YXR1c1BhZ2VDb250ZW50UmVzcG9uc2USOgoLc3RhdHVzX3BhZ2UYASABKAsyJS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN0YXR1c1BhZ2USPAoKY29tcG9uZW50cxgCIAMoCzIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUNvbXBvbmVudBI9CgZncm91cHMYAyADKAsyLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VDb21wb25lbnRHcm91cBJBCg5zdGF0dXNfcmVwb3J0cxgEIAMoCzIpLm9wZW5zdGF0dXMuc3RhdHVzX3JlcG9ydC52MS5TdGF0dXNSZXBvcnQSQwoMbWFpbnRlbmFuY2VzGAUgAygLMi0ub3BlbnN0YXR1cy5tYWludGVuYW5jZS52MS5NYWludGVuYW5jZVN1bW1hcnkiRQoXR2V0T3ZlcmFsbFN0YXR1c1JlcXVlc3QSDAoCaWQYASABKAlIABIOCgRzbHVnGAIgASgJSABCDAoKaWRlbnRpZmllciJhCg9Db21wb25lbnRTdGF0dXMSFAoMY29tcG9uZW50X2lkGAEgASgJEjgKBnN0YXR1cxgCIAEoDjIoLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuT3ZlcmFsbFN0YXR1cyKkAQoYR2V0T3ZlcmFsbFN0YXR1c1Jlc3BvbnNlEkAKDm92ZXJhbGxfc3RhdHVzGAEgASgOMigub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5PdmVyYWxsU3RhdHVzEkYKEmNvbXBvbmVudF9zdGF0dXNlcxgCIAMoCzIqLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ29tcG9uZW50U3RhdHVzIp0BCiNHZXRQYWdlQ29tcG9uZW50RGFpbHlTdW1tYXJ5UmVxdWVzdBIMCgJpZBgBIAEoCUgAEg4KBHNsdWcYAiABKAlIABIjCg1jb21wb25lbnRfaWRzGAMgAygJQgy6SAmSAQYiBHICEAESHAoEZGF5cxgEIAEoBUIJukgGGgQYLSgBSAGIAQFCDAoKaWRlbnRpZmllckIHCgVfZGF5cyLuAQoSQ29tcG9uZW50RGF5QnVja2V0EgsKA2RheRgBIAEoCRINCgVjb3VudBgCIAEoAxIKCgJvaxgDIAEoAxIQCghkZWdyYWRlZBgEIAEoAxINCgVlcnJvchgFIAEoAxI9CgZzdGF0dXMYBiABKA4yLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNvbXBvbmVudERheVN0YXR1cxJFCgZpbXBhY3QYByABKA4yMC5vcGVuc3RhdHVzLnN0YXR1c19yZXBvcnQudjEuUGFnZUNvbXBvbmVudEltcGFjdEgAiAEBQgkKB19pbXBhY3QioAIKDkNvbXBvbmVudEV2ZW50EgoKAmlkGAEgASgJEgwKBG5hbWUYAiABKAkSOwoEdHlwZRgDIAEoDjItLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ29tcG9uZW50RXZlbnRUeXBlEj8KBnN0YXR1cxgEIAEoDjIvLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ29tcG9uZW50RXZlbnRTdGF0dXMSDAoEZnJvbRgFIAEoCRIPCgJ0bxgGIAEoCUgAiAEBEkUKBmltcGFjdBgHIAEoDjIwLm9wZW5zdGF0dXMuc3RhdHVzX3JlcG9ydC52MS5QYWdlQ29tcG9uZW50SW1wYWN0SAGIAQFCBQoDX3RvQgkKB19pbXBhY3QingIKGVBhZ2VDb21wb25lbnREYWlseVN1bW1hcnkSFAoMY29tcG9uZW50X2lkGAEgASgJEjoKBHR5cGUYAiABKA4yLC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VDb21wb25lbnRUeXBlEhcKCm1vbml0b3JfaWQYAyABKAlIAIgBARIMCgRuYW1lGAQgASgJEj4KB2J1Y2tldHMYBSADKAsyLS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNvbXBvbmVudERheUJ1Y2tldBI5CgZldmVudHMYBiADKAsyKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNvbXBvbmVudEV2ZW50Qg0KC19tb25pdG9yX2lkInAKJEdldFBhZ2VDb21wb25lbnREYWlseVN1bW1hcnlSZXNwb25zZRJICgpjb21wb25lbnRzGAEgAygLMjQub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlQ29tcG9uZW50RGFpbHlTdW1tYXJ5KugBChJDb21wb25lbnREYXlTdGF0dXMSJAogQ09NUE9ORU5UX0RBWV9TVEFUVVNfVU5TUEVDSUZJRUQQABIkCiBDT01QT05FTlRfREFZX1NUQVRVU19PUEVSQVRJT05BTBABEiEKHUNPTVBPTkVOVF9EQVlfU1RBVFVTX0RFR1JBREVEEAISHQoZQ09NUE9ORU5UX0RBWV9TVEFUVVNfRE9XThADEiQKIENPTVBPTkVOVF9EQVlfU1RBVFVTX01BSU5URU5BTkNFEAQSHgoaQ09NUE9ORU5UX0RBWV9TVEFUVVNfRU1QVFkQBSqkAQoSQ29tcG9uZW50RXZlbnRUeXBlEiQKIENPTVBPTkVOVF9FVkVOVF9UWVBFX1VOU1BFQ0lGSUVEEAASJAogQ09NUE9ORU5UX0VWRU5UX1RZUEVfTUFJTlRFTkFOQ0UQARIhCh1DT01QT05FTlRfRVZFTlRfVFlQRV9JTkNJREVOVBACEh8KG0NPTVBPTkVOVF9FVkVOVF9UWVBFX1JFUE9SVBADKtQBChRDb21wb25lbnRFdmVudFN0YXR1cxImCiJDT01QT05FTlRfRVZFTlRfU1RBVFVTX1VOU1BFQ0lGSUVEEAASJgoiQ09NUE9ORU5UX0VWRU5UX1NUQVRVU19PUEVSQVRJT05BTBABEiMKH0NPTVBPTkVOVF9FVkVOVF9TVEFUVVNfREVHUkFERUQQAhIfChtDT01QT05FTlRfRVZFTlRfU1RBVFVTX0RPV04QAxImCiJDT01QT05FTlRfRVZFTlRfU1RBVFVTX01BSU5URU5BTkNFEAQyqSMKEVN0YXR1c1BhZ2VTZXJ2aWNlEnsKEENyZWF0ZVN0YXR1c1BhZ2USMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNyZWF0ZVN0YXR1c1BhZ2VSZXF1ZXN0GjMub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5DcmVhdGVTdGF0dXNQYWdlUmVzcG9uc2USdwoNR2V0U3RhdHVzUGFnZRIvLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuR2V0U3RhdHVzUGFnZVJlcXVlc3QaMC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldFN0YXR1c1BhZ2VSZXNwb25zZSIDkAIBEn0KD0xpc3RTdGF0dXNQYWdlcxIxLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN0YXR1c1BhZ2VzUmVxdWVzdBoyLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN0YXR1c1BhZ2VzUmVzcG9uc2UiA5ACARJ7ChBVcGRhdGVTdGF0dXNQYWdlEjIub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5VcGRhdGVTdGF0dXNQYWdlUmVxdWVzdBozLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuVXBkYXRlU3RhdHVzUGFnZVJlc3BvbnNlEnsKEERlbGV0ZVN0YXR1c1BhZ2USMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkRlbGV0ZVN0YXR1c1BhZ2VSZXF1ZXN0GjMub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5EZWxldGVTdGF0dXNQYWdlUmVzcG9uc2UShAEKE0FkZE1vbml0b3JDb21wb25lbnQSNS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkFkZE1vbml0b3JDb21wb25lbnRSZXF1ZXN0GjYub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5BZGRNb25pdG9yQ29tcG9uZW50UmVzcG9uc2USgQEKEkFkZFN0YXRpY0NvbXBvbmVudBI0Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQWRkU3RhdGljQ29tcG9uZW50UmVxdWVzdBo1Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQWRkU3RhdGljQ29tcG9uZW50UmVzcG9uc2USeAoPUmVtb3ZlQ29tcG9uZW50EjEub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5SZW1vdmVDb21wb25lbnRSZXF1ZXN0GjIub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5SZW1vdmVDb21wb25lbnRSZXNwb25zZRJ4Cg9VcGRhdGVDb21wb25lbnQSMS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlVwZGF0ZUNvbXBvbmVudFJlcXVlc3QaMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlVwZGF0ZUNvbXBvbmVudFJlc3BvbnNlEocBChRDcmVhdGVDb21wb25lbnRHcm91cBI2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ3JlYXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0Gjcub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5DcmVhdGVDb21wb25lbnRHcm91cFJlc3BvbnNlEocBChREZWxldGVDb21wb25lbnRHcm91cBI2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuRGVsZXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0Gjcub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5EZWxldGVDb21wb25lbnRHcm91cFJlc3BvbnNlEocBChRVcGRhdGVDb21wb25lbnRHcm91cBI2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuVXBkYXRlQ29tcG9uZW50R3JvdXBSZXF1ZXN0Gjcub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5VcGRhdGVDb21wb25lbnRHcm91cFJlc3BvbnNlErkECg9TdWJzY3JpYmVUb1BhZ2USMS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN1YnNjcmliZVRvUGFnZVJlcXVlc3QaMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlN1YnNjcmliZVRvUGFnZVJlc3BvbnNlIr4Duke6Axq3A0VuZC11c2VyIGVtYWlsIHNlbGYtc2lnbnVwIHdpdGggZG91YmxlIG9wdC1pbiB2ZXJpZmljYXRpb24uIEEgdmVyaWZpY2F0aW9uIGVtYWlsIGlzIHNlbnQgYW5kIHRoZSBzdWJzY3JpcHRpb24gYWN0aXZhdGVzIG9ubHkgYWZ0ZXIgdGhlIHJlY2lwaWVudCBjb25maXJtcy4gSWYgdGhlIGVtYWlsIHdhcyBwcmV2aW91c2x5IHVuc3Vic2NyaWJlZCwgdGhlIGV4aXN0aW5nIHJvdyBpcyByZWFjdGl2YXRlZCBpbnN0ZWFkIG9mIGEgZHVwbGljYXRlIGJlaW5nIGNyZWF0ZWQuIFVzZSB0aGlzIGZvciBzdWJzY3JpYmVyLWluaXRpYXRlZCBzaWdudXBzIG9uIHRoZSBwdWJsaWMgc3RhdHVzIHBhZ2U7IHVzZSBDcmVhdGVQYWdlU3Vic2NyaXB0aW9uIGZvciBvcGVyYXRvci1pbml0aWF0ZWQgKHZlbmRvcikgc3Vic2NyaXB0aW9ucyB0aGF0IHNob3VsZCBza2lwIHZlcmlmaWNhdGlvbi4S/QQKFkNyZWF0ZVBhZ2VTdWJzY3JpcHRpb24SOC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkNyZWF0ZVBhZ2VTdWJzY3JpcHRpb25SZXF1ZXN0Gjkub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5DcmVhdGVQYWdlU3Vic2NyaXB0aW9uUmVzcG9uc2Ui7QO6R+kDGuYDT3BlcmF0b3ItYWRkZWQgc3Vic2NyaWJlciAoZW1haWwgb3Igd2ViaG9vaykgd2l0aCBubyB2ZXJpZmljYXRpb24gZmxvdyDigJQgdGhlIHBhcnRuZXIgc3RhcnRzIHJlY2VpdmluZyBub3RpZmljYXRpb25zIGltbWVkaWF0ZWx5LiBTdXBwb3J0cyBTbGFjayBhbmQgRGlzY29yZCB3ZWJob29rIFVSTHMgaW4gYWRkaXRpb24gdG8gZW1haWwuIEEgbWFuYWdlbWVudCB0b2tlbiBpcyBzdGlsbCBnZW5lcmF0ZWQgc28gdGhlIHN1YnNjcmliZXIgY2FuIHNlbGYtbWFuYWdlICh1cGRhdGUgc2NvcGUsIHVuc3Vic2NyaWJlKSB3aXRob3V0IG9wZXJhdG9yIGludm9sdmVtZW50LiBVc2UgdGhpcyBmb3IgdmVuZG9yL3BhcnRuZXIgaW50ZWdyYXRpb25zIHdoZXJlIGNvbnNlbnQgaXMgZXN0YWJsaXNoZWQgb3V0LW9mLWJhbmQ7IHVzZSBTdWJzY3JpYmVUb1BhZ2UgZm9yIHN1YnNjcmliZXItaW5pdGlhdGVkIHNpZ251cHMgdGhhdCByZXF1aXJlIGRvdWJsZSBvcHQtaW4uEoQBChNVbnN1YnNjcmliZUZyb21QYWdlEjUub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5VbnN1YnNjcmliZUZyb21QYWdlUmVxdWVzdBo2Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuVW5zdWJzY3JpYmVGcm9tUGFnZVJlc3BvbnNlEn0KD0xpc3RTdWJzY3JpYmVycxIxLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN1YnNjcmliZXJzUmVxdWVzdBoyLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTGlzdFN1YnNjcmliZXJzUmVzcG9uc2UiA5ACARLAAwoUR2V0U3RhdHVzUGFnZUNvbnRlbnQSNi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldFN0YXR1c1BhZ2VDb250ZW50UmVxdWVzdBo3Lm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuR2V0U3RhdHVzUGFnZUNvbnRlbnRSZXNwb25zZSK2ApACAbpHrwIarAJSZXR1cm5zIHRoZSBmdWxsIGNvbnRlbnQgb2YgYSBzdGF0dXMgcGFnZSBpbmNsdWRpbmcgaXRzIGNvbXBvbmVudHMsIGNvbXBvbmVudCBncm91cHMsIGFjdGl2ZSBzdGF0dXMgcmVwb3J0cywgYW5kIHNjaGVkdWxlZCBtYWludGVuYW5jZXMuIFN1cHBvcnRzIHR3byBhY2Nlc3MgcGF0aHM6IGJ5IElEIChyZXF1aXJlcyBhdXRoZW50aWNhdGlvbiwgd29ya3NwYWNlLXNjb3BlZCkgb3IgYnkgc2x1ZyAocHVibGljIGFjY2VzcywgcmVxdWlyZXMgdGhlIHBhZ2UgdG8gYmUgcHVibGlzaGVkIHdpdGggYWNjZXNzX3R5cGU9UFVCTElDKS4SqgMKEEdldE92ZXJhbGxTdGF0dXMSMi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldE92ZXJhbGxTdGF0dXNSZXF1ZXN0GjMub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5HZXRPdmVyYWxsU3RhdHVzUmVzcG9uc2UirAKQAgG6R6UCGqICUmV0dXJucyB0aGUgb3ZlcmFsbCBzdGF0dXMgb2YgYSBzdGF0dXMgcGFnZSBhbG9uZyB3aXRoIGluZGl2aWR1YWwgY29tcG9uZW50IHN0YXR1c2VzLiBUaGUgb3ZlcmFsbCBzdGF0dXMgaXMgY29tcHV0ZWQgZnJvbSBhY3RpdmUgc3RhdHVzIHJlcG9ydHMgYW5kIG1haW50ZW5hbmNlcyB3aXRoIHRoZSBmb2xsb3dpbmcgcHJpb3JpdHk6IGRlZ3JhZGVkIChmcm9tIGFjdGl2ZSBzdGF0dXMgcmVwb3J0cykgPiBtYWludGVuYW5jZSAoZnJvbSBhY3RpdmUgbWFpbnRlbmFuY2Ugd2luZG93cykgPiBvcGVyYXRpb25hbC4S1QQKHEdldFBhZ2VDb21wb25lbnREYWlseVN1bW1hcnkSPi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkdldFBhZ2VDb21wb25lbnREYWlseVN1bW1hcnlSZXF1ZXN0Gj8ub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5HZXRQYWdlQ29tcG9uZW50RGFpbHlTdW1tYXJ5UmVzcG9uc2UiswOQAgG6R6wDGqkDUmV0dXJucyBwZXItY29tcG9uZW50IGRhaWx5IHN0YXR1cyBidWNrZXRzIChvay9kZWdyYWRlZC9lcnJvci9jb3VudCBwbHVzIGEgcmVzb2x2ZWQgc3RhdHVzKSBtZXJnZWQgd2l0aCB0aGUgaW5jaWRlbnQsIG1haW50ZW5hbmNlLCBhbmQgc3RhdHVzLXJlcG9ydCB0aW1lbGluZSBvdmVyIHRoZSBsYXN0IE4gZGF5cyAobWF4IDQ1KS4gU3VpdGFibGUgYXMgYSBzaW5nbGUgc291cmNlIG9mIHRydXRoIGZvciBzdGF0dXMtYmFyIGFuZCB1cHRpbWUtY2FsZW5kYXIgcmVuZGVyaW5nLiBTdXBwb3J0cyB0d28gYWNjZXNzIHBhdGhzOiBieSBpZCAoYXV0aGVudGljYXRlZCwgd29ya3NwYWNlLXNjb3BlZCkgb3IgYnkgc2x1ZyAocHVibGljIGFjY2VzcywgcmVxdWlyZXMgdGhlIHBhZ2UgdG8gYmUgcHVibGlzaGVkIHdpdGggYWNjZXNzX3R5cGU9UFVCTElDKS5CWlpYZ2l0aHViLmNvbS9vcGVuc3RhdHVzaHEvb3BlbnN0YXR1cy9wYWNrYWdlcy9wcm90by9vcGVuc3RhdHVzL3N0YXR1c19wYWdlL3YxO3N0YXR1c3BhZ2V2MWIGcHJvdG8z", [file_buf_validate_validate, file_gnostic_openapi_v3_annotations, file_openstatus_maintenance_v1_maintenance, file_openstatus_status_page_v1_page_component, file_openstatus_status_page_v1_page_subscriber, file_openstatus_status_page_v1_status_page, file_openstatus_status_report_v1_status_report]); /** * CreateStatusPageRequest is the request to create a new status page. @@ -134,6 +134,14 @@ export type CreateStatusPageRequest = Message<"openstatus.status_page.v1.CreateS * @generated from field: optional string allowed_ip_ranges = 15; */ allowedIpRanges?: string | undefined; + + /** + * Per-mode CSS variable overrides merged over the theme (optional). + * Only supported variable names are accepted. Requires the custom-theme plan feature. + * + * @generated from field: optional openstatus.status_page.v1.CustomTheme custom_theme = 16; + */ + customTheme?: CustomTheme | undefined; }; /** @@ -379,6 +387,14 @@ export type UpdateStatusPageRequest = Message<"openstatus.status_page.v1.UpdateS * @generated from field: optional string allowed_ip_ranges = 16; */ allowedIpRanges?: string | undefined; + + /** + * New per-mode CSS variable overrides (optional). Omit to keep the current + * value; send an empty message to clear. Requires the custom-theme plan feature. + * + * @generated from field: optional openstatus.status_page.v1.CustomTheme custom_theme = 17; + */ + customTheme?: CustomTheme | undefined; }; /** diff --git a/packages/proto/gen/ts/openstatus/status_page/v1/status_page_pb.ts b/packages/proto/gen/ts/openstatus/status_page/v1/status_page_pb.ts index 10b04287..fd620b90 100644 --- a/packages/proto/gen/ts/openstatus/status_page/v1/status_page_pb.ts +++ b/packages/proto/gen/ts/openstatus/status_page/v1/status_page_pb.ts @@ -11,7 +11,35 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file openstatus/status_page/v1/status_page.proto. */ export const file_openstatus_status_page_v1_status_page: GenFile = /*@__PURE__*/ - fileDesc("CitvcGVuc3RhdHVzL3N0YXR1c19wYWdlL3YxL3N0YXR1c19wYWdlLnByb3RvEhlvcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxIv4ECgpTdGF0dXNQYWdlEgoKAmlkGAEgASgJEg0KBXRpdGxlGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEh4KBHNsdWcYBCABKAlCELpHDToLEglhY21lLWNvcnASMAoNY3VzdG9tX2RvbWFpbhgFIAEoCUIZukcWOhQSEnN0YXR1cy5leGFtcGxlLmNvbRIRCglwdWJsaXNoZWQYBiABKAgSPgoLYWNjZXNzX3R5cGUYByABKA4yKS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VBY2Nlc3NUeXBlEjMKBXRoZW1lGAggASgOMiQub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5QYWdlVGhlbWUSFAoMaG9tZXBhZ2VfdXJsGAkgASgJEhMKC2NvbnRhY3RfdXJsGAogASgJEgwKBGljb24YCyABKAkSLwoKY3JlYXRlZF9hdBgMIAEoCUIbukcYOhYSFDIwMjQtMDEtMTVUMDk6MDA6MDBaEi8KCnVwZGF0ZWRfYXQYDSABKAlCG7pHGDoWEhQyMDI0LTA2LTIwVDE0OjMwOjAwWhI5Cg5kZWZhdWx0X2xvY2FsZRgOIAEoDjIhLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTG9jYWxlEjIKB2xvY2FsZXMYDyADKA4yIS5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkxvY2FsZRIQCghwYXNzd29yZBgQIAEoCRIaChJhdXRoX2VtYWlsX2RvbWFpbnMYESADKAkSEwoLYWxsb3dfaW5kZXgYEiABKAgSGQoRYWxsb3dlZF9pcF9yYW5nZXMYEyABKAkiqQEKEVN0YXR1c1BhZ2VTdW1tYXJ5EgoKAmlkGAEgASgJEg0KBXRpdGxlGAIgASgJEgwKBHNsdWcYAyABKAkSEQoJcHVibGlzaGVkGAQgASgIEhIKCmNyZWF0ZWRfYXQYBSABKAkSEgoKdXBkYXRlZF9hdBgGIAEoCRIwCg1jdXN0b21fZG9tYWluGAcgASgJQhm6RxY6FBISc3RhdHVzLmV4YW1wbGUuY29tKsABCg5QYWdlQWNjZXNzVHlwZRIgChxQQUdFX0FDQ0VTU19UWVBFX1VOU1BFQ0lGSUVEEAASGwoXUEFHRV9BQ0NFU1NfVFlQRV9QVUJMSUMQARInCiNQQUdFX0FDQ0VTU19UWVBFX1BBU1NXT1JEX1BST1RFQ1RFRBACEiIKHlBBR0VfQUNDRVNTX1RZUEVfQVVUSEVOVElDQVRFRBADEiIKHlBBR0VfQUNDRVNTX1RZUEVfSVBfUkVTVFJJQ1RFRBAEKmkKCVBhZ2VUaGVtZRIaChZQQUdFX1RIRU1FX1VOU1BFQ0lGSUVEEAASFQoRUEFHRV9USEVNRV9TWVNURU0QARIUChBQQUdFX1RIRU1FX0xJR0hUEAISEwoPUEFHRV9USEVNRV9EQVJLEAMqTQoGTG9jYWxlEhYKEkxPQ0FMRV9VTlNQRUNJRklFRBAAEg0KCUxPQ0FMRV9FThABEg0KCUxPQ0FMRV9GUhACEg0KCUxPQ0FMRV9ERRADKuwBCg1PdmVyYWxsU3RhdHVzEh4KGk9WRVJBTExfU1RBVFVTX1VOU1BFQ0lGSUVEEAASHgoaT1ZFUkFMTF9TVEFUVVNfT1BFUkFUSU9OQUwQARIbChdPVkVSQUxMX1NUQVRVU19ERUdSQURFRBACEiEKHU9WRVJBTExfU1RBVFVTX1BBUlRJQUxfT1VUQUdFEAMSHwobT1ZFUkFMTF9TVEFUVVNfTUFKT1JfT1VUQUdFEAQSHgoaT1ZFUkFMTF9TVEFUVVNfTUFJTlRFTkFOQ0UQBRIaChZPVkVSQUxMX1NUQVRVU19VTktOT1dOEAZCWlpYZ2l0aHViLmNvbS9vcGVuc3RhdHVzaHEvb3BlbnN0YXR1cy9wYWNrYWdlcy9wcm90by9vcGVuc3RhdHVzL3N0YXR1c19wYWdlL3YxO3N0YXR1c3BhZ2V2MWIGcHJvdG8z", [file_gnostic_openapi_v3_annotations]); + fileDesc("CitvcGVuc3RhdHVzL3N0YXR1c19wYWdlL3YxL3N0YXR1c19wYWdlLnByb3RvEhlvcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxIpMCCgtDdXN0b21UaGVtZRJpCgVsaWdodBgBIAMoCzIxLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuQ3VzdG9tVGhlbWUuTGlnaHRFbnRyeUInukckOiISIHsiLS1wcmltYXJ5IjogImhzbCgyNCA5NCUgNTAlKSJ9Ej4KBGRhcmsYAiADKAsyMC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkN1c3RvbVRoZW1lLkRhcmtFbnRyeRosCgpMaWdodEVudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEaKwoJRGFya0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEi0gUKClN0YXR1c1BhZ2USCgoCaWQYASABKAkSDQoFdGl0bGUYAiABKAkSEwoLZGVzY3JpcHRpb24YAyABKAkSHgoEc2x1ZxgEIAEoCUIQukcNOgsSCWFjbWUtY29ycBIwCg1jdXN0b21fZG9tYWluGAUgASgJQhm6RxY6FBISc3RhdHVzLmV4YW1wbGUuY29tEhEKCXB1Ymxpc2hlZBgGIAEoCBI+CgthY2Nlc3NfdHlwZRgHIAEoDjIpLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuUGFnZUFjY2Vzc1R5cGUSMwoFdGhlbWUYCCABKA4yJC5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLlBhZ2VUaGVtZRIUCgxob21lcGFnZV91cmwYCSABKAkSEwoLY29udGFjdF91cmwYCiABKAkSDAoEaWNvbhgLIAEoCRIvCgpjcmVhdGVkX2F0GAwgASgJQhu6Rxg6FhIUMjAyNC0wMS0xNVQwOTowMDowMFoSLwoKdXBkYXRlZF9hdBgNIAEoCUIbukcYOhYSFDIwMjQtMDYtMjBUMTQ6MzA6MDBaEjkKDmRlZmF1bHRfbG9jYWxlGA4gASgOMiEub3BlbnN0YXR1cy5zdGF0dXNfcGFnZS52MS5Mb2NhbGUSMgoHbG9jYWxlcxgPIAMoDjIhLm9wZW5zdGF0dXMuc3RhdHVzX3BhZ2UudjEuTG9jYWxlEhAKCHBhc3N3b3JkGBAgASgJEhoKEmF1dGhfZW1haWxfZG9tYWlucxgRIAMoCRITCgthbGxvd19pbmRleBgSIAEoCBIZChFhbGxvd2VkX2lwX3JhbmdlcxgTIAEoCRJBCgxjdXN0b21fdGhlbWUYFCABKAsyJi5vcGVuc3RhdHVzLnN0YXR1c19wYWdlLnYxLkN1c3RvbVRoZW1lSACIAQFCDwoNX2N1c3RvbV90aGVtZSKpAQoRU3RhdHVzUGFnZVN1bW1hcnkSCgoCaWQYASABKAkSDQoFdGl0bGUYAiABKAkSDAoEc2x1ZxgDIAEoCRIRCglwdWJsaXNoZWQYBCABKAgSEgoKY3JlYXRlZF9hdBgFIAEoCRISCgp1cGRhdGVkX2F0GAYgASgJEjAKDWN1c3RvbV9kb21haW4YByABKAlCGbpHFjoUEhJzdGF0dXMuZXhhbXBsZS5jb20qwAEKDlBhZ2VBY2Nlc3NUeXBlEiAKHFBBR0VfQUNDRVNTX1RZUEVfVU5TUEVDSUZJRUQQABIbChdQQUdFX0FDQ0VTU19UWVBFX1BVQkxJQxABEicKI1BBR0VfQUNDRVNTX1RZUEVfUEFTU1dPUkRfUFJPVEVDVEVEEAISIgoeUEFHRV9BQ0NFU1NfVFlQRV9BVVRIRU5USUNBVEVEEAMSIgoeUEFHRV9BQ0NFU1NfVFlQRV9JUF9SRVNUUklDVEVEEAQqaQoJUGFnZVRoZW1lEhoKFlBBR0VfVEhFTUVfVU5TUEVDSUZJRUQQABIVChFQQUdFX1RIRU1FX1NZU1RFTRABEhQKEFBBR0VfVEhFTUVfTElHSFQQAhITCg9QQUdFX1RIRU1FX0RBUksQAypNCgZMb2NhbGUSFgoSTE9DQUxFX1VOU1BFQ0lGSUVEEAASDQoJTE9DQUxFX0VOEAESDQoJTE9DQUxFX0ZSEAISDQoJTE9DQUxFX0RFEAMq7AEKDU92ZXJhbGxTdGF0dXMSHgoaT1ZFUkFMTF9TVEFUVVNfVU5TUEVDSUZJRUQQABIeChpPVkVSQUxMX1NUQVRVU19PUEVSQVRJT05BTBABEhsKF09WRVJBTExfU1RBVFVTX0RFR1JBREVEEAISIQodT1ZFUkFMTF9TVEFUVVNfUEFSVElBTF9PVVRBR0UQAxIfChtPVkVSQUxMX1NUQVRVU19NQUpPUl9PVVRBR0UQBBIeChpPVkVSQUxMX1NUQVRVU19NQUlOVEVOQU5DRRAFEhoKFk9WRVJBTExfU1RBVFVTX1VOS05PV04QBkJaWlhnaXRodWIuY29tL29wZW5zdGF0dXNocS9vcGVuc3RhdHVzL3BhY2thZ2VzL3Byb3RvL29wZW5zdGF0dXMvc3RhdHVzX3BhZ2UvdjE7c3RhdHVzcGFnZXYxYgZwcm90bzM", [file_gnostic_openapi_v3_annotations]); + +/** + * CustomTheme holds per-mode CSS variable overrides merged over the page theme. + * + * @generated from message openstatus.status_page.v1.CustomTheme + */ +export type CustomTheme = Message<"openstatus.status_page.v1.CustomTheme"> & { + /** + * CSS variable overrides applied in light mode, keyed by variable name. + * + * @generated from field: map light = 1; + */ + light: { [key: string]: string }; + + /** + * CSS variable overrides applied in dark mode, keyed by variable name. + * + * @generated from field: map dark = 2; + */ + dark: { [key: string]: string }; +}; + +/** + * Describes the message openstatus.status_page.v1.CustomTheme. + * Use `create(CustomThemeSchema)` to create a new message. + */ +export const CustomThemeSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_openstatus_status_page_v1_status_page, 0); /** * StatusPage represents a full status page with all details. @@ -151,6 +179,13 @@ export type StatusPage = Message<"openstatus.status_page.v1.StatusPage"> & { * @generated from field: string allowed_ip_ranges = 19; */ allowedIpRanges: string; + + /** + * Per-mode CSS variable overrides merged over the theme (only set when configured). + * + * @generated from field: optional openstatus.status_page.v1.CustomTheme custom_theme = 20; + */ + customTheme?: CustomTheme | undefined; }; /** @@ -158,7 +193,7 @@ export type StatusPage = Message<"openstatus.status_page.v1.StatusPage"> & { * Use `create(StatusPageSchema)` to create a new message. */ export const StatusPageSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_openstatus_status_page_v1_status_page, 0); + messageDesc(file_openstatus_status_page_v1_status_page, 1); /** * StatusPageSummary represents metadata for a status page (used in list responses). @@ -221,7 +256,7 @@ export type StatusPageSummary = Message<"openstatus.status_page.v1.StatusPageSum * Use `create(StatusPageSummarySchema)` to create a new message. */ export const StatusPageSummarySchema: GenMessage = /*@__PURE__*/ - messageDesc(file_openstatus_status_page_v1_status_page, 1); + messageDesc(file_openstatus_status_page_v1_status_page, 2); /** * PageAccessType defines who can access the status page. -- 2.51.2