riverbankcms 0.4.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/acceptance/matrix.mjs +199 -0
- package/dist/cli/index.mjs +72236 -70544
- package/dist/load-config-D88VsMlB.mjs +5 -0
- package/dist/{load-config-lESIf8eO.mjs → load-config-DKJkcASy.mjs} +1 -1
- package/dist/{sdk-peer-CeNepTFW.mjs → sdk-peer-sZF43pDS.mjs} +2 -2
- package/dist/{style-J8_HbTIm.mjs → style-CeGQc8Oy.mjs} +1882 -512
- package/dist/testing.mjs +2 -2
- package/package.json +1 -1
- package/dist/load-config-FmixWCpv.mjs +0 -5
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { At as ok, B as parseUtcIsoDateTimeString, Ct as unsafeAsSiteScopedStoragePath, D as mapNonEmptyArray, Dt as unsafeAsUploadSessionId, E as fromArray, Et as unsafeAsStoragePath, O as hasText, Ot as unsafeAsUserId, St as unsafeAsSiteId, Tt as unsafeAsStorageBucketName, Z as assertNever, _ as parseFooterCompositionV1, _t as unsafeAsPositiveByteCount, a as parseVerticalPresetId, at as unsafeAsBookingRequirementId, bt as unsafeAsSafeDownloadFilename, c as EVENT_PRESET_VALUES, dt as unsafeAsEventOfferingId, f as createSecret, ft as unsafeAsEventSeriesId, g as footerNavigationLayouts, gt as unsafeAsOriginalFilename, h as stableJsonStringify, ht as unsafeAsOccurrenceId, i as parseVerticalId, k as isPresent, kt as err, l as ORDINARY_EVENT_PRESET_VALUES, lt as unsafeAsDetectedMimeType, m as parseManagementKey, mt as unsafeAsMediaAssetId, n as canonicalizeConfiguredBookingVerticalConfig, nt as parseSiteId, o as resolveSiteVerticalBookingConfig, ot as unsafeAsBookingSessionId, p as exposeSecret, pt as unsafeAsFileChecksumSha256, st as unsafeAsDeclaredMimeType, ut as unsafeAsEntitlementId, vt as unsafeAsPositiveSeconds, w as isCanonicalRichTextDoc, xt as unsafeAsSiteFontId, yt as unsafeAsProductId } from "./src-DKm0bLEA.mjs";
|
|
3
3
|
import { a as classifySdkEventPricing, n as sdkEventOccurrenceInputSchema, o as classifySdkEventPaymentTerms } from "./sdk-event-upsert-Dy08IXYL.mjs";
|
|
4
|
-
import { t as CliSdkPeerError } from "./sdk-peer-
|
|
4
|
+
import { t as CliSdkPeerError } from "./sdk-peer-sZF43pDS.mjs";
|
|
5
5
|
import "node:module";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
8
|
import * as fs from "fs/promises";
|
|
9
|
-
import * as path from "path";
|
|
9
|
+
import * as path$1 from "path";
|
|
10
10
|
import * as readline from "readline";
|
|
11
11
|
import { z } from "zod";
|
|
12
|
-
import {
|
|
12
|
+
import { constants } from "node:fs";
|
|
13
|
+
import { basename, dirname as dirname$1, join } from "node:path";
|
|
14
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
15
|
+
import { access, chmod, link, lstat, mkdir, open, rename, unlink as unlink$1 } from "node:fs/promises";
|
|
13
16
|
|
|
14
17
|
//#region rolldown:runtime
|
|
15
18
|
var __create = Object.create;
|
|
@@ -175,11 +178,11 @@ function sdkCommandApplicationResultSchema(value) {
|
|
|
175
178
|
|
|
176
179
|
//#endregion
|
|
177
180
|
//#region ../api/src/sdkOccurrenceResponseSchemas.ts
|
|
178
|
-
const identifierSchema = z.string().min(1);
|
|
181
|
+
const identifierSchema$1 = z.string().min(1);
|
|
179
182
|
const timestampSchema = z.string().min(1);
|
|
180
183
|
const sdkManagementEventOccurrenceSchema = z.object({
|
|
181
|
-
id: identifierSchema.transform(unsafeAsOccurrenceId),
|
|
182
|
-
seriesId: identifierSchema.transform(unsafeAsEventSeriesId),
|
|
184
|
+
id: identifierSchema$1.transform(unsafeAsOccurrenceId),
|
|
185
|
+
seriesId: identifierSchema$1.transform(unsafeAsEventSeriesId),
|
|
183
186
|
startsAt: timestampSchema,
|
|
184
187
|
endsAt: timestampSchema,
|
|
185
188
|
timeZone: z.string().min(1),
|
|
@@ -214,6 +217,125 @@ const sdkEventOccurrenceResponseSchema = z.object({ occurrence: sdkManagementEve
|
|
|
214
217
|
const sdkDeleteEventOccurrenceResponseSchema = z.object({ deleted: z.literal(true) });
|
|
215
218
|
const sdkReorderResponseSchema = z.object({ reordered: z.literal(true) });
|
|
216
219
|
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region ../api/src/sdkMediaResponseSchemas.ts
|
|
222
|
+
const identifierSchema = z.string().min(1);
|
|
223
|
+
const nullablePathSchema = z.string().nullable();
|
|
224
|
+
const managementMediaAssetSchema = z.object({
|
|
225
|
+
id: identifierSchema,
|
|
226
|
+
siteId: identifierSchema,
|
|
227
|
+
identifier: identifierSchema,
|
|
228
|
+
storageBucket: identifierSchema,
|
|
229
|
+
storagePath: identifierSchema,
|
|
230
|
+
filename: identifierSchema,
|
|
231
|
+
mimeType: identifierSchema,
|
|
232
|
+
width: z.number().int().nonnegative().nullable(),
|
|
233
|
+
height: z.number().int().nonnegative().nullable(),
|
|
234
|
+
bytes: z.number().int().nonnegative().nullable(),
|
|
235
|
+
checksumSha256: z.string().nullable(),
|
|
236
|
+
createdAt: identifierSchema,
|
|
237
|
+
updatedAt: identifierSchema
|
|
238
|
+
});
|
|
239
|
+
const blockLocationFields = {
|
|
240
|
+
siteId: identifierSchema,
|
|
241
|
+
blockId: identifierSchema,
|
|
242
|
+
blockKind: identifierSchema,
|
|
243
|
+
blockIdentifier: z.string().nullable(),
|
|
244
|
+
stage: z.enum(["draft", "published"])
|
|
245
|
+
};
|
|
246
|
+
const mediaUsageLocationSchema = z.discriminatedUnion("kind", [
|
|
247
|
+
z.object({
|
|
248
|
+
kind: z.literal("pageBlock"),
|
|
249
|
+
...blockLocationFields,
|
|
250
|
+
pageId: identifierSchema,
|
|
251
|
+
pageTitle: z.string().nullable(),
|
|
252
|
+
pageDraftPath: nullablePathSchema,
|
|
253
|
+
pagePublishedPath: nullablePathSchema
|
|
254
|
+
}),
|
|
255
|
+
z.object({
|
|
256
|
+
kind: z.literal("entrySlotBlock"),
|
|
257
|
+
...blockLocationFields,
|
|
258
|
+
entryId: identifierSchema,
|
|
259
|
+
entryTitle: z.string(),
|
|
260
|
+
typeKey: identifierSchema,
|
|
261
|
+
routeDraftPath: nullablePathSchema,
|
|
262
|
+
routePublishedPath: nullablePathSchema,
|
|
263
|
+
slotKey: identifierSchema
|
|
264
|
+
}),
|
|
265
|
+
z.object({
|
|
266
|
+
kind: z.literal("surfaceSlotBlock"),
|
|
267
|
+
...blockLocationFields,
|
|
268
|
+
pageSurfaceId: identifierSchema,
|
|
269
|
+
surfaceSlotId: identifierSchema,
|
|
270
|
+
slotKey: identifierSchema
|
|
271
|
+
}),
|
|
272
|
+
z.object({
|
|
273
|
+
kind: z.literal("contentEntry"),
|
|
274
|
+
siteId: identifierSchema,
|
|
275
|
+
entryId: identifierSchema,
|
|
276
|
+
entryTitle: z.string(),
|
|
277
|
+
typeKey: identifierSchema,
|
|
278
|
+
routeDraftPath: nullablePathSchema,
|
|
279
|
+
routePublishedPath: nullablePathSchema,
|
|
280
|
+
stage: z.enum(["draft", "published"])
|
|
281
|
+
}),
|
|
282
|
+
z.object({
|
|
283
|
+
kind: z.literal("productFileDownload"),
|
|
284
|
+
siteId: identifierSchema,
|
|
285
|
+
productId: identifierSchema,
|
|
286
|
+
productTitle: z.string()
|
|
287
|
+
})
|
|
288
|
+
]);
|
|
289
|
+
const mediaUsageItemSchema = z.object({
|
|
290
|
+
assetId: identifierSchema,
|
|
291
|
+
filename: z.string().nullable(),
|
|
292
|
+
usedCount: z.number().int().nonnegative(),
|
|
293
|
+
locations: z.array(mediaUsageLocationSchema)
|
|
294
|
+
});
|
|
295
|
+
const mediaDeleteWarningSchema = z.discriminatedUnion("kind", [z.object({
|
|
296
|
+
kind: z.literal("ORPHAN_CLEANUP_FAILED"),
|
|
297
|
+
message: z.string(),
|
|
298
|
+
storageBucket: identifierSchema,
|
|
299
|
+
storagePath: identifierSchema
|
|
300
|
+
}), z.object({
|
|
301
|
+
kind: z.literal("LABEL_CLEANUP_FAILED"),
|
|
302
|
+
message: z.string()
|
|
303
|
+
})]);
|
|
304
|
+
const sdkMediaListResponseSchema = z.object({
|
|
305
|
+
items: z.array(managementMediaAssetSchema),
|
|
306
|
+
total: z.number().int().nonnegative(),
|
|
307
|
+
limit: z.number().int().positive(),
|
|
308
|
+
offset: z.number().int().nonnegative(),
|
|
309
|
+
nextOffset: z.number().int().nonnegative().optional()
|
|
310
|
+
});
|
|
311
|
+
const sdkMediaDownloadResponseSchema = z.object({
|
|
312
|
+
transferUrl: z.string().url(),
|
|
313
|
+
asset: managementMediaAssetSchema
|
|
314
|
+
});
|
|
315
|
+
const sdkMediaDeletePreviewResponseSchema = z.discriminatedUnion("kind", [
|
|
316
|
+
z.object({ kind: z.literal("missing") }),
|
|
317
|
+
z.object({
|
|
318
|
+
kind: z.literal("in_use"),
|
|
319
|
+
usage: mediaUsageItemSchema
|
|
320
|
+
}),
|
|
321
|
+
z.object({
|
|
322
|
+
kind: z.literal("deletable"),
|
|
323
|
+
assetId: identifierSchema
|
|
324
|
+
})
|
|
325
|
+
]);
|
|
326
|
+
const sdkMediaDeleteApplyResponseSchema = z.discriminatedUnion("kind", [
|
|
327
|
+
z.object({ kind: z.literal("missing") }),
|
|
328
|
+
z.object({
|
|
329
|
+
kind: z.literal("in_use"),
|
|
330
|
+
usage: mediaUsageItemSchema
|
|
331
|
+
}),
|
|
332
|
+
z.object({ kind: z.literal("deleted") }),
|
|
333
|
+
z.object({
|
|
334
|
+
kind: z.literal("deleted_with_warnings"),
|
|
335
|
+
warnings: z.tuple([mediaDeleteWarningSchema], mediaDeleteWarningSchema)
|
|
336
|
+
})
|
|
337
|
+
]);
|
|
338
|
+
|
|
217
339
|
//#endregion
|
|
218
340
|
//#region ../blocks/src/system/types/link.ts
|
|
219
341
|
const nullableString = z.union([z.string().min(1), z.null()]);
|
|
@@ -285,16 +407,16 @@ function visitLinkValueReferences(value, visitor) {
|
|
|
285
407
|
if (!parsed.success) {
|
|
286
408
|
return;
|
|
287
409
|
}
|
|
288
|
-
const link = parsed.data;
|
|
289
|
-
switch (link.kind) {
|
|
410
|
+
const link$1 = parsed.data;
|
|
411
|
+
switch (link$1.kind) {
|
|
290
412
|
case "internal":
|
|
291
|
-
visitor.routeIds.add(link.routeId);
|
|
413
|
+
visitor.routeIds.add(link$1.routeId);
|
|
292
414
|
return;
|
|
293
415
|
case "page":
|
|
294
|
-
visitor.pageIdentifiers.add(link.identifier);
|
|
416
|
+
visitor.pageIdentifiers.add(link$1.identifier);
|
|
295
417
|
return;
|
|
296
418
|
case "entry":
|
|
297
|
-
visitor.entryIdentifiers.add(link.contentType, link.identifier);
|
|
419
|
+
visitor.entryIdentifiers.add(link$1.contentType, link$1.identifier);
|
|
298
420
|
return;
|
|
299
421
|
case "external":
|
|
300
422
|
case "url": return;
|
|
@@ -9249,6 +9371,11 @@ const gradientConfigSchema = z.object({
|
|
|
9249
9371
|
position: z.string().optional(),
|
|
9250
9372
|
stops: z.array(gradientStopSchema).min(2)
|
|
9251
9373
|
});
|
|
9374
|
+
const gradientsSchema = z.object({
|
|
9375
|
+
button: z.string().min(1).nullable().optional(),
|
|
9376
|
+
hero: z.string().min(1).nullable().optional(),
|
|
9377
|
+
background: z.string().min(1).nullable().optional()
|
|
9378
|
+
});
|
|
9252
9379
|
|
|
9253
9380
|
//#endregion
|
|
9254
9381
|
//#region ../theme-core/src/interactive/baseSchema.ts
|
|
@@ -10474,12 +10601,33 @@ const headerContainerSchema = z.object({
|
|
|
10474
10601
|
|
|
10475
10602
|
//#endregion
|
|
10476
10603
|
//#region ../theme-core/src/header/interactionPalette.ts
|
|
10477
|
-
|
|
10604
|
+
/**
|
|
10605
|
+
* The palettes that fill the nav link with a colour on hover and focus. Each
|
|
10606
|
+
* one names a background/foreground token pair.
|
|
10607
|
+
*/
|
|
10608
|
+
const headerInteractionPillPaletteValues = [
|
|
10478
10609
|
"primary",
|
|
10479
10610
|
"secondary",
|
|
10480
10611
|
"accent"
|
|
10481
10612
|
];
|
|
10613
|
+
/**
|
|
10614
|
+
* "No pill": the nav link keeps whatever hover treatment its recipe compiled,
|
|
10615
|
+
* and the palette step writes nothing at all (#1318 Phase 4c decision 2).
|
|
10616
|
+
*
|
|
10617
|
+
* It exists because "a colour change on hover, no filled background" was not
|
|
10618
|
+
* expressible in the vocabulary, and it is what
|
|
10619
|
+
* `anna-raven-therapy-and-yoga` and `kirtan-lucy` have rendered since before
|
|
10620
|
+
* the palette existed. Without it those two rows can only be classified as a
|
|
10621
|
+
* conscious visual change.
|
|
10622
|
+
*/
|
|
10623
|
+
const headerInteractionQuietPalette = "quiet";
|
|
10624
|
+
const headerInteractionPaletteValues = [...headerInteractionPillPaletteValues, headerInteractionQuietPalette];
|
|
10482
10625
|
const headerInteractionPaletteSchema = z.enum(headerInteractionPaletteValues);
|
|
10626
|
+
/**
|
|
10627
|
+
* Narrowed to the pill palettes on purpose: `'quiet'` has no token pair, and a
|
|
10628
|
+
* caller that reaches this function without excluding it first is a type error
|
|
10629
|
+
* rather than a runtime surprise.
|
|
10630
|
+
*/
|
|
10483
10631
|
function headerInteractionPalettePair(palette) {
|
|
10484
10632
|
switch (palette) {
|
|
10485
10633
|
case "primary": return {
|
|
@@ -10879,11 +11027,12 @@ const inputStyle = z.object({
|
|
|
10879
11027
|
]),
|
|
10880
11028
|
label: z.enum(["inside", "above"])
|
|
10881
11029
|
});
|
|
10882
|
-
const
|
|
11030
|
+
const headerPositioningValues = [
|
|
10883
11031
|
"static",
|
|
10884
11032
|
"sticky",
|
|
10885
11033
|
"fixed"
|
|
10886
|
-
]
|
|
11034
|
+
];
|
|
11035
|
+
const headerPositioning = z.enum(headerPositioningValues);
|
|
10887
11036
|
const headerNavStyle = z.enum([
|
|
10888
11037
|
"minimal",
|
|
10889
11038
|
"underline",
|
|
@@ -11018,7 +11167,7 @@ const headerSchema = z.object({
|
|
|
11018
11167
|
variant: headerVariantWithLegacyAliasSchema,
|
|
11019
11168
|
positioning: headerPositioning,
|
|
11020
11169
|
shrinkOnScroll: z.boolean(),
|
|
11021
|
-
maxWidth: headerMaxWidth,
|
|
11170
|
+
maxWidth: headerMaxWidth.default("container"),
|
|
11022
11171
|
scrollTreatment: headerScrollTreatmentSchema,
|
|
11023
11172
|
background: headerBackgroundSchema.default({
|
|
11024
11173
|
type: "color",
|
|
@@ -11212,11 +11361,6 @@ const bodyTextSchema = z.object({ intro: z.object({
|
|
|
11212
11361
|
paragraphSize: bodyTextIntroTypographySizeSchema.optional(),
|
|
11213
11362
|
headingSize: bodyTextIntroTypographySizeSchema.optional()
|
|
11214
11363
|
}).optional() }).optional();
|
|
11215
|
-
const gradientsSchema = z.object({
|
|
11216
|
-
button: z.string().min(1).nullable().optional(),
|
|
11217
|
-
hero: z.string().min(1).nullable().optional(),
|
|
11218
|
-
background: z.string().min(1).nullable().optional()
|
|
11219
|
-
});
|
|
11220
11364
|
/**
|
|
11221
11365
|
* Testimonial quote-mark treatment, owned by the site style (analogous to the
|
|
11222
11366
|
* accordion icon). `style` is the coarse selector; the optional fields fine-tune
|
|
@@ -11832,7 +11976,7 @@ function resolveAccessibleSurface(input) {
|
|
|
11832
11976
|
threshold,
|
|
11833
11977
|
rejectedCandidates
|
|
11834
11978
|
});
|
|
11835
|
-
const link = resolveRequiredForeground({
|
|
11979
|
+
const link$1 = resolveRequiredForeground({
|
|
11836
11980
|
role: "link",
|
|
11837
11981
|
candidates: recipe.linkCandidates,
|
|
11838
11982
|
backgroundToken,
|
|
@@ -11841,7 +11985,7 @@ function resolveAccessibleSurface(input) {
|
|
|
11841
11985
|
threshold,
|
|
11842
11986
|
rejectedCandidates
|
|
11843
11987
|
});
|
|
11844
|
-
if (!heading || !text || !link) continue;
|
|
11988
|
+
if (!heading || !text || !link$1) continue;
|
|
11845
11989
|
const supporting = resolveSupportingForeground({
|
|
11846
11990
|
candidates: recipe.supportingCandidates,
|
|
11847
11991
|
backgroundToken,
|
|
@@ -11861,7 +12005,7 @@ function resolveAccessibleSurface(input) {
|
|
|
11861
12005
|
},
|
|
11862
12006
|
heading,
|
|
11863
12007
|
text,
|
|
11864
|
-
link,
|
|
12008
|
+
link: link$1,
|
|
11865
12009
|
supporting,
|
|
11866
12010
|
rejectedCandidates
|
|
11867
12011
|
};
|
|
@@ -13303,6 +13447,32 @@ function replaceSiteStyleCompilationPlan(input) {
|
|
|
13303
13447
|
footerLookId: input.footerLookId ?? null
|
|
13304
13448
|
};
|
|
13305
13449
|
}
|
|
13450
|
+
/**
|
|
13451
|
+
* Writes the brand display / logo size preference onto the header's logo style.
|
|
13452
|
+
*
|
|
13453
|
+
* Moved here from preview-next in #1318 Phase 4a so the header preference
|
|
13454
|
+
* pipeline can be composed once, in theme-core, rather than half in the client.
|
|
13455
|
+
*/
|
|
13456
|
+
function withHeaderBrandThemePreferences(theme, preferences) {
|
|
13457
|
+
const currentLogo = theme.header.logo;
|
|
13458
|
+
if (!currentLogo && preferences.brandDisplay === defaultHeaderBrandPreferences.brandDisplay && preferences.logoSize === defaultHeaderBrandPreferences.logoSize) {
|
|
13459
|
+
return theme;
|
|
13460
|
+
}
|
|
13461
|
+
return {
|
|
13462
|
+
...theme,
|
|
13463
|
+
header: {
|
|
13464
|
+
...theme.header,
|
|
13465
|
+
logo: {
|
|
13466
|
+
fontFamily: currentLogo?.fontFamily ?? "heading",
|
|
13467
|
+
letterSpacing: currentLogo?.letterSpacing ?? "normal",
|
|
13468
|
+
gradient: currentLogo?.gradient ?? false,
|
|
13469
|
+
...currentLogo ?? {},
|
|
13470
|
+
brandDisplay: preferences.brandDisplay,
|
|
13471
|
+
size: preferences.logoSize
|
|
13472
|
+
}
|
|
13473
|
+
}
|
|
13474
|
+
};
|
|
13475
|
+
}
|
|
13306
13476
|
|
|
13307
13477
|
//#endregion
|
|
13308
13478
|
//#region ../theme-core/src/site-styles/schemaVersionedJson.ts
|
|
@@ -16465,6 +16635,62 @@ const overlayLogoAccommodations = [
|
|
|
16465
16635
|
inlineMarkWithTitleLogoAccommodation,
|
|
16466
16636
|
unbackedOverlayLogoAccommodation
|
|
16467
16637
|
];
|
|
16638
|
+
const allCuratedLogoAccommodations = [
|
|
16639
|
+
...inlineClassicLogoAccommodations,
|
|
16640
|
+
...centeredLogoAccommodations,
|
|
16641
|
+
...overlayLogoAccommodations
|
|
16642
|
+
];
|
|
16643
|
+
/**
|
|
16644
|
+
* Whether an accommodation pattern draws its own backing behind the logo.
|
|
16645
|
+
* Derived from the catalogue above rather than restated, so a new backing
|
|
16646
|
+
* accommodation cannot drift out of sync with this answer. Patterns declared in
|
|
16647
|
+
* `LogoAccommodationPattern` but not offered by any layout resolve to `false`,
|
|
16648
|
+
* which is the honest answer: nothing can select them.
|
|
16649
|
+
*/
|
|
16650
|
+
const backingLogoAccommodationPatterns = new Set(allCuratedLogoAccommodations.filter((option) => option.providesBacking === true).map((option) => option.pattern));
|
|
16651
|
+
function logoAccommodationProvidesBacking(pattern) {
|
|
16652
|
+
return backingLogoAccommodationPatterns.has(pattern);
|
|
16653
|
+
}
|
|
16654
|
+
/**
|
|
16655
|
+
* The single answer to "what shape is this site's logo" (#1318 Phase 4b).
|
|
16656
|
+
*
|
|
16657
|
+
* The server and the public frontend each had their own copy of this, and they
|
|
16658
|
+
* disagreed on the one case that matters: a logo whose dimensions are unknown.
|
|
16659
|
+
* The server assumed `horizontal`; the frontend asked whether the persisted
|
|
16660
|
+
* accommodation brings its own backing and answered `square` if it does. The
|
|
16661
|
+
* richer rule is the correct one — a badge or masthead accommodation is
|
|
16662
|
+
* evidence about the asset that raw dimensions would otherwise supply — and it
|
|
16663
|
+
* reduces to `horizontal` whenever there is no backing, so it subsumes the
|
|
16664
|
+
* server's rule rather than contradicting it. The backing question goes through
|
|
16665
|
+
* `logoAccommodationProvidesBacking`, so the pattern list has one home.
|
|
16666
|
+
*/
|
|
16667
|
+
function deriveHeaderLogoShape({ asset, accommodation }) {
|
|
16668
|
+
if (!asset) return "text-only";
|
|
16669
|
+
if (!asset.width || !asset.height) {
|
|
16670
|
+
return accommodation !== undefined && logoAccommodationProvidesBacking(accommodation) ? "square" : "horizontal";
|
|
16671
|
+
}
|
|
16672
|
+
return deriveHeaderLogoShapeFromDimensions(asset);
|
|
16673
|
+
}
|
|
16674
|
+
/**
|
|
16675
|
+
* The site's logo as the header pipeline sees it, from the two database values
|
|
16676
|
+
* that describe it: the asset (or its absence) and the accommodation the row
|
|
16677
|
+
* currently persists, which is evidence about the asset when its dimensions are
|
|
16678
|
+
* missing.
|
|
16679
|
+
*
|
|
16680
|
+
* One definition, shared by every shell that has to answer it — the dashboard's
|
|
16681
|
+
* `loadHeaderLogoContextForSite`, the public frontend, and the Phase 4c repair
|
|
16682
|
+
* generator — because a second copy is a second answer to "what shape is this
|
|
16683
|
+
* site's logo", and the two former copies disagreed on exactly the case this
|
|
16684
|
+
* one is careful about (#1318 Phase 4b/4c).
|
|
16685
|
+
*/
|
|
16686
|
+
function headerLogoShapeContext(input) {
|
|
16687
|
+
return {
|
|
16688
|
+
kind: "shape",
|
|
16689
|
+
shape: deriveHeaderLogoShape(input),
|
|
16690
|
+
...input.brand?.brandDisplay === undefined ? {} : { brandDisplayMode: input.brand.brandDisplay },
|
|
16691
|
+
...input.brand?.logoSize === undefined ? {} : { sizePolicy: input.brand.logoSize }
|
|
16692
|
+
};
|
|
16693
|
+
}
|
|
16468
16694
|
function logoFactsForHeaderContext(logo) {
|
|
16469
16695
|
if (logo.kind === "facts") return logo.facts;
|
|
16470
16696
|
switch (logo.shape) {
|
|
@@ -16505,22 +16731,6 @@ function requireHeaderBrandPresentationSelection(plan) {
|
|
|
16505
16731
|
if (plan.kind === "selected") return plan;
|
|
16506
16732
|
throw new Error(`Selected header layout did not have a compatible logo accommodation: ${plan.reasons.join(", ")}`);
|
|
16507
16733
|
}
|
|
16508
|
-
/**
|
|
16509
|
-
* Starts each compilation from the generated theme's header branch, then
|
|
16510
|
-
* clears optional nested styling fields that would otherwise leak from a
|
|
16511
|
-
* different header selection.
|
|
16512
|
-
*/
|
|
16513
|
-
function baseHeader(theme) {
|
|
16514
|
-
const { navLinkStyle: _navLinkStyle, navFontRole: _navFontRole, navTextSize: _navTextSize, dropdownStyle: _dropdownStyle, ctaVariant: _ctaVariant, navContainer: _navContainer, container: _container, shadow: _shadow, logo: _logo, logoText: _logoText, border: _border, ...header } = theme.header;
|
|
16515
|
-
return {
|
|
16516
|
-
...header,
|
|
16517
|
-
navContainer: {
|
|
16518
|
-
type: "none",
|
|
16519
|
-
tint: null,
|
|
16520
|
-
opacity: .15
|
|
16521
|
-
}
|
|
16522
|
-
};
|
|
16523
|
-
}
|
|
16524
16734
|
const headerLayoutCatalog = defineExactCatalog()([
|
|
16525
16735
|
{
|
|
16526
16736
|
id: asHeaderLayoutId("classic"),
|
|
@@ -16854,6 +17064,10 @@ const clearSplitInteractionPalette = defineLookOptionGroup({
|
|
|
16854
17064
|
{
|
|
16855
17065
|
id: "secondary",
|
|
16856
17066
|
label: "Secondary"
|
|
17067
|
+
},
|
|
17068
|
+
{
|
|
17069
|
+
id: headerInteractionQuietPalette,
|
|
17070
|
+
label: "Text only"
|
|
16857
17071
|
}
|
|
16858
17072
|
]
|
|
16859
17073
|
});
|
|
@@ -16887,10 +17101,11 @@ function availableControlState(group, preference, effectivePalette, repair$1) {
|
|
|
16887
17101
|
}
|
|
16888
17102
|
function applyHeaderInteractionPalette(header, group, preference) {
|
|
16889
17103
|
if (!group) return header;
|
|
17104
|
+
const palette = resolveHeaderInteractionPalette(group, preference).option.id;
|
|
17105
|
+
if (palette === headerInteractionQuietPalette) return header;
|
|
16890
17106
|
if (!header.navLinkStyle) {
|
|
16891
17107
|
throw new Error(`Header interaction palette "${group.id}" requires a nav link style.`);
|
|
16892
17108
|
}
|
|
16893
|
-
const palette = resolveHeaderInteractionPalette(group, preference).option.id;
|
|
16894
17109
|
const pair = headerInteractionPalettePair(palette);
|
|
16895
17110
|
const pillEffect = {
|
|
16896
17111
|
effectId: "background-on-hover",
|
|
@@ -16919,6 +17134,103 @@ function resolveHeaderInteractionPalette(group, preference) {
|
|
|
16919
17134
|
function defaultOptionLabel(group) {
|
|
16920
17135
|
return resolveLookOption(group, undefined).option.label;
|
|
16921
17136
|
}
|
|
17137
|
+
/**
|
|
17138
|
+
* The four fields `applyHeaderInteractionPalette` writes, normalised so the
|
|
17139
|
+
* comparison below can be made against bytes that came back out of `jsonb`.
|
|
17140
|
+
*
|
|
17141
|
+
* `hoverColorToken` collapses absent to `null` because the palette writes an
|
|
17142
|
+
* explicit `null` where a pre-palette row simply has no key, and the two mean
|
|
17143
|
+
* the same thing.
|
|
17144
|
+
*/
|
|
17145
|
+
function headerInteractionPaletteFields(header) {
|
|
17146
|
+
return {
|
|
17147
|
+
hoverColorToken: header.navLinkStyle?.hoverColorToken ?? null,
|
|
17148
|
+
borderRadius: header.navLinkStyle?.borderRadius,
|
|
17149
|
+
hover: header.navLinkStyle?.effects?.hover,
|
|
17150
|
+
focus: header.navLinkStyle?.effects?.focus
|
|
17151
|
+
};
|
|
17152
|
+
}
|
|
17153
|
+
/**
|
|
17154
|
+
* Key-sorted serialisation, because one side of the comparison is built here
|
|
17155
|
+
* and the other comes back out of `jsonb` in whatever order it was stored.
|
|
17156
|
+
* `JSON.stringify` alone reported two identical effect lists as different,
|
|
17157
|
+
* which is the same key-order trap the `logoText` derivation hit in Phase 4a.
|
|
17158
|
+
*/
|
|
17159
|
+
function canonicalJson(value) {
|
|
17160
|
+
if (Array.isArray(value)) {
|
|
17161
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
17162
|
+
}
|
|
17163
|
+
if (typeof value === "object" && value !== null) {
|
|
17164
|
+
const entries = Object.entries(value).filter(([, member]) => member !== undefined).sort(([left], [right]) => left.localeCompare(right));
|
|
17165
|
+
return `{${entries.map(([key, member]) => `${JSON.stringify(key)}:${canonicalJson(member)}`).join(",")}}`;
|
|
17166
|
+
}
|
|
17167
|
+
return value === undefined ? "undefined" : JSON.stringify(value);
|
|
17168
|
+
}
|
|
17169
|
+
/**
|
|
17170
|
+
* The effect id the palette writes. A nav link carrying it is "palette-shaped":
|
|
17171
|
+
* its hover treatment is a filled pill rather than the plain colour change a
|
|
17172
|
+
* pre-palette recipe compiles.
|
|
17173
|
+
*/
|
|
17174
|
+
const paletteEffectId = "background-on-hover";
|
|
17175
|
+
function carriesPaletteShapedEffect(header) {
|
|
17176
|
+
const effects = header.navLinkStyle?.effects;
|
|
17177
|
+
return [effects?.hover, effects?.focus].some((list) => (list ?? []).some((effect) => effect.effectId === paletteEffectId));
|
|
17178
|
+
}
|
|
17179
|
+
/**
|
|
17180
|
+
* Which option of `group` a persisted header's nav link style is the output of
|
|
17181
|
+
* (#1318 Phase 4c decision 2).
|
|
17182
|
+
*
|
|
17183
|
+
* This is a baseline comparison rather than a pattern match on effect bytes.
|
|
17184
|
+
* The palette overwrites its four fields unconditionally, so applying an option
|
|
17185
|
+
* to a header leaves that header unchanged exactly when the header is already
|
|
17186
|
+
* that option's output — which makes "which option produced these bytes" a
|
|
17187
|
+
* fixed-point search over the closed option list.
|
|
17188
|
+
*
|
|
17189
|
+
* Order is load-bearing twice over:
|
|
17190
|
+
*
|
|
17191
|
+
* - `'recommended'` is tried first, so a row compiled from the recommendation
|
|
17192
|
+
* derives the sentinel rather than the default option's own id. As with the
|
|
17193
|
+
* structure rule, an explicit choice that happens to equal the default
|
|
17194
|
+
* collapses to the sentinel; nothing in the bytes distinguishes the two.
|
|
17195
|
+
* - `'quiet'` is tried last, because it writes nothing and is therefore a fixed
|
|
17196
|
+
* point of every header. Reaching it means no pill option produced these
|
|
17197
|
+
* bytes — which is what "this row renders no pill" means, *provided the row
|
|
17198
|
+
* carries no pill*. `carriesPaletteShapedEffect` is that proviso: a
|
|
17199
|
+
* palette-shaped nav link that no option reproduces is `'unrecognised'`, not
|
|
17200
|
+
* `'quiet'`.
|
|
17201
|
+
*/
|
|
17202
|
+
function classifyHeaderInteractionPaletteBytes(header, group) {
|
|
17203
|
+
if (!group) return { kind: "no-palette" };
|
|
17204
|
+
if (!header.navLinkStyle) return { kind: "no-palette" };
|
|
17205
|
+
const persisted = canonicalJson(headerInteractionPaletteFields(header));
|
|
17206
|
+
const match = orderedPaletteCandidates(group).find((candidate$1) => canonicalJson(headerInteractionPaletteFields(applyHeaderInteractionPalette(header, group, candidate$1))) === persisted);
|
|
17207
|
+
if (match === undefined) return { kind: "unrecognised" };
|
|
17208
|
+
if (match === headerInteractionQuietPalette && carriesPaletteShapedEffect(header)) {
|
|
17209
|
+
return { kind: "unrecognised" };
|
|
17210
|
+
}
|
|
17211
|
+
return {
|
|
17212
|
+
kind: "derived",
|
|
17213
|
+
preference: match
|
|
17214
|
+
};
|
|
17215
|
+
}
|
|
17216
|
+
/**
|
|
17217
|
+
* The preference `classifyHeaderInteractionPaletteBytes` derived, or `null`
|
|
17218
|
+
* when the bytes express none — either because the style offers no palette or
|
|
17219
|
+
* because they match no option it does offer. Callers that need to tell those
|
|
17220
|
+
* two apart use the classification directly.
|
|
17221
|
+
*/
|
|
17222
|
+
function readHeaderInteractionPaletteFromBytes(header, group) {
|
|
17223
|
+
const classified = classifyHeaderInteractionPaletteBytes(header, group);
|
|
17224
|
+
return classified.kind === "derived" ? classified.preference : null;
|
|
17225
|
+
}
|
|
17226
|
+
function orderedPaletteCandidates(group) {
|
|
17227
|
+
const ids = group.options.map((option) => option.id);
|
|
17228
|
+
return [
|
|
17229
|
+
"recommended",
|
|
17230
|
+
...ids.filter((id) => id !== headerInteractionQuietPalette),
|
|
17231
|
+
...ids.filter((id) => id === headerInteractionQuietPalette)
|
|
17232
|
+
];
|
|
17233
|
+
}
|
|
16922
17234
|
|
|
16923
17235
|
//#endregion
|
|
16924
17236
|
//#region ../theme-core/src/site-styles/headerStyleDefaults.ts
|
|
@@ -17589,10 +17901,37 @@ function recipeCta(cta, theme) {
|
|
|
17589
17901
|
...cta.gap ? { ctaGap: cta.gap } : {}
|
|
17590
17902
|
};
|
|
17591
17903
|
}
|
|
17592
|
-
/**
|
|
17593
|
-
|
|
17904
|
+
/**
|
|
17905
|
+
* Layout-derived structure. The only part of the header a recipe cannot reach.
|
|
17906
|
+
*
|
|
17907
|
+
* It used to start from `baseHeader(theme)` — the previous header with the
|
|
17908
|
+
* style-owned keys stripped — which is what let a value hand-edited once
|
|
17909
|
+
* survive every later recompile. Phase 4e deleted that (#1318): the layout
|
|
17910
|
+
* writes its own fields and nothing else arrives from the theme's header.
|
|
17911
|
+
*
|
|
17912
|
+
* Three fields moved here as a result, each with a named producer rather than
|
|
17913
|
+
* an inherited value:
|
|
17914
|
+
*
|
|
17915
|
+
* - `maxWidth` is the constant `'container'` (D16). It is layout-owned with one
|
|
17916
|
+
* value; `headerSchema` makes it required on the *output* type, so a
|
|
17917
|
+
* `.default()` at the parse boundary would not do — that would be a hidden
|
|
17918
|
+
* authority, which is the thing this slice removes.
|
|
17919
|
+
* - `navContainer` is `baseHeader`'s old default, which a `frame: {kind:'none'}`
|
|
17920
|
+
* recipe still relies on being present rather than omitted.
|
|
17921
|
+
* - `scrollTreatment` and `interactionPalette` are deliberately **absent**.
|
|
17922
|
+
* Neither is compiled here: `scrollTreatment` is written at render time by
|
|
17923
|
+
* `applyOpeningForeground` from the page's own context, and the interaction
|
|
17924
|
+
* palette's marker field is written by `applyHeaderPreferences` from the
|
|
17925
|
+
* record. Both used to reach the output only by surviving `baseHeader`.
|
|
17926
|
+
*/
|
|
17927
|
+
function headerForLayout(layout) {
|
|
17594
17928
|
return {
|
|
17595
|
-
|
|
17929
|
+
maxWidth: "container",
|
|
17930
|
+
navContainer: {
|
|
17931
|
+
type: "none",
|
|
17932
|
+
tint: null,
|
|
17933
|
+
opacity: .15
|
|
17934
|
+
},
|
|
17596
17935
|
variant: layout.variant,
|
|
17597
17936
|
positioning: layout.defaultPositioning,
|
|
17598
17937
|
shrinkOnScroll: layout.shrinkOnScroll,
|
|
@@ -17607,12 +17946,19 @@ function headerForLayout(theme, layout) {
|
|
|
17607
17946
|
/**
|
|
17608
17947
|
* The single header interpreter. Pure and total: same `(theme, layout, recipe)`
|
|
17609
17948
|
* in, same object out, no throws of its own.
|
|
17949
|
+
*
|
|
17950
|
+
* It **constructs** a `ThemeHeader` rather than starting from one (#1318 Phase
|
|
17951
|
+
* 4e). `theme` is read for the palette-dependent facets only — the themed
|
|
17952
|
+
* shadow and the CTA variant — never for `theme.header`, so no value can
|
|
17953
|
+
* survive a recompile by accident. Every required field of `headerSchema` is
|
|
17954
|
+
* produced by a named branch here or in `headerForLayout`; the type is the
|
|
17955
|
+
* check.
|
|
17610
17956
|
*/
|
|
17611
17957
|
function compileHeaderRecipe(theme, layout, recipe) {
|
|
17612
17958
|
const effective = effectiveRecipe(layout, recipe);
|
|
17613
17959
|
const navLinkStyle = recipeNavLinkStyle(effective);
|
|
17614
17960
|
return {
|
|
17615
|
-
...headerForLayout(
|
|
17961
|
+
...headerForLayout(layout),
|
|
17616
17962
|
textColor: toneColorToken(effective.tone),
|
|
17617
17963
|
navColor: toneColorToken(effective.tone),
|
|
17618
17964
|
dropdownStyle: toneDropdownStyle(effective.tone),
|
|
@@ -17646,11 +17992,19 @@ function compileHeaderRecipe(theme, layout, recipe) {
|
|
|
17646
17992
|
* site style has to say so, because silently falling back to the catalogue
|
|
17647
17993
|
* default is exactly the ownership leak this slice closes. Pass `null` when
|
|
17648
17994
|
* there genuinely is no site style (initial site provisioning).
|
|
17649
|
-
|
|
17650
|
-
|
|
17995
|
+
*
|
|
17996
|
+
* `interactionPalette` is a parameter rather than a read off
|
|
17997
|
+
* `theme.header.interactionPalette` (#1318 Phase 4c). The marker field on the
|
|
17998
|
+
* header is where the preference is *persisted*, but taking it from there made
|
|
17999
|
+
* the palette the one preference the compiler could not be told about — which
|
|
18000
|
+
* is invisible while the marker always equals what was compiled and wrong the
|
|
18001
|
+
* moment a backfill supplies a different one. The caller that owns the
|
|
18002
|
+
* preference record now says which palette to compile.
|
|
18003
|
+
*/
|
|
18004
|
+
function compileHeaderLayoutStyleSelection(theme, resolution, siteStyle, interactionPalette) {
|
|
17651
18005
|
const recipe = resolveHeaderStyleRecipe(siteStyle, resolution.style.id);
|
|
17652
18006
|
const header = compileHeaderRecipe(theme, resolution.layout, recipe);
|
|
17653
|
-
return applyHeaderInteractionPalette(header, recipe.interactionPalette,
|
|
18007
|
+
return applyHeaderInteractionPalette(header, recipe.interactionPalette, interactionPalette);
|
|
17654
18008
|
}
|
|
17655
18009
|
|
|
17656
18010
|
//#endregion
|
|
@@ -20166,10 +20520,22 @@ function defaultPaletteForSiteStyle(siteStyle) {
|
|
|
20166
20520
|
return defaultCuratedChoice(siteStyle.paletteRecommendations);
|
|
20167
20521
|
}
|
|
20168
20522
|
function defaultHeaderSelectionForSiteStyle(siteStyle) {
|
|
20523
|
+
return defaultHeaderCurationForSiteStyle(siteStyle).selection;
|
|
20524
|
+
}
|
|
20525
|
+
/**
|
|
20526
|
+
* The curated default selection together with the style choices that belong to
|
|
20527
|
+
* it. Carrying the styles alongside the ids is what lets a caller fall back to
|
|
20528
|
+
* the default layout without a second, nullable lookup that can never actually
|
|
20529
|
+
* miss — the source of an unreachable throw before #1318 Phase 4a.
|
|
20530
|
+
*/
|
|
20531
|
+
function defaultHeaderCurationForSiteStyle(siteStyle) {
|
|
20169
20532
|
const [{ layout, styles }] = siteStyle.headerCuration;
|
|
20170
20533
|
return {
|
|
20171
|
-
|
|
20172
|
-
|
|
20534
|
+
selection: {
|
|
20535
|
+
layoutId: layout.id,
|
|
20536
|
+
styleId: styles[0].id
|
|
20537
|
+
},
|
|
20538
|
+
styles
|
|
20173
20539
|
};
|
|
20174
20540
|
}
|
|
20175
20541
|
function headerLayoutChoicesForSiteStyle(siteStyle) {
|
|
@@ -22724,6 +23090,878 @@ function reinterpretForSiteStyle(input) {
|
|
|
22724
23090
|
};
|
|
22725
23091
|
}
|
|
22726
23092
|
|
|
23093
|
+
//#endregion
|
|
23094
|
+
//#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
|
|
23095
|
+
const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
|
|
23096
|
+
const quickSiteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceQuickColorTokenNames);
|
|
23097
|
+
const advancedSiteChromeBackgroundColorTokens = siteChromeBackgroundColorTokens.filter((token) => !quickSiteChromeBackgroundColorTokens.includes(token));
|
|
23098
|
+
const defaultSiteChromeBackgroundOverride = { kind: "default" };
|
|
23099
|
+
function defaultSiteChromeBackgroundPreferences() {
|
|
23100
|
+
return {
|
|
23101
|
+
header: defaultSiteChromeBackgroundOverride,
|
|
23102
|
+
footer: defaultSiteChromeBackgroundOverride
|
|
23103
|
+
};
|
|
23104
|
+
}
|
|
23105
|
+
function parseSiteChromeBackgroundColorToken(value) {
|
|
23106
|
+
const token = parseBackgroundAppearanceColorToken(value);
|
|
23107
|
+
return token ? token : null;
|
|
23108
|
+
}
|
|
23109
|
+
function siteChromeBackgroundPreferencesFromTheme(theme) {
|
|
23110
|
+
if (!theme) return defaultSiteChromeBackgroundPreferences();
|
|
23111
|
+
return {
|
|
23112
|
+
header: theme.header ? siteChromeBackgroundOverrideFromHeader(theme.header) : defaultSiteChromeBackgroundOverride,
|
|
23113
|
+
footer: theme.footer ? siteChromeBackgroundOverrideFromFooter(theme.footer) : defaultSiteChromeBackgroundOverride
|
|
23114
|
+
};
|
|
23115
|
+
}
|
|
23116
|
+
function applySiteChromeBackgroundPreferences(theme, preferences) {
|
|
23117
|
+
return {
|
|
23118
|
+
...theme,
|
|
23119
|
+
header: applyHeaderBackgroundOverride(theme, theme.header, preferences.header),
|
|
23120
|
+
footer: applyFooterBackgroundOverride(theme, theme.footer, preferences.footer)
|
|
23121
|
+
};
|
|
23122
|
+
}
|
|
23123
|
+
function canCustomizeSiteChromeBackground(theme, surface$1) {
|
|
23124
|
+
if (surface$1 === "footer") return true;
|
|
23125
|
+
const header = theme?.header;
|
|
23126
|
+
if (!header) return false;
|
|
23127
|
+
const navContainerType = header.navContainer?.type ?? "none";
|
|
23128
|
+
return header.variant !== "floating" && navContainerType !== "glass" && navContainerType !== "pill";
|
|
23129
|
+
}
|
|
23130
|
+
function siteChromeBackgroundOverrideLabel(override) {
|
|
23131
|
+
switch (override.kind) {
|
|
23132
|
+
case "default": return "Default";
|
|
23133
|
+
case "color": return formatSiteChromeBackgroundTokenLabel(override.token);
|
|
23134
|
+
}
|
|
23135
|
+
}
|
|
23136
|
+
function formatSiteChromeBackgroundTokenLabel(token) {
|
|
23137
|
+
return String(token).replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
23138
|
+
}
|
|
23139
|
+
function siteChromeBackgroundOverrideEquals(left, right) {
|
|
23140
|
+
return left.kind === right.kind && (left.kind === "default" || right.kind === "color" && left.token === right.token);
|
|
23141
|
+
}
|
|
23142
|
+
function cycleSiteChromeBackgroundOverride(current, direction) {
|
|
23143
|
+
const fallback = direction === "next" ? quickSiteChromeBackgroundColorTokens[0] : quickSiteChromeBackgroundColorTokens[quickSiteChromeBackgroundColorTokens.length - 1];
|
|
23144
|
+
if (!fallback) return defaultSiteChromeBackgroundOverride;
|
|
23145
|
+
if (current.kind === "default") {
|
|
23146
|
+
return {
|
|
23147
|
+
kind: "color",
|
|
23148
|
+
token: fallback
|
|
23149
|
+
};
|
|
23150
|
+
}
|
|
23151
|
+
const currentIndex = quickSiteChromeBackgroundColorTokens.indexOf(current.token);
|
|
23152
|
+
if (currentIndex === -1) {
|
|
23153
|
+
return {
|
|
23154
|
+
kind: "color",
|
|
23155
|
+
token: fallback
|
|
23156
|
+
};
|
|
23157
|
+
}
|
|
23158
|
+
const nextIndex = direction === "next" ? (currentIndex + 1) % quickSiteChromeBackgroundColorTokens.length : currentIndex === 0 ? quickSiteChromeBackgroundColorTokens.length - 1 : currentIndex - 1;
|
|
23159
|
+
const token = requiredQuickSiteChromeBackgroundColorToken(nextIndex);
|
|
23160
|
+
return {
|
|
23161
|
+
kind: "color",
|
|
23162
|
+
token
|
|
23163
|
+
};
|
|
23164
|
+
}
|
|
23165
|
+
/**
|
|
23166
|
+
* Reads the header background override back out of persisted bytes. Exported
|
|
23167
|
+
* for `readHeaderPreferences`, which needs the header half on its own.
|
|
23168
|
+
*/
|
|
23169
|
+
function siteChromeBackgroundOverrideFromHeader(header) {
|
|
23170
|
+
return siteChromeBackgroundOverrideFromBackground(header.background);
|
|
23171
|
+
}
|
|
23172
|
+
function siteChromeBackgroundOverrideFromFooter(footer) {
|
|
23173
|
+
return siteChromeBackgroundOverrideFromBackground(footer.background);
|
|
23174
|
+
}
|
|
23175
|
+
function siteChromeBackgroundOverrideFromBackground(background) {
|
|
23176
|
+
if (background?.contentOwner !== siteChromeBackgroundOverrideOwner || background.type !== "color") {
|
|
23177
|
+
return defaultSiteChromeBackgroundOverride;
|
|
23178
|
+
}
|
|
23179
|
+
const token = parseSiteChromeBackgroundColorToken(background.color);
|
|
23180
|
+
return token ? {
|
|
23181
|
+
kind: "color",
|
|
23182
|
+
token
|
|
23183
|
+
} : defaultSiteChromeBackgroundOverride;
|
|
23184
|
+
}
|
|
23185
|
+
/**
|
|
23186
|
+
* Header-only background applier. Exported so the header preference pipeline
|
|
23187
|
+
* can apply the header override without going through
|
|
23188
|
+
* `applySiteChromeBackgroundPreferences`, which also writes the footer and
|
|
23189
|
+
* would clobber footer intent (#1318 Correction 20).
|
|
23190
|
+
*/
|
|
23191
|
+
function applyHeaderBackgroundOverride(theme, header, override) {
|
|
23192
|
+
if (override.kind === "default") return header;
|
|
23193
|
+
const background = siteChromeColorBackground(override.token);
|
|
23194
|
+
const foreground = resolveChromeForegroundToken(theme, background);
|
|
23195
|
+
return {
|
|
23196
|
+
...header,
|
|
23197
|
+
background: {
|
|
23198
|
+
...background,
|
|
23199
|
+
textColor: foreground
|
|
23200
|
+
},
|
|
23201
|
+
textColor: foreground,
|
|
23202
|
+
navColor: foreground,
|
|
23203
|
+
...header.navLinkStyle ? { navLinkStyle: {
|
|
23204
|
+
...header.navLinkStyle,
|
|
23205
|
+
colorToken: foreground
|
|
23206
|
+
} } : {}
|
|
23207
|
+
};
|
|
23208
|
+
}
|
|
23209
|
+
/**
|
|
23210
|
+
* Footer-only background applier, the counterpart of
|
|
23211
|
+
* `applyHeaderBackgroundOverride`. Exported so a caller that owns one surface
|
|
23212
|
+
* can write that surface rather than passing a no-op override for the other
|
|
23213
|
+
* (#1318 Correction 20).
|
|
23214
|
+
*/
|
|
23215
|
+
function applyFooterBackgroundOverride(theme, footer, override) {
|
|
23216
|
+
if (override.kind === "default") return footer;
|
|
23217
|
+
const background = siteChromeColorBackground(override.token);
|
|
23218
|
+
const foreground = resolveChromeForegroundToken(theme, background);
|
|
23219
|
+
return {
|
|
23220
|
+
...footer,
|
|
23221
|
+
background: {
|
|
23222
|
+
...background,
|
|
23223
|
+
textColor: foreground
|
|
23224
|
+
},
|
|
23225
|
+
navColor: foreground,
|
|
23226
|
+
...footer.navLinkStyle ? { navLinkStyle: {
|
|
23227
|
+
...footer.navLinkStyle,
|
|
23228
|
+
colorToken: foreground
|
|
23229
|
+
} } : {},
|
|
23230
|
+
...footer.bottomTextLinkStyle ? { bottomTextLinkStyle: {
|
|
23231
|
+
...footer.bottomTextLinkStyle,
|
|
23232
|
+
colorToken: foreground
|
|
23233
|
+
} } : {},
|
|
23234
|
+
...footer.bottomBar ? { bottomBar: {
|
|
23235
|
+
...footer.bottomBar,
|
|
23236
|
+
textColor: foreground
|
|
23237
|
+
} } : {}
|
|
23238
|
+
};
|
|
23239
|
+
}
|
|
23240
|
+
function resolveChromeForegroundToken(theme, background) {
|
|
23241
|
+
const resolved = resolveSurfaceForeground(background, expandPalette(theme.palette));
|
|
23242
|
+
if (resolved.kind === "unknown") {
|
|
23243
|
+
throw new Error(`Site chrome background "${background?.type === "color" ? background.color ?? "unknown" : "unknown"}" has no resolvable AA foreground; the theme palette is incomplete`);
|
|
23244
|
+
}
|
|
23245
|
+
return resolved.roles.text;
|
|
23246
|
+
}
|
|
23247
|
+
function siteChromeColorBackground(color) {
|
|
23248
|
+
return {
|
|
23249
|
+
type: "color",
|
|
23250
|
+
contentOwner: siteChromeBackgroundOverrideOwner,
|
|
23251
|
+
color
|
|
23252
|
+
};
|
|
23253
|
+
}
|
|
23254
|
+
function toSiteChromeBackgroundColorTokens(tokenNames) {
|
|
23255
|
+
return tokenNames.flatMap((name) => {
|
|
23256
|
+
const token = parseSiteChromeBackgroundColorToken(name);
|
|
23257
|
+
return token ? [token] : [];
|
|
23258
|
+
});
|
|
23259
|
+
}
|
|
23260
|
+
function requiredQuickSiteChromeBackgroundColorToken(index) {
|
|
23261
|
+
const token = quickSiteChromeBackgroundColorTokens[index];
|
|
23262
|
+
if (!token) {
|
|
23263
|
+
throw new Error(`Missing quick site chrome background token at index ${index}`);
|
|
23264
|
+
}
|
|
23265
|
+
return token;
|
|
23266
|
+
}
|
|
23267
|
+
|
|
23268
|
+
//#endregion
|
|
23269
|
+
//#region ../theme-core/src/site-styles/siteChromeNavPreferences.ts
|
|
23270
|
+
const headerNavTypographyPreferenceValues = [
|
|
23271
|
+
"inherit",
|
|
23272
|
+
"body",
|
|
23273
|
+
"heading"
|
|
23274
|
+
];
|
|
23275
|
+
const headerNavFontSizePreferenceValues = [
|
|
23276
|
+
"inherit",
|
|
23277
|
+
"sm",
|
|
23278
|
+
"base",
|
|
23279
|
+
"lg",
|
|
23280
|
+
"xl"
|
|
23281
|
+
];
|
|
23282
|
+
const headerNavTypographyOptions = [
|
|
23283
|
+
{
|
|
23284
|
+
value: "inherit",
|
|
23285
|
+
label: "Default"
|
|
23286
|
+
},
|
|
23287
|
+
{
|
|
23288
|
+
value: "body",
|
|
23289
|
+
label: "Body"
|
|
23290
|
+
},
|
|
23291
|
+
{
|
|
23292
|
+
value: "heading",
|
|
23293
|
+
label: "Heading"
|
|
23294
|
+
}
|
|
23295
|
+
];
|
|
23296
|
+
const headerNavFontSizeOptions = [
|
|
23297
|
+
{
|
|
23298
|
+
value: "inherit",
|
|
23299
|
+
label: "Default"
|
|
23300
|
+
},
|
|
23301
|
+
{
|
|
23302
|
+
value: "sm",
|
|
23303
|
+
label: "Small"
|
|
23304
|
+
},
|
|
23305
|
+
{
|
|
23306
|
+
value: "base",
|
|
23307
|
+
label: "Regular"
|
|
23308
|
+
},
|
|
23309
|
+
{
|
|
23310
|
+
value: "lg",
|
|
23311
|
+
label: "Large"
|
|
23312
|
+
},
|
|
23313
|
+
{
|
|
23314
|
+
value: "xl",
|
|
23315
|
+
label: "Huge"
|
|
23316
|
+
}
|
|
23317
|
+
];
|
|
23318
|
+
const defaultHeaderNavPreferences = {
|
|
23319
|
+
typography: "inherit",
|
|
23320
|
+
fontSize: "inherit",
|
|
23321
|
+
interactionPalette: "recommended"
|
|
23322
|
+
};
|
|
23323
|
+
function readHeaderNavPreferences(theme) {
|
|
23324
|
+
const typography = theme?.header?.navFontRole;
|
|
23325
|
+
const fontSize = theme?.header?.navTextSize;
|
|
23326
|
+
const interactionPalette = readHeaderInteractionPalettePreference(theme?.header?.interactionPalette);
|
|
23327
|
+
return typography === "body" || typography === "heading" ? {
|
|
23328
|
+
typography,
|
|
23329
|
+
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
23330
|
+
interactionPalette
|
|
23331
|
+
} : {
|
|
23332
|
+
...defaultHeaderNavPreferences,
|
|
23333
|
+
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
23334
|
+
interactionPalette
|
|
23335
|
+
};
|
|
23336
|
+
}
|
|
23337
|
+
/**
|
|
23338
|
+
* Closes over the palette vocabulary rather than trusting the stored value: a
|
|
23339
|
+
* persisted header is `jsonb` and can carry anything, and a bare `?? ` would
|
|
23340
|
+
* pass a malformed value straight through as if it were a preference.
|
|
23341
|
+
*/
|
|
23342
|
+
function readHeaderInteractionPalettePreference(value) {
|
|
23343
|
+
return headerInteractionPaletteValues.find((palette) => palette === value) ?? "recommended";
|
|
23344
|
+
}
|
|
23345
|
+
function headerNavPreferencesEqual(left, right) {
|
|
23346
|
+
return left.typography === right.typography && left.fontSize === right.fontSize && left.interactionPalette === right.interactionPalette;
|
|
23347
|
+
}
|
|
23348
|
+
function withHeaderNavThemePreferences(theme, preferences) {
|
|
23349
|
+
const { navFontRole: _navFontRole, navTextSize: _navTextSize, interactionPalette: _interactionPalette, ...headerWithoutPreference } = theme.header;
|
|
23350
|
+
const nextHeader = {
|
|
23351
|
+
...headerWithoutPreference,
|
|
23352
|
+
...preferences.typography === "inherit" ? {} : { navFontRole: preferences.typography },
|
|
23353
|
+
...preferences.fontSize === "inherit" ? {} : { navTextSize: preferences.fontSize },
|
|
23354
|
+
...preferences.interactionPalette === "recommended" ? {} : { interactionPalette: preferences.interactionPalette }
|
|
23355
|
+
};
|
|
23356
|
+
return {
|
|
23357
|
+
...theme,
|
|
23358
|
+
header: nextHeader
|
|
23359
|
+
};
|
|
23360
|
+
}
|
|
23361
|
+
/**
|
|
23362
|
+
* Applies the user's nav font-role/size overrides on top of a style-resolved
|
|
23363
|
+
* nav link style. This is where the precedence contract lands:
|
|
23364
|
+
*
|
|
23365
|
+
* - The nav typography TREATMENT (case/tracking identity: plain / heading /
|
|
23366
|
+
* quiet-caps / brand-caps) is ALWAYS owned by the active header style and is
|
|
23367
|
+
* baked into `style` before this call (see `applyNavTypography` in
|
|
23368
|
+
* headerLayoutStyles.ts).
|
|
23369
|
+
* - Each treatment resolves through a DEFAULT font role: `heading` resolves
|
|
23370
|
+
* through the heading role; `plain` / `quiet-caps` / `brand-caps` resolve
|
|
23371
|
+
* through the body role.
|
|
23372
|
+
* - The user's `navFontRole` (surfaced here as `preferences.typography`) only
|
|
23373
|
+
* overrides which font ROLE the treatment resolves through. It never changes
|
|
23374
|
+
* the treatment. `'inherit'` keeps the treatment's default role.
|
|
23375
|
+
*/
|
|
23376
|
+
function mergeHeaderNavTypographyPreference(style, preferences) {
|
|
23377
|
+
return preferences.typography === "inherit" && preferences.fontSize === "inherit" ? style : {
|
|
23378
|
+
...style,
|
|
23379
|
+
...preferences.typography === "inherit" ? {} : { typography: preferences.typography },
|
|
23380
|
+
...preferences.fontSize === "inherit" ? {} : { textSize: preferences.fontSize }
|
|
23381
|
+
};
|
|
23382
|
+
}
|
|
23383
|
+
function readHeaderNavFontSizePreference(value) {
|
|
23384
|
+
return value === "sm" || value === "base" || value === "lg" || value === "xl" ? value : "inherit";
|
|
23385
|
+
}
|
|
23386
|
+
/**
|
|
23387
|
+
* The nav size a header actually renders at, from either of the two places it
|
|
23388
|
+
* can be written.
|
|
23389
|
+
*
|
|
23390
|
+
* `generateHeaderCss` merges `header.navTextSize` over `navLinkStyle.textSize`
|
|
23391
|
+
* at render time (`mergeHeaderNavTypographyPreference`), so the marker field
|
|
23392
|
+
* wins when it is present and the compiled nav link style supplies the size
|
|
23393
|
+
* otherwise. Reading only the marker — which is what
|
|
23394
|
+
* `readHeaderNavPreferences` does, and correctly, because it answers "what does
|
|
23395
|
+
* the control show" — misses a size that reached the row through the compiled
|
|
23396
|
+
* bytes instead.
|
|
23397
|
+
*/
|
|
23398
|
+
function headerNavEffectiveFontSize(header) {
|
|
23399
|
+
const marker = readHeaderNavFontSizePreference(header.navTextSize);
|
|
23400
|
+
return marker === "inherit" ? readHeaderNavFontSizePreference(header.navLinkStyle?.textSize) : marker;
|
|
23401
|
+
}
|
|
23402
|
+
/**
|
|
23403
|
+
* `navTextSize` derived as a preference by baseline comparison (#1318 Phase 4c
|
|
23404
|
+
* decision 1) — the same rule the structure fields already use: compare the
|
|
23405
|
+
* persisted value against what the row's own (layout, recipe) compiles, emit
|
|
23406
|
+
* `'inherit'` when they agree and the explicit value when they do not.
|
|
23407
|
+
*
|
|
23408
|
+
* The reason it needs the rule at all is that nav size is one of the fields a
|
|
23409
|
+
* pre-preferences row could only express by carrying it in the compiled bytes:
|
|
23410
|
+
* `heart-led-yoga` and `therese-millar-yoga` persist `navLinkStyle.textSize:
|
|
23411
|
+
* 'lg'` under a `serif-line` recipe that compiles no size, which is a user's
|
|
23412
|
+
* choice sitting in the wrong place. Reading only `header.navTextSize` derives
|
|
23413
|
+
* `'inherit'` there and a recompile would shrink their nav.
|
|
23414
|
+
*
|
|
23415
|
+
* Backfilling the derived value is pixel-preserving because the CSS merge above
|
|
23416
|
+
* makes `{navTextSize: 'lg'}` over a size-less nav link style render exactly
|
|
23417
|
+
* what `{navLinkStyle: {textSize: 'lg'}}` renders.
|
|
23418
|
+
*
|
|
23419
|
+
* **Two cases lose information, and both are accepted rather than accidental.**
|
|
23420
|
+
* The preference vocabulary is `inherit | sm | base | lg | xl` — there is no
|
|
23421
|
+
* value meaning "no size at all" — so `'inherit'` has to carry both of them:
|
|
23422
|
+
*
|
|
23423
|
+
* 1. *Persisted size equals the baseline.* Nothing in the bytes distinguishes
|
|
23424
|
+
* "chose the size the recipe already compiles" from "never chose", so the
|
|
23425
|
+
* marker field is dropped on recompile. **Pixel-neutral**: the recipe puts
|
|
23426
|
+
* the same size back.
|
|
23427
|
+
* 2. *Persisted has no size and the baseline compiles one.* The row renders at
|
|
23428
|
+
* the browser default and the recipe wants, say, `'sm'`; `'inherit'` is the
|
|
23429
|
+
* only representable answer, so **the recompile adopts the baseline size and
|
|
23430
|
+
* the row's sizeless-ness is lost**. This one is *not* pixel-neutral, and
|
|
23431
|
+
* representing it properly would mean adding a `'none'` arm to the
|
|
23432
|
+
* preference the way `HeaderStructurePreferences.logoText` has one.
|
|
23433
|
+
*
|
|
23434
|
+
* Case 2 is not silent. Adopting the baseline size is a byte change, so such a
|
|
23435
|
+
* row shows up as divergent in the Phase 4c report (`navLinkStyle.textSize
|
|
23436
|
+
* undefined -> "sm"`) and cannot reach Phase 4e unclassified — the report's
|
|
23437
|
+
* zero-rows gate is the safety net for exactly this. No row in either corpus is
|
|
23438
|
+
* in that state today; if one appears, it is a classification decision (widen
|
|
23439
|
+
* the vocabulary, or accept the size) rather than something this function may
|
|
23440
|
+
* guess at.
|
|
23441
|
+
*/
|
|
23442
|
+
function readHeaderNavFontSizeAgainstBaseline(persisted, compiled) {
|
|
23443
|
+
const size = headerNavEffectiveFontSize(persisted);
|
|
23444
|
+
return size === headerNavEffectiveFontSize(compiled) ? "inherit" : size;
|
|
23445
|
+
}
|
|
23446
|
+
|
|
23447
|
+
//#endregion
|
|
23448
|
+
//#region ../theme-core/src/site-styles/headerBrandPresentation.ts
|
|
23449
|
+
/**
|
|
23450
|
+
* Whether the header wears a brand presentation at all (#1318 Phase 4c).
|
|
23451
|
+
*
|
|
23452
|
+
* `applyHeaderBrandPresentation` is unconditional today: given a logo context
|
|
23453
|
+
* it always writes a `logo` object carrying a resolved display, size and
|
|
23454
|
+
* accommodation. That makes "this header has no brand presentation block" —
|
|
23455
|
+
* which is what several live sites persist, including one whose site *does*
|
|
23456
|
+
* have a logo asset — a state the preference record could not express, so a
|
|
23457
|
+
* recompile would silently give the site a logo treatment it has never
|
|
23458
|
+
* rendered.
|
|
23459
|
+
*
|
|
23460
|
+
* This is the third field in this phase with that shape, after the interaction
|
|
23461
|
+
* palette and the nav text size: the persisted bytes carry intent the reader
|
|
23462
|
+
* cannot say, so the recompile "corrects" it. The fix is the same one — widen
|
|
23463
|
+
* the closed vocabulary with a named member and derive it by baseline
|
|
23464
|
+
* comparison — and it is a *widening*, not a restyle: `'recommended'` is the
|
|
23465
|
+
* default and reproduces today's behaviour exactly, so no row whose bytes carry
|
|
23466
|
+
* a presentation compiles differently for `'plain'` existing.
|
|
23467
|
+
*
|
|
23468
|
+
* `'plain'` is deliberately not "preserve whatever bytes are there". It is a
|
|
23469
|
+
* constructive instruction — *write no presentation block* — so Phase 4e's
|
|
23470
|
+
* compiler, which builds the header from nothing, can produce it.
|
|
23471
|
+
*/
|
|
23472
|
+
const headerBrandPresentationValues = ["recommended", "plain"];
|
|
23473
|
+
const defaultHeaderBrandPresentation = "recommended";
|
|
23474
|
+
/**
|
|
23475
|
+
* Key-order-safe structural equality over the persisted `logo` object.
|
|
23476
|
+
*
|
|
23477
|
+
* The persisted value comes back from `jsonb`, whose key order is not the order
|
|
23478
|
+
* the applier builds; a `JSON.stringify` comparison would read two equal logo
|
|
23479
|
+
* blocks as different and quietly classify a `'recommended'` row as
|
|
23480
|
+
* unrecognised — the same trap the interaction-palette transcription test
|
|
23481
|
+
* exists to catch.
|
|
23482
|
+
*/
|
|
23483
|
+
function jsonEquals(left, right) {
|
|
23484
|
+
if (left === right) return true;
|
|
23485
|
+
if (left === null || right === null) return false;
|
|
23486
|
+
if (typeof left !== "object" || typeof right !== "object") return false;
|
|
23487
|
+
if (Array.isArray(left) !== Array.isArray(right)) return false;
|
|
23488
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
23489
|
+
return left.length === right.length && left.every((value, index) => jsonEquals(value, right[index]));
|
|
23490
|
+
}
|
|
23491
|
+
const leftRecord = left;
|
|
23492
|
+
const rightRecord = right;
|
|
23493
|
+
const keys = new Set([...Object.keys(leftRecord), ...Object.keys(rightRecord)]);
|
|
23494
|
+
for (const key of keys) {
|
|
23495
|
+
if (!jsonEquals(leftRecord[key], rightRecord[key])) return false;
|
|
23496
|
+
}
|
|
23497
|
+
return true;
|
|
23498
|
+
}
|
|
23499
|
+
/**
|
|
23500
|
+
* The two keys the brand appliers write with a documented `'auto'` default, so
|
|
23501
|
+
* a persisted block that omits them means the same thing as one that spells
|
|
23502
|
+
* them out. This is the "implicit brand defaults made explicit" class the 4a
|
|
23503
|
+
* sweep pinned; treating it as a difference here would classify two live rows
|
|
23504
|
+
* as unrecognised over a value nobody chose and nothing renders differently.
|
|
23505
|
+
*/
|
|
23506
|
+
function withImplicitBrandDefaults(logo) {
|
|
23507
|
+
return {
|
|
23508
|
+
...logo,
|
|
23509
|
+
brandDisplay: logo.brandDisplay ?? "auto",
|
|
23510
|
+
size: logo.size ?? "auto"
|
|
23511
|
+
};
|
|
23512
|
+
}
|
|
23513
|
+
/**
|
|
23514
|
+
* Equal modulo the implicit brand defaults, and `null`-tolerant on both sides:
|
|
23515
|
+
* an absent block and an absent block are the same answer.
|
|
23516
|
+
*/
|
|
23517
|
+
function logoBytesEqual(left, right) {
|
|
23518
|
+
const leftAbsent = left === undefined || left === null;
|
|
23519
|
+
const rightAbsent = right === undefined || right === null;
|
|
23520
|
+
if (leftAbsent || rightAbsent) return leftAbsent && rightAbsent;
|
|
23521
|
+
return jsonEquals(withImplicitBrandDefaults(left), withImplicitBrandDefaults(right));
|
|
23522
|
+
}
|
|
23523
|
+
/**
|
|
23524
|
+
* Which mode reproduces this row's persisted logo bytes.
|
|
23525
|
+
*
|
|
23526
|
+
* Both candidates are supplied rather than reconstructed here, because both are
|
|
23527
|
+
* the compilation's own output and rebuilding either would be a second
|
|
23528
|
+
* authority on what the modes mean. `'recommended'` is tested first so that a
|
|
23529
|
+
* row the resolver already agrees with keeps the live mode; `'plain'` is the
|
|
23530
|
+
* answer for the rows that carry no presentation — an absent block, or a block
|
|
23531
|
+
* holding only the recipe's own brand typography.
|
|
23532
|
+
*/
|
|
23533
|
+
function classifyHeaderBrandPresentationBytes(persisted, recommended, plain) {
|
|
23534
|
+
if (logoBytesEqual(persisted.logo, recommended.logo)) {
|
|
23535
|
+
return { kind: "recommended" };
|
|
23536
|
+
}
|
|
23537
|
+
if (logoBytesEqual(persisted.logo, plain.logo)) return { kind: "plain" };
|
|
23538
|
+
return { kind: "unrecognised" };
|
|
23539
|
+
}
|
|
23540
|
+
/**
|
|
23541
|
+
* The preference the bytes express, or `null` when they express none the record
|
|
23542
|
+
* can carry. `null` is the caller's cue to fall back to the default and say so.
|
|
23543
|
+
*/
|
|
23544
|
+
function readHeaderBrandPresentationFromBytes(persisted, recommended, plain) {
|
|
23545
|
+
const classification = classifyHeaderBrandPresentationBytes(persisted, recommended, plain);
|
|
23546
|
+
return classification.kind === "unrecognised" ? null : classification.kind;
|
|
23547
|
+
}
|
|
23548
|
+
|
|
23549
|
+
//#endregion
|
|
23550
|
+
//#region ../theme-core/src/site-styles/headerPreferences.ts
|
|
23551
|
+
const defaultHeaderStructurePreferences = {
|
|
23552
|
+
positioning: "inherit",
|
|
23553
|
+
shrinkOnScroll: "inherit",
|
|
23554
|
+
logoText: "inherit"
|
|
23555
|
+
};
|
|
23556
|
+
const defaultHeaderPreferences = {
|
|
23557
|
+
brandPresentation: defaultHeaderBrandPresentation,
|
|
23558
|
+
brandDisplay: defaultHeaderBrandPreferences.brandDisplay,
|
|
23559
|
+
logoSize: defaultHeaderBrandPreferences.logoSize,
|
|
23560
|
+
navFontRole: defaultHeaderNavPreferences.typography,
|
|
23561
|
+
navTextSize: defaultHeaderNavPreferences.fontSize,
|
|
23562
|
+
interactionPalette: defaultHeaderNavPreferences.interactionPalette,
|
|
23563
|
+
background: { kind: "default" },
|
|
23564
|
+
structure: defaultHeaderStructurePreferences
|
|
23565
|
+
};
|
|
23566
|
+
/**
|
|
23567
|
+
* Field-wise rather than `JSON.stringify`: the persisted value comes back from
|
|
23568
|
+
* `jsonb` and the compiled one is built by the interpreter, so their key order
|
|
23569
|
+
* is not guaranteed to match and a string comparison would report two equal
|
|
23570
|
+
* records as different.
|
|
23571
|
+
*
|
|
23572
|
+
* The destructure is exhaustive on purpose — adding a field to
|
|
23573
|
+
* `headerLogoTextSchema` without deciding whether it participates in this
|
|
23574
|
+
* comparison breaks the build here rather than silently collapsing two
|
|
23575
|
+
* different records to `'inherit'`.
|
|
23576
|
+
*
|
|
23577
|
+
* Note the asymmetry this leaves at the edge: a persisted `{}` against a
|
|
23578
|
+
* compiled `undefined` is *not* equal, so it derives to an explicit `{}`
|
|
23579
|
+
* rather than to `'none'`. That is deliberate — the row does carry a logo-text
|
|
23580
|
+
* record, it just carries an empty one — but it means `'none'` means "no
|
|
23581
|
+
* record at all", never "a record with nothing in it".
|
|
23582
|
+
*/
|
|
23583
|
+
function headerLogoTextEquals(left, right) {
|
|
23584
|
+
if (left === undefined || right === undefined) return left === right;
|
|
23585
|
+
const comparisons = {
|
|
23586
|
+
mobileWrapLines: left.mobileWrapLines === right.mobileWrapLines,
|
|
23587
|
+
mobileMaxWidth: left.mobileMaxWidth === right.mobileMaxWidth,
|
|
23588
|
+
hideOnShrink: left.hideOnShrink === right.hideOnShrink
|
|
23589
|
+
};
|
|
23590
|
+
return Object.values(comparisons).every((equal) => equal);
|
|
23591
|
+
}
|
|
23592
|
+
/**
|
|
23593
|
+
* Brand presentation runs *after* the structure preferences and forces
|
|
23594
|
+
* `shrinkOnScroll: true` for a display-size logo on a backing accommodation
|
|
23595
|
+
* over a non-static header (`applyHeaderBrandPresentation` below). On such a
|
|
23596
|
+
* row the persisted `true` therefore carries no intent — it would be written
|
|
23597
|
+
* whatever the user had chosen — so the field is not derivable there and
|
|
23598
|
+
* collapses to `'inherit'`.
|
|
23599
|
+
*
|
|
23600
|
+
* This reads the row's own persisted logo rather than a live logo context, so
|
|
23601
|
+
* it holds for callers that have no context to supply (the divergence report,
|
|
23602
|
+
* and 4c's backfill). A caller that *does* supply the presentation gets the
|
|
23603
|
+
* same answer through the baseline, which already carries the forced value.
|
|
23604
|
+
*/
|
|
23605
|
+
function headerBrandPresentationForcesShrink(header) {
|
|
23606
|
+
const accommodation = header.logo?.accommodation;
|
|
23607
|
+
return header.logo?.size === "display" && accommodation !== undefined && logoAccommodationProvidesBacking(accommodation) && header.positioning !== "static";
|
|
23608
|
+
}
|
|
23609
|
+
/**
|
|
23610
|
+
* The three structure fields are read back out of `jsonb`, so each one is
|
|
23611
|
+
* validated against its own vocabulary rather than trusted. A malformed value
|
|
23612
|
+
* expresses no intent, so it derives to `'inherit'` — never to a default that
|
|
23613
|
+
* would then be persisted as if the user had chosen it.
|
|
23614
|
+
*/
|
|
23615
|
+
function readPersistedPositioning(value) {
|
|
23616
|
+
return headerPositioningValues.find((candidate$1) => candidate$1 === value) ?? null;
|
|
23617
|
+
}
|
|
23618
|
+
function readPersistedShrinkOnScroll(value) {
|
|
23619
|
+
return typeof value === "boolean" ? value : null;
|
|
23620
|
+
}
|
|
23621
|
+
/** "Present, but not a value this field's vocabulary accepts." */
|
|
23622
|
+
const invalidField = Symbol("invalid persisted field");
|
|
23623
|
+
const logoTextWrapLines = [1, 2];
|
|
23624
|
+
const logoTextMobileMaxWidths = [
|
|
23625
|
+
"xs",
|
|
23626
|
+
"sm",
|
|
23627
|
+
"md"
|
|
23628
|
+
];
|
|
23629
|
+
function readOptionalMember(value, allowed) {
|
|
23630
|
+
if (value === undefined) return undefined;
|
|
23631
|
+
return allowed.find((candidate$1) => candidate$1 === value) ?? invalidField;
|
|
23632
|
+
}
|
|
23633
|
+
function readOptionalBoolean(value) {
|
|
23634
|
+
if (value === undefined) return undefined;
|
|
23635
|
+
return typeof value === "boolean" ? value : invalidField;
|
|
23636
|
+
}
|
|
23637
|
+
function readPersistedLogoText(value) {
|
|
23638
|
+
if (value === undefined) return { kind: "absent" };
|
|
23639
|
+
if (typeof value !== "object" || value === null) return { kind: "invalid" };
|
|
23640
|
+
const wrapLines = readOptionalMember(value.mobileWrapLines, logoTextWrapLines);
|
|
23641
|
+
const maxWidth = readOptionalMember(value.mobileMaxWidth, logoTextMobileMaxWidths);
|
|
23642
|
+
const hide = readOptionalBoolean(value.hideOnShrink);
|
|
23643
|
+
if (wrapLines === invalidField || maxWidth === invalidField || hide === invalidField) {
|
|
23644
|
+
return { kind: "invalid" };
|
|
23645
|
+
}
|
|
23646
|
+
return {
|
|
23647
|
+
kind: "present",
|
|
23648
|
+
value: {
|
|
23649
|
+
...wrapLines === undefined ? {} : { mobileWrapLines: wrapLines },
|
|
23650
|
+
...maxWidth === undefined ? {} : { mobileMaxWidth: maxWidth },
|
|
23651
|
+
...hide === undefined ? {} : { hideOnShrink: hide }
|
|
23652
|
+
}
|
|
23653
|
+
};
|
|
23654
|
+
}
|
|
23655
|
+
function deriveLogoTextPreference(persisted, compiled) {
|
|
23656
|
+
const read = readPersistedLogoText(persisted);
|
|
23657
|
+
switch (read.kind) {
|
|
23658
|
+
case "invalid": return "inherit";
|
|
23659
|
+
case "absent": return compiled === undefined ? "inherit" : "none";
|
|
23660
|
+
case "present": return headerLogoTextEquals(read.value, compiled) ? "inherit" : read.value;
|
|
23661
|
+
}
|
|
23662
|
+
}
|
|
23663
|
+
/**
|
|
23664
|
+
* The written-down backfill rule (#1318 Phase 4a): compare the persisted value
|
|
23665
|
+
* against the value the row's own (layout, recipe) compiles, and emit
|
|
23666
|
+
* `'inherit'` when they are equal, the explicit value when they are not.
|
|
23667
|
+
*
|
|
23668
|
+
* Equal-to-default therefore collapses to `'inherit'`. Nothing in the data
|
|
23669
|
+
* distinguishes a deliberate "sticky, same as the default" from an untouched
|
|
23670
|
+
* row, so the backfill does not pretend to recover it; going forward the editor
|
|
23671
|
+
* records what the user picks and the distinction is real for new intent.
|
|
23672
|
+
*
|
|
23673
|
+
* Two qualifications on "the value it compiles", both of which keep the rule
|
|
23674
|
+
* from inventing intent:
|
|
23675
|
+
*
|
|
23676
|
+
* 1. A value that no vocabulary accepts expresses nothing, so it derives to
|
|
23677
|
+
* `'inherit'` rather than to whatever a fallback would have picked.
|
|
23678
|
+
* 2. A field a later pipeline step overwrites unconditionally cannot carry
|
|
23679
|
+
* intent at all. That is `shrinkOnScroll` on a display-logo row — see
|
|
23680
|
+
* `headerBrandPresentationForcesShrink`.
|
|
23681
|
+
*/
|
|
23682
|
+
function readHeaderStructurePreferences(persisted, compiled) {
|
|
23683
|
+
const positioning = readPersistedPositioning(persisted.positioning);
|
|
23684
|
+
const shrinkOnScroll = readPersistedShrinkOnScroll(persisted.shrinkOnScroll);
|
|
23685
|
+
return {
|
|
23686
|
+
positioning: positioning === null || positioning === compiled.positioning ? "inherit" : positioning,
|
|
23687
|
+
shrinkOnScroll: shrinkOnScroll === null || shrinkOnScroll === compiled.shrinkOnScroll || headerBrandPresentationForcesShrink(persisted) ? "inherit" : shrinkOnScroll,
|
|
23688
|
+
logoText: deriveLogoTextPreference(persisted.logoText, compiled.logoText)
|
|
23689
|
+
};
|
|
23690
|
+
}
|
|
23691
|
+
/**
|
|
23692
|
+
* Total over any persisted theme, including a malformed one: every field goes
|
|
23693
|
+
* through a reader that closes over the vocabulary rather than trusting the
|
|
23694
|
+
* stored value.
|
|
23695
|
+
*
|
|
23696
|
+
* `compiledHeader` is the structure baseline — the header this row's own
|
|
23697
|
+
* (layout, recipe) compiles. It is a required parameter rather than something
|
|
23698
|
+
* derived inside, because a `Theme` alone does not carry the selection ids the
|
|
23699
|
+
* baseline needs; asking the caller for it keeps the rule single-valued.
|
|
23700
|
+
*
|
|
23701
|
+
* `navTextSize` is derived against that same baseline rather than read off
|
|
23702
|
+
* `header.navTextSize` (#1318 Phase 4c decision 1). It is the one nav field a
|
|
23703
|
+
* pre-preferences row could express only through the compiled bytes, so
|
|
23704
|
+
* reading the marker alone loses it — see
|
|
23705
|
+
* `readHeaderNavFontSizeAgainstBaseline`. `navFontRole` stays a marker read:
|
|
23706
|
+
* unlike size, `navLinkStyle.typography` is written by the recipe's own
|
|
23707
|
+
* treatment on every row, so a baseline comparison there would report the
|
|
23708
|
+
* treatment's default role as user intent.
|
|
23709
|
+
*
|
|
23710
|
+
* `interactionPaletteGroup` is the palette the row's resolved recipe offers,
|
|
23711
|
+
* and the same reasoning applies to it (#1318 Phase 4c decision 2): a row that
|
|
23712
|
+
* predates the palette carries its hover treatment in the compiled bytes and
|
|
23713
|
+
* nothing in `header.interactionPalette`. `undefined` means the caller does not
|
|
23714
|
+
* know which palette applies — it is not "the style has none" — and the marker
|
|
23715
|
+
* read stands.
|
|
23716
|
+
*
|
|
23717
|
+
* `brandPresentationCandidates` are the two headers the two presentation modes
|
|
23718
|
+
* produce for this row. The mode is derived by comparing the persisted `logo`
|
|
23719
|
+
* against both, so a row that carries no presentation block keeps having none
|
|
23720
|
+
* instead of being given one it has never rendered (#1318 Phase 4c decision
|
|
23721
|
+
* 5b). They are supplied rather than rebuilt here because they are the
|
|
23722
|
+
* compilation's own output, and a second construction of them would be a second
|
|
23723
|
+
* authority on what the modes mean — `headerBrandPresentationCandidates` builds
|
|
23724
|
+
* them with the real appliers.
|
|
23725
|
+
*/
|
|
23726
|
+
function readHeaderPreferences(theme, compiledHeader, interactionPaletteGroup, brandPresentationCandidates) {
|
|
23727
|
+
const nav = readHeaderNavPreferences(theme);
|
|
23728
|
+
const brand = headerBrandPreferencesFromTheme(theme);
|
|
23729
|
+
const brandPresentation = readHeaderBrandPresentationFromBytes(theme.header, brandPresentationCandidates.recommended, brandPresentationCandidates.plain) ?? defaultHeaderBrandPresentation;
|
|
23730
|
+
return {
|
|
23731
|
+
brandPresentation,
|
|
23732
|
+
brandDisplay: brand.brandDisplay,
|
|
23733
|
+
logoSize: brand.logoSize,
|
|
23734
|
+
navFontRole: nav.typography,
|
|
23735
|
+
navTextSize: readHeaderNavFontSizeAgainstBaseline(theme.header, compiledHeader),
|
|
23736
|
+
interactionPalette: readHeaderInteractionPaletteFromBytes(theme.header, interactionPaletteGroup) ?? nav.interactionPalette,
|
|
23737
|
+
background: siteChromeBackgroundOverrideFromHeader(theme.header),
|
|
23738
|
+
structure: readHeaderStructurePreferences(theme.header, brandPresentationCandidates[brandPresentation])
|
|
23739
|
+
};
|
|
23740
|
+
}
|
|
23741
|
+
/**
|
|
23742
|
+
* Both candidates, built with the same two appliers the pipeline uses.
|
|
23743
|
+
*
|
|
23744
|
+
* There is no circularity to resolve: the only record fields that reach
|
|
23745
|
+
* `header.logo` are `brandDisplay` and `logoSize`, and those are read straight
|
|
23746
|
+
* off the persisted theme rather than derived. The structure preferences run
|
|
23747
|
+
* before the presentation in the named order but touch `positioning`,
|
|
23748
|
+
* `shrinkOnScroll` and `logoText` — never `logo` — so leaving them out here
|
|
23749
|
+
* cannot change either answer.
|
|
23750
|
+
*
|
|
23751
|
+
* One known over-specification, recorded rather than fixed because it changes
|
|
23752
|
+
* no bytes. `applyHeaderBrandPresentation` reads `positioning` when it decides
|
|
23753
|
+
* whether to force `shrinkOnScroll` for a display logo, and it sees the
|
|
23754
|
+
* *structure-adjusted* value at apply time while it sees the layout default
|
|
23755
|
+
* here. On a `'recommended'` row that overrides positioning to `'static'` the
|
|
23756
|
+
* candidate can therefore carry a forced `shrinkOnScroll` the applied header
|
|
23757
|
+
* will not, which makes `readHeaderStructurePreferences` emit an explicit
|
|
23758
|
+
* `shrinkOnScroll` where `'inherit'` would have done. The record is more
|
|
23759
|
+
* specific than it needs to be; the compiled bytes are identical either way,
|
|
23760
|
+
* because the explicit value it writes is the one the row already has. Fixing
|
|
23761
|
+
* it means deriving `positioning` before the candidates, which is a second
|
|
23762
|
+
* derivation pass for no byte change (#1318 Phase 4c review round 2, item 10).
|
|
23763
|
+
*/
|
|
23764
|
+
function headerBrandPresentationCandidates(theme, compiledBaseline, presentation) {
|
|
23765
|
+
const brand = headerBrandPreferencesFromTheme(theme);
|
|
23766
|
+
const withBrand = (header) => withHeaderBrandThemePreferences({
|
|
23767
|
+
...theme,
|
|
23768
|
+
header
|
|
23769
|
+
}, brand).header;
|
|
23770
|
+
return {
|
|
23771
|
+
recommended: withBrand(presentation === null ? compiledBaseline : applyHeaderBrandPresentation(compiledBaseline, presentation)),
|
|
23772
|
+
plain: withBrand(compiledBaseline)
|
|
23773
|
+
};
|
|
23774
|
+
}
|
|
23775
|
+
function applyHeaderStructurePreferences(header, preferences) {
|
|
23776
|
+
const { logoText: currentLogoText, ...headerWithoutLogoText } = header;
|
|
23777
|
+
return {
|
|
23778
|
+
...headerWithoutLogoText,
|
|
23779
|
+
...preferences.positioning === "inherit" ? {} : { positioning: preferences.positioning },
|
|
23780
|
+
...preferences.shrinkOnScroll === "inherit" ? {} : { shrinkOnScroll: preferences.shrinkOnScroll },
|
|
23781
|
+
...logoTextField(currentLogoText, preferences.logoText)
|
|
23782
|
+
};
|
|
23783
|
+
}
|
|
23784
|
+
function logoTextField(current, preference) {
|
|
23785
|
+
if (preference === "inherit") {
|
|
23786
|
+
return current === undefined ? {} : { logoText: current };
|
|
23787
|
+
}
|
|
23788
|
+
if (preference === "none") return {};
|
|
23789
|
+
return { logoText: preference };
|
|
23790
|
+
}
|
|
23791
|
+
/**
|
|
23792
|
+
* The brand presentation step, which sits between the structure preferences and
|
|
23793
|
+
* the nav/logo/background ones because it reads `positioning` and can force
|
|
23794
|
+
* `shrinkOnScroll` for a display logo (`previewCompiler.ts`).
|
|
23795
|
+
*/
|
|
23796
|
+
/**
|
|
23797
|
+
* Writes a resolved brand presentation onto the header. Lives here rather than
|
|
23798
|
+
* in `previewCompiler` so the pipeline step has one definition: it sits between
|
|
23799
|
+
* the structure preferences and the nav/logo/background ones, and it is the
|
|
23800
|
+
* reason that position is load-bearing — it reads `positioning` and can force
|
|
23801
|
+
* `shrinkOnScroll` for a display logo that brings its own backing.
|
|
23802
|
+
*/
|
|
23803
|
+
function applyHeaderBrandPresentation(header, presentation) {
|
|
23804
|
+
const shouldShrinkDisplayLogo = presentation.size === "display" && presentation.accommodation.providesBacking === true && header.positioning !== "static";
|
|
23805
|
+
return {
|
|
23806
|
+
...header,
|
|
23807
|
+
shrinkOnScroll: shouldShrinkDisplayLogo ? true : header.shrinkOnScroll,
|
|
23808
|
+
logo: {
|
|
23809
|
+
fontFamily: header.logo?.fontFamily ?? "heading",
|
|
23810
|
+
letterSpacing: header.logo?.letterSpacing ?? "normal",
|
|
23811
|
+
gradient: header.logo?.gradient ?? false,
|
|
23812
|
+
brandDisplay: presentation.display,
|
|
23813
|
+
size: presentation.size,
|
|
23814
|
+
accommodation: presentation.accommodation.pattern
|
|
23815
|
+
}
|
|
23816
|
+
};
|
|
23817
|
+
}
|
|
23818
|
+
/**
|
|
23819
|
+
* The preference half of the header pipeline, composed once so that no caller
|
|
23820
|
+
* assembles its own order. Named order, tail of the recompile:
|
|
23821
|
+
*
|
|
23822
|
+
* structure preferences → brand presentation → brand display/logo size
|
|
23823
|
+
* → nav preferences → header background override
|
|
23824
|
+
*
|
|
23825
|
+
* `brandPresentation` is data rather than a callback so the step is inspectable
|
|
23826
|
+
* and cannot vary by caller; `null` is "this caller has no logo context".
|
|
23827
|
+
*
|
|
23828
|
+
* The background applier is the header-only one on purpose: the header+footer
|
|
23829
|
+
* pair function would clobber footer intent (#1318 Correction 20).
|
|
23830
|
+
*/
|
|
23831
|
+
function applyHeaderPreferences(theme, preferences, brandPresentation) {
|
|
23832
|
+
const structured = applyHeaderStructurePreferences(theme.header, preferences.structure);
|
|
23833
|
+
const presented = brandPresentation === null || preferences.brandPresentation === "plain" ? structured : applyHeaderBrandPresentation(structured, brandPresentation);
|
|
23834
|
+
const branded = withHeaderBrandThemePreferences({
|
|
23835
|
+
...theme,
|
|
23836
|
+
header: presented
|
|
23837
|
+
}, {
|
|
23838
|
+
brandDisplay: preferences.brandDisplay,
|
|
23839
|
+
logoSize: preferences.logoSize
|
|
23840
|
+
});
|
|
23841
|
+
const navved = withHeaderNavThemePreferences(branded, {
|
|
23842
|
+
typography: preferences.navFontRole,
|
|
23843
|
+
fontSize: preferences.navTextSize,
|
|
23844
|
+
interactionPalette: preferences.interactionPalette
|
|
23845
|
+
});
|
|
23846
|
+
return {
|
|
23847
|
+
...navved,
|
|
23848
|
+
header: applyHeaderBackgroundOverride(navved, navved.header, preferences.background)
|
|
23849
|
+
};
|
|
23850
|
+
}
|
|
23851
|
+
|
|
23852
|
+
//#endregion
|
|
23853
|
+
//#region ../theme-core/src/site-styles/headerRecompile.ts
|
|
23854
|
+
/**
|
|
23855
|
+
* Curated repair followed by catalogue repair — the first two steps of the
|
|
23856
|
+
* header compilation pipeline, in one place so no caller re-derives them.
|
|
23857
|
+
*/
|
|
23858
|
+
function resolveCuratedHeaderSelection(siteStyle, requested) {
|
|
23859
|
+
const requestedLayoutChoices = headerStyleChoicesForLayout(siteStyle, requested.layoutId);
|
|
23860
|
+
const curatedDefault = defaultHeaderCurationForSiteStyle(siteStyle);
|
|
23861
|
+
const resolvedLayoutId = requestedLayoutChoices ? requested.layoutId : curatedDefault.selection.layoutId;
|
|
23862
|
+
const curatedChoices = requestedLayoutChoices ?? curatedDefault.styles;
|
|
23863
|
+
const resolution = resolveHeaderLayoutStyleSelection({
|
|
23864
|
+
layoutId: resolvedLayoutId,
|
|
23865
|
+
styleId: deriveNearestCompatibleHeaderStyle(requested.styleId, resolvedLayoutId, curatedChoices.map((choice) => choice.id))
|
|
23866
|
+
});
|
|
23867
|
+
return {
|
|
23868
|
+
resolution,
|
|
23869
|
+
repair: resolution.selection.layoutId === requested.layoutId && resolution.selection.styleId === requested.styleId ? { kind: "exact" } : {
|
|
23870
|
+
kind: "repaired",
|
|
23871
|
+
reason: requestedLayoutChoices ? "style-not-curated-for-layout" : "layout-not-curated-for-site-style"
|
|
23872
|
+
}
|
|
23873
|
+
};
|
|
23874
|
+
}
|
|
23875
|
+
/**
|
|
23876
|
+
* Curated repair → catalogue repair → recipe → interaction palette → brand
|
|
23877
|
+
* presentation. Everything that runs before the preference record, in one
|
|
23878
|
+
* place: `previewCompiler.compileHeaderDirective` and the recompile below are
|
|
23879
|
+
* both callers, so the middle of the pipeline has a single composition.
|
|
23880
|
+
*
|
|
23881
|
+
* `baseline` and `header` are both returned because they answer different
|
|
23882
|
+
* questions. `header` is what a preview wants to render. `baseline` is what
|
|
23883
|
+
* `readHeaderPreferences` compares the persisted bytes against for every field
|
|
23884
|
+
* the presentation does not touch.
|
|
23885
|
+
*
|
|
23886
|
+
* The interaction-palette preference is threaded in through `theme.header`
|
|
23887
|
+
* because that is where `compileHeaderLayoutStyleSelection` reads it from
|
|
23888
|
+
* today; 4b can pass it explicitly once the preferences column exists.
|
|
23889
|
+
*/
|
|
23890
|
+
function compileHeaderForCuratedSelection(theme, siteStyle, requested, requestedBrandPresentation, requestedInteractionPalette) {
|
|
23891
|
+
const { resolution, repair: repair$1 } = resolveCuratedHeaderSelection(siteStyle, requested);
|
|
23892
|
+
const interactionPalette = resolveHeaderStyleRecipe(siteStyle, resolution.style.id).interactionPalette;
|
|
23893
|
+
const baseline = compileHeaderLayoutStyleSelection(theme, resolution, siteStyle, resolveInteractionPaletteRequest(theme, interactionPalette, requestedInteractionPalette));
|
|
23894
|
+
const brandPresentation = requestedBrandPresentation.kind === "apply-logo-context" ? resolveHeaderBrandPresentationForLayout({
|
|
23895
|
+
layout: resolution.layout,
|
|
23896
|
+
logo: requestedBrandPresentation.logo
|
|
23897
|
+
}) : null;
|
|
23898
|
+
return {
|
|
23899
|
+
baseline,
|
|
23900
|
+
header: brandPresentation === null ? baseline : applyHeaderBrandPresentation(baseline, brandPresentation),
|
|
23901
|
+
brandPresentation,
|
|
23902
|
+
interactionPalette,
|
|
23903
|
+
resolution,
|
|
23904
|
+
repair: repair$1
|
|
23905
|
+
};
|
|
23906
|
+
}
|
|
23907
|
+
/**
|
|
23908
|
+
* The bytes-derived answer wins when the recipe offers a palette at all;
|
|
23909
|
+
* otherwise the marker field on the theme stands, which is what every caller
|
|
23910
|
+
* that holds no preference record has always used.
|
|
23911
|
+
*/
|
|
23912
|
+
function resolveInteractionPaletteRequest(theme, group, request) {
|
|
23913
|
+
if (request.kind === "preference") return request.preference;
|
|
23914
|
+
return readHeaderInteractionPaletteFromBytes(theme.header, group) ?? readHeaderNavPreferences(theme).interactionPalette;
|
|
23915
|
+
}
|
|
23916
|
+
/**
|
|
23917
|
+
* The whole header pipeline, in one named function so 4b, 4c and 4d cannot
|
|
23918
|
+
* drift into three orders (#1318 Phase 4):
|
|
23919
|
+
*
|
|
23920
|
+
* curated repair → catalogue repair → recipe → layout defaults
|
|
23921
|
+
* → interaction palette → structure preferences → brand presentation
|
|
23922
|
+
* → nav/logo/background preferences
|
|
23923
|
+
*/
|
|
23924
|
+
function recompileSiteChromeHeader(input) {
|
|
23925
|
+
return applyPreferencesToCompiledHeader(input.theme, compileHeaderForCuratedSelection(input.theme, input.siteStyle, input.requested, input.brandPresentation, {
|
|
23926
|
+
kind: "preference",
|
|
23927
|
+
preference: input.preferences.interactionPalette
|
|
23928
|
+
}), input.preferences);
|
|
23929
|
+
}
|
|
23930
|
+
/**
|
|
23931
|
+
* The preferences run against `baseline`, not `header`: the brand presentation
|
|
23932
|
+
* belongs *after* the structure preferences in the named order, and
|
|
23933
|
+
* `applyHeaderPreferences` applies it there. Starting from `header` would apply
|
|
23934
|
+
* it twice and put the earlier copy in the wrong place.
|
|
23935
|
+
*/
|
|
23936
|
+
function applyPreferencesToCompiledHeader(theme, compiled, preferences) {
|
|
23937
|
+
const applied = applyHeaderPreferences({
|
|
23938
|
+
...theme,
|
|
23939
|
+
header: compiled.baseline
|
|
23940
|
+
}, preferences, compiled.brandPresentation);
|
|
23941
|
+
return {
|
|
23942
|
+
header: applied.header,
|
|
23943
|
+
resolution: compiled.resolution,
|
|
23944
|
+
repair: compiled.repair
|
|
23945
|
+
};
|
|
23946
|
+
}
|
|
23947
|
+
/**
|
|
23948
|
+
* The read-then-recompile round trip: derive the row's intent from its own
|
|
23949
|
+
* persisted bytes, then rebuild the header from that intent. On a row whose
|
|
23950
|
+
* bytes are compiler output this returns the persisted header unchanged, which
|
|
23951
|
+
* is what the Phase 4c divergence report measures.
|
|
23952
|
+
*/
|
|
23953
|
+
function recompileSiteChromeHeaderFromPersistedIntent(theme, siteStyle, requested, brandPresentation) {
|
|
23954
|
+
const compiled = compileHeaderForCuratedSelection(theme, siteStyle, requested, brandPresentation, { kind: "derive-from-persisted-bytes" });
|
|
23955
|
+
const candidates = headerBrandPresentationCandidates(theme, compiled.baseline, compiled.brandPresentation);
|
|
23956
|
+
const preferences = readHeaderPreferences(theme, compiled.header, compiled.interactionPalette, candidates);
|
|
23957
|
+
return {
|
|
23958
|
+
recompiled: applyPreferencesToCompiledHeader(theme, compiled, preferences),
|
|
23959
|
+
preferences,
|
|
23960
|
+
interactionPaletteBytes: classifyHeaderInteractionPaletteBytes(theme.header, compiled.interactionPalette),
|
|
23961
|
+
brandPresentationBytes: classifyHeaderBrandPresentationBytes(theme.header, candidates.recommended, candidates.plain)
|
|
23962
|
+
};
|
|
23963
|
+
}
|
|
23964
|
+
|
|
22727
23965
|
//#endregion
|
|
22728
23966
|
//#region ../theme-core/src/site-styles/runtimeBackgroundResolution.ts
|
|
22729
23967
|
function parseCanonicalRuntimeBackground(content) {
|
|
@@ -23010,43 +24248,56 @@ function isCycleableDesignOption(option) {
|
|
|
23010
24248
|
//#endregion
|
|
23011
24249
|
//#region ../theme-core/src/site-styles/previewCompiler.ts
|
|
23012
24250
|
const previewCustomizePurpose = "preview-customize";
|
|
24251
|
+
/**
|
|
24252
|
+
* "Leave the header as the user last chose it" (#1318 Phase 4d).
|
|
24253
|
+
*
|
|
24254
|
+
* Before 4d this returned the caller's bytes verbatim, which was a fourth path
|
|
24255
|
+
* by which a hand-written header reached persistence. It now recompiles from
|
|
24256
|
+
* the row's stored selection and preferences — the stored record when the
|
|
24257
|
+
* caller holds one, and the row's own bytes read back through the Phase 4a
|
|
24258
|
+
* reader while the Phase 4c backfill is outstanding. On a row whose bytes are
|
|
24259
|
+
* compiler output this returns the persisted header unchanged, which is what
|
|
24260
|
+
* the divergence report measures; on a row whose bytes diverge, the intent is
|
|
24261
|
+
* what survives, which is the point of the phase.
|
|
24262
|
+
*/
|
|
24263
|
+
function compilePreservedHeaderDirective(directive, input) {
|
|
24264
|
+
const recompiled = directive.preferences === null ? recompileSiteChromeHeaderFromPersistedIntent(directive.persistedTheme, input.siteStyle, input.currentSelection, directive.brandPresentation).recompiled : recompileSiteChromeHeader({
|
|
24265
|
+
theme: directive.persistedTheme,
|
|
24266
|
+
siteStyle: input.siteStyle,
|
|
24267
|
+
requested: input.currentSelection,
|
|
24268
|
+
preferences: directive.preferences,
|
|
24269
|
+
brandPresentation: directive.brandPresentation
|
|
24270
|
+
});
|
|
24271
|
+
return {
|
|
24272
|
+
header: recompiled.header,
|
|
24273
|
+
brandPresentation: null,
|
|
24274
|
+
resolvedHeaderSelection: recompiled.resolution.selection,
|
|
24275
|
+
warning: recompiled.repair.kind === "exact" ? null : {
|
|
24276
|
+
kind: "repaired-header-selection",
|
|
24277
|
+
requested: input.currentSelection,
|
|
24278
|
+
resolved: recompiled.resolution.selection,
|
|
24279
|
+
reason: recompiled.repair.reason
|
|
24280
|
+
}
|
|
24281
|
+
};
|
|
24282
|
+
}
|
|
23013
24283
|
function compileHeaderDirective(input) {
|
|
23014
24284
|
if (input.directive.kind === "preserve") {
|
|
23015
|
-
return
|
|
23016
|
-
header: input.directive.header,
|
|
23017
|
-
brandPresentation: null,
|
|
23018
|
-
resolvedHeaderSelection: input.currentSelection,
|
|
23019
|
-
warning: null
|
|
23020
|
-
};
|
|
24285
|
+
return compilePreservedHeaderDirective(input.directive, input);
|
|
23021
24286
|
}
|
|
23022
24287
|
const requested = input.directive.selection;
|
|
23023
|
-
const
|
|
23024
|
-
|
|
23025
|
-
|
|
23026
|
-
const curatedChoices = requestedLayoutChoices ?? headerStyleChoicesForLayout(input.siteStyle, curatedDefault.layoutId);
|
|
23027
|
-
if (!curatedChoices) {
|
|
23028
|
-
throw new Error(`Site style ${input.siteStyle.id} has invalid header curation`);
|
|
23029
|
-
}
|
|
23030
|
-
const resolvedStyleId = deriveNearestCompatibleHeaderStyle(requested.styleId, resolvedLayoutId, curatedChoices.map((choice) => choice.id));
|
|
23031
|
-
const resolution = resolveHeaderLayoutStyleSelection({
|
|
23032
|
-
layoutId: resolvedLayoutId,
|
|
23033
|
-
styleId: resolvedStyleId
|
|
24288
|
+
const { header, brandPresentation, resolution, repair: repair$1 } = compileHeaderForCuratedSelection(input.theme, input.siteStyle, requested, input.directive.brandPresentation, {
|
|
24289
|
+
kind: "preference",
|
|
24290
|
+
preference: readHeaderNavPreferences(input.theme).interactionPalette
|
|
23034
24291
|
});
|
|
23035
|
-
const header = compileHeaderLayoutStyleSelection(input.theme, resolution, input.siteStyle);
|
|
23036
|
-
const brandPresentation = input.directive.brandPresentation.kind === "apply-logo-context" ? resolveHeaderBrandPresentationForLayout({
|
|
23037
|
-
layout: resolution.layout,
|
|
23038
|
-
logo: input.directive.brandPresentation.logo
|
|
23039
|
-
}) : null;
|
|
23040
|
-
const headerWithBrandPresentation = brandPresentation !== null ? applyHeaderBrandPresentation(header, brandPresentation) : header;
|
|
23041
24292
|
return {
|
|
23042
|
-
header
|
|
24293
|
+
header,
|
|
23043
24294
|
brandPresentation,
|
|
23044
24295
|
resolvedHeaderSelection: resolution.selection,
|
|
23045
|
-
warning:
|
|
24296
|
+
warning: repair$1.kind === "exact" ? null : {
|
|
23046
24297
|
kind: "repaired-header-selection",
|
|
23047
24298
|
requested,
|
|
23048
24299
|
resolved: resolution.selection,
|
|
23049
|
-
reason:
|
|
24300
|
+
reason: repair$1.reason
|
|
23050
24301
|
}
|
|
23051
24302
|
};
|
|
23052
24303
|
}
|
|
@@ -23080,21 +24331,6 @@ function compileIncrementalFooter(input) {
|
|
|
23080
24331
|
resolvedFooterLookId: result.selectedLookId
|
|
23081
24332
|
};
|
|
23082
24333
|
}
|
|
23083
|
-
function applyHeaderBrandPresentation(header, presentation) {
|
|
23084
|
-
const shouldShrinkDisplayLogo = presentation.size === "display" && presentation.accommodation.providesBacking === true && header.positioning !== "static";
|
|
23085
|
-
return {
|
|
23086
|
-
...header,
|
|
23087
|
-
shrinkOnScroll: shouldShrinkDisplayLogo ? true : header.shrinkOnScroll,
|
|
23088
|
-
logo: {
|
|
23089
|
-
fontFamily: header.logo?.fontFamily ?? "heading",
|
|
23090
|
-
letterSpacing: header.logo?.letterSpacing ?? "normal",
|
|
23091
|
-
gradient: header.logo?.gradient ?? false,
|
|
23092
|
-
brandDisplay: presentation.display,
|
|
23093
|
-
size: presentation.size,
|
|
23094
|
-
accommodation: presentation.accommodation.pattern
|
|
23095
|
-
}
|
|
23096
|
-
};
|
|
23097
|
-
}
|
|
23098
24334
|
function applyFooterBrandPresentation(footer, presentation) {
|
|
23099
24335
|
const size = footerLogoSizeFromBrandPresentation(presentation.size);
|
|
23100
24336
|
return {
|
|
@@ -24170,6 +25406,123 @@ function runGeneratedPageDesignPipeline(input) {
|
|
|
24170
25406
|
};
|
|
24171
25407
|
}
|
|
24172
25408
|
|
|
25409
|
+
//#endregion
|
|
25410
|
+
//#region ../theme-core/src/site-styles/headerPreferencesPersistence.ts
|
|
25411
|
+
const backgroundColorTokenSchema = z.unknown().transform((value, ctx) => {
|
|
25412
|
+
const token = parseSiteChromeBackgroundColorToken(value);
|
|
25413
|
+
if (token === null) {
|
|
25414
|
+
ctx.addIssue({
|
|
25415
|
+
code: "custom",
|
|
25416
|
+
message: "Unknown site chrome background colour token"
|
|
25417
|
+
});
|
|
25418
|
+
return z.NEVER;
|
|
25419
|
+
}
|
|
25420
|
+
return token;
|
|
25421
|
+
});
|
|
25422
|
+
const backgroundOverrideSchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("default") }).strict(), z.object({
|
|
25423
|
+
kind: z.literal("color"),
|
|
25424
|
+
token: backgroundColorTokenSchema
|
|
25425
|
+
}).strict()]);
|
|
25426
|
+
const logoTextIntentSchema = z.object({
|
|
25427
|
+
mobileMaxWidth: z.enum([
|
|
25428
|
+
"xs",
|
|
25429
|
+
"sm",
|
|
25430
|
+
"md"
|
|
25431
|
+
]).optional(),
|
|
25432
|
+
mobileWrapLines: z.union([z.literal(1), z.literal(2)]).optional(),
|
|
25433
|
+
hideOnShrink: z.boolean().optional()
|
|
25434
|
+
}).strict();
|
|
25435
|
+
const structureSchema = z.object({
|
|
25436
|
+
positioning: z.union([z.literal("inherit"), z.enum(headerPositioningValues)]),
|
|
25437
|
+
shrinkOnScroll: z.union([z.literal("inherit"), z.boolean()]),
|
|
25438
|
+
logoText: z.union([
|
|
25439
|
+
z.literal("inherit"),
|
|
25440
|
+
z.literal("none"),
|
|
25441
|
+
logoTextIntentSchema
|
|
25442
|
+
])
|
|
25443
|
+
}).strict();
|
|
25444
|
+
const headerPreferencesShape = {
|
|
25445
|
+
brandPresentation: z.enum(headerBrandPresentationValues),
|
|
25446
|
+
brandDisplay: z.enum(brandDisplayModeValues),
|
|
25447
|
+
logoSize: z.enum(logoSizePolicyValues),
|
|
25448
|
+
navFontRole: z.enum(headerNavTypographyPreferenceValues),
|
|
25449
|
+
navTextSize: z.enum(headerNavFontSizePreferenceValues),
|
|
25450
|
+
interactionPalette: z.union([z.literal("recommended"), z.enum(headerInteractionPaletteValues)]),
|
|
25451
|
+
background: backgroundOverrideSchema,
|
|
25452
|
+
structure: structureSchema
|
|
25453
|
+
};
|
|
25454
|
+
/**
|
|
25455
|
+
* The persisted shape of `HeaderPreferences`, closed at every field.
|
|
25456
|
+
*
|
|
25457
|
+
* `.strict()` throughout is deliberate: an unknown key in the stored blob means
|
|
25458
|
+
* the writer knew about a field this reader does not, and silently dropping it
|
|
25459
|
+
* would let a save round-trip away someone else's intent. Every field is
|
|
25460
|
+
* required for the same reason a default would be a hidden authority: a stored
|
|
25461
|
+
* record that omits one is not a record this reader may complete.
|
|
25462
|
+
*/
|
|
25463
|
+
const headerPreferencesSchema = z.object(headerPreferencesShape).strict();
|
|
25464
|
+
/** Only controls the client can edit may cross the write boundary. */
|
|
25465
|
+
const headerPreferencesWireSchema = z.object(headerPreferencesShape).omit({
|
|
25466
|
+
brandPresentation: true,
|
|
25467
|
+
structure: true
|
|
25468
|
+
}).strict();
|
|
25469
|
+
function parsePersistedHeaderPreferences(value) {
|
|
25470
|
+
if (value === null || value === undefined) return { kind: "absent" };
|
|
25471
|
+
const parsed = headerPreferencesSchema.safeParse(value);
|
|
25472
|
+
if (parsed.success) return {
|
|
25473
|
+
kind: "present",
|
|
25474
|
+
preferences: parsed.data
|
|
25475
|
+
};
|
|
25476
|
+
return {
|
|
25477
|
+
kind: "invalid",
|
|
25478
|
+
issues: parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`)
|
|
25479
|
+
};
|
|
25480
|
+
}
|
|
25481
|
+
|
|
25482
|
+
//#endregion
|
|
25483
|
+
//#region ../theme-core/src/site-styles/headerBytesComparison.ts
|
|
25484
|
+
function isPlainObject(value) {
|
|
25485
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25486
|
+
}
|
|
25487
|
+
/**
|
|
25488
|
+
* Structural diff by JSON path. Values are compared through
|
|
25489
|
+
* `JSON.stringify`, so a key present with the value `undefined` and an absent
|
|
25490
|
+
* key read as the same thing — which is what `jsonb` does to them anyway
|
|
25491
|
+
* (#1318 Correction 5).
|
|
25492
|
+
*/
|
|
25493
|
+
function diffJsonPaths(left, right, path$2 = "") {
|
|
25494
|
+
if (isPlainObject(left) && isPlainObject(right)) {
|
|
25495
|
+
const keys = [...new Set([...Object.keys(left), ...Object.keys(right)])].sort();
|
|
25496
|
+
return keys.flatMap((key) => diffJsonPaths(left[key], right[key], path$2 ? `${path$2}.${key}` : key));
|
|
25497
|
+
}
|
|
25498
|
+
if (JSON.stringify(left) === JSON.stringify(right)) return [];
|
|
25499
|
+
return [{
|
|
25500
|
+
path: path$2,
|
|
25501
|
+
left,
|
|
25502
|
+
right
|
|
25503
|
+
}];
|
|
25504
|
+
}
|
|
25505
|
+
function roundTrip(header) {
|
|
25506
|
+
return JSON.parse(JSON.stringify(header));
|
|
25507
|
+
}
|
|
25508
|
+
/**
|
|
25509
|
+
* The Phase 4b equality assertion: does the header a client compiled match the
|
|
25510
|
+
* one the server compiles from the same `(selection, preferences)`?
|
|
25511
|
+
*
|
|
25512
|
+
* Both sides go through a JSON round trip first, because the persisted form is
|
|
25513
|
+
* `jsonb` and an in-memory `ctaVariant: undefined` is not a difference the
|
|
25514
|
+
* database could ever store (#1318 Correction 5). Comparing the in-memory
|
|
25515
|
+
* objects directly would report a mismatch that no save could produce.
|
|
25516
|
+
*/
|
|
25517
|
+
function compareCompiledHeaders(left, right) {
|
|
25518
|
+
const diffs = diffJsonPaths(roundTrip(left), roundTrip(right));
|
|
25519
|
+
return diffs.length === 0 ? { kind: "equal" } : {
|
|
25520
|
+
kind: "different",
|
|
25521
|
+
diffs,
|
|
25522
|
+
differingPaths: diffs.map((diff) => diff.path)
|
|
25523
|
+
};
|
|
25524
|
+
}
|
|
25525
|
+
|
|
24173
25526
|
//#endregion
|
|
24174
25527
|
//#region ../theme-core/src/site-styles/headerOpeningForeground.ts
|
|
24175
25528
|
function headerStyleAvailability(recipe, context) {
|
|
@@ -24609,276 +25962,6 @@ function boundaryPinsEqual(left, right) {
|
|
|
24609
25962
|
return left.boundaryId === right.boundaryId && left.previousBlockId === right.previousBlockId && left.nextBlockId === right.nextBlockId && boundaryOptionIdForTreatment(left.choice.value) === boundaryOptionIdForTreatment(right.choice.value);
|
|
24610
25963
|
}
|
|
24611
25964
|
|
|
24612
|
-
//#endregion
|
|
24613
|
-
//#region ../theme-core/src/site-styles/siteChromeBackgrounds.ts
|
|
24614
|
-
const siteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceColorTokenNames);
|
|
24615
|
-
const quickSiteChromeBackgroundColorTokens = toSiteChromeBackgroundColorTokens(backgroundAppearanceQuickColorTokenNames);
|
|
24616
|
-
const advancedSiteChromeBackgroundColorTokens = siteChromeBackgroundColorTokens.filter((token) => !quickSiteChromeBackgroundColorTokens.includes(token));
|
|
24617
|
-
const defaultSiteChromeBackgroundOverride = { kind: "default" };
|
|
24618
|
-
function defaultSiteChromeBackgroundPreferences() {
|
|
24619
|
-
return {
|
|
24620
|
-
header: defaultSiteChromeBackgroundOverride,
|
|
24621
|
-
footer: defaultSiteChromeBackgroundOverride
|
|
24622
|
-
};
|
|
24623
|
-
}
|
|
24624
|
-
function parseSiteChromeBackgroundColorToken(value) {
|
|
24625
|
-
const token = parseBackgroundAppearanceColorToken(value);
|
|
24626
|
-
return token ? token : null;
|
|
24627
|
-
}
|
|
24628
|
-
function siteChromeBackgroundPreferencesFromTheme(theme) {
|
|
24629
|
-
if (!theme) return defaultSiteChromeBackgroundPreferences();
|
|
24630
|
-
return {
|
|
24631
|
-
header: theme.header ? siteChromeBackgroundOverrideFromHeader(theme.header) : defaultSiteChromeBackgroundOverride,
|
|
24632
|
-
footer: theme.footer ? siteChromeBackgroundOverrideFromFooter(theme.footer) : defaultSiteChromeBackgroundOverride
|
|
24633
|
-
};
|
|
24634
|
-
}
|
|
24635
|
-
function applySiteChromeBackgroundPreferences(theme, preferences) {
|
|
24636
|
-
return {
|
|
24637
|
-
...theme,
|
|
24638
|
-
header: applyHeaderBackgroundOverride(theme, theme.header, preferences.header),
|
|
24639
|
-
footer: applyFooterBackgroundOverride(theme, theme.footer, preferences.footer)
|
|
24640
|
-
};
|
|
24641
|
-
}
|
|
24642
|
-
function canCustomizeSiteChromeBackground(theme, surface$1) {
|
|
24643
|
-
if (surface$1 === "footer") return true;
|
|
24644
|
-
const header = theme?.header;
|
|
24645
|
-
if (!header) return false;
|
|
24646
|
-
const navContainerType = header.navContainer?.type ?? "none";
|
|
24647
|
-
return header.variant !== "floating" && navContainerType !== "glass" && navContainerType !== "pill";
|
|
24648
|
-
}
|
|
24649
|
-
function siteChromeBackgroundOverrideLabel(override) {
|
|
24650
|
-
switch (override.kind) {
|
|
24651
|
-
case "default": return "Default";
|
|
24652
|
-
case "color": return formatSiteChromeBackgroundTokenLabel(override.token);
|
|
24653
|
-
}
|
|
24654
|
-
}
|
|
24655
|
-
function formatSiteChromeBackgroundTokenLabel(token) {
|
|
24656
|
-
return String(token).replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
24657
|
-
}
|
|
24658
|
-
function siteChromeBackgroundOverrideEquals(left, right) {
|
|
24659
|
-
return left.kind === right.kind && (left.kind === "default" || right.kind === "color" && left.token === right.token);
|
|
24660
|
-
}
|
|
24661
|
-
function cycleSiteChromeBackgroundOverride(current, direction) {
|
|
24662
|
-
const fallback = direction === "next" ? quickSiteChromeBackgroundColorTokens[0] : quickSiteChromeBackgroundColorTokens[quickSiteChromeBackgroundColorTokens.length - 1];
|
|
24663
|
-
if (!fallback) return defaultSiteChromeBackgroundOverride;
|
|
24664
|
-
if (current.kind === "default") {
|
|
24665
|
-
return {
|
|
24666
|
-
kind: "color",
|
|
24667
|
-
token: fallback
|
|
24668
|
-
};
|
|
24669
|
-
}
|
|
24670
|
-
const currentIndex = quickSiteChromeBackgroundColorTokens.indexOf(current.token);
|
|
24671
|
-
if (currentIndex === -1) {
|
|
24672
|
-
return {
|
|
24673
|
-
kind: "color",
|
|
24674
|
-
token: fallback
|
|
24675
|
-
};
|
|
24676
|
-
}
|
|
24677
|
-
const nextIndex = direction === "next" ? (currentIndex + 1) % quickSiteChromeBackgroundColorTokens.length : currentIndex === 0 ? quickSiteChromeBackgroundColorTokens.length - 1 : currentIndex - 1;
|
|
24678
|
-
const token = requiredQuickSiteChromeBackgroundColorToken(nextIndex);
|
|
24679
|
-
return {
|
|
24680
|
-
kind: "color",
|
|
24681
|
-
token
|
|
24682
|
-
};
|
|
24683
|
-
}
|
|
24684
|
-
function siteChromeBackgroundOverrideFromHeader(header) {
|
|
24685
|
-
return siteChromeBackgroundOverrideFromBackground(header.background);
|
|
24686
|
-
}
|
|
24687
|
-
function siteChromeBackgroundOverrideFromFooter(footer) {
|
|
24688
|
-
return siteChromeBackgroundOverrideFromBackground(footer.background);
|
|
24689
|
-
}
|
|
24690
|
-
function siteChromeBackgroundOverrideFromBackground(background) {
|
|
24691
|
-
if (background?.contentOwner !== siteChromeBackgroundOverrideOwner || background.type !== "color") {
|
|
24692
|
-
return defaultSiteChromeBackgroundOverride;
|
|
24693
|
-
}
|
|
24694
|
-
const token = parseSiteChromeBackgroundColorToken(background.color);
|
|
24695
|
-
return token ? {
|
|
24696
|
-
kind: "color",
|
|
24697
|
-
token
|
|
24698
|
-
} : defaultSiteChromeBackgroundOverride;
|
|
24699
|
-
}
|
|
24700
|
-
function applyHeaderBackgroundOverride(theme, header, override) {
|
|
24701
|
-
if (override.kind === "default") return header;
|
|
24702
|
-
const background = siteChromeColorBackground(override.token);
|
|
24703
|
-
const foreground = resolveChromeForegroundToken(theme, background);
|
|
24704
|
-
return {
|
|
24705
|
-
...header,
|
|
24706
|
-
background: {
|
|
24707
|
-
...background,
|
|
24708
|
-
textColor: foreground
|
|
24709
|
-
},
|
|
24710
|
-
textColor: foreground,
|
|
24711
|
-
navColor: foreground,
|
|
24712
|
-
...header.navLinkStyle ? { navLinkStyle: {
|
|
24713
|
-
...header.navLinkStyle,
|
|
24714
|
-
colorToken: foreground
|
|
24715
|
-
} } : {}
|
|
24716
|
-
};
|
|
24717
|
-
}
|
|
24718
|
-
function applyFooterBackgroundOverride(theme, footer, override) {
|
|
24719
|
-
if (override.kind === "default") return footer;
|
|
24720
|
-
const background = siteChromeColorBackground(override.token);
|
|
24721
|
-
const foreground = resolveChromeForegroundToken(theme, background);
|
|
24722
|
-
return {
|
|
24723
|
-
...footer,
|
|
24724
|
-
background: {
|
|
24725
|
-
...background,
|
|
24726
|
-
textColor: foreground
|
|
24727
|
-
},
|
|
24728
|
-
navColor: foreground,
|
|
24729
|
-
...footer.navLinkStyle ? { navLinkStyle: {
|
|
24730
|
-
...footer.navLinkStyle,
|
|
24731
|
-
colorToken: foreground
|
|
24732
|
-
} } : {},
|
|
24733
|
-
...footer.bottomTextLinkStyle ? { bottomTextLinkStyle: {
|
|
24734
|
-
...footer.bottomTextLinkStyle,
|
|
24735
|
-
colorToken: foreground
|
|
24736
|
-
} } : {},
|
|
24737
|
-
...footer.bottomBar ? { bottomBar: {
|
|
24738
|
-
...footer.bottomBar,
|
|
24739
|
-
textColor: foreground
|
|
24740
|
-
} } : {}
|
|
24741
|
-
};
|
|
24742
|
-
}
|
|
24743
|
-
function resolveChromeForegroundToken(theme, background) {
|
|
24744
|
-
const resolved = resolveSurfaceForeground(background, expandPalette(theme.palette));
|
|
24745
|
-
if (resolved.kind === "unknown") {
|
|
24746
|
-
throw new Error(`Site chrome background "${background?.type === "color" ? background.color ?? "unknown" : "unknown"}" has no resolvable AA foreground; the theme palette is incomplete`);
|
|
24747
|
-
}
|
|
24748
|
-
return resolved.roles.text;
|
|
24749
|
-
}
|
|
24750
|
-
function siteChromeColorBackground(color) {
|
|
24751
|
-
return {
|
|
24752
|
-
type: "color",
|
|
24753
|
-
contentOwner: siteChromeBackgroundOverrideOwner,
|
|
24754
|
-
color
|
|
24755
|
-
};
|
|
24756
|
-
}
|
|
24757
|
-
function toSiteChromeBackgroundColorTokens(tokenNames) {
|
|
24758
|
-
return tokenNames.flatMap((name) => {
|
|
24759
|
-
const token = parseSiteChromeBackgroundColorToken(name);
|
|
24760
|
-
return token ? [token] : [];
|
|
24761
|
-
});
|
|
24762
|
-
}
|
|
24763
|
-
function requiredQuickSiteChromeBackgroundColorToken(index) {
|
|
24764
|
-
const token = quickSiteChromeBackgroundColorTokens[index];
|
|
24765
|
-
if (!token) {
|
|
24766
|
-
throw new Error(`Missing quick site chrome background token at index ${index}`);
|
|
24767
|
-
}
|
|
24768
|
-
return token;
|
|
24769
|
-
}
|
|
24770
|
-
|
|
24771
|
-
//#endregion
|
|
24772
|
-
//#region ../theme-core/src/site-styles/siteChromeNavPreferences.ts
|
|
24773
|
-
const headerNavTypographyPreferenceValues = [
|
|
24774
|
-
"inherit",
|
|
24775
|
-
"body",
|
|
24776
|
-
"heading"
|
|
24777
|
-
];
|
|
24778
|
-
const headerNavFontSizePreferenceValues = [
|
|
24779
|
-
"inherit",
|
|
24780
|
-
"sm",
|
|
24781
|
-
"base",
|
|
24782
|
-
"lg",
|
|
24783
|
-
"xl"
|
|
24784
|
-
];
|
|
24785
|
-
const headerNavTypographyOptions = [
|
|
24786
|
-
{
|
|
24787
|
-
value: "inherit",
|
|
24788
|
-
label: "Default"
|
|
24789
|
-
},
|
|
24790
|
-
{
|
|
24791
|
-
value: "body",
|
|
24792
|
-
label: "Body"
|
|
24793
|
-
},
|
|
24794
|
-
{
|
|
24795
|
-
value: "heading",
|
|
24796
|
-
label: "Heading"
|
|
24797
|
-
}
|
|
24798
|
-
];
|
|
24799
|
-
const headerNavFontSizeOptions = [
|
|
24800
|
-
{
|
|
24801
|
-
value: "inherit",
|
|
24802
|
-
label: "Default"
|
|
24803
|
-
},
|
|
24804
|
-
{
|
|
24805
|
-
value: "sm",
|
|
24806
|
-
label: "Small"
|
|
24807
|
-
},
|
|
24808
|
-
{
|
|
24809
|
-
value: "base",
|
|
24810
|
-
label: "Regular"
|
|
24811
|
-
},
|
|
24812
|
-
{
|
|
24813
|
-
value: "lg",
|
|
24814
|
-
label: "Large"
|
|
24815
|
-
},
|
|
24816
|
-
{
|
|
24817
|
-
value: "xl",
|
|
24818
|
-
label: "Huge"
|
|
24819
|
-
}
|
|
24820
|
-
];
|
|
24821
|
-
const defaultHeaderNavPreferences = {
|
|
24822
|
-
typography: "inherit",
|
|
24823
|
-
fontSize: "inherit",
|
|
24824
|
-
interactionPalette: "recommended"
|
|
24825
|
-
};
|
|
24826
|
-
function readHeaderNavPreferences(theme) {
|
|
24827
|
-
const typography = theme?.header?.navFontRole;
|
|
24828
|
-
const fontSize = theme?.header?.navTextSize;
|
|
24829
|
-
const interactionPalette = theme?.header?.interactionPalette ?? "recommended";
|
|
24830
|
-
return typography === "body" || typography === "heading" ? {
|
|
24831
|
-
typography,
|
|
24832
|
-
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
24833
|
-
interactionPalette
|
|
24834
|
-
} : {
|
|
24835
|
-
...defaultHeaderNavPreferences,
|
|
24836
|
-
fontSize: readHeaderNavFontSizePreference(fontSize),
|
|
24837
|
-
interactionPalette
|
|
24838
|
-
};
|
|
24839
|
-
}
|
|
24840
|
-
function headerNavPreferencesEqual(left, right) {
|
|
24841
|
-
return left.typography === right.typography && left.fontSize === right.fontSize && left.interactionPalette === right.interactionPalette;
|
|
24842
|
-
}
|
|
24843
|
-
function withHeaderNavThemePreferences(theme, preferences) {
|
|
24844
|
-
const { navFontRole: _navFontRole, navTextSize: _navTextSize, interactionPalette: _interactionPalette, ...headerWithoutPreference } = theme.header;
|
|
24845
|
-
const nextHeader = {
|
|
24846
|
-
...headerWithoutPreference,
|
|
24847
|
-
...preferences.typography === "inherit" ? {} : { navFontRole: preferences.typography },
|
|
24848
|
-
...preferences.fontSize === "inherit" ? {} : { navTextSize: preferences.fontSize },
|
|
24849
|
-
...preferences.interactionPalette === "recommended" ? {} : { interactionPalette: preferences.interactionPalette }
|
|
24850
|
-
};
|
|
24851
|
-
return {
|
|
24852
|
-
...theme,
|
|
24853
|
-
header: nextHeader
|
|
24854
|
-
};
|
|
24855
|
-
}
|
|
24856
|
-
/**
|
|
24857
|
-
* Applies the user's nav font-role/size overrides on top of a style-resolved
|
|
24858
|
-
* nav link style. This is where the precedence contract lands:
|
|
24859
|
-
*
|
|
24860
|
-
* - The nav typography TREATMENT (case/tracking identity: plain / heading /
|
|
24861
|
-
* quiet-caps / brand-caps) is ALWAYS owned by the active header style and is
|
|
24862
|
-
* baked into `style` before this call (see `applyNavTypography` in
|
|
24863
|
-
* headerLayoutStyles.ts).
|
|
24864
|
-
* - Each treatment resolves through a DEFAULT font role: `heading` resolves
|
|
24865
|
-
* through the heading role; `plain` / `quiet-caps` / `brand-caps` resolve
|
|
24866
|
-
* through the body role.
|
|
24867
|
-
* - The user's `navFontRole` (surfaced here as `preferences.typography`) only
|
|
24868
|
-
* overrides which font ROLE the treatment resolves through. It never changes
|
|
24869
|
-
* the treatment. `'inherit'` keeps the treatment's default role.
|
|
24870
|
-
*/
|
|
24871
|
-
function mergeHeaderNavTypographyPreference(style, preferences) {
|
|
24872
|
-
return preferences.typography === "inherit" && preferences.fontSize === "inherit" ? style : {
|
|
24873
|
-
...style,
|
|
24874
|
-
...preferences.typography === "inherit" ? {} : { typography: preferences.typography },
|
|
24875
|
-
...preferences.fontSize === "inherit" ? {} : { textSize: preferences.fontSize }
|
|
24876
|
-
};
|
|
24877
|
-
}
|
|
24878
|
-
function readHeaderNavFontSizePreference(value) {
|
|
24879
|
-
return value === "sm" || value === "base" || value === "lg" || value === "xl" ? value : "inherit";
|
|
24880
|
-
}
|
|
24881
|
-
|
|
24882
25965
|
//#endregion
|
|
24883
25966
|
//#region ../theme-core/src/site-styles/tokenRecipes.ts
|
|
24884
25967
|
const themeTokenRecipeOptions = {
|
|
@@ -24994,24 +26077,24 @@ function normalizeHex$1(value) {
|
|
|
24994
26077
|
const blue = triplet.charAt(2);
|
|
24995
26078
|
return `#${red}${red}${green}${green}${blue}${blue}`;
|
|
24996
26079
|
}
|
|
24997
|
-
function collectDifferences(before, after, path$
|
|
26080
|
+
function collectDifferences(before, after, path$2, differences) {
|
|
24998
26081
|
if (Object.is(before, after)) return;
|
|
24999
26082
|
if (Array.isArray(before) && Array.isArray(after)) {
|
|
25000
26083
|
const sharedLength = Math.min(before.length, after.length);
|
|
25001
26084
|
for (let index = 0; index < sharedLength; index += 1) {
|
|
25002
|
-
collectDifferences(before[index], after[index], `${path$
|
|
26085
|
+
collectDifferences(before[index], after[index], `${path$2}[${index}]`, differences);
|
|
25003
26086
|
}
|
|
25004
26087
|
for (let index = sharedLength; index < before.length; index += 1) {
|
|
25005
26088
|
differences.push({
|
|
25006
26089
|
kind: "missing",
|
|
25007
|
-
path: `${path$
|
|
26090
|
+
path: `${path$2}[${index}]`,
|
|
25008
26091
|
before: before[index]
|
|
25009
26092
|
});
|
|
25010
26093
|
}
|
|
25011
26094
|
for (let index = sharedLength; index < after.length; index += 1) {
|
|
25012
26095
|
differences.push({
|
|
25013
26096
|
kind: "extra",
|
|
25014
|
-
path: `${path$
|
|
26097
|
+
path: `${path$2}[${index}]`,
|
|
25015
26098
|
after: after[index]
|
|
25016
26099
|
});
|
|
25017
26100
|
}
|
|
@@ -25022,7 +26105,7 @@ function collectDifferences(before, after, path$1, differences) {
|
|
|
25022
26105
|
const afterKeys = new Set(Object.keys(after));
|
|
25023
26106
|
const keys = [...new Set([...beforeKeys, ...afterKeys])].sort();
|
|
25024
26107
|
for (const key of keys) {
|
|
25025
|
-
const childPath = `${path$
|
|
26108
|
+
const childPath = `${path$2}.${key}`;
|
|
25026
26109
|
if (!afterKeys.has(key)) {
|
|
25027
26110
|
differences.push({
|
|
25028
26111
|
kind: "missing",
|
|
@@ -25045,7 +26128,7 @@ function collectDifferences(before, after, path$1, differences) {
|
|
|
25045
26128
|
}
|
|
25046
26129
|
differences.push({
|
|
25047
26130
|
kind: "changed",
|
|
25048
|
-
path: path$
|
|
26131
|
+
path: path$2,
|
|
25049
26132
|
before,
|
|
25050
26133
|
after
|
|
25051
26134
|
});
|
|
@@ -25216,8 +26299,8 @@ const sdkConfigureBookingVerticalResponseSchema = sdkCommandApplicationResultSch
|
|
|
25216
26299
|
function isRecord$8(value) {
|
|
25217
26300
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
25218
26301
|
}
|
|
25219
|
-
function pullSchemaForRequest(path$
|
|
25220
|
-
switch (path$
|
|
26302
|
+
function pullSchemaForRequest(path$2) {
|
|
26303
|
+
switch (path$2) {
|
|
25221
26304
|
case "/pull/entries/all": return sdkPullEntriesAllResponseSchema;
|
|
25222
26305
|
case "/pull/entries": return sdkPullEntriesResponseSchema;
|
|
25223
26306
|
case "/pull/pages": return sdkPullPagesResponseSchema;
|
|
@@ -25229,12 +26312,12 @@ function pullSchemaForRequest(path$1) {
|
|
|
25229
26312
|
case "/pull/venues": return sdkPullVenuesResponseSchema;
|
|
25230
26313
|
case "/pull/event-categories": return sdkPullEventCategoriesResponseSchema;
|
|
25231
26314
|
case "/pull/events": return sdkPullEventsResponseSchema;
|
|
25232
|
-
default: return /^\/pull\/entries\/[^/]+$/.test(path$
|
|
26315
|
+
default: return /^\/pull\/entries\/[^/]+$/.test(path$2) ? sdkPullEntriesByTypeResponseSchema : undefined;
|
|
25233
26316
|
}
|
|
25234
26317
|
}
|
|
25235
|
-
function rootMutationSchema(method, path$
|
|
26318
|
+
function rootMutationSchema(method, path$2, body) {
|
|
25236
26319
|
if (method === "POST") {
|
|
25237
|
-
switch (path$
|
|
26320
|
+
switch (path$2) {
|
|
25238
26321
|
case "/entries": return isRecord$8(body) && body.publication === "publish" ? sdkUpsertEntryPublishResponseSchema : sdkUpsertEntryDraftResponseSchema;
|
|
25239
26322
|
case "/pages": return isRecord$8(body) && body.publication === "publish" ? sdkUpsertPagePublishResponseSchema : sdkUpsertPageDraftResponseSchema;
|
|
25240
26323
|
case "/navigation": return sdkUpsertNavigationMenuResponseSchema;
|
|
@@ -25247,11 +26330,11 @@ function rootMutationSchema(method, path$1, body) {
|
|
|
25247
26330
|
case "/footer": return sdkUpsertFooterResponseSchema;
|
|
25248
26331
|
}
|
|
25249
26332
|
}
|
|
25250
|
-
if (method === "PATCH" && path$
|
|
25251
|
-
if (method === "POST" && path$
|
|
26333
|
+
if (method === "PATCH" && path$2 === "/settings") return sdkUpsertSettingsResponseSchema;
|
|
26334
|
+
if (method === "POST" && path$2 === "/settings/booking/vertical") return sdkConfigureBookingVerticalResponseSchema;
|
|
25252
26335
|
return undefined;
|
|
25253
26336
|
}
|
|
25254
|
-
function isDeleteMutationPath(path$
|
|
26337
|
+
function isDeleteMutationPath(path$2) {
|
|
25255
26338
|
return [
|
|
25256
26339
|
/^\/entries\/[^/]+\/[^/]+$/,
|
|
25257
26340
|
/^\/pages\/[^/]+$/,
|
|
@@ -25261,35 +26344,42 @@ function isDeleteMutationPath(path$1) {
|
|
|
25261
26344
|
/^\/venues\/[^/]+$/,
|
|
25262
26345
|
/^\/event-categories\/[^/]+$/,
|
|
25263
26346
|
/^\/events\/[^/]+$/
|
|
25264
|
-
].some((pattern) => pattern.test(path$
|
|
26347
|
+
].some((pattern) => pattern.test(path$2));
|
|
25265
26348
|
}
|
|
25266
|
-
function nestedMutationSchema(method, path$
|
|
25267
|
-
if (method === "POST" && /^\/pages\/[^/]+\/blocks$/.test(path$
|
|
25268
|
-
if (method === "POST" && /^\/pages\/[^/]+\/blocks\/reorder$/.test(path$
|
|
25269
|
-
if (method === "PATCH" && /^\/pages\/[^/]+\/blocks\/[^/]+$/.test(path$
|
|
25270
|
-
if (method === "POST" && /^\/entries\/[^/]+\/[^/]+\/(publish|unpublish)$/.test(path$
|
|
26349
|
+
function nestedMutationSchema(method, path$2) {
|
|
26350
|
+
if (method === "POST" && /^\/pages\/[^/]+\/blocks$/.test(path$2)) return sdkUpsertBlockResponseSchema;
|
|
26351
|
+
if (method === "POST" && /^\/pages\/[^/]+\/blocks\/reorder$/.test(path$2)) return sdkReorderResponseSchema;
|
|
26352
|
+
if (method === "PATCH" && /^\/pages\/[^/]+\/blocks\/[^/]+$/.test(path$2)) return sdkPatchPageBlockResponseSchema;
|
|
26353
|
+
if (method === "POST" && /^\/entries\/[^/]+\/[^/]+\/(publish|unpublish)$/.test(path$2)) {
|
|
25271
26354
|
return sdkCommandApplicationResultSchema(sdkUpsertEntryDraftResponseSchema.shape.value);
|
|
25272
26355
|
}
|
|
25273
|
-
if (method === "POST" && /^\/pages\/[^/]+\/(publish|unpublish)$/.test(path$
|
|
26356
|
+
if (method === "POST" && /^\/pages\/[^/]+\/(publish|unpublish)$/.test(path$2)) {
|
|
25274
26357
|
return sdkCommandApplicationResultSchema(sdkUpsertPageDraftResponseSchema.shape.value);
|
|
25275
26358
|
}
|
|
25276
|
-
if (method === "DELETE" && isDeleteMutationPath(path$
|
|
26359
|
+
if (method === "DELETE" && isDeleteMutationPath(path$2)) return sdkDeleteResponseSchema;
|
|
25277
26360
|
return undefined;
|
|
25278
26361
|
}
|
|
25279
|
-
function occurrenceResponseSchema(method, path$
|
|
26362
|
+
function occurrenceResponseSchema(method, path$2) {
|
|
25280
26363
|
const collection = /^\/events\/[^/]+\/runs\/[^/]+\/occurrences$/;
|
|
25281
26364
|
const member = /^\/events\/[^/]+\/runs\/[^/]+\/occurrences\/[^/]+$/;
|
|
25282
|
-
if (method === "GET" && collection.test(path$
|
|
25283
|
-
if (method === "POST" && collection.test(path$
|
|
25284
|
-
if (method === "PATCH" && member.test(path$
|
|
25285
|
-
if (method === "DELETE" && member.test(path$
|
|
25286
|
-
return method === "POST" && /^\/events\/[^/]+\/runs\/[^/]+\/occurrences\/[^/]+\/cancel$/.test(path$
|
|
26365
|
+
if (method === "GET" && collection.test(path$2)) return sdkListEventOccurrencesResponseSchema;
|
|
26366
|
+
if (method === "POST" && collection.test(path$2)) return sdkEventOccurrenceResponseSchema;
|
|
26367
|
+
if (method === "PATCH" && member.test(path$2)) return sdkEventOccurrenceResponseSchema;
|
|
26368
|
+
if (method === "DELETE" && member.test(path$2)) return sdkDeleteEventOccurrenceResponseSchema;
|
|
26369
|
+
return method === "POST" && /^\/events\/[^/]+\/runs\/[^/]+\/occurrences\/[^/]+\/cancel$/.test(path$2) ? sdkEventOccurrenceResponseSchema : undefined;
|
|
26370
|
+
}
|
|
26371
|
+
function mediaResponseSchema(method, path$2) {
|
|
26372
|
+
if (method === "GET" && path$2 === "/media") return sdkMediaListResponseSchema;
|
|
26373
|
+
if (method === "GET" && /^\/media\/[^/]+\/download$/.test(path$2)) return sdkMediaDownloadResponseSchema;
|
|
26374
|
+
if (method === "POST" && /^\/media\/[^/]+\/delete-preview$/.test(path$2)) return sdkMediaDeletePreviewResponseSchema;
|
|
26375
|
+
if (method === "DELETE" && /^\/media\/[^/]+$/.test(path$2)) return sdkMediaDeleteApplyResponseSchema;
|
|
26376
|
+
return undefined;
|
|
25287
26377
|
}
|
|
25288
|
-
function schemaForManagementRequest(method, path$
|
|
26378
|
+
function schemaForManagementRequest(method, path$2, body) {
|
|
25289
26379
|
if (method === "GET") {
|
|
25290
|
-
return pullSchemaForRequest(path$
|
|
26380
|
+
return pullSchemaForRequest(path$2) ?? occurrenceResponseSchema(method, path$2) ?? mediaResponseSchema(method, path$2);
|
|
25291
26381
|
}
|
|
25292
|
-
return rootMutationSchema(method, path$
|
|
26382
|
+
return rootMutationSchema(method, path$2, body) ?? occurrenceResponseSchema(method, path$2) ?? mediaResponseSchema(method, path$2) ?? nestedMutationSchema(method, path$2);
|
|
25293
26383
|
}
|
|
25294
26384
|
function validateManagementResponse(value, schema) {
|
|
25295
26385
|
const parsed = schema.safeParse(value);
|
|
@@ -25444,10 +26534,10 @@ async function readApiResponse(response, requestInfo, options = {}) {
|
|
|
25444
26534
|
}
|
|
25445
26535
|
return envelope;
|
|
25446
26536
|
}
|
|
25447
|
-
function requestUrl(baseUrl, path$
|
|
25448
|
-
if (!params || Object.keys(params).length === 0) return `${baseUrl}${path$
|
|
26537
|
+
function requestUrl(baseUrl, path$2, params) {
|
|
26538
|
+
if (!params || Object.keys(params).length === 0) return `${baseUrl}${path$2}`;
|
|
25449
26539
|
const query = new URLSearchParams(params).toString();
|
|
25450
|
-
return `${baseUrl}${path$
|
|
26540
|
+
return `${baseUrl}${path$2}?${query}`;
|
|
25451
26541
|
}
|
|
25452
26542
|
function requestFetchOptions(config, method, body) {
|
|
25453
26543
|
const options = {
|
|
@@ -25461,10 +26551,10 @@ function requestFetchOptions(config, method, body) {
|
|
|
25461
26551
|
if (body !== undefined && method !== "GET") options.body = JSON.stringify(body);
|
|
25462
26552
|
return options;
|
|
25463
26553
|
}
|
|
25464
|
-
async function fetchManagementResponse(config, method, path$
|
|
26554
|
+
async function fetchManagementResponse(config, method, path$2, url, baseUrl, options) {
|
|
25465
26555
|
const requestInfo = buildRequestInfo({
|
|
25466
26556
|
method,
|
|
25467
|
-
path: path$
|
|
26557
|
+
path: path$2,
|
|
25468
26558
|
url,
|
|
25469
26559
|
baseUrl,
|
|
25470
26560
|
dashboardUrl: config.dashboardUrl,
|
|
@@ -25490,12 +26580,12 @@ function throwApiError(response, json, requestInfo) {
|
|
|
25490
26580
|
}
|
|
25491
26581
|
throw new ManagementApiError(`Request failed with status ${response.status}`, "sdk:http-error", undefined, response.status, requestInfo);
|
|
25492
26582
|
}
|
|
25493
|
-
async function requestWithConfig(config, method, path$
|
|
26583
|
+
async function requestWithConfig(config, method, path$2, options) {
|
|
25494
26584
|
const baseUrl = `${config.dashboardUrl}/api/sdk/${config.siteId}`;
|
|
25495
|
-
const url = requestUrl(baseUrl, path$
|
|
26585
|
+
const url = requestUrl(baseUrl, path$2, options?.params);
|
|
25496
26586
|
const fetchOptions = requestFetchOptions(config, method, options?.body);
|
|
25497
|
-
const { response, requestInfo } = await fetchManagementResponse(config, method, path$
|
|
25498
|
-
const responseSchema = options?.responseSchema ?? schemaForManagementRequest(method, path$
|
|
26587
|
+
const { response, requestInfo } = await fetchManagementResponse(config, method, path$2, url, baseUrl, fetchOptions);
|
|
26588
|
+
const responseSchema = options?.responseSchema ?? schemaForManagementRequest(method, path$2, options?.body);
|
|
25499
26589
|
const json = responseSchema ? await readApiResponse(response, requestInfo, { invalidOkResponse: true }) : await readApiResponse(response, requestInfo, { allowNonJsonOk: true });
|
|
25500
26590
|
if (!json) return undefined;
|
|
25501
26591
|
throwApiError(response, json, requestInfo);
|
|
@@ -25503,38 +26593,38 @@ async function requestWithConfig(config, method, path$1, options) {
|
|
|
25503
26593
|
}
|
|
25504
26594
|
function createHttpClient(config) {
|
|
25505
26595
|
return {
|
|
25506
|
-
async get(path$
|
|
25507
|
-
return requestWithConfig(config, "GET", path$
|
|
26596
|
+
async get(path$2, params) {
|
|
26597
|
+
return requestWithConfig(config, "GET", path$2, { params });
|
|
25508
26598
|
},
|
|
25509
|
-
async post(path$
|
|
25510
|
-
return requestWithConfig(config, "POST", path$
|
|
26599
|
+
async post(path$2, body) {
|
|
26600
|
+
return requestWithConfig(config, "POST", path$2, { body });
|
|
25511
26601
|
},
|
|
25512
|
-
async patch(path$
|
|
25513
|
-
return requestWithConfig(config, "PATCH", path$
|
|
26602
|
+
async patch(path$2, body) {
|
|
26603
|
+
return requestWithConfig(config, "PATCH", path$2, { body });
|
|
25514
26604
|
},
|
|
25515
|
-
async delete(path$
|
|
25516
|
-
return requestWithConfig(config, "DELETE", path$
|
|
26605
|
+
async delete(path$2, body) {
|
|
26606
|
+
return requestWithConfig(config, "DELETE", path$2, { body });
|
|
25517
26607
|
},
|
|
25518
|
-
async getValidated(path$
|
|
25519
|
-
return requestWithConfig(config, "GET", path$
|
|
26608
|
+
async getValidated(path$2, schema, params) {
|
|
26609
|
+
return requestWithConfig(config, "GET", path$2, {
|
|
25520
26610
|
params,
|
|
25521
26611
|
responseSchema: schema
|
|
25522
26612
|
});
|
|
25523
26613
|
},
|
|
25524
|
-
async postValidated(path$
|
|
25525
|
-
return requestWithConfig(config, "POST", path$
|
|
26614
|
+
async postValidated(path$2, schema, body) {
|
|
26615
|
+
return requestWithConfig(config, "POST", path$2, {
|
|
25526
26616
|
body,
|
|
25527
26617
|
responseSchema: schema
|
|
25528
26618
|
});
|
|
25529
26619
|
},
|
|
25530
|
-
async patchValidated(path$
|
|
25531
|
-
return requestWithConfig(config, "PATCH", path$
|
|
26620
|
+
async patchValidated(path$2, schema, body) {
|
|
26621
|
+
return requestWithConfig(config, "PATCH", path$2, {
|
|
25532
26622
|
body,
|
|
25533
26623
|
responseSchema: schema
|
|
25534
26624
|
});
|
|
25535
26625
|
},
|
|
25536
|
-
async deleteValidated(path$
|
|
25537
|
-
return requestWithConfig(config, "DELETE", path$
|
|
26626
|
+
async deleteValidated(path$2, schema, body) {
|
|
26627
|
+
return requestWithConfig(config, "DELETE", path$2, {
|
|
25538
26628
|
body,
|
|
25539
26629
|
responseSchema: schema
|
|
25540
26630
|
});
|
|
@@ -26149,17 +27239,51 @@ function createIdentifiersOperations(http) {
|
|
|
26149
27239
|
|
|
26150
27240
|
//#endregion
|
|
26151
27241
|
//#region ../management-client/src/media.ts
|
|
27242
|
+
const productionMediaOperationsDeps = {
|
|
27243
|
+
createHttpClient,
|
|
27244
|
+
fetch
|
|
27245
|
+
};
|
|
26152
27246
|
/**
|
|
26153
27247
|
* Create media operations for the management client.
|
|
26154
27248
|
* Uses direct-to-storage signed upload URLs for file uploads.
|
|
26155
27249
|
*/
|
|
26156
|
-
function createMediaOperations(config) {
|
|
27250
|
+
function createMediaOperations(config, deps = productionMediaOperationsDeps) {
|
|
26157
27251
|
const baseUrl = `${config.dashboardUrl}/api/sdk/${config.siteId}`;
|
|
26158
27252
|
const uploadTimeout = config.timeout ?? 10 * 60 * 1e3;
|
|
26159
|
-
const http = createHttpClient(config);
|
|
27253
|
+
const http = deps.createHttpClient(config);
|
|
26160
27254
|
return {
|
|
27255
|
+
async list(options = {}) {
|
|
27256
|
+
const params = {};
|
|
27257
|
+
if (options.search !== undefined) params.search = options.search;
|
|
27258
|
+
if (options.limit !== undefined) params.limit = String(options.limit);
|
|
27259
|
+
if (options.offset !== undefined) params.offset = String(options.offset);
|
|
27260
|
+
return http.get("/media", params);
|
|
27261
|
+
},
|
|
27262
|
+
async download(identifier, maxBytes) {
|
|
27263
|
+
const path$2 = `/media/${encodeURIComponent(identifier)}/download`;
|
|
27264
|
+
const transfer = await http.get(path$2);
|
|
27265
|
+
const requestInfo = buildRequestInfo({
|
|
27266
|
+
method: "GET",
|
|
27267
|
+
path: path$2,
|
|
27268
|
+
url: `${baseUrl}${path$2}`,
|
|
27269
|
+
baseUrl,
|
|
27270
|
+
dashboardUrl: config.dashboardUrl,
|
|
27271
|
+
siteId: config.siteId
|
|
27272
|
+
});
|
|
27273
|
+
const response = await getMediaFile(transfer.transferUrl, uploadTimeout, requestInfo, deps.fetch);
|
|
27274
|
+
return {
|
|
27275
|
+
data: await readBoundedResponseBody(response, maxBytes),
|
|
27276
|
+
asset: transfer.asset
|
|
27277
|
+
};
|
|
27278
|
+
},
|
|
27279
|
+
async previewDelete(identifier) {
|
|
27280
|
+
return http.post(`/media/${encodeURIComponent(identifier)}/delete-preview`);
|
|
27281
|
+
},
|
|
27282
|
+
async delete(identifier) {
|
|
27283
|
+
return http.delete(`/media/${encodeURIComponent(identifier)}`);
|
|
27284
|
+
},
|
|
26161
27285
|
async upload(file) {
|
|
26162
|
-
return uploadMediaFile(config, http, baseUrl, uploadTimeout, file);
|
|
27286
|
+
return uploadMediaFile(config, http, baseUrl, uploadTimeout, file, deps.fetch);
|
|
26163
27287
|
},
|
|
26164
27288
|
async exists(storagePath) {
|
|
26165
27289
|
const result = await http.post("/media/exists", { storagePath });
|
|
@@ -26174,7 +27298,48 @@ function createMediaOperations(config) {
|
|
|
26174
27298
|
}
|
|
26175
27299
|
};
|
|
26176
27300
|
}
|
|
26177
|
-
async function
|
|
27301
|
+
async function readBoundedResponseBody(response, maxBytes) {
|
|
27302
|
+
const declaredLength = response.headers.get("content-length");
|
|
27303
|
+
if (declaredLength !== null && Number(declaredLength) > maxBytes) {
|
|
27304
|
+
throw new ManagementApiError(`Download exceeds the ${maxBytes}-byte limit`, "sdk:download-too-large");
|
|
27305
|
+
}
|
|
27306
|
+
if (!response.body) return Buffer.alloc(0);
|
|
27307
|
+
const chunks = [];
|
|
27308
|
+
let totalBytes = 0;
|
|
27309
|
+
const reader = response.body.getReader();
|
|
27310
|
+
try {
|
|
27311
|
+
for (;;) {
|
|
27312
|
+
const chunk = await reader.read();
|
|
27313
|
+
if (chunk.done) break;
|
|
27314
|
+
totalBytes += chunk.value.byteLength;
|
|
27315
|
+
if (totalBytes > maxBytes) {
|
|
27316
|
+
await reader.cancel();
|
|
27317
|
+
throw new ManagementApiError(`Download exceeds the ${maxBytes}-byte limit`, "sdk:download-too-large");
|
|
27318
|
+
}
|
|
27319
|
+
chunks.push(chunk.value);
|
|
27320
|
+
}
|
|
27321
|
+
} finally {
|
|
27322
|
+
reader.releaseLock();
|
|
27323
|
+
}
|
|
27324
|
+
return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk)), totalBytes);
|
|
27325
|
+
}
|
|
27326
|
+
async function getMediaFile(transferUrl, timeout, requestInfo, fetchImpl) {
|
|
27327
|
+
try {
|
|
27328
|
+
const response = await fetchImpl(transferUrl, {
|
|
27329
|
+
method: "GET",
|
|
27330
|
+
signal: AbortSignal.timeout(timeout)
|
|
27331
|
+
});
|
|
27332
|
+
if (!response.ok) {
|
|
27333
|
+
throw new ManagementApiError(`Download failed with status ${response.status}`, "sdk:http-error", undefined, response.status, requestInfo);
|
|
27334
|
+
}
|
|
27335
|
+
return response;
|
|
27336
|
+
} catch (error) {
|
|
27337
|
+
if (error instanceof ManagementApiError) throw error;
|
|
27338
|
+
const timedOut = error instanceof Error && error.name === "TimeoutError";
|
|
27339
|
+
throw new ManagementApiError(timedOut ? `Download timed out after ${timeout}ms` : error instanceof Error ? error.message : "Network request failed", timedOut ? "sdk:timeout" : "sdk:network-error", undefined, undefined, requestInfo);
|
|
27340
|
+
}
|
|
27341
|
+
}
|
|
27342
|
+
async function uploadMediaFile(config, http, baseUrl, uploadTimeout, file, fetchImpl) {
|
|
26178
27343
|
const filename = file.filename || "upload";
|
|
26179
27344
|
const mimeType = file.contentType || "application/octet-stream";
|
|
26180
27345
|
const { signedUrl, storagePath, bucket } = await http.post("/media/upload-url", {
|
|
@@ -26192,7 +27357,7 @@ async function uploadMediaFile(config, http, baseUrl, uploadTimeout, file) {
|
|
|
26192
27357
|
dashboardUrl: config.dashboardUrl,
|
|
26193
27358
|
siteId: config.siteId
|
|
26194
27359
|
});
|
|
26195
|
-
const response = await putMediaFile(signedUrl, file.data, mimeType, uploadTimeout, requestInfo);
|
|
27360
|
+
const response = await putMediaFile(signedUrl, file.data, mimeType, uploadTimeout, requestInfo, fetchImpl);
|
|
26196
27361
|
if (!response.ok && response.status !== 409) {
|
|
26197
27362
|
throw new ManagementApiError(`Upload failed with status ${response.status}`, "sdk:http-error", undefined, response.status, requestInfo);
|
|
26198
27363
|
}
|
|
@@ -26206,9 +27371,9 @@ async function uploadMediaFile(config, http, baseUrl, uploadTimeout, file) {
|
|
|
26206
27371
|
identifier: file.identifier
|
|
26207
27372
|
});
|
|
26208
27373
|
}
|
|
26209
|
-
async function putMediaFile(signedUrl, data, mimeType, timeout, requestInfo) {
|
|
27374
|
+
async function putMediaFile(signedUrl, data, mimeType, timeout, requestInfo, fetchImpl) {
|
|
26210
27375
|
try {
|
|
26211
|
-
return await
|
|
27376
|
+
return await fetchImpl(signedUrl, {
|
|
26212
27377
|
method: "PUT",
|
|
26213
27378
|
headers: { "Content-Type": mimeType },
|
|
26214
27379
|
body: new Uint8Array(data),
|
|
@@ -26951,8 +28116,8 @@ async function withRiverbankEnv(target, run) {
|
|
|
26951
28116
|
}
|
|
26952
28117
|
const previous = envScopeQueue;
|
|
26953
28118
|
let release;
|
|
26954
|
-
envScopeQueue = new Promise((resolve$
|
|
26955
|
-
release = resolve$
|
|
28119
|
+
envScopeQueue = new Promise((resolve$2) => {
|
|
28120
|
+
release = resolve$2;
|
|
26956
28121
|
});
|
|
26957
28122
|
await previous;
|
|
26958
28123
|
const previousEnv = process.env.RIVERBANK_ENV;
|
|
@@ -27134,7 +28299,7 @@ function shouldConfirm(plan, prepared) {
|
|
|
27134
28299
|
return readyTargets(prepared).length > 0 && !hasPreparationBarrierFailure(plan, prepared);
|
|
27135
28300
|
}
|
|
27136
28301
|
function hasPreparationBarrierFailure(plan, prepared) {
|
|
27137
|
-
return plan.targets.length > 1 && plan.policy.confirmation === "command" && prepared.some((entry) => entry.kind === "failed");
|
|
28302
|
+
return plan.targets.length > 1 && (plan.policy.confirmation === "command" || plan.policy.preparationBarrier === "all-target") && prepared.some((entry) => entry.kind === "failed");
|
|
27138
28303
|
}
|
|
27139
28304
|
async function confirmPrepared(plan, prepared, deps) {
|
|
27140
28305
|
if (!shouldConfirm(plan, prepared)) return true;
|
|
@@ -27442,7 +28607,7 @@ function formatDateShort(isoDate) {
|
|
|
27442
28607
|
*/
|
|
27443
28608
|
async function parseJsonData(options) {
|
|
27444
28609
|
if (options.file) {
|
|
27445
|
-
const filePath = path.resolve(options.file);
|
|
28610
|
+
const filePath = path$1.resolve(options.file);
|
|
27446
28611
|
const content = await fs.readFile(filePath, "utf-8");
|
|
27447
28612
|
return JSON.parse(content);
|
|
27448
28613
|
}
|
|
@@ -27496,12 +28661,12 @@ async function confirmAction(message, options, isRemote) {
|
|
|
27496
28661
|
input: process.stdin,
|
|
27497
28662
|
output: process.stderr
|
|
27498
28663
|
});
|
|
27499
|
-
return new Promise((resolve$
|
|
28664
|
+
return new Promise((resolve$2) => {
|
|
27500
28665
|
let settled = false;
|
|
27501
28666
|
const finish = (confirmed) => {
|
|
27502
28667
|
if (settled) return;
|
|
27503
28668
|
settled = true;
|
|
27504
|
-
resolve$
|
|
28669
|
+
resolve$2(confirmed);
|
|
27505
28670
|
};
|
|
27506
28671
|
rl.once("close", () => finish(false));
|
|
27507
28672
|
rl.question(`${message} (y/N): `, (answer) => {
|
|
@@ -30624,19 +31789,19 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30624
31789
|
prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
|
|
30625
31790
|
actScopeDepth = prevActScopeDepth;
|
|
30626
31791
|
}
|
|
30627
|
-
function recursivelyFlushAsyncActWork(returnValue, resolve$
|
|
31792
|
+
function recursivelyFlushAsyncActWork(returnValue, resolve$2, reject) {
|
|
30628
31793
|
var queue = ReactSharedInternals$1.actQueue;
|
|
30629
31794
|
if (null !== queue) if (0 !== queue.length) try {
|
|
30630
31795
|
flushActQueue(queue);
|
|
30631
31796
|
enqueueTask(function() {
|
|
30632
|
-
return recursivelyFlushAsyncActWork(returnValue, resolve$
|
|
31797
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve$2, reject);
|
|
30633
31798
|
});
|
|
30634
31799
|
return;
|
|
30635
31800
|
} catch (error) {
|
|
30636
31801
|
ReactSharedInternals$1.thrownErrors.push(error);
|
|
30637
31802
|
}
|
|
30638
31803
|
else ReactSharedInternals$1.actQueue = null;
|
|
30639
|
-
0 < ReactSharedInternals$1.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, reject(queue)) : resolve$
|
|
31804
|
+
0 < ReactSharedInternals$1.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, reject(queue)) : resolve$2(returnValue);
|
|
30640
31805
|
}
|
|
30641
31806
|
function flushActQueue(queue) {
|
|
30642
31807
|
if (!isFlushing) {
|
|
@@ -30797,14 +31962,14 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30797
31962
|
queueSeveralMicrotasks(function() {
|
|
30798
31963
|
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = !0, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
|
|
30799
31964
|
});
|
|
30800
|
-
return { then: function(resolve$
|
|
31965
|
+
return { then: function(resolve$2, reject) {
|
|
30801
31966
|
didAwaitActCall = !0;
|
|
30802
31967
|
thenable.then(function(returnValue) {
|
|
30803
31968
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
30804
31969
|
if (0 === prevActScopeDepth) {
|
|
30805
31970
|
try {
|
|
30806
31971
|
flushActQueue(queue), enqueueTask(function() {
|
|
30807
|
-
return recursivelyFlushAsyncActWork(returnValue, resolve$
|
|
31972
|
+
return recursivelyFlushAsyncActWork(returnValue, resolve$2, reject);
|
|
30808
31973
|
});
|
|
30809
31974
|
} catch (error$0) {
|
|
30810
31975
|
ReactSharedInternals$1.thrownErrors.push(error$0);
|
|
@@ -30814,7 +31979,7 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30814
31979
|
ReactSharedInternals$1.thrownErrors.length = 0;
|
|
30815
31980
|
reject(_thrownError);
|
|
30816
31981
|
}
|
|
30817
|
-
} else resolve$
|
|
31982
|
+
} else resolve$2(returnValue);
|
|
30818
31983
|
}, function(error) {
|
|
30819
31984
|
popActScope(prevActQueue, prevActScopeDepth);
|
|
30820
31985
|
0 < ReactSharedInternals$1.thrownErrors.length ? (error = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, reject(error)) : reject(error);
|
|
@@ -30827,11 +31992,11 @@ var require_react_development = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
30827
31992
|
didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = !0, console.error("A component suspended inside an `act` scope, but the `act` call was not awaited. When testing React components that depend on asynchronous data, you must await the result:\n\nawait act(() => ...)"));
|
|
30828
31993
|
}), ReactSharedInternals$1.actQueue = null);
|
|
30829
31994
|
if (0 < ReactSharedInternals$1.thrownErrors.length) throw callback = aggregateErrors(ReactSharedInternals$1.thrownErrors), ReactSharedInternals$1.thrownErrors.length = 0, callback;
|
|
30830
|
-
return { then: function(resolve$
|
|
31995
|
+
return { then: function(resolve$2, reject) {
|
|
30831
31996
|
didAwaitActCall = !0;
|
|
30832
31997
|
0 === prevActScopeDepth ? (ReactSharedInternals$1.actQueue = queue, enqueueTask(function() {
|
|
30833
|
-
return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve$
|
|
30834
|
-
})) : resolve$
|
|
31998
|
+
return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve$2, reject);
|
|
31999
|
+
})) : resolve$2(returnValue$jscomp$0);
|
|
30835
32000
|
} };
|
|
30836
32001
|
};
|
|
30837
32002
|
exports.cache = function(fn) {
|
|
@@ -33777,13 +34942,13 @@ function optionalRef(ref, field) {
|
|
|
33777
34942
|
field
|
|
33778
34943
|
}] : [];
|
|
33779
34944
|
}
|
|
33780
|
-
function collectVenueRefsFromIntervals(intervals, path$
|
|
33781
|
-
return intervals.flatMap((interval, index) => optionalRef(interval.venueRef, `${path$
|
|
34945
|
+
function collectVenueRefsFromIntervals(intervals, path$2) {
|
|
34946
|
+
return intervals.flatMap((interval, index) => optionalRef(interval.venueRef, `${path$2}[${index}].venueRef`));
|
|
33782
34947
|
}
|
|
33783
|
-
function collectNavigationRefs(items, path$
|
|
34948
|
+
function collectNavigationRefs(items, path$2) {
|
|
33784
34949
|
return items.flatMap((item, index) => {
|
|
33785
34950
|
if (!isRecord$5(item)) return [];
|
|
33786
|
-
const itemPath = `${path$
|
|
34951
|
+
const itemPath = `${path$2}[${index}]`;
|
|
33787
34952
|
const refs = [];
|
|
33788
34953
|
if (typeof item.pageRef === "string") {
|
|
33789
34954
|
refs.push({
|
|
@@ -33803,26 +34968,26 @@ function collectNavigationRefs(items, path$1) {
|
|
|
33803
34968
|
return refs;
|
|
33804
34969
|
});
|
|
33805
34970
|
}
|
|
33806
|
-
function collectBlockRefs(blocks, path$
|
|
34971
|
+
function collectBlockRefs(blocks, path$2) {
|
|
33807
34972
|
return blocks.flatMap((block, index) => {
|
|
33808
34973
|
if (!isRecord$5(block)) return [];
|
|
33809
|
-
const blockPath = `${path$
|
|
34974
|
+
const blockPath = `${path$2}[${index}]`;
|
|
33810
34975
|
return [...collectNestedRefs(block.props, `${blockPath}.props`), ...collectNestedRefs(block.children, `${blockPath}.children`)];
|
|
33811
34976
|
});
|
|
33812
34977
|
}
|
|
33813
|
-
function collectNestedRefs(value, path$
|
|
34978
|
+
function collectNestedRefs(value, path$2) {
|
|
33814
34979
|
if (typeof value === "string") {
|
|
33815
34980
|
const parsed = parseResourceRef(value);
|
|
33816
34981
|
return parsed.ok ? [{
|
|
33817
34982
|
ref: value,
|
|
33818
|
-
field: path$
|
|
34983
|
+
field: path$2
|
|
33819
34984
|
}] : [];
|
|
33820
34985
|
}
|
|
33821
34986
|
if (Array.isArray(value)) {
|
|
33822
|
-
return value.flatMap((item, index) => collectNestedRefs(item, `${path$
|
|
34987
|
+
return value.flatMap((item, index) => collectNestedRefs(item, `${path$2}[${index}]`));
|
|
33823
34988
|
}
|
|
33824
34989
|
if (!isRecord$5(value)) return [];
|
|
33825
|
-
return Object.entries(value).flatMap(([key, child]) => collectNestedRefs(child, `${path$
|
|
34990
|
+
return Object.entries(value).flatMap(([key, child]) => collectNestedRefs(child, `${path$2}.${key}`));
|
|
33826
34991
|
}
|
|
33827
34992
|
|
|
33828
34993
|
//#endregion
|
|
@@ -34088,8 +35253,8 @@ function invalidPayloadError(input, issue) {
|
|
|
34088
35253
|
message: `${field}: ${issue.message}`
|
|
34089
35254
|
};
|
|
34090
35255
|
}
|
|
34091
|
-
function commandIndexForIssuePath(path$
|
|
34092
|
-
return path$
|
|
35256
|
+
function commandIndexForIssuePath(path$2) {
|
|
35257
|
+
return path$2[0] === "commands" && typeof path$2[1] === "number" ? path$2[1] : -1;
|
|
34093
35258
|
}
|
|
34094
35259
|
function commandTypeForIssue(input, commandIndex) {
|
|
34095
35260
|
if (!isRecord$5(input) || !isUnknownArray(input.commands)) return null;
|
|
@@ -34099,9 +35264,9 @@ function commandTypeForIssue(input, commandIndex) {
|
|
|
34099
35264
|
function isUnknownArray(value) {
|
|
34100
35265
|
return Array.isArray(value);
|
|
34101
35266
|
}
|
|
34102
|
-
function formatIssuePath(path$
|
|
34103
|
-
if (path$
|
|
34104
|
-
return path$
|
|
35267
|
+
function formatIssuePath(path$2) {
|
|
35268
|
+
if (path$2.length === 0) return "<root>";
|
|
35269
|
+
return path$2.reduce((formatted, segment) => {
|
|
34105
35270
|
if (typeof segment === "number") return `${formatted}[${segment}]`;
|
|
34106
35271
|
if (formatted.length === 0) return String(segment);
|
|
34107
35272
|
return `${formatted}.${String(segment)}`;
|
|
@@ -34834,6 +35999,8 @@ const FILE_SIZE_LIMITS = {
|
|
|
34834
35999
|
instagramZip: unsafeAsPositiveByteCount(100 * 1024 * 1024),
|
|
34835
36000
|
bookingEvidence: unsafeAsPositiveByteCount(25 * 1024 * 1024)
|
|
34836
36001
|
};
|
|
36002
|
+
/** Shared admission ceiling for SDK-management media uploads before intent-specific finalization. */
|
|
36003
|
+
const MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES = unsafeAsPositiveByteCount(250 * 1024 * 1024);
|
|
34837
36004
|
const DEFAULT_PRIVATE_FILE_SIGNED_URL_EXPIRY_SECONDS = unsafeAsPositiveSeconds(5 * 60);
|
|
34838
36005
|
const MAX_SAFE_DOWNLOAD_FILENAME_LENGTH = 180;
|
|
34839
36006
|
function asPositiveByteCount(value) {
|
|
@@ -35097,6 +36264,16 @@ function classifyKnownMimeType(mimeType) {
|
|
|
35097
36264
|
default: return err("unsupported-mime-type");
|
|
35098
36265
|
}
|
|
35099
36266
|
}
|
|
36267
|
+
function managementMediaLibraryUploadLimitForMimeType(mimeType) {
|
|
36268
|
+
const kind = classifyKnownMimeType(mimeType);
|
|
36269
|
+
if (!kind.ok) return kind;
|
|
36270
|
+
const renderUse = kind.value.tag;
|
|
36271
|
+
if (renderUse !== "image" && renderUse !== "video" && renderUse !== "audio") {
|
|
36272
|
+
return err("unsupported-mime-type");
|
|
36273
|
+
}
|
|
36274
|
+
const libraryLimit = mediaLibraryMaxBytes([renderUse]);
|
|
36275
|
+
return ok(unsafeAsPositiveByteCount(Math.min(libraryLimit, MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES)));
|
|
36276
|
+
}
|
|
35100
36277
|
function isMimeTypeAllowedForPolicy(mimeType, policy) {
|
|
35101
36278
|
const kindResult = classifyKnownMimeType(mimeType);
|
|
35102
36279
|
return kindResult.ok && isFileKindAllowed(kindResult.value, policy);
|
|
@@ -35664,9 +36841,9 @@ function buildDurableFileStoragePath(input) {
|
|
|
35664
36841
|
input.fileAssetId,
|
|
35665
36842
|
input.safeDownloadFilename
|
|
35666
36843
|
];
|
|
35667
|
-
const path$
|
|
35668
|
-
if (!path$
|
|
35669
|
-
return ok(unsafeAsSiteScopedStoragePath(path$
|
|
36844
|
+
const path$2 = joinStoragePathSegments(segments);
|
|
36845
|
+
if (!path$2.ok) return path$2;
|
|
36846
|
+
return ok(unsafeAsSiteScopedStoragePath(path$2.value));
|
|
35670
36847
|
}
|
|
35671
36848
|
function parseStoragePath(value) {
|
|
35672
36849
|
return joinStoragePathSegments(value.split("/"));
|
|
@@ -37091,10 +38268,10 @@ function emptyStatePatch(patch) {
|
|
|
37091
38268
|
rejection_reason: patch.rejection_reason ?? null
|
|
37092
38269
|
};
|
|
37093
38270
|
}
|
|
37094
|
-
function traverse(items, parse) {
|
|
38271
|
+
function traverse(items, parse$1) {
|
|
37095
38272
|
const values = [];
|
|
37096
38273
|
for (const item of items) {
|
|
37097
|
-
const parsed = parse(item);
|
|
38274
|
+
const parsed = parse$1(item);
|
|
37098
38275
|
if (!parsed.ok) return parsed;
|
|
37099
38276
|
values.push(parsed.value);
|
|
37100
38277
|
}
|
|
@@ -37775,18 +38952,18 @@ function resolveFileDownload(input) {
|
|
|
37775
38952
|
assetKind: input.asset.kind
|
|
37776
38953
|
});
|
|
37777
38954
|
}
|
|
37778
|
-
const access = authorizeFileAccess(input.actor, input.asset.accessPolicy);
|
|
37779
|
-
if (!access.ok) {
|
|
38955
|
+
const access$1 = authorizeFileAccess(input.actor, input.asset.accessPolicy);
|
|
38956
|
+
if (!access$1.ok) {
|
|
37780
38957
|
return err({
|
|
37781
38958
|
tag: "access-denied",
|
|
37782
|
-
reason: access.error
|
|
38959
|
+
reason: access$1.error
|
|
37783
38960
|
});
|
|
37784
38961
|
}
|
|
37785
38962
|
return ok(fileAccessGrantToDownloadPlan({
|
|
37786
38963
|
asset: input.asset,
|
|
37787
38964
|
requestedUse: input.requestedUse,
|
|
37788
38965
|
disposition: input.disposition ?? defaultDispositionForFileUse(input.requestedUse),
|
|
37789
|
-
grant: access.value
|
|
38966
|
+
grant: access$1.value
|
|
37790
38967
|
}));
|
|
37791
38968
|
}
|
|
37792
38969
|
function fileAccessGrantToDownloadPlan(input) {
|
|
@@ -38317,9 +39494,9 @@ const EXTENSIONS_BY_MIME_TYPE = {
|
|
|
38317
39494
|
};
|
|
38318
39495
|
function fileExtension(filename) {
|
|
38319
39496
|
const pathSegments = filename.replace(/\\/g, "/").split("/");
|
|
38320
|
-
const basename = pathSegments[pathSegments.length - 1] ?? filename;
|
|
38321
|
-
const dotIndex = basename.lastIndexOf(".");
|
|
38322
|
-
return dotIndex <= 0 ? "" : basename.slice(dotIndex).toLowerCase();
|
|
39497
|
+
const basename$1 = pathSegments[pathSegments.length - 1] ?? filename;
|
|
39498
|
+
const dotIndex = basename$1.lastIndexOf(".");
|
|
39499
|
+
return dotIndex <= 0 ? "" : basename$1.slice(dotIndex).toLowerCase();
|
|
38323
39500
|
}
|
|
38324
39501
|
function getMimeType(filename) {
|
|
38325
39502
|
return MIME_TYPES_BY_EXTENSION[fileExtension(filename)] ?? "application/octet-stream";
|
|
@@ -38867,20 +40044,20 @@ function formatMediaUsageStage(stage) {
|
|
|
38867
40044
|
function mediaUsageLocationLabel(location$1) {
|
|
38868
40045
|
switch (location$1.kind) {
|
|
38869
40046
|
case "pageBlock": {
|
|
38870
|
-
const path$
|
|
40047
|
+
const path$2 = location$1.pageDraftPath ?? location$1.pagePublishedPath ?? null;
|
|
38871
40048
|
const title = location$1.pageTitle ?? "Untitled page";
|
|
38872
|
-
const where = path$
|
|
40049
|
+
const where = path$2 ? `${title} (${path$2})` : title;
|
|
38873
40050
|
return `${where} — ${formatMediaUsageStage(location$1.stage)} — Block: ${location$1.blockKind}`;
|
|
38874
40051
|
}
|
|
38875
40052
|
case "entrySlotBlock": {
|
|
38876
|
-
const path$
|
|
38877
|
-
const where = path$
|
|
40053
|
+
const path$2 = location$1.routeDraftPath ?? location$1.routePublishedPath ?? null;
|
|
40054
|
+
const where = path$2 ? `${location$1.entryTitle} (${path$2})` : location$1.entryTitle;
|
|
38878
40055
|
return `${where} — Page Content: ${location$1.slotKey} — ${formatMediaUsageStage(location$1.stage)} — Block: ${location$1.blockKind}`;
|
|
38879
40056
|
}
|
|
38880
40057
|
case "surfaceSlotBlock": return `Page surface ${location$1.pageSurfaceId} — Page Content: ${location$1.slotKey} — ${formatMediaUsageStage(location$1.stage)} — Block: ${location$1.blockKind}`;
|
|
38881
40058
|
case "contentEntry": {
|
|
38882
|
-
const path$
|
|
38883
|
-
const where = path$
|
|
40059
|
+
const path$2 = location$1.routeDraftPath ?? location$1.routePublishedPath ?? null;
|
|
40060
|
+
const where = path$2 ? `${location$1.entryTitle} (${path$2})` : location$1.entryTitle;
|
|
38884
40061
|
return `${where} — ${formatMediaUsageStage(location$1.stage)}`;
|
|
38885
40062
|
}
|
|
38886
40063
|
case "productFileDownload": return `${location$1.productTitle} — Product download`;
|
|
@@ -38910,10 +40087,17 @@ function hasMediaUsage(response) {
|
|
|
38910
40087
|
return response.items.some((item) => item.usedCount > 0);
|
|
38911
40088
|
}
|
|
38912
40089
|
function planBulkDeleteCompletionToast(result) {
|
|
40090
|
+
const hasWarnings = (result.warnings?.length ?? 0) > 0;
|
|
38913
40091
|
if (result.failed > 0) {
|
|
38914
40092
|
return {
|
|
38915
40093
|
kind: "error",
|
|
38916
|
-
message: `Deleted ${result.deleted}. Failed to delete ${result.failed}.`
|
|
40094
|
+
message: hasWarnings ? `Deleted ${result.deleted}. Failed to delete ${result.failed}. Some cleanup could not be completed.` : `Deleted ${result.deleted}. Failed to delete ${result.failed}.`
|
|
40095
|
+
};
|
|
40096
|
+
}
|
|
40097
|
+
if (hasWarnings) {
|
|
40098
|
+
return {
|
|
40099
|
+
kind: "warning",
|
|
40100
|
+
message: result.deleted === 1 ? "Deleted 1 media item, but some cleanup could not be completed." : `Deleted ${result.deleted} media items, but some cleanup could not be completed.`
|
|
38917
40101
|
};
|
|
38918
40102
|
}
|
|
38919
40103
|
return {
|
|
@@ -39178,7 +40362,7 @@ const ORDER = [
|
|
|
39178
40362
|
];
|
|
39179
40363
|
const getImageUrl = (asset, params = {}, basePath = DEFAULT_BASE_PATH) => {
|
|
39180
40364
|
const trimmedBase = basePath.endsWith("/") ? basePath.slice(0, Math.max(basePath.length - 1, 0)) : basePath;
|
|
39181
|
-
const path$
|
|
40365
|
+
const path$2 = `${trimmedBase}/${encodeURIComponent(asset.id)}`;
|
|
39182
40366
|
const searchParams = new URLSearchParams();
|
|
39183
40367
|
if (params.w !== undefined) searchParams.set("w", formatNumber(params.w));
|
|
39184
40368
|
if (params.h !== undefined) searchParams.set("h", formatNumber(params.h));
|
|
@@ -39197,7 +40381,7 @@ const getImageUrl = (asset, params = {}, basePath = DEFAULT_BASE_PATH) => {
|
|
|
39197
40381
|
if (value !== null) sorted.set(key, value);
|
|
39198
40382
|
});
|
|
39199
40383
|
const query = sorted.toString();
|
|
39200
|
-
return query ? `${path$
|
|
40384
|
+
return query ? `${path$2}?${query}` : path$2;
|
|
39201
40385
|
};
|
|
39202
40386
|
|
|
39203
40387
|
//#endregion
|
|
@@ -39967,8 +41151,8 @@ function mediaIdentifierOrFallback(current, fallback) {
|
|
|
39967
41151
|
function isRecord$4(value) {
|
|
39968
41152
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39969
41153
|
}
|
|
39970
|
-
function formatContentFieldPath(path$
|
|
39971
|
-
return path$
|
|
41154
|
+
function formatContentFieldPath(path$2) {
|
|
41155
|
+
return path$2.map((segment, index) => typeof segment === "number" ? `[${segment}]` : index === 0 ? segment : `.${segment}`).join("");
|
|
39972
41156
|
}
|
|
39973
41157
|
function summarizeContentMediaValue(value) {
|
|
39974
41158
|
if (typeof value === "string") {
|
|
@@ -39981,7 +41165,7 @@ function summarizeContentMediaValue(value) {
|
|
|
39981
41165
|
if (isRecord$4(value)) return `object(${Object.keys(value).sort().join(",")})`;
|
|
39982
41166
|
return typeof value;
|
|
39983
41167
|
}
|
|
39984
|
-
function auditMediaValue(context, path$
|
|
41168
|
+
function auditMediaValue(context, path$2, allowedMediaTypes$1, value) {
|
|
39985
41169
|
const expectedMediaType = allowedMediaTypes$1[0];
|
|
39986
41170
|
if (value === undefined || value === null) return [];
|
|
39987
41171
|
if (typeof value === "string") {
|
|
@@ -39990,7 +41174,7 @@ function auditMediaValue(context, path$1, allowedMediaTypes$1, value) {
|
|
|
39990
41174
|
return [{
|
|
39991
41175
|
tag: "blank-string",
|
|
39992
41176
|
context,
|
|
39993
|
-
path: path$
|
|
41177
|
+
path: path$2,
|
|
39994
41178
|
valueSummary: summarizeContentMediaValue(value),
|
|
39995
41179
|
expectedMediaType
|
|
39996
41180
|
}];
|
|
@@ -39998,7 +41182,7 @@ function auditMediaValue(context, path$1, allowedMediaTypes$1, value) {
|
|
|
39998
41182
|
return [{
|
|
39999
41183
|
tag: "legacy-string",
|
|
40000
41184
|
context,
|
|
40001
|
-
path: path$
|
|
41185
|
+
path: path$2,
|
|
40002
41186
|
valueSummary: summarizeContentMediaValue(value),
|
|
40003
41187
|
originalValue: value,
|
|
40004
41188
|
expectedMediaType,
|
|
@@ -40014,7 +41198,7 @@ function auditMediaValue(context, path$1, allowedMediaTypes$1, value) {
|
|
|
40014
41198
|
return [{
|
|
40015
41199
|
tag: "malformed-object",
|
|
40016
41200
|
context,
|
|
40017
|
-
path: path$
|
|
41201
|
+
path: path$2,
|
|
40018
41202
|
valueSummary: summarizeContentMediaValue(value),
|
|
40019
41203
|
expectedMediaType
|
|
40020
41204
|
}];
|
|
@@ -40417,8 +41601,8 @@ function mediaTypeFromAsset(row) {
|
|
|
40417
41601
|
}
|
|
40418
41602
|
function legacyStorageValues(siteId, storagePath) {
|
|
40419
41603
|
const sitePrefix = `sites/${siteId}/`;
|
|
40420
|
-
const relative = storagePath.startsWith(sitePrefix) ? storagePath.slice(sitePrefix.length) : storagePath;
|
|
40421
|
-
return [...new Set([storagePath, `/${relative}`])];
|
|
41604
|
+
const relative$1 = storagePath.startsWith(sitePrefix) ? storagePath.slice(sitePrefix.length) : storagePath;
|
|
41605
|
+
return [...new Set([storagePath, `/${relative$1}`])];
|
|
40422
41606
|
}
|
|
40423
41607
|
function assetCandidates(rows) {
|
|
40424
41608
|
return rows.flatMap((row) => {
|
|
@@ -40806,10 +41990,10 @@ var BrowserImage = class extends ImageBase {
|
|
|
40806
41990
|
return Promise.reject(new Error(`Cannot load buffer as an image in browser`));
|
|
40807
41991
|
}
|
|
40808
41992
|
this.image = img;
|
|
40809
|
-
return new Promise((resolve$
|
|
41993
|
+
return new Promise((resolve$2, reject) => {
|
|
40810
41994
|
const onImageLoad = () => {
|
|
40811
41995
|
this._initCanvas();
|
|
40812
|
-
resolve$
|
|
41996
|
+
resolve$2(this);
|
|
40813
41997
|
};
|
|
40814
41998
|
if (img.complete) {
|
|
40815
41999
|
onImageLoad();
|
|
@@ -42395,7 +43579,7 @@ const isMediaValue = (value) => isRecordValue(value) && !("attrs" in value) && t
|
|
|
42395
43579
|
//#endregion
|
|
42396
43580
|
//#region src/content/media-manifest.ts
|
|
42397
43581
|
const MANIFEST_VERSION = 2;
|
|
42398
|
-
const getManifestPath = (contentDir) => path.join(contentDir, "media", ".manifest.json");
|
|
43582
|
+
const getManifestPath = (contentDir) => path$1.join(contentDir, "media", ".manifest.json");
|
|
42399
43583
|
const ensureDir = async (dirPath) => {
|
|
42400
43584
|
try {
|
|
42401
43585
|
await fs.mkdir(dirPath, { recursive: true });
|
|
@@ -42487,7 +43671,7 @@ async function readMediaManifest(contentDir) {
|
|
|
42487
43671
|
return createEmptyMediaManifest();
|
|
42488
43672
|
}
|
|
42489
43673
|
async function writeMediaManifest(contentDir, manifest) {
|
|
42490
|
-
const mediaDir = path.join(contentDir, "media");
|
|
43674
|
+
const mediaDir = path$1.join(contentDir, "media");
|
|
42491
43675
|
await ensureDir(mediaDir);
|
|
42492
43676
|
const updatedManifest = {
|
|
42493
43677
|
...manifest,
|
|
@@ -42623,7 +43807,7 @@ const DEFAULT_RETRY_CONFIG = {
|
|
|
42623
43807
|
* Sleep for a given number of milliseconds.
|
|
42624
43808
|
*/
|
|
42625
43809
|
function sleep(ms) {
|
|
42626
|
-
return new Promise((resolve$
|
|
43810
|
+
return new Promise((resolve$2) => setTimeout(resolve$2, ms));
|
|
42627
43811
|
}
|
|
42628
43812
|
/**
|
|
42629
43813
|
* Calculate exponential backoff delay with jitter.
|
|
@@ -42732,8 +43916,8 @@ const TRANSFORMABLE_EXTENSIONS = [
|
|
|
42732
43916
|
* Check if a file path is an image that can be transformed by Supabase.
|
|
42733
43917
|
* @param path - The file path or storage path
|
|
42734
43918
|
*/
|
|
42735
|
-
function isTransformableImage(path$
|
|
42736
|
-
const ext = path$
|
|
43919
|
+
function isTransformableImage(path$2) {
|
|
43920
|
+
const ext = path$2.split(".").pop()?.toLowerCase();
|
|
42737
43921
|
return TRANSFORMABLE_EXTENSIONS.includes(ext ?? "");
|
|
42738
43922
|
}
|
|
42739
43923
|
/**
|
|
@@ -43044,7 +44228,7 @@ function collectRichTextImageAttrs(value, out = [], location$1 = "") {
|
|
|
43044
44228
|
//#endregion
|
|
43045
44229
|
//#region src/media/pull-manifest.ts
|
|
43046
44230
|
const buildLocalFilename = (identifier, asset) => {
|
|
43047
|
-
const extensionFromFilename = path.extname(asset.filename ?? "");
|
|
44231
|
+
const extensionFromFilename = path$1.extname(asset.filename ?? "");
|
|
43048
44232
|
const extension = extensionFromFilename || getExtensionForMimeType(asset.mimeType) || "";
|
|
43049
44233
|
return extension ? `${identifier}${extension}` : identifier;
|
|
43050
44234
|
};
|
|
@@ -43366,6 +44550,165 @@ function sha256(buffer) {
|
|
|
43366
44550
|
return createHash("sha256").update(buffer).digest("hex");
|
|
43367
44551
|
}
|
|
43368
44552
|
|
|
44553
|
+
//#endregion
|
|
44554
|
+
//#region src/files/stable-binary-file.ts
|
|
44555
|
+
function isNodeError(error) {
|
|
44556
|
+
return error instanceof Error && "code" in error;
|
|
44557
|
+
}
|
|
44558
|
+
function observationsMatch(left, right) {
|
|
44559
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
|
|
44560
|
+
}
|
|
44561
|
+
function admitStableBinaryRead(stats, maxBytes) {
|
|
44562
|
+
if (!stats.isFile()) return err({ tag: "not_a_regular_file" });
|
|
44563
|
+
return stats.size > maxBytes ? err({
|
|
44564
|
+
tag: "file_too_large",
|
|
44565
|
+
maxBytes,
|
|
44566
|
+
actualBytes: stats.size
|
|
44567
|
+
}) : ok(undefined);
|
|
44568
|
+
}
|
|
44569
|
+
function mapStableBinaryReadFailure(error) {
|
|
44570
|
+
if (!isNodeError(error)) return { tag: "read_failed" };
|
|
44571
|
+
if (error.code === "ENOENT") return { tag: "file_not_found" };
|
|
44572
|
+
return error.code === "ELOOP" || error.code === "EISDIR" ? { tag: "not_a_regular_file" } : { tag: "read_failed" };
|
|
44573
|
+
}
|
|
44574
|
+
async function readObservedByteLength(handle, byteLength) {
|
|
44575
|
+
const data = Buffer.allocUnsafe(byteLength);
|
|
44576
|
+
let offset = 0;
|
|
44577
|
+
while (offset < byteLength) {
|
|
44578
|
+
const read = await handle.read(data, offset, byteLength - offset, offset);
|
|
44579
|
+
if (read.bytesRead === 0) return null;
|
|
44580
|
+
offset += read.bytesRead;
|
|
44581
|
+
}
|
|
44582
|
+
const growthProbe = Buffer.allocUnsafe(1);
|
|
44583
|
+
const extra = await handle.read(growthProbe, 0, 1, byteLength);
|
|
44584
|
+
return extra.bytesRead === 0 ? data : null;
|
|
44585
|
+
}
|
|
44586
|
+
async function readStableBinaryFile(filePath, maxBytes) {
|
|
44587
|
+
let handle;
|
|
44588
|
+
try {
|
|
44589
|
+
const noFollow = constants.O_NOFOLLOW ?? 0;
|
|
44590
|
+
handle = await open(filePath, constants.O_RDONLY | noFollow);
|
|
44591
|
+
const before = await handle.stat();
|
|
44592
|
+
const admission = admitStableBinaryRead(before, maxBytes);
|
|
44593
|
+
if (!admission.ok) return admission;
|
|
44594
|
+
const data = await readObservedByteLength(handle, before.size);
|
|
44595
|
+
const after = await handle.stat();
|
|
44596
|
+
if (data === null || !observationsMatch(before, after)) {
|
|
44597
|
+
return err({ tag: "file_changed_during_read" });
|
|
44598
|
+
}
|
|
44599
|
+
return ok(data);
|
|
44600
|
+
} catch (error) {
|
|
44601
|
+
return err(mapStableBinaryReadFailure(error));
|
|
44602
|
+
} finally {
|
|
44603
|
+
await handle?.close().catch(() => undefined);
|
|
44604
|
+
}
|
|
44605
|
+
}
|
|
44606
|
+
async function inspectDestination(filePath, overwrite) {
|
|
44607
|
+
try {
|
|
44608
|
+
const stats = await lstat(filePath);
|
|
44609
|
+
if (overwrite === "refuse") return err({ tag: "destination_exists" });
|
|
44610
|
+
return stats.isFile() ? ok(undefined) : err({ tag: "unsafe_destination" });
|
|
44611
|
+
} catch (error) {
|
|
44612
|
+
if (isNodeError(error) && error.code === "ENOENT") return ok(undefined);
|
|
44613
|
+
return err({ tag: "write_failed" });
|
|
44614
|
+
}
|
|
44615
|
+
}
|
|
44616
|
+
/**
|
|
44617
|
+
* Admit an output path before a remote operation while preserving the atomic
|
|
44618
|
+
* writer's second check at finalization time to close the race window.
|
|
44619
|
+
*/
|
|
44620
|
+
async function admitBinaryWriteDestination(filePath, overwrite) {
|
|
44621
|
+
const destination = await inspectDestination(filePath, overwrite);
|
|
44622
|
+
if (!destination.ok) return destination;
|
|
44623
|
+
try {
|
|
44624
|
+
const parent = dirname$1(filePath);
|
|
44625
|
+
const parentStats = await lstat(parent);
|
|
44626
|
+
if (!parentStats.isDirectory()) return err({ tag: "unsafe_destination" });
|
|
44627
|
+
await access(parent, constants.W_OK);
|
|
44628
|
+
return ok(undefined);
|
|
44629
|
+
} catch {
|
|
44630
|
+
return err({ tag: "write_failed" });
|
|
44631
|
+
}
|
|
44632
|
+
}
|
|
44633
|
+
async function syncDirectory(directory) {
|
|
44634
|
+
if (process.platform === "win32") return true;
|
|
44635
|
+
let handle;
|
|
44636
|
+
try {
|
|
44637
|
+
handle = await open(directory, "r");
|
|
44638
|
+
await handle.sync();
|
|
44639
|
+
return true;
|
|
44640
|
+
} catch {
|
|
44641
|
+
return false;
|
|
44642
|
+
} finally {
|
|
44643
|
+
await handle?.close().catch(() => undefined);
|
|
44644
|
+
}
|
|
44645
|
+
}
|
|
44646
|
+
function mapAtomicBinaryWriteFailure(error) {
|
|
44647
|
+
return isNodeError(error) && (error.code === "ELOOP" || error.code === "EISDIR" || error.code === "ENOTDIR") ? { tag: "unsafe_destination" } : { tag: "write_failed" };
|
|
44648
|
+
}
|
|
44649
|
+
async function writeTemporaryBinaryFile(temporaryPath, data, mode) {
|
|
44650
|
+
let handle;
|
|
44651
|
+
try {
|
|
44652
|
+
handle = await open(temporaryPath, "wx", mode);
|
|
44653
|
+
await handle.writeFile(data);
|
|
44654
|
+
await handle.sync();
|
|
44655
|
+
await handle.close();
|
|
44656
|
+
handle = undefined;
|
|
44657
|
+
await chmod(temporaryPath, mode);
|
|
44658
|
+
} finally {
|
|
44659
|
+
await handle?.close().catch(() => undefined);
|
|
44660
|
+
}
|
|
44661
|
+
}
|
|
44662
|
+
async function finalizeTemporaryBinaryFile(temporaryPath, filePath, overwrite) {
|
|
44663
|
+
if (overwrite === "allow") {
|
|
44664
|
+
await rename(temporaryPath, filePath);
|
|
44665
|
+
return ok([]);
|
|
44666
|
+
}
|
|
44667
|
+
try {
|
|
44668
|
+
await link(temporaryPath, filePath);
|
|
44669
|
+
} catch (error) {
|
|
44670
|
+
return isNodeError(error) && error.code === "EEXIST" ? err({ tag: "destination_exists" }) : err(mapAtomicBinaryWriteFailure(error));
|
|
44671
|
+
}
|
|
44672
|
+
try {
|
|
44673
|
+
await unlink$1(temporaryPath);
|
|
44674
|
+
return ok([]);
|
|
44675
|
+
} catch {
|
|
44676
|
+
return ok([{
|
|
44677
|
+
tag: "temporary_cleanup_failed",
|
|
44678
|
+
temporaryPath
|
|
44679
|
+
}]);
|
|
44680
|
+
}
|
|
44681
|
+
}
|
|
44682
|
+
function toAtomicBinaryWriteOutcome(warnings) {
|
|
44683
|
+
const [first, ...rest] = warnings;
|
|
44684
|
+
return first === undefined ? { tag: "written" } : {
|
|
44685
|
+
tag: "written_with_warnings",
|
|
44686
|
+
warnings: [first, ...rest]
|
|
44687
|
+
};
|
|
44688
|
+
}
|
|
44689
|
+
async function writeBinaryFileAtomic(filePath, data, options) {
|
|
44690
|
+
const parent = dirname$1(filePath);
|
|
44691
|
+
const mode = options.mode ?? 384;
|
|
44692
|
+
const suffix = options.randomSuffix ?? (() => `${process.pid}-${Date.now()}-${randomUUID()}`);
|
|
44693
|
+
let temporaryPath;
|
|
44694
|
+
try {
|
|
44695
|
+
if (options.ensureParent === true) await mkdir(parent, { recursive: true });
|
|
44696
|
+
const admitted = await inspectDestination(filePath, options.overwrite);
|
|
44697
|
+
if (!admitted.ok) return admitted;
|
|
44698
|
+
temporaryPath = join(parent, `.${basename(filePath)}.${suffix()}.tmp`);
|
|
44699
|
+
await writeTemporaryBinaryFile(temporaryPath, data, mode);
|
|
44700
|
+
const finalized = await finalizeTemporaryBinaryFile(temporaryPath, filePath, options.overwrite);
|
|
44701
|
+
if (!finalized.ok) return finalized;
|
|
44702
|
+
temporaryPath = undefined;
|
|
44703
|
+
const warnings = await syncDirectory(parent) ? finalized.value : [...finalized.value, { tag: "directory_sync_failed" }];
|
|
44704
|
+
return ok(toAtomicBinaryWriteOutcome(warnings));
|
|
44705
|
+
} catch (error) {
|
|
44706
|
+
return err(mapAtomicBinaryWriteFailure(error));
|
|
44707
|
+
} finally {
|
|
44708
|
+
if (temporaryPath) await unlink$1(temporaryPath).catch(() => undefined);
|
|
44709
|
+
}
|
|
44710
|
+
}
|
|
44711
|
+
|
|
43369
44712
|
//#endregion
|
|
43370
44713
|
//#region src/media/local-media.ts
|
|
43371
44714
|
const MEDIA_DIR_NAME = "media";
|
|
@@ -43387,10 +44730,10 @@ const listMediaDirFiles = async (mediaDir) => {
|
|
|
43387
44730
|
}
|
|
43388
44731
|
};
|
|
43389
44732
|
const findMatchingFilename = async (contentDir, identifier, manifest) => {
|
|
43390
|
-
const mediaDir = path.join(contentDir, MEDIA_DIR_NAME);
|
|
44733
|
+
const mediaDir = path$1.join(contentDir, MEDIA_DIR_NAME);
|
|
43391
44734
|
const manifestEntry = manifest.assets[identifier];
|
|
43392
44735
|
if (manifestEntry?.filename) {
|
|
43393
|
-
const candidatePath = path.join(mediaDir, manifestEntry.filename);
|
|
44736
|
+
const candidatePath = path$1.join(mediaDir, manifestEntry.filename);
|
|
43394
44737
|
if (await fileExists(candidatePath)) {
|
|
43395
44738
|
return manifestEntry.filename;
|
|
43396
44739
|
}
|
|
@@ -43398,7 +44741,7 @@ const findMatchingFilename = async (contentDir, identifier, manifest) => {
|
|
|
43398
44741
|
const files = await listMediaDirFiles(mediaDir);
|
|
43399
44742
|
const matches = files.filter((name) => {
|
|
43400
44743
|
if (name.startsWith(".")) return false;
|
|
43401
|
-
const parsed = path.parse(name);
|
|
44744
|
+
const parsed = path$1.parse(name);
|
|
43402
44745
|
return parsed.name === identifier;
|
|
43403
44746
|
});
|
|
43404
44747
|
if (matches.length > 1) {
|
|
@@ -43409,9 +44752,13 @@ const findMatchingFilename = async (contentDir, identifier, manifest) => {
|
|
|
43409
44752
|
async function loadLocalMediaFile(contentDir, identifier, manifest) {
|
|
43410
44753
|
const filename = await findMatchingFilename(contentDir, identifier, manifest);
|
|
43411
44754
|
if (!filename) return null;
|
|
43412
|
-
const mediaDir = path.join(contentDir, MEDIA_DIR_NAME);
|
|
43413
|
-
const filePath = path.join(mediaDir, filename);
|
|
43414
|
-
const
|
|
44755
|
+
const mediaDir = path$1.join(contentDir, MEDIA_DIR_NAME);
|
|
44756
|
+
const filePath = path$1.join(mediaDir, filename);
|
|
44757
|
+
const read = await readStableBinaryFile(filePath, MAX_MANAGEMENT_MEDIA_UPLOAD_BYTES);
|
|
44758
|
+
if (!read.ok) {
|
|
44759
|
+
throw new Error(`Failed to read media file ${filePath}: ${read.error.tag}`);
|
|
44760
|
+
}
|
|
44761
|
+
const data = read.value;
|
|
43415
44762
|
const checksum = sha256(data);
|
|
43416
44763
|
const mimeType = getMimeType(filename);
|
|
43417
44764
|
manifest.assets[identifier] = {
|
|
@@ -43430,15 +44777,31 @@ async function loadLocalMediaFile(contentDir, identifier, manifest) {
|
|
|
43430
44777
|
};
|
|
43431
44778
|
}
|
|
43432
44779
|
async function writeLocalMediaFile(contentDir, filename, data) {
|
|
43433
|
-
const mediaDir = path.join(contentDir, MEDIA_DIR_NAME);
|
|
44780
|
+
const mediaDir = path$1.join(contentDir, MEDIA_DIR_NAME);
|
|
43434
44781
|
await fs.mkdir(mediaDir, { recursive: true });
|
|
43435
|
-
const filePath = path.join(mediaDir, filename);
|
|
43436
|
-
await
|
|
43437
|
-
|
|
44782
|
+
const filePath = path$1.join(mediaDir, filename);
|
|
44783
|
+
const written = await writeBinaryFileAtomic(filePath, data, {
|
|
44784
|
+
overwrite: "allow",
|
|
44785
|
+
mode: 420
|
|
44786
|
+
});
|
|
44787
|
+
if (!written.ok) {
|
|
44788
|
+
throw new Error(`Failed to write media file ${filePath}: ${written.error.tag}`);
|
|
44789
|
+
}
|
|
44790
|
+
return written.value.tag === "written" ? {
|
|
44791
|
+
kind: "written",
|
|
44792
|
+
filePath
|
|
44793
|
+
} : {
|
|
44794
|
+
kind: "written_with_warnings",
|
|
44795
|
+
filePath,
|
|
44796
|
+
warnings: written.value.warnings
|
|
44797
|
+
};
|
|
43438
44798
|
}
|
|
43439
|
-
async function readLocalMediaChecksum(filePath) {
|
|
43440
|
-
const
|
|
43441
|
-
|
|
44799
|
+
async function readLocalMediaChecksum(filePath, maxBytes) {
|
|
44800
|
+
const read = await readStableBinaryFile(filePath, maxBytes);
|
|
44801
|
+
if (!read.ok) {
|
|
44802
|
+
throw new Error(`Failed to read media file ${filePath}: ${read.error.tag}`);
|
|
44803
|
+
}
|
|
44804
|
+
return sha256(read.value);
|
|
43442
44805
|
}
|
|
43443
44806
|
async function localMediaFileExists(filePath) {
|
|
43444
44807
|
return fileExists(filePath);
|
|
@@ -43503,7 +44866,7 @@ const parseFilenameFromUrl = (src) => {
|
|
|
43503
44866
|
}
|
|
43504
44867
|
};
|
|
43505
44868
|
const ensureFilenameExtension = (filename, mimeType) => {
|
|
43506
|
-
if (path.extname(filename)) return filename;
|
|
44869
|
+
if (path$1.extname(filename)) return filename;
|
|
43507
44870
|
const ext = getExtensionForMimeType(mimeType);
|
|
43508
44871
|
return ext ? `${filename}${ext}` : filename;
|
|
43509
44872
|
};
|
|
@@ -44680,7 +46043,7 @@ const removeStaleLocalFile = async (options) => {
|
|
|
44680
46043
|
if (options.dryRun || !options.previousFilename || options.previousFilename === options.filename) {
|
|
44681
46044
|
return;
|
|
44682
46045
|
}
|
|
44683
|
-
const previousPath = path.join(options.contentDir, "media", options.previousFilename);
|
|
46046
|
+
const previousPath = path$1.join(options.contentDir, "media", options.previousFilename);
|
|
44684
46047
|
if (await localMediaFileExists(previousPath)) {
|
|
44685
46048
|
await fs.unlink(previousPath).catch(() => {});
|
|
44686
46049
|
}
|
|
@@ -44711,7 +46074,8 @@ const applyDownloadDecision = async (decision, context) => {
|
|
|
44711
46074
|
context.warnings.push(`Failed to download media for "${decision.identifier}" from ${urlResult.value}.`);
|
|
44712
46075
|
return;
|
|
44713
46076
|
}
|
|
44714
|
-
await writeLocalMediaFile(context.contentDir, decision.filename, downloaded.data);
|
|
46077
|
+
const written = await writeLocalMediaFile(context.contentDir, decision.filename, downloaded.data);
|
|
46078
|
+
context.warnings.push(...localMediaWriteWarningMessages(decision.identifier, written));
|
|
44715
46079
|
context.manifest.assets[decision.identifier] = {
|
|
44716
46080
|
checksum: sha256(downloaded.data),
|
|
44717
46081
|
filename: decision.filename,
|
|
@@ -44728,7 +46092,7 @@ const syncPullMediaDownload = async (planned, indexes, context) => {
|
|
|
44728
46092
|
return;
|
|
44729
46093
|
}
|
|
44730
46094
|
const filename = buildLocalFilename(identifier, asset);
|
|
44731
|
-
const localFilePath = path.join(context.contentDir, "media", filename);
|
|
46095
|
+
const localFilePath = path$1.join(context.contentDir, "media", filename);
|
|
44732
46096
|
const existingEntry = context.manifest.assets[identifier];
|
|
44733
46097
|
await removeStaleLocalFile({
|
|
44734
46098
|
contentDir: context.contentDir,
|
|
@@ -44766,10 +46130,16 @@ const syncPullMediaDownloads = async (options) => {
|
|
|
44766
46130
|
}
|
|
44767
46131
|
if (!options.dryRun) options.summary.pendingDownloads = 0;
|
|
44768
46132
|
};
|
|
44769
|
-
const sha256FromFile =
|
|
44770
|
-
|
|
44771
|
-
|
|
44772
|
-
|
|
46133
|
+
const sha256FromFile = (filePath) => readLocalMediaChecksum(filePath, FILE_SIZE_LIMITS.rawVideoBeforeCompression);
|
|
46134
|
+
function localMediaWriteWarningMessages(identifier, outcome) {
|
|
46135
|
+
if (outcome.kind === "written") return [];
|
|
46136
|
+
return outcome.warnings.map((warning) => {
|
|
46137
|
+
switch (warning.tag) {
|
|
46138
|
+
case "temporary_cleanup_failed": return `Downloaded media for "${identifier}", but could not remove temporary file ${warning.temporaryPath}.`;
|
|
46139
|
+
case "directory_sync_failed": return `Downloaded media for "${identifier}", but could not confirm the destination directory was synced.`;
|
|
46140
|
+
}
|
|
46141
|
+
});
|
|
46142
|
+
}
|
|
44773
46143
|
|
|
44774
46144
|
//#endregion
|
|
44775
46145
|
//#region src/media/pull-workflow.ts
|
|
@@ -45189,4 +46559,4 @@ Examples:
|
|
|
45189
46559
|
}
|
|
45190
46560
|
|
|
45191
46561
|
//#endregion
|
|
45192
|
-
export {
|
|
46562
|
+
export { mapEventCommandContent as $, headingDecorationColorTokenValues as $n, generateEffectsCSS as $r, requireEnvironmentWriteAuthorization as $t, isExplicitRectCrop as A, customLinkSchema$1 as Ai, boxBackgroundSchema as An, normalizeCardRole as Ar, createCommandContext as At, resolveVideoSelectionIntentForMediaField as B, sdkConfiguredBookingVerticalConfigSchema as Bi, MQ_MD_DOWN as Bn, resolveContextualButtonRole as Br, parseJsonArray as Bt, attachRenderableMediaAssetFields as C, parseBlockColorToken as Ci, parseBackgroundAppearanceColorToken as Cn, ICON_SIZE_PX as Cr, createPageRef as Ct, looksLikeUuid as D, persistableMediaSchema as Di, sdkThemeWireSchema as Dn, generateShadowValue as Dr, slugPart as Dt, deleteEnvSpecificKeys as E, mediaTypeDiscriminants as Ei, sdkSettingsUpdateSchema as En, generateCardCss as Er, refSlug as Et, getImageUrl as F, internalRouteOnlyLinkSchema as Fi, BREAKPOINT_LG as Fn, resolvedButtonColourDeclarations as Fr, formatDateShort as Ft, classifyKnownMimeType as G, pageDesignEditorBackgroundOverrideOwner as Gn, backgroundColorStyle as Gr, withCommandContext as Gt, asRecord as H, MQ_SM_UP as Hn, resolveOnColorForThemeToken as Hr, parsePaginationOptions as Ht, isSiteScopedDirectImageStoragePath as I, isInternalResolvedLinkValue as Ii, BREAKPOINT_MD as In, resolvedButtonFocusDeclarations as Ir, getOutputContext as It, venueManagementFields as J, defineFrameCapability as Jn, mergeStyles as Jr, withOutputOnlyErrorHandling as Jt, managementMediaLibraryUploadLimitForMimeType as K, assertNever$5 as Kn, borderColorStyle as Kr, withConfirmation as Kt, buildMediaIdentifierFromFilename as L, isResolvedInternalLinkEntityType as Li, BREAKPOINT_SM as Ln, contextualButtonSurfaceResetStyles as Lr, getOutputOnlyContext as Lt, resolveMediaTransformForAspect as M, externalLinkSchema$1 as Mi, generateMobileOverlayBackdropFilterFix as Mn, AFFORDANCE_SUPPRESS_CLASS as Mr, createListCommand$1 as Mt, getDirectImageUrlForRelativePath as N, internalLinkSchema as Ni, getHeaderPaintTargets as Nn, generateButtonCss as Nr, createOutputForCommand as Nt, rotateNormalizedPoint as O, linkPayloadSchema as Oi, themeSchema as On, cardRoleSchema as Or, confirmAction as Ot, getDirectImageUrlForSiteScopedPath as P, internalResolvedLinkSchema as Pi, renderHeaderPaintTargetRules as Pn, RESOLVED_BUTTON_DISABLED_DECLARATIONS as Pr, currentInvocationEnvelope as Pt, parseAndPlanCommandBatchForTypes as Q, featureCardCompositionTreatmentValues as Qn, generateDefaultButtonSystem as Qr, planEnvironmentWriteAuthorization as Qt, parseMediaIdentifier as R, linkSchema as Ri, BREAKPOINT_XL as Rn, contextualButtonSurfaceStyles as Rr, handleCommandError as Rt, isMediaValue as S, unsafeAsOpaqueButtonColourToken as Si, backgroundAppearanceQuickColorTokenNames as Sn, ICON_CONFIG as Sr, createNavigationRef as St, walkAndCollectImageAttrs as T, mediaSchema as Ti, defineBlockDesignMetadata as Tn, getDefaultCardVariants as Tr, createVenueRef as Tt, parseRelativeStoragePath as U, MQ_TABLET_ONLY as Un, resolveThemeColorReferenceValue as Ur, resolveCommandPath as Ut, getMimeType as V, __toESM as Vi, MQ_SM_DOWN as Vn, isCssColorLiteral as Vr, parseJsonData as Vt, FILE_SIZE_LIMITS as W, headerVariantValues as Wn, resolveTokenOrCssColorValue as Wr, runWithInvocation as Wt, stableStringify as X, eventListTreatmentValues as Xn, textColorStyle as Xr, parseEnvironmentSelection as Xt, sortRecord as Y, defineNoBlockDesignCapability as Yn, rgbColorValue as Yr, executeEnvironmentPlan as Yt, createDryRunReport as Z, eyebrowTreatmentValues as Zn, resolveSurfaceForeground as Zr, planEnvironmentExecution as Zt, extractMediaIdentifiers as _, ORDINARY_TEXT_MIN_CONTRAST_RATIO as _i, GRADIENT_PRESET_IDS as _n, LABEL_FONT_SIZE_MAP as _r, createContentEntryRef as _t, syncLocalMediaForPull as a, parseThemeForPersistence as ai, getEnvironmentDebugInfo as an, getComponentBorderWidthPx as ar, entryIdentifierForCommand as at, hasOwnSettingsField as b, VARIANT_ROLES as bi, boundaryRuntimeMetricsForTransition as bn, ACCORDION_TRIGGER_FONT_SCALE as br, createFooterRef as bt, admitBinaryWriteDestination as c, generateShadowValue$1 as ci, loadSdkEnvironment as cn, parseCustomCssRules as cr, pagePathForCommand as ct, sha256 as d, surfaceForegroundStyle as di, ManagementApiError as dn, NAV_LINK_PADDING_MAP as dr, require_react as dt, generateBaseInteractiveCSS as ei, withRiverbankEnv as en, introCompositionTreatmentValues as er, evaluateSiteCommandBatchExposure as et, normalizeMediaIdentifierInput as f, surfaceForegroundValue as fi, retargetCompiledBoundaryRuntimeForContentAdjacency as fn, NAV_LINK_PADDING_X_MAP as fr, pickShadeTowardBackground as ft, downloadMedia as g, getTextColorForBackground as gi, gradientToCss as gn, INPUT_SPACING_MAP as gr, createCommandRef as gt, buildStorageUrl as h, onColorTokenName as hi, getCuratedSiteStyle as hn, INPUT_FONT_SIZE_MAP as hr, createBlocksRef as ht, syncLocalMediaForPush as i, themeButtonClassName as ii, getEnvVarName as in, visibleHeadingDecorationFamilyValues as ir, blockOrderIdentifiersForCommand as it, parseAspectRatioValue as j, entryLinkSchema$1 as ji, generateHeaderScrollTreatmentStyles as jn, PUBLIC_BUTTON_ROLES as jr, createGetCommand as jt, normalizeWidthCandidates as k, RESOLVED_INTERNAL_LINK_ENTITY_TYPES as ki, boxBackgroundOverlaySchema as kn, cardVariantRoleValues as kr, createClientForEnv as kt, readStableBinaryFile as l, DEFAULT_SURFACE_TEXT_COLOR as li, createManagementClient as ln, NAV_LINK_BORDER_RADIUS_MAP as lr, hasConfiguredBookingVertical as lt, walkLocalContentMedia as m, isOnColorBackgroundCandidateToken as mi, readHeaderNavFontSizePreference as mn, FONT_WEIGHT_MAP as mr, createBlockRef as mt, createStyleCommand as n, TokenResolver as ni, CliExit as nn, testimonialsCompositionTreatmentValues as nr, getSiteCommandMetadata as nt, applyLocalMediaPush as o, compileButtonColourStates as oi, isLocalConfigured as on, getComponentShadowVar as or, pageIdentifierForBlockCommand as ot, normalizeMediaValueToIdentifierOnly as p, expandPalette as pi, mergeHeaderNavTypographyPreference as pn, NAV_LINK_TEXT_SIZE_MAP as pr, generateCssVars as pt, parseSafeDownloadFilename as q, defineBlockDesignCapability as qn, cssVarValue as qr, withErrorHandling as qt, revalidateLocalMediaPushPlan as r, parseThemeButtonVariantId as ri, getEnvPrefix as rn, tileTreatmentValues as rr, blockIdentifierForCommand as rt, prepareLocalMediaForPush as s, formatButtonColourContractError as si, isRemoteConfigured as sn, parseBlockCustomCssRules as sr, pageIdentifierForCommand as st, buildStyleSelectionInitializeCommandFromOptions as t, generateInteractiveChildInheritanceCSS as ti, formatError as tn, offeringsPreviewCompositionTreatmentValues as tr, evaluateSiteCommandExposure as tt, writeBinaryFileAtomic as u, surfaceForegroundDeclarations as ui, formatIgnoredDocumentSyncSectionWarning as un, NAV_LINK_LETTER_SPACING_MAP as ur, require_jsx_runtime as ut, SETTINGS_FILE_FIELDS as v, featuresFromHex as vi, getGradientPreset as vn, ACCORDION_OPEN_ACCENT_THICKNESS as vr, createEventCategoryRef as vt, buildRenderableMediaAssetIndex as w, mediaIdentifierSchema as wi, defaultThemeTokens as wn, generateCardCoreCss as wr, createSiteCommandSiteRef as wt, writeMediaManifest as x, buttonVariantIdSchema as xi, backgroundAppearanceColorTokenNames as xn, ACCORDION_TRIGGER_LETTER_SPACING as xr, createFormRef as xt, buildSettingsUpdateInput as y, hexToRgb$1 as yi, parseGradientPresetId as yn, ACCORDION_SPACING_MAP as yr, createEventSeriesRef as yt, deriveVideoSlotPolicy as z, pageLinkSchema$1 as zi, MQ_LG_UP as zn, deriveButtonSurfaceContext as zr, mapEntryForOutput as zt };
|