@odla-ai/brand 0.7.2 → 0.8.1

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.cjs CHANGED
@@ -31,6 +31,7 @@ __export(src_exports, {
31
31
  BRAND_EMITTED_TOKENS: () => BRAND_EMITTED_TOKENS,
32
32
  BRAND_INSTRUCTIONS: () => BRAND_INSTRUCTIONS,
33
33
  BRAND_NS: () => BRAND_NS,
34
+ BRAND_PROMOTION_ADAPTER: () => BRAND_PROMOTION_ADAPTER,
34
35
  BRAND_REQUIRED_TOKENS: () => BRAND_REQUIRED_TOKENS,
35
36
  BRAND_RULES: () => BRAND_RULES,
36
37
  BRAND_SCHEMA: () => BRAND_SCHEMA,
@@ -119,6 +120,7 @@ __export(src_exports, {
119
120
  digestDesignBundle: () => digestDesignBundle,
120
121
  digestDesignHtml: () => digestDesignHtml,
121
122
  dispatchBrandTurn: () => dispatchBrandTurn,
123
+ exportBrandPromotion: () => exportBrandPromotion,
122
124
  extractColors: () => extractColors,
123
125
  extractDesignTokens: () => extractDesignTokens,
124
126
  extractFonts: () => extractFonts,
@@ -843,14 +845,14 @@ var SNAPSHOT_REQUIRED = [
843
845
  ];
844
846
  function validSnapshotShape(value) {
845
847
  if (!record3(value) || !exactKeys(value, SNAPSHOT_REQUIRED)) return false;
846
- const provenance = value.provenance;
847
- 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, [
848
+ const provenance2 = value.provenance;
849
+ 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(provenance2) && exactKeys(provenance2, [
848
850
  "sourceAssetId",
849
851
  "sourceAsset",
850
852
  "messageId",
851
853
  "turnId",
852
854
  "taintLabels"
853
- ]) && (provenance.sourceAssetId === null || boundedString(provenance.sourceAssetId)) && (provenance.sourceAsset === null || record3(provenance.sourceAsset) && exactKeys(provenance.sourceAsset, [
855
+ ]) && (provenance2.sourceAssetId === null || boundedString(provenance2.sourceAssetId)) && (provenance2.sourceAsset === null || record3(provenance2.sourceAsset) && exactKeys(provenance2.sourceAsset, [
854
856
  "assetId",
855
857
  "contentDigest",
856
858
  "objectEtag",
@@ -859,7 +861,7 @@ function validSnapshotShape(value) {
859
861
  "contentType",
860
862
  "analysisRevision",
861
863
  "analysisDigest"
862
- ]) && boundedString(provenance.sourceAsset.assetId) && typeof provenance.sourceAsset.contentDigest === "string" && DIGEST.test(provenance.sourceAsset.contentDigest) && boundedString(provenance.sourceAsset.objectEtag) && Number.isSafeInteger(provenance.sourceAsset.objectSize) && provenance.sourceAsset.objectSize > 0 && typeof provenance.sourceAsset.pathDigest === "string" && DIGEST.test(provenance.sourceAsset.pathDigest) && (provenance.sourceAsset.contentType === null || boundedString(provenance.sourceAsset.contentType, 160)) && (provenance.sourceAsset.analysisRevision === null || Number.isSafeInteger(provenance.sourceAsset.analysisRevision) && provenance.sourceAsset.analysisRevision >= 0) && (provenance.sourceAsset.analysisDigest === null || typeof provenance.sourceAsset.analysisDigest === "string" && DIGEST.test(provenance.sourceAsset.analysisDigest))) && (provenance.sourceAssetId === null && provenance.sourceAsset === null || provenance.sourceAssetId !== null && provenance.sourceAsset !== null && provenance.sourceAsset.assetId === provenance.sourceAssetId) && (provenance.messageId === null || boundedString(provenance.messageId)) && (provenance.turnId === null || boundedString(provenance.turnId)) && Array.isArray(provenance.taintLabels) && provenance.taintLabels.length <= 16 && provenance.taintLabels.every((label) => boundedString(label, 120)) && new Set(provenance.taintLabels).size === provenance.taintLabels.length && typeof value.reviewDigest === "string" && DIGEST.test(value.reviewDigest) && Array.isArray(value.audience) && value.audience.length > 0 && value.audience.length <= 100 && value.audience.every((id) => boundedString(id)) && new Set(value.audience).size === value.audience.length && boundedString(value.createdBy) && boundedString(value.createdAuthorityRef) && safeTime(value.createdAt);
864
+ ]) && boundedString(provenance2.sourceAsset.assetId) && typeof provenance2.sourceAsset.contentDigest === "string" && DIGEST.test(provenance2.sourceAsset.contentDigest) && boundedString(provenance2.sourceAsset.objectEtag) && Number.isSafeInteger(provenance2.sourceAsset.objectSize) && provenance2.sourceAsset.objectSize > 0 && typeof provenance2.sourceAsset.pathDigest === "string" && DIGEST.test(provenance2.sourceAsset.pathDigest) && (provenance2.sourceAsset.contentType === null || boundedString(provenance2.sourceAsset.contentType, 160)) && (provenance2.sourceAsset.analysisRevision === null || Number.isSafeInteger(provenance2.sourceAsset.analysisRevision) && provenance2.sourceAsset.analysisRevision >= 0) && (provenance2.sourceAsset.analysisDigest === null || typeof provenance2.sourceAsset.analysisDigest === "string" && DIGEST.test(provenance2.sourceAsset.analysisDigest))) && (provenance2.sourceAssetId === null && provenance2.sourceAsset === null || provenance2.sourceAssetId !== null && provenance2.sourceAsset !== null && provenance2.sourceAsset.assetId === provenance2.sourceAssetId) && (provenance2.messageId === null || boundedString(provenance2.messageId)) && (provenance2.turnId === null || boundedString(provenance2.turnId)) && Array.isArray(provenance2.taintLabels) && provenance2.taintLabels.length <= 16 && provenance2.taintLabels.every((label) => boundedString(label, 120)) && new Set(provenance2.taintLabels).size === provenance2.taintLabels.length && typeof value.reviewDigest === "string" && DIGEST.test(value.reviewDigest) && Array.isArray(value.audience) && value.audience.length > 0 && value.audience.length <= 100 && value.audience.every((id) => boundedString(id)) && new Set(value.audience).size === value.audience.length && boundedString(value.createdBy) && boundedString(value.createdAuthorityRef) && safeTime(value.createdAt);
863
865
  }
864
866
  var CONSUMPTION_REQUIRED = [
865
867
  "id",
@@ -2884,6 +2886,13 @@ function compileBrandTokens(input) {
2884
2886
  return { light, dark, warnings };
2885
2887
  }
2886
2888
 
2889
+ // src/link-shape.ts
2890
+ function hasExactOneLink(value, expectedId) {
2891
+ if (Array.isArray(value))
2892
+ return value.length === 1 && value[0]?.id === expectedId;
2893
+ return typeof value === "object" && value !== null && value.id === expectedId;
2894
+ }
2895
+
2887
2896
  // src/skill/asset-tools.ts
2888
2897
  var MAX_VIEW_BYTES = 4718592;
2889
2898
  var IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
@@ -2926,7 +2935,7 @@ function assetTools(ctx) {
2926
2935
  }
2927
2936
  });
2928
2937
  const row = (res[BRAND_NS.asset] ?? [])[0];
2929
- if (!row || row.status !== "live" || row.deletedAt || !Array.isArray(row.book) || row.book.length !== 1 || row.book[0]?.id !== book.id) throw new BrandNotFoundError(`asset ${assetId}`);
2938
+ if (!row || row.status !== "live" || row.deletedAt || !hasExactOneLink(row.book, book.id)) throw new BrandNotFoundError(`asset ${assetId}`);
2930
2939
  return row;
2931
2940
  };
2932
2941
  const viewAsset = {
@@ -3014,7 +3023,7 @@ async function proposalSourceAssetId(ctx, book, value) {
3014
3023
  });
3015
3024
  const rows = result[BRAND_NS.asset] ?? [];
3016
3025
  const asset = rows.length === 1 ? rows[0] : void 0;
3017
- if (!asset || asset.id !== sourceAssetId || asset.bookId !== book.id || asset.status !== "live" || asset.deletedAt !== void 0 || !Array.isArray(asset.book) || asset.book.length !== 1 || asset.book[0]?.id !== book.id) throw new BrandInputError(
3026
+ if (!asset || asset.id !== sourceAssetId || asset.bookId !== book.id || asset.status !== "live" || asset.deletedAt !== void 0 || !hasExactOneLink(asset.book, book.id)) throw new BrandInputError(
3018
3027
  "sourceAssetId must be a live asset in this brand book"
3019
3028
  );
3020
3029
  return sourceAssetId;
@@ -3456,7 +3465,7 @@ function readTools(ctx) {
3456
3465
  book: {}
3457
3466
  }
3458
3467
  });
3459
- const linked = (rows) => rows.filter((row) => row.bookId === authorizedBook.id && Array.isArray(row.book) && row.book.length === 1 && row.book[0]?.id === authorizedBook.id);
3468
+ const linked = (rows) => rows.filter((row) => row.bookId === authorizedBook.id && hasExactOneLink(row.book, authorizedBook.id));
3460
3469
  const sections = linked(
3461
3470
  res[BRAND_NS.section] ?? []
3462
3471
  );
@@ -3506,7 +3515,7 @@ function readTools(ctx) {
3506
3515
  book: {}
3507
3516
  }
3508
3517
  });
3509
- const rows = (res[BRAND_NS.asset] ?? []).filter((asset) => asset.bookId === ctx.bookId && Array.isArray(asset.book) && asset.book.length === 1 && asset.book[0]?.id === ctx.bookId);
3518
+ const rows = (res[BRAND_NS.asset] ?? []).filter((asset) => asset.bookId === ctx.bookId && hasExactOneLink(asset.book, ctx.bookId));
3510
3519
  if (rows.length === 0) {
3511
3520
  return { content: input.kind ? `(no ${String(input.kind)} assets uploaded yet)` : "(no assets uploaded yet)" };
3512
3521
  }
@@ -3635,7 +3644,7 @@ async function linkedAsset(ctx, book, assetId, allowDeleting = false) {
3635
3644
  });
3636
3645
  const row = rowAsWritten(BRAND_NS.asset, (result[BRAND_NS.asset] ?? [])[0]);
3637
3646
  const links = row?.book;
3638
- 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}`);
3647
+ if (!row || row.bookId !== book.id || !hasExactOneLink(links, book.id) || row.status !== "live" && !(allowDeleting && row.status === "deleting")) throw new BrandNotFoundError(`asset ${assetId}`);
3639
3648
  return row;
3640
3649
  }
3641
3650
 
@@ -3838,7 +3847,7 @@ async function handleAssetsRoot(ctx, req, bookId) {
3838
3847
  });
3839
3848
  const assets = (result[BRAND_NS.asset] ?? []).filter((value) => {
3840
3849
  const row = value;
3841
- return row.bookId === book.id && Array.isArray(row.book) && row.book.length === 1 && row.book[0]?.id === book.id;
3850
+ return row.bookId === book.id && hasExactOneLink(row.book, book.id);
3842
3851
  });
3843
3852
  return json({ assets });
3844
3853
  }
@@ -4324,7 +4333,7 @@ async function proposalReceiptByMutation(ctx, mutationKey) {
4324
4333
  }
4325
4334
  });
4326
4335
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4327
- if (!row || !Array.isArray(row.book) || row.book.length !== 1 || row.book[0]?.id !== row.bookId) return void 0;
4336
+ if (!row || !hasExactOneLink(row.book, row.bookId)) return void 0;
4328
4337
  const { book: _book, ...receipt2 } = row;
4329
4338
  return receiptAsWritten(receipt2);
4330
4339
  }
@@ -4359,7 +4368,7 @@ async function receipt(ctx, id, actionDigest, expected) {
4359
4368
  }
4360
4369
  });
4361
4370
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4362
- 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`);
4371
+ if (!row || row.actionDigest !== actionDigest || row.resolution !== "accepted" || row.bookId !== expected.bookId || row.reviewedProposal.bookId !== expected.bookId || row.reviewedProposal.kind !== expected.kind || !hasExactOneLink(row.book, 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`);
4363
4372
  const { book: _book, ...stored } = row;
4364
4373
  const unhydrated = receiptAsWritten(stored);
4365
4374
  if (!await verifyBrandApprovalReceipt(unhydrated))
@@ -4375,7 +4384,7 @@ async function activePalette(ctx, book) {
4375
4384
  }
4376
4385
  });
4377
4386
  const palette = (result[BRAND_NS.palette] ?? [])[0];
4378
- if (!palette || palette.bookId !== book.id || palette.status !== "active" || !palette.proposalId || !palette.approvalReceiptId || !palette.approvalActionDigest || !Array.isArray(palette.book) || palette.book.length !== 1 || palette.book[0]?.id !== book.id) throw new BrandConflictError(
4387
+ if (!palette || palette.bookId !== book.id || palette.status !== "active" || !palette.proposalId || !palette.approvalReceiptId || !palette.approvalActionDigest || !hasExactOneLink(palette.book, book.id)) throw new BrandConflictError(
4379
4388
  "active palette is missing valid approval provenance"
4380
4389
  );
4381
4390
  const approval = await receipt(
@@ -4424,7 +4433,7 @@ async function approvedTypography(ctx, book) {
4424
4433
  });
4425
4434
  const section = (result[BRAND_NS.section] ?? [])[0];
4426
4435
  if (!section || section.status !== "approved") return {};
4427
- if (section.bookId !== book.id || section.kind !== "typography" || !section.approvalReceiptId || !section.approvalActionDigest || !Array.isArray(section.book) || section.book.length !== 1 || section.book[0]?.id !== book.id) throw new BrandConflictError(
4436
+ if (section.bookId !== book.id || section.kind !== "typography" || !section.approvalReceiptId || !section.approvalActionDigest || !hasExactOneLink(section.book, book.id)) throw new BrandConflictError(
4428
4437
  "approved typography is missing valid approval provenance"
4429
4438
  );
4430
4439
  assertSectionContent("typography", section.content);
@@ -4465,7 +4474,7 @@ async function proposalDecisionState(ctx, req, book, proposal, resolution, recei
4465
4474
  }
4466
4475
  });
4467
4476
  sourceAsset = (result[BRAND_NS.asset] ?? [])[0];
4468
- if (!sourceAsset || sourceAsset.id !== source.assetId || sourceAsset.bookId !== book.id || sourceAsset.status !== "live" || sourceAsset.deletedAt !== void 0 || sourceAsset.contentDigest !== source.contentDigest || sourceAsset.size !== source.objectSize || sourceAsset.contentType !== source.contentType || await brandJsonDigest({ appId: ctx.appId, path: sourceAsset.path }) !== source.pathDigest || sourceAsset.analysisRevision !== source.analysisRevision || (sourceAsset.analysisDigest ?? null) !== source.analysisDigest || !Array.isArray(sourceAsset.book) || sourceAsset.book.length !== 1 || sourceAsset.book[0]?.id !== book.id) throw new BrandConflictError(
4477
+ if (!sourceAsset || sourceAsset.id !== source.assetId || sourceAsset.bookId !== book.id || sourceAsset.status !== "live" || sourceAsset.deletedAt !== void 0 || sourceAsset.contentDigest !== source.contentDigest || sourceAsset.size !== source.objectSize || sourceAsset.contentType !== source.contentType || await brandJsonDigest({ appId: ctx.appId, path: sourceAsset.path }) !== source.pathDigest || sourceAsset.analysisRevision !== source.analysisRevision || (sourceAsset.analysisDigest ?? null) !== source.analysisDigest || !hasExactOneLink(sourceAsset.book, book.id)) throw new BrandConflictError(
4469
4478
  "source asset changed since the proposal was reviewed"
4470
4479
  );
4471
4480
  if (!await ctx.verifySourceAssetSnapshot({
@@ -4649,7 +4658,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
4649
4658
  }
4650
4659
  });
4651
4660
  const proposal = proposalAsWritten((result[BRAND_NS.proposal] ?? [])[0]);
4652
- 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}`);
4661
+ if (!proposal || proposal.bookId !== book.id || !hasExactOneLink(proposal.book, book.id)) throw new BrandNotFoundError(`proposal ${proposalId}`);
4653
4662
  const current = proposalReviewSnapshot(proposal);
4654
4663
  const { reviewDigest, ...unsignedReview } = current;
4655
4664
  if (await brandJsonDigest(unsignedReview) !== reviewDigest || canonicalBrandJson(current) !== canonicalBrandJson(reviewed)) throw new BrandConflictError("proposal changed since review");
@@ -4793,7 +4802,7 @@ async function acceptedReceipt(db, bookId, receiptId, actionDigest) {
4793
4802
  }
4794
4803
  });
4795
4804
  const receipt2 = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4796
- if (!receipt2 || receipt2.resolution !== "accepted" || receipt2.actionDigest !== actionDigest || !Array.isArray(receipt2.book) || receipt2.book.length !== 1 || receipt2.book[0]?.id !== bookId) return missing(bookId);
4805
+ if (!receipt2 || receipt2.resolution !== "accepted" || receipt2.actionDigest !== actionDigest || !hasExactOneLink(receipt2.book, bookId)) return missing(bookId);
4797
4806
  const { book: _book, ...stored } = receipt2;
4798
4807
  const unhydrated = receiptAsWritten(stored);
4799
4808
  if (!await verifyBrandApprovalReceipt(unhydrated)) return missing(bookId);
@@ -4809,7 +4818,7 @@ async function approvedCache(db, book) {
4809
4818
  }
4810
4819
  });
4811
4820
  const palette = (paletteResult[BRAND_NS.palette] ?? [])[0];
4812
- if (!palette || palette.status !== "active" || palette.approvalReceiptId !== cache.paletteReceiptId || palette.approvalActionDigest !== cache.paletteActionDigest || !Array.isArray(palette.book) || palette.book.length !== 1 || palette.book[0]?.id !== book.id) return missing(book.id);
4821
+ if (!palette || palette.status !== "active" || palette.approvalReceiptId !== cache.paletteReceiptId || palette.approvalActionDigest !== cache.paletteActionDigest || !hasExactOneLink(palette.book, book.id)) return missing(book.id);
4813
4822
  await acceptedReceipt(
4814
4823
  db,
4815
4824
  book.id,
@@ -4827,7 +4836,7 @@ async function approvedCache(db, book) {
4827
4836
  });
4828
4837
  const typography = (sectionResult[BRAND_NS.section] ?? [])[0];
4829
4838
  if (hasTypeId) {
4830
- if (!typography || typography.bookId !== book.id || typography.kind !== "typography" || typography.status !== "approved" || typography.approvalReceiptId !== cache.typographyReceiptId || typography.approvalActionDigest !== cache.typographyActionDigest || !Array.isArray(typography.book) || typography.book.length !== 1 || typography.book[0]?.id !== book.id) return missing(book.id);
4839
+ if (!typography || typography.bookId !== book.id || typography.kind !== "typography" || typography.status !== "approved" || typography.approvalReceiptId !== cache.typographyReceiptId || typography.approvalActionDigest !== cache.typographyActionDigest || !hasExactOneLink(typography.book, book.id)) return missing(book.id);
4831
4840
  await acceptedReceipt(
4832
4841
  db,
4833
4842
  book.id,
@@ -5232,7 +5241,7 @@ async function attachedBrandAssetIds(db, book, row) {
5232
5241
  const rows = result[BRAND_NS.asset] ?? [];
5233
5242
  if (rows.length !== 1) return null;
5234
5243
  const asset = rows[0];
5235
- return asset.id === id && asset.bookId === book.id && asset.status === "live" && asset.deletedAt === void 0 && Array.isArray(asset.book) && asset.book.length === 1 && asset.book[0]?.id === book.id ? id : null;
5244
+ return asset.id === id && asset.bookId === book.id && asset.status === "live" && asset.deletedAt === void 0 && hasExactOneLink(asset.book, book.id) ? id : null;
5236
5245
  }));
5237
5246
  return assets.filter((id) => id !== null);
5238
5247
  }
@@ -5455,4 +5464,111 @@ function createBrandIntegration(options = {}) {
5455
5464
  probes: [{ path: `${basePath}/books`, expectedStatus: 401 }]
5456
5465
  };
5457
5466
  }
5467
+
5468
+ // src/promotion.ts
5469
+ var import_promotion = require("@odla-ai/promotion");
5470
+ var BRAND_PROMOTION_ADAPTER = "@odla-ai/brand/v1";
5471
+ var provenance = (sourceIds, receiptDigests = []) => ({
5472
+ adapter: BRAND_PROMOTION_ADAPTER,
5473
+ sourceIds: [...sourceIds].sort(),
5474
+ receiptDigests: [...receiptDigests].sort()
5475
+ });
5476
+ var exclusions = (book) => [
5477
+ {
5478
+ kind: "brand.runtime",
5479
+ id: book.slug,
5480
+ reason: "environment_local",
5481
+ paths: [`${BRAND_NS.book}.ownerId`, `${BRAND_NS.book}.memberIds`, `${BRAND_NS.book}.channelId`, `${BRAND_NS.book}.*At`],
5482
+ detail: "Ownership, roster, channel, and timestamps remain local to the target environment."
5483
+ },
5484
+ {
5485
+ kind: "brand.drafts",
5486
+ id: book.slug,
5487
+ reason: "draft_state",
5488
+ paths: [`${BRAND_NS.proposal}.*`, `${BRAND_NS.section}[status=draft]`],
5489
+ detail: "Open proposals and unapproved sections are review state, not published brand content."
5490
+ },
5491
+ {
5492
+ kind: "brand.assets",
5493
+ id: book.slug,
5494
+ reason: "provider_identity",
5495
+ paths: [`${BRAND_NS.asset}.storageObjectId`, `${BRAND_NS.asset}.path`, `${BRAND_NS.asset}.*AuthorityRef`],
5496
+ detail: "Stored files and provider object identities require a separate asset-transfer contract."
5497
+ },
5498
+ {
5499
+ kind: "brand.authority",
5500
+ id: book.slug,
5501
+ reason: "authority_history",
5502
+ paths: [`${BRAND_NS.approvalReceipt}.*`, `${BRAND_NS.palette}.audience`],
5503
+ detail: "Authority rows stay immutable in their source environment; artifacts retain only receipt digests and ids."
5504
+ }
5505
+ ];
5506
+ function exportBrandPromotion(input) {
5507
+ const { book } = input;
5508
+ if (book.status !== "active") throw new TypeError(`brand book ${book.slug} is not active`);
5509
+ const revision = `brand:${book.slug}:${book.activationRevision}`;
5510
+ const artifacts = [];
5511
+ if (book.tokens) {
5512
+ const {
5513
+ compiledAt: _compiledAt,
5514
+ paletteReceiptId,
5515
+ paletteActionDigest,
5516
+ typographyReceiptId,
5517
+ typographyActionDigest,
5518
+ sourceDigest,
5519
+ ...stableTokens
5520
+ } = book.tokens;
5521
+ artifacts.push({
5522
+ kind: "brand.tokens",
5523
+ id: book.slug,
5524
+ sourceRevision: revision,
5525
+ provenance: provenance(
5526
+ [book.id, book.tokens.paletteId, paletteReceiptId, typographyReceiptId].filter((value) => Boolean(value)),
5527
+ [sourceDigest, paletteActionDigest, typographyActionDigest].filter((value) => Boolean(value))
5528
+ ),
5529
+ content: (0, import_promotion.canonicalPromotionValue)({ bookSlug: book.slug, tokens: stableTokens })
5530
+ });
5531
+ }
5532
+ for (const section of input.sections.filter((row) => row.bookId === book.id && row.status === "approved")) {
5533
+ if (!section.approvalReceiptId || !section.approvalActionDigest) {
5534
+ throw new TypeError(`approved brand section ${section.key} lacks approval provenance`);
5535
+ }
5536
+ artifacts.push({
5537
+ kind: "brand.section",
5538
+ id: `${book.slug}/${section.kind}`,
5539
+ sourceRevision: revision,
5540
+ provenance: provenance([book.id, section.id, section.approvalReceiptId], [section.approvalActionDigest]),
5541
+ content: (0, import_promotion.canonicalPromotionValue)({ bookSlug: book.slug, kind: section.kind, content: section.content })
5542
+ });
5543
+ }
5544
+ const activePalette2 = input.palettes.find((row) => row.bookId === book.id && row.id === book.activePaletteId && row.status === "active");
5545
+ if (book.activePaletteId && !activePalette2) throw new TypeError(`active palette ${book.activePaletteId} is unavailable`);
5546
+ if (activePalette2) {
5547
+ artifacts.push({
5548
+ kind: "brand.palette",
5549
+ id: `${book.slug}/${activePalette2.id}`,
5550
+ sourceRevision: revision,
5551
+ provenance: provenance([book.id, activePalette2.id, activePalette2.proposalId, activePalette2.approvalReceiptId], [activePalette2.approvalActionDigest]),
5552
+ content: (0, import_promotion.canonicalPromotionValue)({
5553
+ bookSlug: book.slug,
5554
+ name: activePalette2.name,
5555
+ swatches: activePalette2.swatches,
5556
+ source: activePalette2.source,
5557
+ ...activePalette2.seedHex ? { seedHex: activePalette2.seedHex } : {},
5558
+ ...activePalette2.rationale ? { rationale: activePalette2.rationale } : {}
5559
+ })
5560
+ });
5561
+ }
5562
+ return {
5563
+ adapter: BRAND_PROMOTION_ADAPTER,
5564
+ artifacts,
5565
+ exclusions: exclusions(book),
5566
+ preconditions: [{
5567
+ code: "brand.schema",
5568
+ path: "services.brand.schemaVersion",
5569
+ expected: 1,
5570
+ detail: "The target must expose the Brand v1 approved-content schema."
5571
+ }]
5572
+ };
5573
+ }
5458
5574
  //# sourceMappingURL=index.cjs.map