@odla-ai/brand 0.7.2 → 0.8.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.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",
@@ -5455,4 +5457,111 @@ function createBrandIntegration(options = {}) {
5455
5457
  probes: [{ path: `${basePath}/books`, expectedStatus: 401 }]
5456
5458
  };
5457
5459
  }
5460
+
5461
+ // src/promotion.ts
5462
+ var import_promotion = require("@odla-ai/promotion");
5463
+ var BRAND_PROMOTION_ADAPTER = "@odla-ai/brand/v1";
5464
+ var provenance = (sourceIds, receiptDigests = []) => ({
5465
+ adapter: BRAND_PROMOTION_ADAPTER,
5466
+ sourceIds: [...sourceIds].sort(),
5467
+ receiptDigests: [...receiptDigests].sort()
5468
+ });
5469
+ var exclusions = (book) => [
5470
+ {
5471
+ kind: "brand.runtime",
5472
+ id: book.slug,
5473
+ reason: "environment_local",
5474
+ paths: [`${BRAND_NS.book}.ownerId`, `${BRAND_NS.book}.memberIds`, `${BRAND_NS.book}.channelId`, `${BRAND_NS.book}.*At`],
5475
+ detail: "Ownership, roster, channel, and timestamps remain local to the target environment."
5476
+ },
5477
+ {
5478
+ kind: "brand.drafts",
5479
+ id: book.slug,
5480
+ reason: "draft_state",
5481
+ paths: [`${BRAND_NS.proposal}.*`, `${BRAND_NS.section}[status=draft]`],
5482
+ detail: "Open proposals and unapproved sections are review state, not published brand content."
5483
+ },
5484
+ {
5485
+ kind: "brand.assets",
5486
+ id: book.slug,
5487
+ reason: "provider_identity",
5488
+ paths: [`${BRAND_NS.asset}.storageObjectId`, `${BRAND_NS.asset}.path`, `${BRAND_NS.asset}.*AuthorityRef`],
5489
+ detail: "Stored files and provider object identities require a separate asset-transfer contract."
5490
+ },
5491
+ {
5492
+ kind: "brand.authority",
5493
+ id: book.slug,
5494
+ reason: "authority_history",
5495
+ paths: [`${BRAND_NS.approvalReceipt}.*`, `${BRAND_NS.palette}.audience`],
5496
+ detail: "Authority rows stay immutable in their source environment; artifacts retain only receipt digests and ids."
5497
+ }
5498
+ ];
5499
+ function exportBrandPromotion(input) {
5500
+ const { book } = input;
5501
+ if (book.status !== "active") throw new TypeError(`brand book ${book.slug} is not active`);
5502
+ const revision = `brand:${book.slug}:${book.activationRevision}`;
5503
+ const artifacts = [];
5504
+ if (book.tokens) {
5505
+ const {
5506
+ compiledAt: _compiledAt,
5507
+ paletteReceiptId,
5508
+ paletteActionDigest,
5509
+ typographyReceiptId,
5510
+ typographyActionDigest,
5511
+ sourceDigest,
5512
+ ...stableTokens
5513
+ } = book.tokens;
5514
+ artifacts.push({
5515
+ kind: "brand.tokens",
5516
+ id: book.slug,
5517
+ sourceRevision: revision,
5518
+ provenance: provenance(
5519
+ [book.id, book.tokens.paletteId, paletteReceiptId, typographyReceiptId].filter((value) => Boolean(value)),
5520
+ [sourceDigest, paletteActionDigest, typographyActionDigest].filter((value) => Boolean(value))
5521
+ ),
5522
+ content: (0, import_promotion.canonicalPromotionValue)({ bookSlug: book.slug, tokens: stableTokens })
5523
+ });
5524
+ }
5525
+ for (const section of input.sections.filter((row) => row.bookId === book.id && row.status === "approved")) {
5526
+ if (!section.approvalReceiptId || !section.approvalActionDigest) {
5527
+ throw new TypeError(`approved brand section ${section.key} lacks approval provenance`);
5528
+ }
5529
+ artifacts.push({
5530
+ kind: "brand.section",
5531
+ id: `${book.slug}/${section.kind}`,
5532
+ sourceRevision: revision,
5533
+ provenance: provenance([book.id, section.id, section.approvalReceiptId], [section.approvalActionDigest]),
5534
+ content: (0, import_promotion.canonicalPromotionValue)({ bookSlug: book.slug, kind: section.kind, content: section.content })
5535
+ });
5536
+ }
5537
+ const activePalette2 = input.palettes.find((row) => row.bookId === book.id && row.id === book.activePaletteId && row.status === "active");
5538
+ if (book.activePaletteId && !activePalette2) throw new TypeError(`active palette ${book.activePaletteId} is unavailable`);
5539
+ if (activePalette2) {
5540
+ artifacts.push({
5541
+ kind: "brand.palette",
5542
+ id: `${book.slug}/${activePalette2.id}`,
5543
+ sourceRevision: revision,
5544
+ provenance: provenance([book.id, activePalette2.id, activePalette2.proposalId, activePalette2.approvalReceiptId], [activePalette2.approvalActionDigest]),
5545
+ content: (0, import_promotion.canonicalPromotionValue)({
5546
+ bookSlug: book.slug,
5547
+ name: activePalette2.name,
5548
+ swatches: activePalette2.swatches,
5549
+ source: activePalette2.source,
5550
+ ...activePalette2.seedHex ? { seedHex: activePalette2.seedHex } : {},
5551
+ ...activePalette2.rationale ? { rationale: activePalette2.rationale } : {}
5552
+ })
5553
+ });
5554
+ }
5555
+ return {
5556
+ adapter: BRAND_PROMOTION_ADAPTER,
5557
+ artifacts,
5558
+ exclusions: exclusions(book),
5559
+ preconditions: [{
5560
+ code: "brand.schema",
5561
+ path: "services.brand.schemaVersion",
5562
+ expected: 1,
5563
+ detail: "The target must expose the Brand v1 approved-content schema."
5564
+ }]
5565
+ };
5566
+ }
5458
5567
  //# sourceMappingURL=index.cjs.map