@odla-ai/brand 0.5.0 → 0.7.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/dist/index.d.cts CHANGED
@@ -627,6 +627,33 @@ declare function assertAssetContentType(value: unknown, kind?: string): string;
627
627
  */
628
628
  declare function assertAnalysis(value: unknown): AssetAnalysis;
629
629
 
630
+ /**
631
+ * A stored row in the shape it was WRITTEN, ready to verify.
632
+ *
633
+ * The engine stores a `date` as the epoch ms it was given and hands a reader
634
+ * ISO-8601 text back. Brand digests rows that CONTAIN dates — a proposal's
635
+ * `reviewDigest` covers its `createdAt`, and an approval receipt's
636
+ * `receiptDigest` covers its own — so a row read straight out of the store can
637
+ * never match the digest stored beside it. Two production symptoms, both 409:
638
+ * resolving any proposal ("proposal changed since review") and reading a book's
639
+ * tokens after approval.
640
+ *
641
+ * Normalising at the READ is the fix, not tolerating both shapes in the
642
+ * verifier. Tolerance would repair a `safeTime` check and leave the digest
643
+ * failing, because the digest was taken over the number and nothing can change
644
+ * that after the fact.
645
+ *
646
+ * Anything that is not date-shaped text passes through untouched, so a row that
647
+ * never round-tripped verifies exactly as before. `json` attrs are left alone:
648
+ * their contents round-trip as written, which is why an `authorityConsumption`
649
+ * and the `createdAt` beside it disagreed at all.
650
+ */
651
+ declare function rowAsWritten<T>(ns: string, row: T): T;
652
+ /** A stored approval receipt in the shape it was written. */
653
+ declare function receiptAsWritten<T>(row: T): T;
654
+ /** A stored proposal in the shape it was written, so `reviewDigest` matches. */
655
+ declare function proposalAsWritten<T>(row: T): T;
656
+
630
657
  /** Stay materially below the server's whole-guards parser budget. */
631
658
  declare function isBoundedBrandJson(root: unknown, limits?: {
632
659
  maxDepth?: number;
@@ -1874,4 +1901,4 @@ interface CreateBrandIntegrationOptions {
1874
1901
  */
1875
1902
  declare function createBrandIntegration(options?: CreateBrandIntegrationOptions): BrandIntegrationDescriptor;
1876
1903
 
1877
- export { ASSET_CONTENT_TYPES, type AcceptProposalInput, AssetAnalysis, AssetKind, type AttrType, type AudienceChildren, BRAND_AGENT_PROFILE, BRAND_DISCUSSION_REFERENCE_KINDS, BRAND_INSTRUCTIONS, BRAND_RULES, BRAND_SCHEMA, BookStatus, type BrandActor, type BrandAgentBridge, type BrandAgentProfile, type BrandApprovalReceipt, type BrandAsset, type BrandAttrs, type BrandBook, type BrandBotTriggerOpts, type BrandCapabilityAuthority, BrandConflictError, type BrandDb, type BrandDecisionBinding, type BrandDeps, type BrandDiscussionReference, type BrandDiscussionReferenceKind, type BrandDiscussionReferenceTarget, type BrandDiscussionSwatch, type BrandDispatchBody, type BrandDispatchDeps, type BrandEntityRef, type BrandFetchedBytes, type BrandFileRecord, BrandForbiddenError, BrandGoneError, type BrandHumanAuthorityConsumption, BrandInputError, type BrandIntegrationDescriptor, type BrandIntegrationProbe, type BrandLookup, BrandNotFoundError, type BrandOp, type BrandPalette, type BrandPrincipalProjection, type BrandProposal, type BrandProposalProvenance, type BrandProposalReviewSnapshot, type BrandResult, BrandReviewStateChangedError, type BrandRouteCtx, type BrandRouteOpts, type BrandRow, type BrandRule, type BrandRules, type BrandScalar, type BrandSection, type BrandSkillOpts, type BrandSkillSelf, type BrandSourceAssetSnapshot, type BrandStorage, BrandTokensSnapshot, type BrandToolCtx, type BrandTransactGuard, type BrandTransactOptions, type BrandTrigger, type BrandUploadBody, type BrandVisionCapabilities, type BrandVisionSpec, CHART_DELTA_MIN, CHAT_MESSAGE_NS, CSS_NAMED_COLORS, type ContrastKind, type CreateAssetInput, type CreateBookInput, type CreateBrandIntegrationOptions, type CreateBrandPersonaOpts, DEFAULT_BRAND_SYSTEM, DESIGN_CONTENT_TYPES, type DeriveChartOptions, type DerivePaletteOptions, type DesignAssetGroup, type DesignBundle, type DesignBundleAsset, type DesignColorUse, type DesignDecompilation, type DesignDigest, type DesignManifestEntry, type DesignOutlineEntry, type DesignProp, type DesignTokenSets, type DesignTokenSkip, type Hsl, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type LightnessDirection, MAX_COLORS, MAX_EXTERNALS, MAX_FONTS, MAX_HEADING_CHARS, MAX_OUTLINE_ENTRIES, MAX_PROPS, MAX_SWATCHES, MAX_THUMBNAIL_CHARS, MAX_VIEW_BYTES, type NamedColor, type NearestNamedColor, type Oklab, type Oklch, PICK_TEXT_DEFAULT_CANDIDATES, PaletteSource, PaletteStatus, ProposalKind, ProposalStatus, type ProposePaletteInput, type ProposeSectionInput, RAMP_L_MAX, RAMP_L_MIN, type RejectProposalInput, type ResolvedBrandDeps, type Rgb, type ScannedDeclaration, SectionKind, SectionStatus, type SerializedAttr, type SerializedEntity, type SerializedLink, type SerializedLinkEnd, type SerializedSchema, Swatch, SwatchRole, type UpsertSectionInput, acceptProposalOps, adjustLightnessUntil, analogous, assertAnalysis, assertAssetContentType, assertHex, assertSectionContent, assertSwatches, assetTools, attachedBrandAssetIds, audienceFanoutOps, base64ByteLength, base64FromBytes, beginAssetDeleteOps, bookForChannel, bookTools, brandAuthorityRef, brandBotTrigger, brandDiscussionReferenceHref, brandDiscussionReferenceId, brandInputFor, brandIntegration, brandJsonDigest, brandRules, brandSkill, canonicalBrandJson, capString, capStringArray, clamp01, clampToGamut, collapseWhitespace, complementary, contrastRatio, contrastReport, createAssetOps, createBookOps, createBrandIntegration, createBrandPersona, createBrandRoutes, cssColorToHex, decodeEntities, deltaEOK, deltaEOKLab, deriveChartColors, derivePalette, digestDesignBundle, digestDesignHtml, dispatchBrandTurn, extractColors, extractDesignTokens, extractFonts, extractOutline, extractProps, extractThumbnailSvg, extractTitle, finishAssetDeleteOps, formatBrandDiscussionReference, groupAssets, hexToOklch, hslToRgb, htmlToText, inSrgbGamut, isBoundedBrandJson, isBrandHumanAuthorityConsumption, isDesignBundle, linearToSrgb, meetsAA, meetsAAA, monochrome, nearestNamedColor, normalizeHex, oklabToOklch, oklabToRgb, oklchToHex, oklchToOklab, paletteTools, parseBrandDiscussionReference, parseBrandDispatch, parseDesignBundle, parseHex, pickTextOn, proposalReviewSnapshot, proposePaletteOps, proposeSectionOps, readDesignManifest, readTools, recordAnalysisOps, rejectProposalOps, relativeLuminance, resolveDeps, resolveVarRefs, rgbToHsl, rgbToOklab, rotateHue, safeFileName, scanCustomProperties, sectionKey, splitComplementary, srgbToLinear, stripCssComments, styleSheetText, supportsBrandVision, swatchesFromDesignTokens, tetradic, tintShadeRamp, toHex, triadic, updateBookOps, upsertSectionOps, verifyBrandApprovalReceipt };
1904
+ export { ASSET_CONTENT_TYPES, type AcceptProposalInput, AssetAnalysis, AssetKind, type AttrType, type AudienceChildren, BRAND_AGENT_PROFILE, BRAND_DISCUSSION_REFERENCE_KINDS, BRAND_INSTRUCTIONS, BRAND_RULES, BRAND_SCHEMA, BookStatus, type BrandActor, type BrandAgentBridge, type BrandAgentProfile, type BrandApprovalReceipt, type BrandAsset, type BrandAttrs, type BrandBook, type BrandBotTriggerOpts, type BrandCapabilityAuthority, BrandConflictError, type BrandDb, type BrandDecisionBinding, type BrandDeps, type BrandDiscussionReference, type BrandDiscussionReferenceKind, type BrandDiscussionReferenceTarget, type BrandDiscussionSwatch, type BrandDispatchBody, type BrandDispatchDeps, type BrandEntityRef, type BrandFetchedBytes, type BrandFileRecord, BrandForbiddenError, BrandGoneError, type BrandHumanAuthorityConsumption, BrandInputError, type BrandIntegrationDescriptor, type BrandIntegrationProbe, type BrandLookup, BrandNotFoundError, type BrandOp, type BrandPalette, type BrandPrincipalProjection, type BrandProposal, type BrandProposalProvenance, type BrandProposalReviewSnapshot, type BrandResult, BrandReviewStateChangedError, type BrandRouteCtx, type BrandRouteOpts, type BrandRow, type BrandRule, type BrandRules, type BrandScalar, type BrandSection, type BrandSkillOpts, type BrandSkillSelf, type BrandSourceAssetSnapshot, type BrandStorage, BrandTokensSnapshot, type BrandToolCtx, type BrandTransactGuard, type BrandTransactOptions, type BrandTrigger, type BrandUploadBody, type BrandVisionCapabilities, type BrandVisionSpec, CHART_DELTA_MIN, CHAT_MESSAGE_NS, CSS_NAMED_COLORS, type ContrastKind, type CreateAssetInput, type CreateBookInput, type CreateBrandIntegrationOptions, type CreateBrandPersonaOpts, DEFAULT_BRAND_SYSTEM, DESIGN_CONTENT_TYPES, type DeriveChartOptions, type DerivePaletteOptions, type DesignAssetGroup, type DesignBundle, type DesignBundleAsset, type DesignColorUse, type DesignDecompilation, type DesignDigest, type DesignManifestEntry, type DesignOutlineEntry, type DesignProp, type DesignTokenSets, type DesignTokenSkip, type Hsl, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type LightnessDirection, MAX_COLORS, MAX_EXTERNALS, MAX_FONTS, MAX_HEADING_CHARS, MAX_OUTLINE_ENTRIES, MAX_PROPS, MAX_SWATCHES, MAX_THUMBNAIL_CHARS, MAX_VIEW_BYTES, type NamedColor, type NearestNamedColor, type Oklab, type Oklch, PICK_TEXT_DEFAULT_CANDIDATES, PaletteSource, PaletteStatus, ProposalKind, ProposalStatus, type ProposePaletteInput, type ProposeSectionInput, RAMP_L_MAX, RAMP_L_MIN, type RejectProposalInput, type ResolvedBrandDeps, type Rgb, type ScannedDeclaration, SectionKind, SectionStatus, type SerializedAttr, type SerializedEntity, type SerializedLink, type SerializedLinkEnd, type SerializedSchema, Swatch, SwatchRole, type UpsertSectionInput, acceptProposalOps, adjustLightnessUntil, analogous, assertAnalysis, assertAssetContentType, assertHex, assertSectionContent, assertSwatches, assetTools, attachedBrandAssetIds, audienceFanoutOps, base64ByteLength, base64FromBytes, beginAssetDeleteOps, bookForChannel, bookTools, brandAuthorityRef, brandBotTrigger, brandDiscussionReferenceHref, brandDiscussionReferenceId, brandInputFor, brandIntegration, brandJsonDigest, brandRules, brandSkill, canonicalBrandJson, capString, capStringArray, clamp01, clampToGamut, collapseWhitespace, complementary, contrastRatio, contrastReport, createAssetOps, createBookOps, createBrandIntegration, createBrandPersona, createBrandRoutes, cssColorToHex, decodeEntities, deltaEOK, deltaEOKLab, deriveChartColors, derivePalette, digestDesignBundle, digestDesignHtml, dispatchBrandTurn, extractColors, extractDesignTokens, extractFonts, extractOutline, extractProps, extractThumbnailSvg, extractTitle, finishAssetDeleteOps, formatBrandDiscussionReference, groupAssets, hexToOklch, hslToRgb, htmlToText, inSrgbGamut, isBoundedBrandJson, isBrandHumanAuthorityConsumption, isDesignBundle, linearToSrgb, meetsAA, meetsAAA, monochrome, nearestNamedColor, normalizeHex, oklabToOklch, oklabToRgb, oklchToHex, oklchToOklab, paletteTools, parseBrandDiscussionReference, parseBrandDispatch, parseDesignBundle, parseHex, pickTextOn, proposalAsWritten, proposalReviewSnapshot, proposePaletteOps, proposeSectionOps, readDesignManifest, readTools, receiptAsWritten, recordAnalysisOps, rejectProposalOps, relativeLuminance, resolveDeps, resolveVarRefs, rgbToHsl, rgbToOklab, rotateHue, rowAsWritten, safeFileName, scanCustomProperties, sectionKey, splitComplementary, srgbToLinear, stripCssComments, styleSheetText, supportsBrandVision, swatchesFromDesignTokens, tetradic, tintShadeRamp, toHex, triadic, updateBookOps, upsertSectionOps, verifyBrandApprovalReceipt };
package/dist/index.d.ts CHANGED
@@ -627,6 +627,33 @@ declare function assertAssetContentType(value: unknown, kind?: string): string;
627
627
  */
628
628
  declare function assertAnalysis(value: unknown): AssetAnalysis;
629
629
 
630
+ /**
631
+ * A stored row in the shape it was WRITTEN, ready to verify.
632
+ *
633
+ * The engine stores a `date` as the epoch ms it was given and hands a reader
634
+ * ISO-8601 text back. Brand digests rows that CONTAIN dates — a proposal's
635
+ * `reviewDigest` covers its `createdAt`, and an approval receipt's
636
+ * `receiptDigest` covers its own — so a row read straight out of the store can
637
+ * never match the digest stored beside it. Two production symptoms, both 409:
638
+ * resolving any proposal ("proposal changed since review") and reading a book's
639
+ * tokens after approval.
640
+ *
641
+ * Normalising at the READ is the fix, not tolerating both shapes in the
642
+ * verifier. Tolerance would repair a `safeTime` check and leave the digest
643
+ * failing, because the digest was taken over the number and nothing can change
644
+ * that after the fact.
645
+ *
646
+ * Anything that is not date-shaped text passes through untouched, so a row that
647
+ * never round-tripped verifies exactly as before. `json` attrs are left alone:
648
+ * their contents round-trip as written, which is why an `authorityConsumption`
649
+ * and the `createdAt` beside it disagreed at all.
650
+ */
651
+ declare function rowAsWritten<T>(ns: string, row: T): T;
652
+ /** A stored approval receipt in the shape it was written. */
653
+ declare function receiptAsWritten<T>(row: T): T;
654
+ /** A stored proposal in the shape it was written, so `reviewDigest` matches. */
655
+ declare function proposalAsWritten<T>(row: T): T;
656
+
630
657
  /** Stay materially below the server's whole-guards parser budget. */
631
658
  declare function isBoundedBrandJson(root: unknown, limits?: {
632
659
  maxDepth?: number;
@@ -1874,4 +1901,4 @@ interface CreateBrandIntegrationOptions {
1874
1901
  */
1875
1902
  declare function createBrandIntegration(options?: CreateBrandIntegrationOptions): BrandIntegrationDescriptor;
1876
1903
 
1877
- export { ASSET_CONTENT_TYPES, type AcceptProposalInput, AssetAnalysis, AssetKind, type AttrType, type AudienceChildren, BRAND_AGENT_PROFILE, BRAND_DISCUSSION_REFERENCE_KINDS, BRAND_INSTRUCTIONS, BRAND_RULES, BRAND_SCHEMA, BookStatus, type BrandActor, type BrandAgentBridge, type BrandAgentProfile, type BrandApprovalReceipt, type BrandAsset, type BrandAttrs, type BrandBook, type BrandBotTriggerOpts, type BrandCapabilityAuthority, BrandConflictError, type BrandDb, type BrandDecisionBinding, type BrandDeps, type BrandDiscussionReference, type BrandDiscussionReferenceKind, type BrandDiscussionReferenceTarget, type BrandDiscussionSwatch, type BrandDispatchBody, type BrandDispatchDeps, type BrandEntityRef, type BrandFetchedBytes, type BrandFileRecord, BrandForbiddenError, BrandGoneError, type BrandHumanAuthorityConsumption, BrandInputError, type BrandIntegrationDescriptor, type BrandIntegrationProbe, type BrandLookup, BrandNotFoundError, type BrandOp, type BrandPalette, type BrandPrincipalProjection, type BrandProposal, type BrandProposalProvenance, type BrandProposalReviewSnapshot, type BrandResult, BrandReviewStateChangedError, type BrandRouteCtx, type BrandRouteOpts, type BrandRow, type BrandRule, type BrandRules, type BrandScalar, type BrandSection, type BrandSkillOpts, type BrandSkillSelf, type BrandSourceAssetSnapshot, type BrandStorage, BrandTokensSnapshot, type BrandToolCtx, type BrandTransactGuard, type BrandTransactOptions, type BrandTrigger, type BrandUploadBody, type BrandVisionCapabilities, type BrandVisionSpec, CHART_DELTA_MIN, CHAT_MESSAGE_NS, CSS_NAMED_COLORS, type ContrastKind, type CreateAssetInput, type CreateBookInput, type CreateBrandIntegrationOptions, type CreateBrandPersonaOpts, DEFAULT_BRAND_SYSTEM, DESIGN_CONTENT_TYPES, type DeriveChartOptions, type DerivePaletteOptions, type DesignAssetGroup, type DesignBundle, type DesignBundleAsset, type DesignColorUse, type DesignDecompilation, type DesignDigest, type DesignManifestEntry, type DesignOutlineEntry, type DesignProp, type DesignTokenSets, type DesignTokenSkip, type Hsl, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type LightnessDirection, MAX_COLORS, MAX_EXTERNALS, MAX_FONTS, MAX_HEADING_CHARS, MAX_OUTLINE_ENTRIES, MAX_PROPS, MAX_SWATCHES, MAX_THUMBNAIL_CHARS, MAX_VIEW_BYTES, type NamedColor, type NearestNamedColor, type Oklab, type Oklch, PICK_TEXT_DEFAULT_CANDIDATES, PaletteSource, PaletteStatus, ProposalKind, ProposalStatus, type ProposePaletteInput, type ProposeSectionInput, RAMP_L_MAX, RAMP_L_MIN, type RejectProposalInput, type ResolvedBrandDeps, type Rgb, type ScannedDeclaration, SectionKind, SectionStatus, type SerializedAttr, type SerializedEntity, type SerializedLink, type SerializedLinkEnd, type SerializedSchema, Swatch, SwatchRole, type UpsertSectionInput, acceptProposalOps, adjustLightnessUntil, analogous, assertAnalysis, assertAssetContentType, assertHex, assertSectionContent, assertSwatches, assetTools, attachedBrandAssetIds, audienceFanoutOps, base64ByteLength, base64FromBytes, beginAssetDeleteOps, bookForChannel, bookTools, brandAuthorityRef, brandBotTrigger, brandDiscussionReferenceHref, brandDiscussionReferenceId, brandInputFor, brandIntegration, brandJsonDigest, brandRules, brandSkill, canonicalBrandJson, capString, capStringArray, clamp01, clampToGamut, collapseWhitespace, complementary, contrastRatio, contrastReport, createAssetOps, createBookOps, createBrandIntegration, createBrandPersona, createBrandRoutes, cssColorToHex, decodeEntities, deltaEOK, deltaEOKLab, deriveChartColors, derivePalette, digestDesignBundle, digestDesignHtml, dispatchBrandTurn, extractColors, extractDesignTokens, extractFonts, extractOutline, extractProps, extractThumbnailSvg, extractTitle, finishAssetDeleteOps, formatBrandDiscussionReference, groupAssets, hexToOklch, hslToRgb, htmlToText, inSrgbGamut, isBoundedBrandJson, isBrandHumanAuthorityConsumption, isDesignBundle, linearToSrgb, meetsAA, meetsAAA, monochrome, nearestNamedColor, normalizeHex, oklabToOklch, oklabToRgb, oklchToHex, oklchToOklab, paletteTools, parseBrandDiscussionReference, parseBrandDispatch, parseDesignBundle, parseHex, pickTextOn, proposalReviewSnapshot, proposePaletteOps, proposeSectionOps, readDesignManifest, readTools, recordAnalysisOps, rejectProposalOps, relativeLuminance, resolveDeps, resolveVarRefs, rgbToHsl, rgbToOklab, rotateHue, safeFileName, scanCustomProperties, sectionKey, splitComplementary, srgbToLinear, stripCssComments, styleSheetText, supportsBrandVision, swatchesFromDesignTokens, tetradic, tintShadeRamp, toHex, triadic, updateBookOps, upsertSectionOps, verifyBrandApprovalReceipt };
1904
+ export { ASSET_CONTENT_TYPES, type AcceptProposalInput, AssetAnalysis, AssetKind, type AttrType, type AudienceChildren, BRAND_AGENT_PROFILE, BRAND_DISCUSSION_REFERENCE_KINDS, BRAND_INSTRUCTIONS, BRAND_RULES, BRAND_SCHEMA, BookStatus, type BrandActor, type BrandAgentBridge, type BrandAgentProfile, type BrandApprovalReceipt, type BrandAsset, type BrandAttrs, type BrandBook, type BrandBotTriggerOpts, type BrandCapabilityAuthority, BrandConflictError, type BrandDb, type BrandDecisionBinding, type BrandDeps, type BrandDiscussionReference, type BrandDiscussionReferenceKind, type BrandDiscussionReferenceTarget, type BrandDiscussionSwatch, type BrandDispatchBody, type BrandDispatchDeps, type BrandEntityRef, type BrandFetchedBytes, type BrandFileRecord, BrandForbiddenError, BrandGoneError, type BrandHumanAuthorityConsumption, BrandInputError, type BrandIntegrationDescriptor, type BrandIntegrationProbe, type BrandLookup, BrandNotFoundError, type BrandOp, type BrandPalette, type BrandPrincipalProjection, type BrandProposal, type BrandProposalProvenance, type BrandProposalReviewSnapshot, type BrandResult, BrandReviewStateChangedError, type BrandRouteCtx, type BrandRouteOpts, type BrandRow, type BrandRule, type BrandRules, type BrandScalar, type BrandSection, type BrandSkillOpts, type BrandSkillSelf, type BrandSourceAssetSnapshot, type BrandStorage, BrandTokensSnapshot, type BrandToolCtx, type BrandTransactGuard, type BrandTransactOptions, type BrandTrigger, type BrandUploadBody, type BrandVisionCapabilities, type BrandVisionSpec, CHART_DELTA_MIN, CHAT_MESSAGE_NS, CSS_NAMED_COLORS, type ContrastKind, type CreateAssetInput, type CreateBookInput, type CreateBrandIntegrationOptions, type CreateBrandPersonaOpts, DEFAULT_BRAND_SYSTEM, DESIGN_CONTENT_TYPES, type DeriveChartOptions, type DerivePaletteOptions, type DesignAssetGroup, type DesignBundle, type DesignBundleAsset, type DesignColorUse, type DesignDecompilation, type DesignDigest, type DesignManifestEntry, type DesignOutlineEntry, type DesignProp, type DesignTokenSets, type DesignTokenSkip, type Hsl, type IntegrationProvision, type IntegrationSecret, type IntegrationSetting, type LightnessDirection, MAX_COLORS, MAX_EXTERNALS, MAX_FONTS, MAX_HEADING_CHARS, MAX_OUTLINE_ENTRIES, MAX_PROPS, MAX_SWATCHES, MAX_THUMBNAIL_CHARS, MAX_VIEW_BYTES, type NamedColor, type NearestNamedColor, type Oklab, type Oklch, PICK_TEXT_DEFAULT_CANDIDATES, PaletteSource, PaletteStatus, ProposalKind, ProposalStatus, type ProposePaletteInput, type ProposeSectionInput, RAMP_L_MAX, RAMP_L_MIN, type RejectProposalInput, type ResolvedBrandDeps, type Rgb, type ScannedDeclaration, SectionKind, SectionStatus, type SerializedAttr, type SerializedEntity, type SerializedLink, type SerializedLinkEnd, type SerializedSchema, Swatch, SwatchRole, type UpsertSectionInput, acceptProposalOps, adjustLightnessUntil, analogous, assertAnalysis, assertAssetContentType, assertHex, assertSectionContent, assertSwatches, assetTools, attachedBrandAssetIds, audienceFanoutOps, base64ByteLength, base64FromBytes, beginAssetDeleteOps, bookForChannel, bookTools, brandAuthorityRef, brandBotTrigger, brandDiscussionReferenceHref, brandDiscussionReferenceId, brandInputFor, brandIntegration, brandJsonDigest, brandRules, brandSkill, canonicalBrandJson, capString, capStringArray, clamp01, clampToGamut, collapseWhitespace, complementary, contrastRatio, contrastReport, createAssetOps, createBookOps, createBrandIntegration, createBrandPersona, createBrandRoutes, cssColorToHex, decodeEntities, deltaEOK, deltaEOKLab, deriveChartColors, derivePalette, digestDesignBundle, digestDesignHtml, dispatchBrandTurn, extractColors, extractDesignTokens, extractFonts, extractOutline, extractProps, extractThumbnailSvg, extractTitle, finishAssetDeleteOps, formatBrandDiscussionReference, groupAssets, hexToOklch, hslToRgb, htmlToText, inSrgbGamut, isBoundedBrandJson, isBrandHumanAuthorityConsumption, isDesignBundle, linearToSrgb, meetsAA, meetsAAA, monochrome, nearestNamedColor, normalizeHex, oklabToOklch, oklabToRgb, oklchToHex, oklchToOklab, paletteTools, parseBrandDiscussionReference, parseBrandDispatch, parseDesignBundle, parseHex, pickTextOn, proposalAsWritten, proposalReviewSnapshot, proposePaletteOps, proposeSectionOps, readDesignManifest, readTools, receiptAsWritten, recordAnalysisOps, rejectProposalOps, relativeLuminance, resolveDeps, resolveVarRefs, rgbToHsl, rgbToOklab, rotateHue, rowAsWritten, safeFileName, scanCustomProperties, sectionKey, splitComplementary, srgbToLinear, stripCssComments, styleSheetText, supportsBrandVision, swatchesFromDesignTokens, tetradic, tintShadeRamp, toHex, triadic, updateBookOps, upsertSectionOps, verifyBrandApprovalReceipt };
package/dist/index.js CHANGED
@@ -154,24 +154,6 @@ var BrandReviewStateChangedError = class extends BrandConflictError {
154
154
  }
155
155
  };
156
156
 
157
- // src/deps.ts
158
- async function defaultFetchBytes(url) {
159
- const res = await fetch(url);
160
- if (!res.ok) throw new Error(`asset fetch failed: ${res.status} for ${url}`);
161
- return {
162
- bytes: new Uint8Array(await res.arrayBuffer()),
163
- contentType: res.headers.get("content-type") ?? "application/octet-stream"
164
- };
165
- }
166
- function resolveDeps(deps) {
167
- return {
168
- db: deps.db,
169
- now: deps.now ?? Date.now,
170
- newId: deps.newId ?? (() => crypto.randomUUID()),
171
- fetchBytes: deps.fetchBytes ?? defaultFetchBytes
172
- };
173
- }
174
-
175
157
  // src/schema.ts
176
158
  var a = (type, o = {}) => ({
177
159
  type,
@@ -358,6 +340,70 @@ var BRAND_SCHEMA = {
358
340
  }
359
341
  };
360
342
 
343
+ // src/read-shape.ts
344
+ var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
345
+ var DATE_FIELDS = /* @__PURE__ */ new Map();
346
+ for (const [ns, entity] of Object.entries(BRAND_SCHEMA.entities))
347
+ DATE_FIELDS.set(
348
+ ns,
349
+ new Set(
350
+ Object.entries(entity.attrs).filter(([, attr]) => attr.type === "date").map(([label]) => label)
351
+ )
352
+ );
353
+ function rowAsWritten(ns, row) {
354
+ const dates = DATE_FIELDS.get(ns);
355
+ if (!dates?.size || !record(row)) return row;
356
+ let changed = false;
357
+ const out = { ...row };
358
+ for (const field of dates) {
359
+ const value = out[field];
360
+ if (typeof value !== "string") continue;
361
+ const parsed = Date.parse(value);
362
+ if (!Number.isSafeInteger(parsed) || parsed < 0) continue;
363
+ out[field] = parsed;
364
+ changed = true;
365
+ }
366
+ return changed ? out : row;
367
+ }
368
+ function receiptAsWritten(row) {
369
+ return rowAsWritten(BRAND_NS.approvalReceipt, row);
370
+ }
371
+ function proposalAsWritten(row) {
372
+ return rowAsWritten(BRAND_NS.proposal, row);
373
+ }
374
+ function resultAsWritten(result) {
375
+ const out = { ...result };
376
+ for (const [ns, rows] of Object.entries(out)) {
377
+ if (Array.isArray(rows)) out[ns] = rows.map((row) => rowAsWritten(ns, row));
378
+ }
379
+ return out;
380
+ }
381
+
382
+ // src/deps.ts
383
+ async function defaultFetchBytes(url) {
384
+ const res = await fetch(url);
385
+ if (!res.ok) throw new Error(`asset fetch failed: ${res.status} for ${url}`);
386
+ return {
387
+ bytes: new Uint8Array(await res.arrayBuffer()),
388
+ contentType: res.headers.get("content-type") ?? "application/octet-stream"
389
+ };
390
+ }
391
+ function readingAsWritten(db) {
392
+ return {
393
+ query: async (q) => resultAsWritten(await db.query(q)),
394
+ transact: (ops, opts) => db.transact(ops, opts),
395
+ storage: db.storage
396
+ };
397
+ }
398
+ function resolveDeps(deps) {
399
+ return {
400
+ db: readingAsWritten(deps.db),
401
+ now: deps.now ?? Date.now,
402
+ newId: deps.newId ?? (() => crypto.randomUUID()),
403
+ fetchBytes: deps.fetchBytes ?? defaultFetchBytes
404
+ };
405
+ }
406
+
361
407
  // src/rules.ts
362
408
  var CURRENT_BOOK_MEMBER = "ref('book.memberIds').exists(members, auth.id in members)";
363
409
  var BRAND_RULES = {
@@ -559,7 +605,7 @@ function assertAnalysis(value) {
559
605
  }
560
606
 
561
607
  // src/review-json.ts
562
- var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
608
+ var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
563
609
  function isBoundedBrandJson(root, limits = {}) {
564
610
  const maxDepth = limits.maxDepth ?? 12;
565
611
  const maxNodes = limits.maxNodes ?? 2048;
@@ -583,7 +629,7 @@ function isBoundedBrandJson(root, limits = {}) {
583
629
  if (Array.isArray(value)) {
584
630
  for (const child of value)
585
631
  stack.push({ value: child, depth: depth + 1 });
586
- } else if (record(value)) {
632
+ } else if (record2(value)) {
587
633
  for (const child of Object.values(value))
588
634
  stack.push({ value: child, depth: depth + 1 });
589
635
  } else {
@@ -620,7 +666,7 @@ async function brandJsonDigest(value) {
620
666
 
621
667
  // src/review.ts
622
668
  var DIGEST = /^sha256:[0-9a-f]{64}$/;
623
- var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
669
+ var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
624
670
  var exactKeys = (value, required, optional = []) => {
625
671
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
626
672
  return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => allowed.has(key));
@@ -645,15 +691,15 @@ var SNAPSHOT_REQUIRED = [
645
691
  "createdAt"
646
692
  ];
647
693
  function validSnapshotShape(value) {
648
- if (!record2(value) || !exactKeys(value, SNAPSHOT_REQUIRED)) return false;
694
+ if (!record3(value) || !exactKeys(value, SNAPSHOT_REQUIRED)) return false;
649
695
  const provenance = value.provenance;
650
- return boundedString(value.id) && boundedString(value.bookId) && typeof value.kind === "string" && PROPOSAL_KINDS.includes(value.kind) && value.status === "open" && record2(value.payload) && boundedString(value.rationale, 2e3) && record2(provenance) && exactKeys(provenance, [
696
+ return boundedString(value.id) && boundedString(value.bookId) && typeof value.kind === "string" && PROPOSAL_KINDS.includes(value.kind) && value.status === "open" && record3(value.payload) && boundedString(value.rationale, 2e3) && record3(provenance) && exactKeys(provenance, [
651
697
  "sourceAssetId",
652
698
  "sourceAsset",
653
699
  "messageId",
654
700
  "turnId",
655
701
  "taintLabels"
656
- ]) && (provenance.sourceAssetId === null || boundedString(provenance.sourceAssetId)) && (provenance.sourceAsset === null || record2(provenance.sourceAsset) && exactKeys(provenance.sourceAsset, [
702
+ ]) && (provenance.sourceAssetId === null || boundedString(provenance.sourceAssetId)) && (provenance.sourceAsset === null || record3(provenance.sourceAsset) && exactKeys(provenance.sourceAsset, [
657
703
  "assetId",
658
704
  "contentDigest",
659
705
  "objectEtag",
@@ -686,8 +732,8 @@ var CONSUMPTION_REQUIRED = [
686
732
  "consumedAt"
687
733
  ];
688
734
  function isBrandHumanAuthorityConsumption(value) {
689
- if (!record2(value) || !exactKeys(value, CONSUMPTION_REQUIRED)) return false;
690
- return boundedString(value.id) && boundedString(value.grantId) && Number.isSafeInteger(value.grantVersion) && value.grantVersion > 0 && Number.isSafeInteger(value.useNumber) && value.useNumber > 0 && boundedString(value.actorPrincipalId) && value.actorKind === "human" && boundedString(value.credentialId) && value.credentialKind === "clerk" && boundedString(value.appId) && typeof value.appIncarnation === "string" && /^[a-f0-9]{32}$/.test(value.appIncarnation) && value.capability === "brand.proposal.resolve" && value.projectCapability === "brand.approve" && value.effect === "internal" && typeof value.actionDigest === "string" && DIGEST.test(value.actionDigest) && typeof value.resourceDigest === "string" && DIGEST.test(value.resourceDigest) && record2(value.constraintEvidence) && boundedString(value.consumptionIdempotencyKey) && typeof value.requestDigest === "string" && DIGEST.test(value.requestDigest) && safeTime(value.consumedAt);
735
+ if (!record3(value) || !exactKeys(value, CONSUMPTION_REQUIRED)) return false;
736
+ return boundedString(value.id) && boundedString(value.grantId) && Number.isSafeInteger(value.grantVersion) && value.grantVersion > 0 && Number.isSafeInteger(value.useNumber) && value.useNumber > 0 && boundedString(value.actorPrincipalId) && value.actorKind === "human" && boundedString(value.credentialId) && value.credentialKind === "clerk" && boundedString(value.appId) && typeof value.appIncarnation === "string" && /^[a-f0-9]{32}$/.test(value.appIncarnation) && value.capability === "brand.proposal.resolve" && value.projectCapability === "brand.approve" && value.effect === "internal" && typeof value.actionDigest === "string" && DIGEST.test(value.actionDigest) && typeof value.resourceDigest === "string" && DIGEST.test(value.resourceDigest) && record3(value.constraintEvidence) && boundedString(value.consumptionIdempotencyKey) && typeof value.requestDigest === "string" && DIGEST.test(value.requestDigest) && safeTime(value.consumedAt);
691
737
  }
692
738
  var RECEIPT_REQUIRED = [
693
739
  "version",
@@ -713,10 +759,10 @@ async function verifyBrandApprovalReceipt(receipt2) {
713
759
  try {
714
760
  if (!isBoundedBrandJson(receipt2, { maxDepth: 14, maxNodes: 2500, maxBytes: 48 * 1024 }))
715
761
  return false;
716
- if (!record2(receipt2) || !exactKeys(receipt2, RECEIPT_REQUIRED, ["paletteId", "resolutionNote"]))
762
+ if (!record3(receipt2) || !exactKeys(receipt2, RECEIPT_REQUIRED, ["paletteId", "resolutionNote"]))
717
763
  return false;
718
764
  const binding = receipt2.decisionBinding;
719
- if (receipt2.version !== 1 || !boundedString(receipt2.id) || !boundedString(receipt2.mutationKey) || !boundedString(receipt2.bookId) || !boundedString(receipt2.proposalId) || receipt2.resolution !== "accepted" && receipt2.resolution !== "rejected" || !validSnapshotShape(receipt2.reviewedProposal) || typeof receipt2.actionDigest !== "string" || !DIGEST.test(receipt2.actionDigest) || !record2(binding) || !exactKeys(binding, [
765
+ if (receipt2.version !== 1 || !boundedString(receipt2.id) || !boundedString(receipt2.mutationKey) || !boundedString(receipt2.bookId) || !boundedString(receipt2.proposalId) || receipt2.resolution !== "accepted" && receipt2.resolution !== "rejected" || !validSnapshotShape(receipt2.reviewedProposal) || typeof receipt2.actionDigest !== "string" || !DIGEST.test(receipt2.actionDigest) || !record3(binding) || !exactKeys(binding, [
720
766
  "version",
721
767
  "bookVersion",
722
768
  "activationRevision",
@@ -2481,7 +2527,7 @@ async function linkedAsset(ctx, book, assetId, allowDeleting = false) {
2481
2527
  book: {}
2482
2528
  }
2483
2529
  });
2484
- const row = (result[BRAND_NS.asset] ?? [])[0];
2530
+ const row = rowAsWritten(BRAND_NS.asset, (result[BRAND_NS.asset] ?? [])[0]);
2485
2531
  const links = row?.book;
2486
2532
  if (!row || row.bookId !== book.id || !Array.isArray(links) || links.length !== 1 || links[0]?.id !== book.id || row.status !== "live" && !(allowDeleting && row.status === "deleting")) throw new BrandNotFoundError(`asset ${assetId}`);
2487
2533
  return row;
@@ -2555,7 +2601,7 @@ async function loadBook(db, bookId) {
2555
2601
  const res = await db.query({ [BRAND_NS.book]: { $: { where: { id: bookId } } } });
2556
2602
  const row = (res[BRAND_NS.book] ?? [])[0];
2557
2603
  if (!row) throw new BrandNotFoundError(`brand book ${bookId}`);
2558
- return row;
2604
+ return rowAsWritten(BRAND_NS.book, row);
2559
2605
  }
2560
2606
  async function loadMemberBook(db, bookId, actorId) {
2561
2607
  const book = await loadBook(db, bookId);
@@ -2610,14 +2656,14 @@ async function uploadAsset(ctx, req, bookId) {
2610
2656
  "internal",
2611
2657
  book.id
2612
2658
  );
2613
- const record6 = await ctx.db.storage.upload(
2659
+ const record7 = await ctx.db.storage.upload(
2614
2660
  path,
2615
2661
  file,
2616
2662
  contentType,
2617
2663
  { private: true }
2618
2664
  );
2619
- if (record6.path !== path) {
2620
- await ctx.db.storage.delete(record6.path);
2665
+ if (record7.path !== path) {
2666
+ await ctx.db.storage.delete(record7.path);
2621
2667
  throw new BrandConflictError(
2622
2668
  "private storage returned an unexpected asset path"
2623
2669
  );
@@ -2636,11 +2682,11 @@ async function uploadAsset(ctx, req, bookId) {
2636
2682
  id,
2637
2683
  bookId: book.id,
2638
2684
  kind,
2639
- path: record6.path,
2640
- storageObjectId: record6.id,
2685
+ path: record7.path,
2686
+ storageObjectId: record7.id,
2641
2687
  contentDigest: await contentDigest(file),
2642
2688
  contentType,
2643
- size: record6.size,
2689
+ size: record7.size,
2644
2690
  uploadedBy: ctx.actor.id,
2645
2691
  uploadedAuthorityRef: authority.authorityRef,
2646
2692
  audience: book.memberIds,
@@ -2667,7 +2713,7 @@ async function uploadAsset(ctx, req, bookId) {
2667
2713
  asPrincipalKind: "human"
2668
2714
  });
2669
2715
  } catch (error) {
2670
- await ctx.db.storage.delete(record6.path);
2716
+ await ctx.db.storage.delete(record7.path);
2671
2717
  throw error;
2672
2718
  }
2673
2719
  return json(await linkedAsset(ctx, book, id), 201);
@@ -2782,7 +2828,7 @@ async function handleAssetItem(ctx, req, bookId, assetId) {
2782
2828
  async function handleBooksRoot(ctx, req) {
2783
2829
  if (req.method === "GET") {
2784
2830
  const res = await ctx.db.query({ [BRAND_NS.book]: { $: { order: { createdAt: "asc" } } } });
2785
- const books = (res[BRAND_NS.book] ?? []).filter(
2831
+ const books = (res[BRAND_NS.book] ?? []).map((b) => rowAsWritten(BRAND_NS.book, b)).filter(
2786
2832
  (b) => isMember(b, ctx.actor.id)
2787
2833
  );
2788
2834
  return json({ books });
@@ -2947,7 +2993,7 @@ var SNAPSHOT_KEYS = [
2947
2993
  "reviewDigest",
2948
2994
  "status"
2949
2995
  ];
2950
- var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2996
+ var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2951
2997
  function parseProposalResolutionRequest(body, bookId, proposalId) {
2952
2998
  const allowed = /* @__PURE__ */ new Set([
2953
2999
  "mutationId",
@@ -2991,13 +3037,13 @@ function proposalReviewSnapshot(proposal) {
2991
3037
  function parseReviewedProposal(value, bookId, proposalId) {
2992
3038
  if (!isBoundedBrandJson(value, { maxDepth: 12, maxNodes: 1500, maxBytes: 32 * 1024 }))
2993
3039
  throw new BrandInputError('"reviewedProposal" is too deeply nested, complex, or large');
2994
- if (!record3(value)) throw new BrandInputError('"reviewedProposal" must be an object');
3040
+ if (!record4(value)) throw new BrandInputError('"reviewedProposal" must be an object');
2995
3041
  const keys = Object.keys(value).sort();
2996
3042
  if (keys.length !== SNAPSHOT_KEYS.length || !SNAPSHOT_KEYS.every((key, index) => key === keys[index])) throw new BrandInputError('"reviewedProposal" has an invalid shape');
2997
3043
  if (value.id !== proposalId || value.bookId !== bookId || value.status !== "open")
2998
3044
  throw new BrandInputError('"reviewedProposal" must identify this open proposal');
2999
- if (typeof value.kind !== "string" || !PROPOSAL_KINDS.includes(value.kind) || !record3(value.payload) || typeof value.rationale !== "string" || !Array.isArray(value.audience) || value.audience.length < 1 || value.audience.length > 100 || value.audience.some((id) => typeof id !== "string" || id.length < 1 || id.length > 200) || new Set(value.audience).size !== value.audience.length || typeof value.createdBy !== "string" || value.createdBy.length < 1 || value.createdBy.length > 200 || typeof value.createdAuthorityRef !== "string" || value.createdAuthorityRef.length < 1 || value.createdAuthorityRef.length > 200 || !Number.isSafeInteger(value.createdAt) || value.createdAt < 0 || typeof value.reviewDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.reviewDigest)) throw new BrandInputError('"reviewedProposal" has invalid fields');
3000
- if (!record3(value.provenance) || Object.keys(value.provenance).sort().join(",") !== "messageId,sourceAsset,sourceAssetId,taintLabels,turnId" || value.provenance.sourceAssetId !== null && (typeof value.provenance.sourceAssetId !== "string" || value.provenance.sourceAssetId.length < 1 || value.provenance.sourceAssetId.length > 200) || value.provenance.sourceAsset !== null && (!record3(value.provenance.sourceAsset) || Object.keys(value.provenance.sourceAsset).sort().join(",") !== "analysisDigest,analysisRevision,assetId,contentDigest,contentType,objectEtag,objectSize,pathDigest" || typeof value.provenance.sourceAsset.assetId !== "string" || value.provenance.sourceAsset.assetId.length < 1 || value.provenance.sourceAsset.assetId.length > 200 || typeof value.provenance.sourceAsset.objectEtag !== "string" || value.provenance.sourceAsset.objectEtag.length < 1 || value.provenance.sourceAsset.objectEtag.length > 200 || !Number.isSafeInteger(value.provenance.sourceAsset.objectSize) || value.provenance.sourceAsset.objectSize < 1 || typeof value.provenance.sourceAsset.pathDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.provenance.sourceAsset.pathDigest) || value.provenance.sourceAsset.contentType !== null && (typeof value.provenance.sourceAsset.contentType !== "string" || value.provenance.sourceAsset.contentType.length < 1 || value.provenance.sourceAsset.contentType.length > 160) || typeof value.provenance.sourceAsset.contentDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.provenance.sourceAsset.contentDigest) || value.provenance.sourceAsset.analysisRevision !== null && (!Number.isSafeInteger(value.provenance.sourceAsset.analysisRevision) || value.provenance.sourceAsset.analysisRevision < 0) || value.provenance.sourceAsset.analysisDigest !== null && (typeof value.provenance.sourceAsset.analysisDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(
3045
+ if (typeof value.kind !== "string" || !PROPOSAL_KINDS.includes(value.kind) || !record4(value.payload) || typeof value.rationale !== "string" || !Array.isArray(value.audience) || value.audience.length < 1 || value.audience.length > 100 || value.audience.some((id) => typeof id !== "string" || id.length < 1 || id.length > 200) || new Set(value.audience).size !== value.audience.length || typeof value.createdBy !== "string" || value.createdBy.length < 1 || value.createdBy.length > 200 || typeof value.createdAuthorityRef !== "string" || value.createdAuthorityRef.length < 1 || value.createdAuthorityRef.length > 200 || !Number.isSafeInteger(value.createdAt) || value.createdAt < 0 || typeof value.reviewDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.reviewDigest)) throw new BrandInputError('"reviewedProposal" has invalid fields');
3046
+ if (!record4(value.provenance) || Object.keys(value.provenance).sort().join(",") !== "messageId,sourceAsset,sourceAssetId,taintLabels,turnId" || value.provenance.sourceAssetId !== null && (typeof value.provenance.sourceAssetId !== "string" || value.provenance.sourceAssetId.length < 1 || value.provenance.sourceAssetId.length > 200) || value.provenance.sourceAsset !== null && (!record4(value.provenance.sourceAsset) || Object.keys(value.provenance.sourceAsset).sort().join(",") !== "analysisDigest,analysisRevision,assetId,contentDigest,contentType,objectEtag,objectSize,pathDigest" || typeof value.provenance.sourceAsset.assetId !== "string" || value.provenance.sourceAsset.assetId.length < 1 || value.provenance.sourceAsset.assetId.length > 200 || typeof value.provenance.sourceAsset.objectEtag !== "string" || value.provenance.sourceAsset.objectEtag.length < 1 || value.provenance.sourceAsset.objectEtag.length > 200 || !Number.isSafeInteger(value.provenance.sourceAsset.objectSize) || value.provenance.sourceAsset.objectSize < 1 || typeof value.provenance.sourceAsset.pathDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.provenance.sourceAsset.pathDigest) || value.provenance.sourceAsset.contentType !== null && (typeof value.provenance.sourceAsset.contentType !== "string" || value.provenance.sourceAsset.contentType.length < 1 || value.provenance.sourceAsset.contentType.length > 160) || typeof value.provenance.sourceAsset.contentDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(value.provenance.sourceAsset.contentDigest) || value.provenance.sourceAsset.analysisRevision !== null && (!Number.isSafeInteger(value.provenance.sourceAsset.analysisRevision) || value.provenance.sourceAsset.analysisRevision < 0) || value.provenance.sourceAsset.analysisDigest !== null && (typeof value.provenance.sourceAsset.analysisDigest !== "string" || !/^sha256:[0-9a-f]{64}$/.test(
3001
3047
  value.provenance.sourceAsset.analysisDigest
3002
3048
  )) || value.provenance.sourceAsset.assetId !== value.provenance.sourceAssetId) || value.provenance.sourceAssetId === null !== (value.provenance.sourceAsset === null) || value.provenance.messageId !== null && (typeof value.provenance.messageId !== "string" || value.provenance.messageId.length < 1 || value.provenance.messageId.length > 200) || value.provenance.turnId !== null && (typeof value.provenance.turnId !== "string" || value.provenance.turnId.length < 1 || value.provenance.turnId.length > 200) || !Array.isArray(value.provenance.taintLabels) || value.provenance.taintLabels.length > 16 || value.provenance.taintLabels.some((label) => typeof label !== "string" || label.length < 1 || label.length > 120) || new Set(value.provenance.taintLabels).size !== value.provenance.taintLabels.length) throw new BrandInputError('"reviewedProposal.provenance" is invalid');
3003
3049
  return value;
@@ -3170,7 +3216,7 @@ async function proposalReceiptByMutation(ctx, mutationKey) {
3170
3216
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
3171
3217
  if (!row || !Array.isArray(row.book) || row.book.length !== 1 || row.book[0]?.id !== row.bookId) return void 0;
3172
3218
  const { book: _book, ...receipt2 } = row;
3173
- return receipt2;
3219
+ return receiptAsWritten(receipt2);
3174
3220
  }
3175
3221
  async function consumeProposalAuthority(ctx, req, bookId, proposalId, actionDigest, mutationKey) {
3176
3222
  const authority = await ctx.consumeHumanExact({
@@ -3204,7 +3250,8 @@ async function receipt(ctx, id, actionDigest, expected) {
3204
3250
  });
3205
3251
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
3206
3252
  if (!row || row.actionDigest !== actionDigest || row.resolution !== "accepted" || row.bookId !== expected.bookId || row.reviewedProposal.bookId !== expected.bookId || row.reviewedProposal.kind !== expected.kind || !Array.isArray(row.book) || row.book.length !== 1 || row.book[0]?.id !== expected.bookId || expected.paletteId !== void 0 && row.paletteId !== expected.paletteId || expected.proposalId !== void 0 && row.proposalId !== expected.proposalId || expected.content !== void 0 && canonicalBrandJson(row.reviewedProposal.payload.content) !== canonicalBrandJson(expected.content)) throw new BrandConflictError(`approval receipt ${id} is invalid`);
3207
- const { book: _book, ...unhydrated } = row;
3253
+ const { book: _book, ...stored } = row;
3254
+ const unhydrated = receiptAsWritten(stored);
3208
3255
  if (!await verifyBrandApprovalReceipt(unhydrated))
3209
3256
  throw new BrandConflictError(`approval receipt ${id} is invalid`);
3210
3257
  return unhydrated;
@@ -3442,9 +3489,9 @@ async function proposalDecisionState(ctx, req, book, proposal, resolution, recei
3442
3489
  }
3443
3490
 
3444
3491
  // src/routes/proposals.ts
3445
- var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3492
+ var record5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3446
3493
  var sameStrings2 = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
3447
- var guardFailure2 = (error) => record4(error) && error.code === "transact_guard_failed";
3494
+ var guardFailure2 = (error) => record5(error) && error.code === "transact_guard_failed";
3448
3495
  async function handleProposalResolution(ctx, req, bookId, proposalId) {
3449
3496
  if (req.method !== "POST") return methodNotAllowed();
3450
3497
  const book = await loadMemberBook(ctx.db, bookId, ctx.actor.id);
@@ -3491,7 +3538,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
3491
3538
  book: {}
3492
3539
  }
3493
3540
  });
3494
- const proposal = (result[BRAND_NS.proposal] ?? [])[0];
3541
+ const proposal = proposalAsWritten((result[BRAND_NS.proposal] ?? [])[0]);
3495
3542
  if (!proposal || proposal.bookId !== book.id || !Array.isArray(proposal.book) || proposal.book.length !== 1 || proposal.book[0]?.id !== book.id) throw new BrandNotFoundError(`proposal ${proposalId}`);
3496
3543
  const current = proposalReviewSnapshot(proposal);
3497
3544
  const { reviewDigest, ...unsignedReview } = current;
@@ -3624,7 +3671,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
3624
3671
  }
3625
3672
 
3626
3673
  // src/routes/tokens.ts
3627
- var record5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3674
+ var record6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
3628
3675
  var missing = (bookId) => {
3629
3676
  throw new BrandNotFoundError(`compiled tokens for brand book ${bookId}`);
3630
3677
  };
@@ -3637,13 +3684,14 @@ async function acceptedReceipt(db, bookId, receiptId, actionDigest) {
3637
3684
  });
3638
3685
  const receipt2 = (result[BRAND_NS.approvalReceipt] ?? [])[0];
3639
3686
  if (!receipt2 || receipt2.resolution !== "accepted" || receipt2.actionDigest !== actionDigest || !Array.isArray(receipt2.book) || receipt2.book.length !== 1 || receipt2.book[0]?.id !== bookId) return missing(bookId);
3640
- const { book: _book, ...unhydrated } = receipt2;
3687
+ const { book: _book, ...stored } = receipt2;
3688
+ const unhydrated = receiptAsWritten(stored);
3641
3689
  if (!await verifyBrandApprovalReceipt(unhydrated)) return missing(bookId);
3642
3690
  return unhydrated;
3643
3691
  }
3644
3692
  async function approvedCache(db, book) {
3645
3693
  const cache = book.tokens;
3646
- if (!cache || !record5(cache.light) || !record5(cache.dark) || !book.activePaletteId || cache.paletteId !== book.activePaletteId || !cache.paletteReceiptId || !cache.paletteActionDigest || !cache.sourceDigest) return missing(book.id);
3694
+ if (!cache || !record6(cache.light) || !record6(cache.dark) || !book.activePaletteId || cache.paletteId !== book.activePaletteId || !cache.paletteReceiptId || !cache.paletteActionDigest || !cache.sourceDigest) return missing(book.id);
3647
3695
  const paletteResult = await db.query({
3648
3696
  [BRAND_NS.palette]: {
3649
3697
  $: { where: { id: cache.paletteId, bookId: book.id } },
@@ -4427,11 +4475,13 @@ export {
4427
4475
  parseDesignBundle,
4428
4476
  parseHex,
4429
4477
  pickTextOn,
4478
+ proposalAsWritten,
4430
4479
  proposalReviewSnapshot,
4431
4480
  proposePaletteOps,
4432
4481
  proposeSectionOps,
4433
4482
  readDesignManifest,
4434
4483
  readTools,
4484
+ receiptAsWritten,
4435
4485
  recordAnalysisOps,
4436
4486
  rejectProposalOps,
4437
4487
  relativeLuminance,
@@ -4441,6 +4491,7 @@ export {
4441
4491
  rgbToHsl,
4442
4492
  rgbToOklab,
4443
4493
  rotateHue,
4494
+ rowAsWritten,
4444
4495
  safeFileName,
4445
4496
  scanCustomProperties,
4446
4497
  sectionKey,