@odla-ai/brand 0.5.0 → 0.6.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 +69 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +69 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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,
|
|
@@ -690,8 +693,40 @@ function assertAnalysis(value) {
|
|
|
690
693
|
};
|
|
691
694
|
}
|
|
692
695
|
|
|
696
|
+
// src/read-shape.ts
|
|
697
|
+
var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
698
|
+
var DATE_FIELDS = /* @__PURE__ */ new Map();
|
|
699
|
+
for (const [ns, entity] of Object.entries(BRAND_SCHEMA.entities))
|
|
700
|
+
DATE_FIELDS.set(
|
|
701
|
+
ns,
|
|
702
|
+
new Set(
|
|
703
|
+
Object.entries(entity.attrs).filter(([, attr]) => attr.type === "date").map(([label]) => label)
|
|
704
|
+
)
|
|
705
|
+
);
|
|
706
|
+
function rowAsWritten(ns, row) {
|
|
707
|
+
const dates = DATE_FIELDS.get(ns);
|
|
708
|
+
if (!dates?.size || !record(row)) return row;
|
|
709
|
+
let changed = false;
|
|
710
|
+
const out = { ...row };
|
|
711
|
+
for (const field of dates) {
|
|
712
|
+
const value = out[field];
|
|
713
|
+
if (typeof value !== "string") continue;
|
|
714
|
+
const parsed = Date.parse(value);
|
|
715
|
+
if (!Number.isSafeInteger(parsed) || parsed < 0) continue;
|
|
716
|
+
out[field] = parsed;
|
|
717
|
+
changed = true;
|
|
718
|
+
}
|
|
719
|
+
return changed ? out : row;
|
|
720
|
+
}
|
|
721
|
+
function receiptAsWritten(row) {
|
|
722
|
+
return rowAsWritten(BRAND_NS.approvalReceipt, row);
|
|
723
|
+
}
|
|
724
|
+
function proposalAsWritten(row) {
|
|
725
|
+
return rowAsWritten(BRAND_NS.proposal, row);
|
|
726
|
+
}
|
|
727
|
+
|
|
693
728
|
// src/review-json.ts
|
|
694
|
-
var
|
|
729
|
+
var record2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
695
730
|
function isBoundedBrandJson(root, limits = {}) {
|
|
696
731
|
const maxDepth = limits.maxDepth ?? 12;
|
|
697
732
|
const maxNodes = limits.maxNodes ?? 2048;
|
|
@@ -715,7 +750,7 @@ function isBoundedBrandJson(root, limits = {}) {
|
|
|
715
750
|
if (Array.isArray(value)) {
|
|
716
751
|
for (const child of value)
|
|
717
752
|
stack.push({ value: child, depth: depth + 1 });
|
|
718
|
-
} else if (
|
|
753
|
+
} else if (record2(value)) {
|
|
719
754
|
for (const child of Object.values(value))
|
|
720
755
|
stack.push({ value: child, depth: depth + 1 });
|
|
721
756
|
} else {
|
|
@@ -752,7 +787,7 @@ async function brandJsonDigest(value) {
|
|
|
752
787
|
|
|
753
788
|
// src/review.ts
|
|
754
789
|
var DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
755
|
-
var
|
|
790
|
+
var record3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
|
|
756
791
|
var exactKeys = (value, required, optional = []) => {
|
|
757
792
|
const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
|
|
758
793
|
return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => allowed.has(key));
|
|
@@ -777,15 +812,15 @@ var SNAPSHOT_REQUIRED = [
|
|
|
777
812
|
"createdAt"
|
|
778
813
|
];
|
|
779
814
|
function validSnapshotShape(value) {
|
|
780
|
-
if (!
|
|
815
|
+
if (!record3(value) || !exactKeys(value, SNAPSHOT_REQUIRED)) return false;
|
|
781
816
|
const provenance = value.provenance;
|
|
782
|
-
return boundedString(value.id) && boundedString(value.bookId) && typeof value.kind === "string" && PROPOSAL_KINDS.includes(value.kind) && value.status === "open" &&
|
|
817
|
+
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
818
|
"sourceAssetId",
|
|
784
819
|
"sourceAsset",
|
|
785
820
|
"messageId",
|
|
786
821
|
"turnId",
|
|
787
822
|
"taintLabels"
|
|
788
|
-
]) && (provenance.sourceAssetId === null || boundedString(provenance.sourceAssetId)) && (provenance.sourceAsset === null ||
|
|
823
|
+
]) && (provenance.sourceAssetId === null || boundedString(provenance.sourceAssetId)) && (provenance.sourceAsset === null || record3(provenance.sourceAsset) && exactKeys(provenance.sourceAsset, [
|
|
789
824
|
"assetId",
|
|
790
825
|
"contentDigest",
|
|
791
826
|
"objectEtag",
|
|
@@ -818,8 +853,8 @@ var CONSUMPTION_REQUIRED = [
|
|
|
818
853
|
"consumedAt"
|
|
819
854
|
];
|
|
820
855
|
function isBrandHumanAuthorityConsumption(value) {
|
|
821
|
-
if (!
|
|
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) &&
|
|
856
|
+
if (!record3(value) || !exactKeys(value, CONSUMPTION_REQUIRED)) return false;
|
|
857
|
+
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
858
|
}
|
|
824
859
|
var RECEIPT_REQUIRED = [
|
|
825
860
|
"version",
|
|
@@ -845,10 +880,10 @@ async function verifyBrandApprovalReceipt(receipt2) {
|
|
|
845
880
|
try {
|
|
846
881
|
if (!isBoundedBrandJson(receipt2, { maxDepth: 14, maxNodes: 2500, maxBytes: 48 * 1024 }))
|
|
847
882
|
return false;
|
|
848
|
-
if (!
|
|
883
|
+
if (!record3(receipt2) || !exactKeys(receipt2, RECEIPT_REQUIRED, ["paletteId", "resolutionNote"]))
|
|
849
884
|
return false;
|
|
850
885
|
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) || !
|
|
886
|
+
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
887
|
"version",
|
|
853
888
|
"bookVersion",
|
|
854
889
|
"activationRevision",
|
|
@@ -3568,7 +3603,7 @@ async function linkedAsset(ctx, book, assetId, allowDeleting = false) {
|
|
|
3568
3603
|
book: {}
|
|
3569
3604
|
}
|
|
3570
3605
|
});
|
|
3571
|
-
const row = (result[BRAND_NS.asset] ?? [])[0];
|
|
3606
|
+
const row = rowAsWritten(BRAND_NS.asset, (result[BRAND_NS.asset] ?? [])[0]);
|
|
3572
3607
|
const links = row?.book;
|
|
3573
3608
|
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
3609
|
return row;
|
|
@@ -3642,7 +3677,7 @@ async function loadBook(db, bookId) {
|
|
|
3642
3677
|
const res = await db.query({ [BRAND_NS.book]: { $: { where: { id: bookId } } } });
|
|
3643
3678
|
const row = (res[BRAND_NS.book] ?? [])[0];
|
|
3644
3679
|
if (!row) throw new BrandNotFoundError(`brand book ${bookId}`);
|
|
3645
|
-
return row;
|
|
3680
|
+
return rowAsWritten(BRAND_NS.book, row);
|
|
3646
3681
|
}
|
|
3647
3682
|
async function loadMemberBook(db, bookId, actorId) {
|
|
3648
3683
|
const book = await loadBook(db, bookId);
|
|
@@ -3697,14 +3732,14 @@ async function uploadAsset(ctx, req, bookId) {
|
|
|
3697
3732
|
"internal",
|
|
3698
3733
|
book.id
|
|
3699
3734
|
);
|
|
3700
|
-
const
|
|
3735
|
+
const record7 = await ctx.db.storage.upload(
|
|
3701
3736
|
path,
|
|
3702
3737
|
file,
|
|
3703
3738
|
contentType,
|
|
3704
3739
|
{ private: true }
|
|
3705
3740
|
);
|
|
3706
|
-
if (
|
|
3707
|
-
await ctx.db.storage.delete(
|
|
3741
|
+
if (record7.path !== path) {
|
|
3742
|
+
await ctx.db.storage.delete(record7.path);
|
|
3708
3743
|
throw new BrandConflictError(
|
|
3709
3744
|
"private storage returned an unexpected asset path"
|
|
3710
3745
|
);
|
|
@@ -3723,11 +3758,11 @@ async function uploadAsset(ctx, req, bookId) {
|
|
|
3723
3758
|
id,
|
|
3724
3759
|
bookId: book.id,
|
|
3725
3760
|
kind,
|
|
3726
|
-
path:
|
|
3727
|
-
storageObjectId:
|
|
3761
|
+
path: record7.path,
|
|
3762
|
+
storageObjectId: record7.id,
|
|
3728
3763
|
contentDigest: await contentDigest(file),
|
|
3729
3764
|
contentType,
|
|
3730
|
-
size:
|
|
3765
|
+
size: record7.size,
|
|
3731
3766
|
uploadedBy: ctx.actor.id,
|
|
3732
3767
|
uploadedAuthorityRef: authority.authorityRef,
|
|
3733
3768
|
audience: book.memberIds,
|
|
@@ -3754,7 +3789,7 @@ async function uploadAsset(ctx, req, bookId) {
|
|
|
3754
3789
|
asPrincipalKind: "human"
|
|
3755
3790
|
});
|
|
3756
3791
|
} catch (error) {
|
|
3757
|
-
await ctx.db.storage.delete(
|
|
3792
|
+
await ctx.db.storage.delete(record7.path);
|
|
3758
3793
|
throw error;
|
|
3759
3794
|
}
|
|
3760
3795
|
return json(await linkedAsset(ctx, book, id), 201);
|
|
@@ -3869,7 +3904,7 @@ async function handleAssetItem(ctx, req, bookId, assetId) {
|
|
|
3869
3904
|
async function handleBooksRoot(ctx, req) {
|
|
3870
3905
|
if (req.method === "GET") {
|
|
3871
3906
|
const res = await ctx.db.query({ [BRAND_NS.book]: { $: { order: { createdAt: "asc" } } } });
|
|
3872
|
-
const books = (res[BRAND_NS.book] ?? []).filter(
|
|
3907
|
+
const books = (res[BRAND_NS.book] ?? []).map((b) => rowAsWritten(BRAND_NS.book, b)).filter(
|
|
3873
3908
|
(b) => isMember(b, ctx.actor.id)
|
|
3874
3909
|
);
|
|
3875
3910
|
return json({ books });
|
|
@@ -4034,7 +4069,7 @@ var SNAPSHOT_KEYS = [
|
|
|
4034
4069
|
"reviewDigest",
|
|
4035
4070
|
"status"
|
|
4036
4071
|
];
|
|
4037
|
-
var
|
|
4072
|
+
var record4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4038
4073
|
function parseProposalResolutionRequest(body, bookId, proposalId) {
|
|
4039
4074
|
const allowed = /* @__PURE__ */ new Set([
|
|
4040
4075
|
"mutationId",
|
|
@@ -4078,13 +4113,13 @@ function proposalReviewSnapshot(proposal) {
|
|
|
4078
4113
|
function parseReviewedProposal(value, bookId, proposalId) {
|
|
4079
4114
|
if (!isBoundedBrandJson(value, { maxDepth: 12, maxNodes: 1500, maxBytes: 32 * 1024 }))
|
|
4080
4115
|
throw new BrandInputError('"reviewedProposal" is too deeply nested, complex, or large');
|
|
4081
|
-
if (!
|
|
4116
|
+
if (!record4(value)) throw new BrandInputError('"reviewedProposal" must be an object');
|
|
4082
4117
|
const keys = Object.keys(value).sort();
|
|
4083
4118
|
if (keys.length !== SNAPSHOT_KEYS.length || !SNAPSHOT_KEYS.every((key, index) => key === keys[index])) throw new BrandInputError('"reviewedProposal" has an invalid shape');
|
|
4084
4119
|
if (value.id !== proposalId || value.bookId !== bookId || value.status !== "open")
|
|
4085
4120
|
throw new BrandInputError('"reviewedProposal" must identify this open proposal');
|
|
4086
|
-
if (typeof value.kind !== "string" || !PROPOSAL_KINDS.includes(value.kind) || !
|
|
4087
|
-
if (!
|
|
4121
|
+
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');
|
|
4122
|
+
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
4123
|
value.provenance.sourceAsset.analysisDigest
|
|
4089
4124
|
)) || 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
4125
|
return value;
|
|
@@ -4257,7 +4292,7 @@ async function proposalReceiptByMutation(ctx, mutationKey) {
|
|
|
4257
4292
|
const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
|
|
4258
4293
|
if (!row || !Array.isArray(row.book) || row.book.length !== 1 || row.book[0]?.id !== row.bookId) return void 0;
|
|
4259
4294
|
const { book: _book, ...receipt2 } = row;
|
|
4260
|
-
return receipt2;
|
|
4295
|
+
return receiptAsWritten(receipt2);
|
|
4261
4296
|
}
|
|
4262
4297
|
async function consumeProposalAuthority(ctx, req, bookId, proposalId, actionDigest, mutationKey) {
|
|
4263
4298
|
const authority = await ctx.consumeHumanExact({
|
|
@@ -4291,7 +4326,8 @@ async function receipt(ctx, id, actionDigest, expected) {
|
|
|
4291
4326
|
});
|
|
4292
4327
|
const row = (result[BRAND_NS.approvalReceipt] ?? [])[0];
|
|
4293
4328
|
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, ...
|
|
4329
|
+
const { book: _book, ...stored } = row;
|
|
4330
|
+
const unhydrated = receiptAsWritten(stored);
|
|
4295
4331
|
if (!await verifyBrandApprovalReceipt(unhydrated))
|
|
4296
4332
|
throw new BrandConflictError(`approval receipt ${id} is invalid`);
|
|
4297
4333
|
return unhydrated;
|
|
@@ -4529,9 +4565,9 @@ async function proposalDecisionState(ctx, req, book, proposal, resolution, recei
|
|
|
4529
4565
|
}
|
|
4530
4566
|
|
|
4531
4567
|
// src/routes/proposals.ts
|
|
4532
|
-
var
|
|
4568
|
+
var record5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4533
4569
|
var sameStrings2 = (left, right) => left.length === right.length && left.every((value, index) => value === right[index]);
|
|
4534
|
-
var guardFailure2 = (error) =>
|
|
4570
|
+
var guardFailure2 = (error) => record5(error) && error.code === "transact_guard_failed";
|
|
4535
4571
|
async function handleProposalResolution(ctx, req, bookId, proposalId) {
|
|
4536
4572
|
if (req.method !== "POST") return methodNotAllowed();
|
|
4537
4573
|
const book = await loadMemberBook(ctx.db, bookId, ctx.actor.id);
|
|
@@ -4578,7 +4614,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
|
|
|
4578
4614
|
book: {}
|
|
4579
4615
|
}
|
|
4580
4616
|
});
|
|
4581
|
-
const proposal = (result[BRAND_NS.proposal] ?? [])[0];
|
|
4617
|
+
const proposal = proposalAsWritten((result[BRAND_NS.proposal] ?? [])[0]);
|
|
4582
4618
|
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
4619
|
const current = proposalReviewSnapshot(proposal);
|
|
4584
4620
|
const { reviewDigest, ...unsignedReview } = current;
|
|
@@ -4711,7 +4747,7 @@ async function handleProposalResolution(ctx, req, bookId, proposalId) {
|
|
|
4711
4747
|
}
|
|
4712
4748
|
|
|
4713
4749
|
// src/routes/tokens.ts
|
|
4714
|
-
var
|
|
4750
|
+
var record6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4715
4751
|
var missing = (bookId) => {
|
|
4716
4752
|
throw new BrandNotFoundError(`compiled tokens for brand book ${bookId}`);
|
|
4717
4753
|
};
|
|
@@ -4724,13 +4760,14 @@ async function acceptedReceipt(db, bookId, receiptId, actionDigest) {
|
|
|
4724
4760
|
});
|
|
4725
4761
|
const receipt2 = (result[BRAND_NS.approvalReceipt] ?? [])[0];
|
|
4726
4762
|
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, ...
|
|
4763
|
+
const { book: _book, ...stored } = receipt2;
|
|
4764
|
+
const unhydrated = receiptAsWritten(stored);
|
|
4728
4765
|
if (!await verifyBrandApprovalReceipt(unhydrated)) return missing(bookId);
|
|
4729
4766
|
return unhydrated;
|
|
4730
4767
|
}
|
|
4731
4768
|
async function approvedCache(db, book) {
|
|
4732
4769
|
const cache = book.tokens;
|
|
4733
|
-
if (!cache || !
|
|
4770
|
+
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
4771
|
const paletteResult = await db.query({
|
|
4735
4772
|
[BRAND_NS.palette]: {
|
|
4736
4773
|
$: { where: { id: cache.paletteId, bookId: book.id } },
|