@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.cjs CHANGED
@@ -153,11 +153,13 @@ __export(src_exports, {
153
153
  parseDesignBundle: () => parseDesignBundle,
154
154
  parseHex: () => parseHex,
155
155
  pickTextOn: () => pickTextOn,
156
+ proposalAsWritten: () => proposalAsWritten,
156
157
  proposalReviewSnapshot: () => proposalReviewSnapshot,
157
158
  proposePaletteOps: () => proposePaletteOps,
158
159
  proposeSectionOps: () => proposeSectionOps,
159
160
  readDesignManifest: () => readDesignManifest,
160
161
  readTools: () => readTools,
162
+ receiptAsWritten: () => receiptAsWritten,
161
163
  recordAnalysisOps: () => recordAnalysisOps,
162
164
  rejectProposalOps: () => rejectProposalOps,
163
165
  relativeLuminance: () => relativeLuminance,
@@ -167,6 +169,7 @@ __export(src_exports, {
167
169
  rgbToHsl: () => rgbToHsl,
168
170
  rgbToOklab: () => rgbToOklab,
169
171
  rotateHue: () => rotateHue,
172
+ rowAsWritten: () => rowAsWritten,
170
173
  safeFileName: () => safeFileName,
171
174
  scanCustomProperties: () => scanCustomProperties,
172
175
  sectionKey: () => sectionKey,
@@ -286,24 +289,6 @@ var BrandReviewStateChangedError = class extends BrandConflictError {
286
289
  }
287
290
  };
288
291
 
289
- // src/deps.ts
290
- async function defaultFetchBytes(url) {
291
- const res = await fetch(url);
292
- if (!res.ok) throw new Error(`asset fetch failed: ${res.status} for ${url}`);
293
- return {
294
- bytes: new Uint8Array(await res.arrayBuffer()),
295
- contentType: res.headers.get("content-type") ?? "application/octet-stream"
296
- };
297
- }
298
- function resolveDeps(deps) {
299
- return {
300
- db: deps.db,
301
- now: deps.now ?? Date.now,
302
- newId: deps.newId ?? (() => crypto.randomUUID()),
303
- fetchBytes: deps.fetchBytes ?? defaultFetchBytes
304
- };
305
- }
306
-
307
292
  // src/schema.ts
308
293
  var a = (type, o = {}) => ({
309
294
  type,
@@ -490,6 +475,70 @@ var BRAND_SCHEMA = {
490
475
  }
491
476
  };
492
477
 
478
+ // src/read-shape.ts
479
+ var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
480
+ var DATE_FIELDS = /* @__PURE__ */ new Map();
481
+ for (const [ns, entity] of Object.entries(BRAND_SCHEMA.entities))
482
+ DATE_FIELDS.set(
483
+ ns,
484
+ new Set(
485
+ Object.entries(entity.attrs).filter(([, attr]) => attr.type === "date").map(([label]) => label)
486
+ )
487
+ );
488
+ function rowAsWritten(ns, row) {
489
+ const dates = DATE_FIELDS.get(ns);
490
+ if (!dates?.size || !record(row)) return row;
491
+ let changed = false;
492
+ const out = { ...row };
493
+ for (const field of dates) {
494
+ const value = out[field];
495
+ if (typeof value !== "string") continue;
496
+ const parsed = Date.parse(value);
497
+ if (!Number.isSafeInteger(parsed) || parsed < 0) continue;
498
+ out[field] = parsed;
499
+ changed = true;
500
+ }
501
+ return changed ? out : row;
502
+ }
503
+ function receiptAsWritten(row) {
504
+ return rowAsWritten(BRAND_NS.approvalReceipt, row);
505
+ }
506
+ function proposalAsWritten(row) {
507
+ return rowAsWritten(BRAND_NS.proposal, row);
508
+ }
509
+ function resultAsWritten(result) {
510
+ const out = { ...result };
511
+ for (const [ns, rows] of Object.entries(out)) {
512
+ if (Array.isArray(rows)) out[ns] = rows.map((row) => rowAsWritten(ns, row));
513
+ }
514
+ return out;
515
+ }
516
+
517
+ // src/deps.ts
518
+ async function defaultFetchBytes(url) {
519
+ const res = await fetch(url);
520
+ if (!res.ok) throw new Error(`asset fetch failed: ${res.status} for ${url}`);
521
+ return {
522
+ bytes: new Uint8Array(await res.arrayBuffer()),
523
+ contentType: res.headers.get("content-type") ?? "application/octet-stream"
524
+ };
525
+ }
526
+ function readingAsWritten(db) {
527
+ return {
528
+ query: async (q) => resultAsWritten(await db.query(q)),
529
+ transact: (ops, opts) => db.transact(ops, opts),
530
+ storage: db.storage
531
+ };
532
+ }
533
+ function resolveDeps(deps) {
534
+ return {
535
+ db: readingAsWritten(deps.db),
536
+ now: deps.now ?? Date.now,
537
+ newId: deps.newId ?? (() => crypto.randomUUID()),
538
+ fetchBytes: deps.fetchBytes ?? defaultFetchBytes
539
+ };
540
+ }
541
+
493
542
  // src/rules.ts
494
543
  var CURRENT_BOOK_MEMBER = "ref('book.memberIds').exists(members, auth.id in members)";
495
544
  var BRAND_RULES = {
@@ -691,7 +740,7 @@ function assertAnalysis(value) {
691
740
  }
692
741
 
693
742
  // src/review-json.ts
694
- var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
743
+ var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
695
744
  function isBoundedBrandJson(root, limits = {}) {
696
745
  const maxDepth = limits.maxDepth ?? 12;
697
746
  const maxNodes = limits.maxNodes ?? 2048;
@@ -715,7 +764,7 @@ function isBoundedBrandJson(root, limits = {}) {
715
764
  if (Array.isArray(value)) {
716
765
  for (const child of value)
717
766
  stack.push({ value: child, depth: depth + 1 });
718
- } else if (record(value)) {
767
+ } else if (record2(value)) {
719
768
  for (const child of Object.values(value))
720
769
  stack.push({ value: child, depth: depth + 1 });
721
770
  } else {
@@ -752,7 +801,7 @@ async function brandJsonDigest(value) {
752
801
 
753
802
  // src/review.ts
754
803
  var DIGEST = /^sha256:[0-9a-f]{64}$/;
755
- var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
804
+ var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
756
805
  var exactKeys = (value, required, optional = []) => {
757
806
  const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
758
807
  return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => allowed.has(key));
@@ -777,15 +826,15 @@ var SNAPSHOT_REQUIRED = [
777
826
  "createdAt"
778
827
  ];
779
828
  function validSnapshotShape(value) {
780
- if (!record2(value) || !exactKeys(value, SNAPSHOT_REQUIRED)) return false;
829
+ if (!record3(value) || !exactKeys(value, SNAPSHOT_REQUIRED)) return false;
781
830
  const provenance = value.provenance;
782
- 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, [
831
+ 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, [
783
832
  "sourceAssetId",
784
833
  "sourceAsset",
785
834
  "messageId",
786
835
  "turnId",
787
836
  "taintLabels"
788
- ]) && (provenance.sourceAssetId === null || boundedString(provenance.sourceAssetId)) && (provenance.sourceAsset === null || record2(provenance.sourceAsset) && exactKeys(provenance.sourceAsset, [
837
+ ]) && (provenance.sourceAssetId === null || boundedString(provenance.sourceAssetId)) && (provenance.sourceAsset === null || record3(provenance.sourceAsset) && exactKeys(provenance.sourceAsset, [
789
838
  "assetId",
790
839
  "contentDigest",
791
840
  "objectEtag",
@@ -818,8 +867,8 @@ var CONSUMPTION_REQUIRED = [
818
867
  "consumedAt"
819
868
  ];
820
869
  function isBrandHumanAuthorityConsumption(value) {
821
- if (!record2(value) || !exactKeys(value, CONSUMPTION_REQUIRED)) return false;
822
- 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);
870
+ if (!record3(value) || !exactKeys(value, CONSUMPTION_REQUIRED)) return false;
871
+ 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);
823
872
  }
824
873
  var RECEIPT_REQUIRED = [
825
874
  "version",
@@ -845,10 +894,10 @@ async function verifyBrandApprovalReceipt(receipt2) {
845
894
  try {
846
895
  if (!isBoundedBrandJson(receipt2, { maxDepth: 14, maxNodes: 2500, maxBytes: 48 * 1024 }))
847
896
  return false;
848
- if (!record2(receipt2) || !exactKeys(receipt2, RECEIPT_REQUIRED, ["paletteId", "resolutionNote"]))
897
+ if (!record3(receipt2) || !exactKeys(receipt2, RECEIPT_REQUIRED, ["paletteId", "resolutionNote"]))
849
898
  return false;
850
899
  const binding = receipt2.decisionBinding;
851
- 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, [
900
+ 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, [
852
901
  "version",
853
902
  "bookVersion",
854
903
  "activationRevision",
@@ -3568,7 +3617,7 @@ async function linkedAsset(ctx, book, assetId, allowDeleting = false) {
3568
3617
  book: {}
3569
3618
  }
3570
3619
  });
3571
- const row = (result[BRAND_NS.asset] ?? [])[0];
3620
+ const row = rowAsWritten(BRAND_NS.asset, (result[BRAND_NS.asset] ?? [])[0]);
3572
3621
  const links = row?.book;
3573
3622
  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}`);
3574
3623
  return row;
@@ -3642,7 +3691,7 @@ async function loadBook(db, bookId) {
3642
3691
  const res = await db.query({ [BRAND_NS.book]: { $: { where: { id: bookId } } } });
3643
3692
  const row = (res[BRAND_NS.book] ?? [])[0];
3644
3693
  if (!row) throw new BrandNotFoundError(`brand book ${bookId}`);
3645
- return row;
3694
+ return rowAsWritten(BRAND_NS.book, row);
3646
3695
  }
3647
3696
  async function loadMemberBook(db, bookId, actorId) {
3648
3697
  const book = await loadBook(db, bookId);
@@ -3697,14 +3746,14 @@ async function uploadAsset(ctx, req, bookId) {
3697
3746
  "internal",
3698
3747
  book.id
3699
3748
  );
3700
- const record6 = await ctx.db.storage.upload(
3749
+ const record7 = await ctx.db.storage.upload(
3701
3750
  path,
3702
3751
  file,
3703
3752
  contentType,
3704
3753
  { private: true }
3705
3754
  );
3706
- if (record6.path !== path) {
3707
- await ctx.db.storage.delete(record6.path);
3755
+ if (record7.path !== path) {
3756
+ await ctx.db.storage.delete(record7.path);
3708
3757
  throw new BrandConflictError(
3709
3758
  "private storage returned an unexpected asset path"
3710
3759
  );
@@ -3723,11 +3772,11 @@ async function uploadAsset(ctx, req, bookId) {
3723
3772
  id,
3724
3773
  bookId: book.id,
3725
3774
  kind,
3726
- path: record6.path,
3727
- storageObjectId: record6.id,
3775
+ path: record7.path,
3776
+ storageObjectId: record7.id,
3728
3777
  contentDigest: await contentDigest(file),
3729
3778
  contentType,
3730
- size: record6.size,
3779
+ size: record7.size,
3731
3780
  uploadedBy: ctx.actor.id,
3732
3781
  uploadedAuthorityRef: authority.authorityRef,
3733
3782
  audience: book.memberIds,
@@ -3754,7 +3803,7 @@ async function uploadAsset(ctx, req, bookId) {
3754
3803
  asPrincipalKind: "human"
3755
3804
  });
3756
3805
  } catch (error) {
3757
- await ctx.db.storage.delete(record6.path);
3806
+ await ctx.db.storage.delete(record7.path);
3758
3807
  throw error;
3759
3808
  }
3760
3809
  return json(await linkedAsset(ctx, book, id), 201);
@@ -3869,7 +3918,7 @@ async function handleAssetItem(ctx, req, bookId, assetId) {
3869
3918
  async function handleBooksRoot(ctx, req) {
3870
3919
  if (req.method === "GET") {
3871
3920
  const res = await ctx.db.query({ [BRAND_NS.book]: { $: { order: { createdAt: "asc" } } } });
3872
- const books = (res[BRAND_NS.book] ?? []).filter(
3921
+ const books = (res[BRAND_NS.book] ?? []).map((b) => rowAsWritten(BRAND_NS.book, b)).filter(
3873
3922
  (b) => isMember(b, ctx.actor.id)
3874
3923
  );
3875
3924
  return json({ books });
@@ -4034,7 +4083,7 @@ var SNAPSHOT_KEYS = [
4034
4083
  "reviewDigest",
4035
4084
  "status"
4036
4085
  ];
4037
- var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4086
+ var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4038
4087
  function parseProposalResolutionRequest(body, bookId, proposalId) {
4039
4088
  const allowed = /* @__PURE__ */ new Set([
4040
4089
  "mutationId",
@@ -4078,13 +4127,13 @@ function proposalReviewSnapshot(proposal) {
4078
4127
  function parseReviewedProposal(value, bookId, proposalId) {
4079
4128
  if (!isBoundedBrandJson(value, { maxDepth: 12, maxNodes: 1500, maxBytes: 32 * 1024 }))
4080
4129
  throw new BrandInputError('"reviewedProposal" is too deeply nested, complex, or large');
4081
- if (!record3(value)) throw new BrandInputError('"reviewedProposal" must be an object');
4130
+ if (!record4(value)) throw new BrandInputError('"reviewedProposal" must be an object');
4082
4131
  const keys = Object.keys(value).sort();
4083
4132
  if (keys.length !== SNAPSHOT_KEYS.length || !SNAPSHOT_KEYS.every((key, index) => key === keys[index])) throw new BrandInputError('"reviewedProposal" has an invalid shape');
4084
4133
  if (value.id !== proposalId || value.bookId !== bookId || value.status !== "open")
4085
4134
  throw new BrandInputError('"reviewedProposal" must identify this open proposal');
4086
- 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');
4087
- 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(
4135
+ 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');
4136
+ 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(
4088
4137
  value.provenance.sourceAsset.analysisDigest
4089
4138
  )) || 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');
4090
4139
  return value;
@@ -4257,7 +4306,7 @@ async function proposalReceiptByMutation(ctx, mutationKey) {
4257
4306
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4258
4307
  if (!row || !Array.isArray(row.book) || row.book.length !== 1 || row.book[0]?.id !== row.bookId) return void 0;
4259
4308
  const { book: _book, ...receipt2 } = row;
4260
- return receipt2;
4309
+ return receiptAsWritten(receipt2);
4261
4310
  }
4262
4311
  async function consumeProposalAuthority(ctx, req, bookId, proposalId, actionDigest, mutationKey) {
4263
4312
  const authority = await ctx.consumeHumanExact({
@@ -4291,7 +4340,8 @@ async function receipt(ctx, id, actionDigest, expected) {
4291
4340
  });
4292
4341
  const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4293
4342
  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`);
4294
- const { book: _book, ...unhydrated } = row;
4343
+ const { book: _book, ...stored } = row;
4344
+ const unhydrated = receiptAsWritten(stored);
4295
4345
  if (!await verifyBrandApprovalReceipt(unhydrated))
4296
4346
  throw new BrandConflictError(`approval receipt ${id} is invalid`);
4297
4347
  return unhydrated;
@@ -4529,9 +4579,9 @@ async function proposalDecisionState(ctx, req, book, proposal, resolution, recei
4529
4579
  }
4530
4580
 
4531
4581
  // src/routes/proposals.ts
4532
- var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4582
+ var record5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4533
4583
  var sameStrings2 = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
4534
- var guardFailure2 = (error) => record4(error) && error.code === "transact_guard_failed";
4584
+ var guardFailure2 = (error) => record5(error) && error.code === "transact_guard_failed";
4535
4585
  async function handleProposalResolution(ctx, req, bookId, proposalId) {
4536
4586
  if (req.method !== "POST") return methodNotAllowed();
4537
4587
  const book = await loadMemberBook(ctx.db, bookId, ctx.actor.id);
@@ -4578,7 +4628,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
4578
4628
  book: {}
4579
4629
  }
4580
4630
  });
4581
- const proposal = (result[BRAND_NS.proposal] ?? [])[0];
4631
+ const proposal = proposalAsWritten((result[BRAND_NS.proposal] ?? [])[0]);
4582
4632
  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}`);
4583
4633
  const current = proposalReviewSnapshot(proposal);
4584
4634
  const { reviewDigest, ...unsignedReview } = current;
@@ -4711,7 +4761,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
4711
4761
  }
4712
4762
 
4713
4763
  // src/routes/tokens.ts
4714
- var record5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4764
+ var record6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4715
4765
  var missing = (bookId) => {
4716
4766
  throw new BrandNotFoundError(`compiled tokens for brand book ${bookId}`);
4717
4767
  };
@@ -4724,13 +4774,14 @@ async function acceptedReceipt(db, bookId, receiptId, actionDigest) {
4724
4774
  });
4725
4775
  const receipt2 = (result[BRAND_NS.approvalReceipt] ?? [])[0];
4726
4776
  if (!receipt2 || receipt2.resolution !== "accepted" || receipt2.actionDigest !== actionDigest || !Array.isArray(receipt2.book) || receipt2.book.length !== 1 || receipt2.book[0]?.id !== bookId) return missing(bookId);
4727
- const { book: _book, ...unhydrated } = receipt2;
4777
+ const { book: _book, ...stored } = receipt2;
4778
+ const unhydrated = receiptAsWritten(stored);
4728
4779
  if (!await verifyBrandApprovalReceipt(unhydrated)) return missing(bookId);
4729
4780
  return unhydrated;
4730
4781
  }
4731
4782
  async function approvedCache(db, book) {
4732
4783
  const cache = book.tokens;
4733
- if (!cache || !record5(cache.light) || !record5(cache.dark) || !book.activePaletteId || cache.paletteId !== book.activePaletteId || !cache.paletteReceiptId || !cache.paletteActionDigest || !cache.sourceDigest) return missing(book.id);
4784
+ if (!cache || !record6(cache.light) || !record6(cache.dark) || !book.activePaletteId || cache.paletteId !== book.activePaletteId || !cache.paletteReceiptId || !cache.paletteActionDigest || !cache.sourceDigest) return missing(book.id);
4734
4785
  const paletteResult = await db.query({
4735
4786
  [BRAND_NS.palette]: {
4736
4787
  $: { where: { id: cache.paletteId, bookId: book.id } },