@odla-ai/brand 0.8.0 → 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
@@ -2886,6 +2886,13 @@ function compileBrandTokens(input) {
2886
2886
  return { light, dark, warnings };
2887
2887
  }
2888
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
+
2889
2896
  // src/skill/asset-tools.ts
2890
2897
  var MAX_VIEW_BYTES = 4718592;
2891
2898
  var IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
@@ -2928,7 +2935,7 @@ function assetTools(ctx) {
2928
2935
  }
2929
2936
  });
2930
2937
  const row = (res[BRAND_NS.asset] ?? [])[0];
2931
- 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}`);
2932
2939
  return row;
2933
2940
  };
2934
2941
  const viewAsset = {
@@ -3016,7 +3023,7 @@ async function proposalSourceAssetId(ctx, book, value) {
3016
3023
  });
3017
3024
  const rows = result[BRAND_NS.asset] ?? [];
3018
3025
  const asset = rows.length === 1 ? rows[0] : void 0;
3019
- 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(
3020
3027
  "sourceAssetId must be a live asset in this brand book"
3021
3028
  );
3022
3029
  return sourceAssetId;
@@ -3458,7 +3465,7 @@ function readTools(ctx) {
3458
3465
  book: {}
3459
3466
  }
3460
3467
  });
3461
- 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));
3462
3469
  const sections = linked(
3463
3470
  res[BRAND_NS.section] ?? []
3464
3471
  );
@@ -3508,7 +3515,7 @@ function readTools(ctx) {
3508
3515
  book: {}
3509
3516
  }
3510
3517
  });
3511
- 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));
3512
3519
  if (rows.length === 0) {
3513
3520
  return { content: input.kind ? `(no ${String(input.kind)} assets uploaded yet)` : "(no assets uploaded yet)" };
3514
3521
  }
@@ -3637,7 +3644,7 @@ async function linkedAsset(ctx, book, assetId, allowDeleting = false) {
3637
3644
  });
3638
3645
  const row = rowAsWritten(BRAND_NS.asset, (result[BRAND_NS.asset] ?? [])[0]);
3639
3646
  const links = row?.book;
3640
- 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}`);
3641
3648
  return row;
3642
3649
  }
3643
3650
 
@@ -3840,7 +3847,7 @@ async function handleAssetsRoot(ctx, req, bookId) {
3840
3847
  });
3841
3848
  const assets = (result[BRAND_NS.asset] ?? []).filter((value) => {
3842
3849
  const row = value;
3843
- 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);
3844
3851
  });
3845
3852
  return json({ assets });
3846
3853
  }
@@ -4326,7 +4333,7 @@ async function proposalReceiptByMutation(ctx, mutationKey) {
4326
4333
  }
4327
4334
  });
4328
4335
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4329
- 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;
4330
4337
  const { book: _book, ...receipt2 } = row;
4331
4338
  return receiptAsWritten(receipt2);
4332
4339
  }
@@ -4361,7 +4368,7 @@ async function receipt(ctx, id, actionDigest, expected) {
4361
4368
  }
4362
4369
  });
4363
4370
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4364
- 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`);
4365
4372
  const { book: _book, ...stored } = row;
4366
4373
  const unhydrated = receiptAsWritten(stored);
4367
4374
  if (!await verifyBrandApprovalReceipt(unhydrated))
@@ -4377,7 +4384,7 @@ async function activePalette(ctx, book) {
4377
4384
  }
4378
4385
  });
4379
4386
  const palette = (result[BRAND_NS.palette] ?? [])[0];
4380
- 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(
4381
4388
  "active palette is missing valid approval provenance"
4382
4389
  );
4383
4390
  const approval = await receipt(
@@ -4426,7 +4433,7 @@ async function approvedTypography(ctx, book) {
4426
4433
  });
4427
4434
  const section = (result[BRAND_NS.section] ?? [])[0];
4428
4435
  if (!section || section.status !== "approved") return {};
4429
- 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(
4430
4437
  "approved typography is missing valid approval provenance"
4431
4438
  );
4432
4439
  assertSectionContent("typography", section.content);
@@ -4467,7 +4474,7 @@ async function proposalDecisionState(ctx, req, book, proposal, resolution, recei
4467
4474
  }
4468
4475
  });
4469
4476
  sourceAsset = (result[BRAND_NS.asset] ?? [])[0];
4470
- 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(
4471
4478
  "source asset changed since the proposal was reviewed"
4472
4479
  );
4473
4480
  if (!await ctx.verifySourceAssetSnapshot({
@@ -4651,7 +4658,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
4651
4658
  }
4652
4659
  });
4653
4660
  const proposal = proposalAsWritten((result[BRAND_NS.proposal] ?? [])[0]);
4654
- 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}`);
4655
4662
  const current = proposalReviewSnapshot(proposal);
4656
4663
  const { reviewDigest, ...unsignedReview } = current;
4657
4664
  if (await brandJsonDigest(unsignedReview) !== reviewDigest || canonicalBrandJson(current) !== canonicalBrandJson(reviewed)) throw new BrandConflictError("proposal changed since review");
@@ -4795,7 +4802,7 @@ async function acceptedReceipt(db, bookId, receiptId, actionDigest) {
4795
4802
  }
4796
4803
  });
4797
4804
  const receipt2 = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4798
- 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);
4799
4806
  const { book: _book, ...stored } = receipt2;
4800
4807
  const unhydrated = receiptAsWritten(stored);
4801
4808
  if (!await verifyBrandApprovalReceipt(unhydrated)) return missing(bookId);
@@ -4811,7 +4818,7 @@ async function approvedCache(db, book) {
4811
4818
  }
4812
4819
  });
4813
4820
  const palette = (paletteResult[BRAND_NS.palette] ?? [])[0];
4814
- 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);
4815
4822
  await acceptedReceipt(
4816
4823
  db,
4817
4824
  book.id,
@@ -4829,7 +4836,7 @@ async function approvedCache(db, book) {
4829
4836
  });
4830
4837
  const typography = (sectionResult[BRAND_NS.section] ?? [])[0];
4831
4838
  if (hasTypeId) {
4832
- 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);
4833
4840
  await acceptedReceipt(
4834
4841
  db,
4835
4842
  book.id,
@@ -5234,7 +5241,7 @@ async function attachedBrandAssetIds(db, book, row) {
5234
5241
  const rows = result[BRAND_NS.asset] ?? [];
5235
5242
  if (rows.length !== 1) return null;
5236
5243
  const asset = rows[0];
5237
- 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;
5238
5245
  }));
5239
5246
  return assets.filter((id) => id !== null);
5240
5247
  }