@growth-labs/cms 0.5.29 → 0.5.31
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/engine/foundry-dispatch.d.ts +7 -2
- package/dist/engine/foundry-dispatch.d.ts.map +1 -1
- package/dist/engine/foundry-dispatch.js +24 -6
- package/dist/engine/foundry-dispatch.js.map +1 -1
- package/dist/engine/fronts-publish-intent.d.ts +174 -0
- package/dist/engine/fronts-publish-intent.d.ts.map +1 -0
- package/dist/engine/fronts-publish-intent.js +352 -0
- package/dist/engine/fronts-publish-intent.js.map +1 -0
- package/dist/engine/fronts-publish.d.ts +80 -0
- package/dist/engine/fronts-publish.d.ts.map +1 -0
- package/dist/engine/fronts-publish.js +286 -0
- package/dist/engine/fronts-publish.js.map +1 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +10 -0
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/publication-continuity.d.ts +77 -0
- package/dist/engine/publication-continuity.d.ts.map +1 -0
- package/dist/engine/publication-continuity.js +94 -0
- package/dist/engine/publication-continuity.js.map +1 -0
- package/dist/engine/publication-receipt/canonical-json.d.ts +34 -0
- package/dist/engine/publication-receipt/canonical-json.d.ts.map +1 -0
- package/dist/engine/publication-receipt/canonical-json.js +100 -0
- package/dist/engine/publication-receipt/canonical-json.js.map +1 -0
- package/dist/engine/publication-receipt/receipt.d.ts +316 -0
- package/dist/engine/publication-receipt/receipt.d.ts.map +1 -0
- package/dist/engine/publication-receipt/receipt.js +131 -0
- package/dist/engine/publication-receipt/receipt.js.map +1 -0
- package/dist/engine/publication-receipt/signature.d.ts +36 -0
- package/dist/engine/publication-receipt/signature.d.ts.map +1 -0
- package/dist/engine/publication-receipt/signature.js +79 -0
- package/dist/engine/publication-receipt/signature.js.map +1 -0
- package/dist/engine/publication.d.ts.map +1 -1
- package/dist/engine/publication.js +2 -0
- package/dist/engine/publication.js.map +1 -1
- package/dist/engine/publisher.d.ts.map +1 -1
- package/dist/engine/publisher.js +23 -1
- package/dist/engine/publisher.js.map +1 -1
- package/dist/engine/source-sections.d.ts +20 -0
- package/dist/engine/source-sections.d.ts.map +1 -0
- package/dist/engine/source-sections.js +130 -0
- package/dist/engine/source-sections.js.map +1 -0
- package/dist/engine/validator/checks/source-section-diagnostics.d.ts +7 -0
- package/dist/engine/validator/checks/source-section-diagnostics.d.ts.map +1 -0
- package/dist/engine/validator/checks/source-section-diagnostics.js +120 -0
- package/dist/engine/validator/checks/source-section-diagnostics.js.map +1 -0
- package/dist/engine/validator/index.d.ts +10 -0
- package/dist/engine/validator/index.d.ts.map +1 -1
- package/dist/engine/validator/index.js +4 -0
- package/dist/engine/validator/index.js.map +1 -1
- package/dist/engine/validator/types.d.ts +1 -1
- package/dist/engine/validator/types.d.ts.map +1 -1
- package/dist/engine/validator/types.js +10 -0
- package/dist/engine/validator/types.js.map +1 -1
- package/dist/integration/index.d.ts.map +1 -1
- package/dist/integration/index.js +10 -0
- package/dist/integration/index.js.map +1 -1
- package/dist/providers/types.d.ts +1 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/routes/content.d.ts.map +1 -1
- package/dist/routes/content.js +22 -2
- package/dist/routes/content.js.map +1 -1
- package/dist/routes/fronts-publish.d.ts +10 -0
- package/dist/routes/fronts-publish.d.ts.map +1 -0
- package/dist/routes/fronts-publish.js +250 -0
- package/dist/routes/fronts-publish.js.map +1 -0
- package/dist/routes/index.d.ts +3 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +4 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/surveys.d.ts.map +1 -1
- package/dist/routes/surveys.js +13 -1
- package/dist/routes/surveys.js.map +1 -1
- package/dist/schema/insights-ingest.d.ts +28 -28
- package/dist/schema/layout.d.ts +2 -2
- package/dist/schema/migrations.d.ts.map +1 -1
- package/dist/schema/migrations.js +36 -0
- package/dist/schema/migrations.js.map +1 -1
- package/dist/schema/portable-text.d.ts +2 -2
- package/dist/schema/tables.d.ts +1 -1
- package/dist/schema/tables.d.ts.map +1 -1
- package/dist/schema/tables.js +2 -0
- package/dist/schema/tables.js.map +1 -1
- package/dist/schema/types.d.ts +59 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js.map +1 -1
- package/dist/surveys/access.d.ts +2 -0
- package/dist/surveys/access.d.ts.map +1 -1
- package/dist/surveys/access.js +7 -0
- package/dist/surveys/access.js.map +1 -1
- package/dist/surveys/errors.d.ts +8 -0
- package/dist/surveys/errors.d.ts.map +1 -1
- package/dist/surveys/errors.js +12 -0
- package/dist/surveys/errors.js.map +1 -1
- package/dist/surveys/schema.d.ts +129 -54
- package/dist/surveys/schema.d.ts.map +1 -1
- package/dist/surveys/schema.js +45 -0
- package/dist/surveys/schema.js.map +1 -1
- package/dist/ui/api/fronts/publish-callback.d.ts +3 -0
- package/dist/ui/api/fronts/publish-callback.d.ts.map +1 -0
- package/dist/ui/api/fronts/publish-callback.js +8 -0
- package/dist/ui/api/fronts/publish-callback.js.map +1 -0
- package/dist/ui/api/fronts/schedule.d.ts +3 -0
- package/dist/ui/api/fronts/schedule.d.ts.map +1 -0
- package/dist/ui/api/fronts/schedule.js +8 -0
- package/dist/ui/api/fronts/schedule.js.map +1 -0
- package/dist/ui/editor/ContentForm.d.ts.map +1 -1
- package/dist/ui/editor/ContentForm.js +52 -1
- package/dist/ui/editor/ContentForm.js.map +1 -1
- package/dist/ui/editor/body-warnings.d.ts +27 -0
- package/dist/ui/editor/body-warnings.d.ts.map +1 -0
- package/dist/ui/editor/body-warnings.js +58 -0
- package/dist/ui/editor/body-warnings.js.map +1 -0
- package/dist/ui/screens/SurveyResponsesScreen.d.ts +7 -1
- package/dist/ui/screens/SurveyResponsesScreen.d.ts.map +1 -1
- package/dist/ui/screens/SurveyResponsesScreen.js +141 -32
- package/dist/ui/screens/SurveyResponsesScreen.js.map +1 -1
- package/migrations/0026_fronts_publish.sql +31 -0
- package/migrations/0027_fronts_publish_intent.sql +44 -0
- package/package.json +1 -1
- package/src/engine/foundry-dispatch.ts +22 -6
- package/src/engine/fronts-publish-intent.ts +676 -0
- package/src/engine/fronts-publish.ts +417 -0
- package/src/engine/index.ts +60 -0
- package/src/engine/publication-continuity.ts +140 -0
- package/src/engine/publication-receipt/canonical-json.ts +123 -0
- package/src/engine/publication-receipt/receipt.ts +160 -0
- package/src/engine/publication-receipt/signature.ts +112 -0
- package/src/engine/publication.ts +2 -0
- package/src/engine/publisher.ts +23 -1
- package/src/engine/source-sections.ts +153 -0
- package/src/engine/validator/checks/source-section-diagnostics.ts +187 -0
- package/src/engine/validator/index.ts +12 -0
- package/src/engine/validator/types.ts +19 -0
- package/src/integration/index.ts +10 -0
- package/src/providers/types.ts +1 -1
- package/src/routes/content.ts +23 -2
- package/src/routes/fronts-publish.ts +292 -0
- package/src/routes/index.ts +8 -0
- package/src/routes/surveys.ts +12 -1
- package/src/schema/migrations.ts +36 -0
- package/src/schema/tables.ts +2 -0
- package/src/schema/types.ts +61 -0
- package/src/surveys/access.ts +9 -0
- package/src/surveys/errors.ts +12 -0
- package/src/surveys/schema.ts +47 -0
- package/src/ui/api/fronts/publish-callback.ts +18 -0
- package/src/ui/api/fronts/schedule.ts +18 -0
- package/src/ui/editor/ContentForm.tsx +93 -0
- package/src/ui/editor/body-warnings.ts +78 -0
- package/src/ui/screens/SurveyResponsesScreen.tsx +369 -121
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Publication continuity decisions (#342).
|
|
2
|
+
//
|
|
3
|
+
// Extracted from the Warfronts publish bridge so every site shares one rule set
|
|
4
|
+
// instead of each re-deriving it. These are the decisions a continuity run makes
|
|
5
|
+
// before it touches the database: is this item retired, did the published
|
|
6
|
+
// revision drift from the archive, and does a live probe response mean success.
|
|
7
|
+
//
|
|
8
|
+
// All pure. The bridge's DB, network and filesystem work stays site-owned; only
|
|
9
|
+
// the judgement moves here.
|
|
10
|
+
/** An item the archive has taken down rather than published. */
|
|
11
|
+
export function isRetiredItem(item) {
|
|
12
|
+
return item?.metadata?.originalStatus === 'archived';
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Compare the fields a reader actually sees against the published revision.
|
|
16
|
+
*
|
|
17
|
+
* The site renders from the published revision payload, so an edit to any field
|
|
18
|
+
* this does not examine is classified "already in sync", skipped, and never
|
|
19
|
+
* reaches a reader — while the run reports success. That has stranded real work
|
|
20
|
+
* twice: fourteen restored article bodies, and a `featured` flag flip, both
|
|
21
|
+
* merged, both reporting `failed=0`, neither ever visible.
|
|
22
|
+
*
|
|
23
|
+
* Comparison is exact rather than a length or prefix heuristic. The entire class
|
|
24
|
+
* of bug being prevented is a cheap test that silently misses a real edit: a
|
|
25
|
+
* false "stale" costs one idempotent republish, a false "in sync" loses the edit.
|
|
26
|
+
*
|
|
27
|
+
* NOT COMPARED: tags. The revision stores them canonicalized and sorted while
|
|
28
|
+
* the archive carries raw frontmatter strings, and the engine exports no
|
|
29
|
+
* canonicalizer to reuse. Detecting a field this cannot also REPAIR would be
|
|
30
|
+
* worse than not detecting it — tag writes go through the engine, so a tag-only
|
|
31
|
+
* drift would republish unchanged and stay stale on every run, forever.
|
|
32
|
+
*/
|
|
33
|
+
export function payloadDrift(item, row) {
|
|
34
|
+
// The engine stores an absent body as a single space, so normalize the same
|
|
35
|
+
// way before comparing or the first resync of a body-less article would
|
|
36
|
+
// churn forever.
|
|
37
|
+
const body = item.bodyMarkdown ?? ' ';
|
|
38
|
+
const title = String(item.title ?? '');
|
|
39
|
+
const description = item.description ?? null;
|
|
40
|
+
const featured = item.featured ? 1 : 0;
|
|
41
|
+
const heroImageId = item.heroImageId ?? null;
|
|
42
|
+
const visibility = item.visibility === 'premium' ? 'premium' : 'free';
|
|
43
|
+
const channel = item.primaryCategory ?? null;
|
|
44
|
+
const fields = [];
|
|
45
|
+
if (row.rev_title !== title)
|
|
46
|
+
fields.push('title');
|
|
47
|
+
if ((row.rev_description ?? null) !== description)
|
|
48
|
+
fields.push('description');
|
|
49
|
+
if (Number(row.rev_featured ?? 0) !== featured)
|
|
50
|
+
fields.push('featured');
|
|
51
|
+
if ((row.rev_hero_image_id ?? null) !== heroImageId)
|
|
52
|
+
fields.push('heroImage');
|
|
53
|
+
if ((row.rev_visibility ?? 'free') !== visibility)
|
|
54
|
+
fields.push('visibility');
|
|
55
|
+
if ((row.rev_channel ?? null) !== channel)
|
|
56
|
+
fields.push('channel');
|
|
57
|
+
// A revision predating this check can lack the key entirely; treat an absent
|
|
58
|
+
// body as drift rather than silently matching.
|
|
59
|
+
if ((row.rev_body_markdown ?? null) !== body)
|
|
60
|
+
fields.push('body');
|
|
61
|
+
return {
|
|
62
|
+
drifted: fields.length > 0,
|
|
63
|
+
fields,
|
|
64
|
+
title,
|
|
65
|
+
description,
|
|
66
|
+
featured,
|
|
67
|
+
heroImageId,
|
|
68
|
+
visibility,
|
|
69
|
+
channel,
|
|
70
|
+
body,
|
|
71
|
+
bodyDrifted: fields.includes('body'),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Whether a live probe response means the publication succeeded.
|
|
76
|
+
*
|
|
77
|
+
* LIVE → 200 only.
|
|
78
|
+
* RETIRED → 301/308 (redirected to a surviving twin) or 404 (gone, with no
|
|
79
|
+
* honest target to redirect to).
|
|
80
|
+
*
|
|
81
|
+
* The invariant: **a retired article must never answer 200.** That is the
|
|
82
|
+
* stale-row symptom a teardown exists to remove, and it is the only status this
|
|
83
|
+
* rejects for a retirement. Demanding 200 for retirements instead would fail the
|
|
84
|
+
* run forever on exactly the articles the archive deliberately took down.
|
|
85
|
+
*
|
|
86
|
+
* 301 and 308 are both permanent; a server may answer GET with one and other
|
|
87
|
+
* methods with the other, so both are accepted.
|
|
88
|
+
*/
|
|
89
|
+
export function acceptsProbeStatus(status, retired) {
|
|
90
|
+
if (!retired)
|
|
91
|
+
return status === 200;
|
|
92
|
+
return status === 301 || status === 308 || status === 404;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=publication-continuity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publication-continuity.js","sourceRoot":"","sources":["../../src/engine/publication-continuity.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,0EAA0E;AAC1E,gFAAgF;AAChF,EAAE;AACF,gFAAgF;AAChF,4BAA4B;AAiD5B,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,IAA8C;IAC3E,OAAO,IAAI,EAAE,QAAQ,EAAE,cAAc,KAAK,UAAU,CAAA;AACrD,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,YAAY,CAC3B,IAA2B,EAC3B,GAA2B;IAE3B,4EAA4E;IAC5E,wEAAwE;IACxE,iBAAiB;IACjB,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,IAAI,GAAG,CAAA;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAA;IACtC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAA;IAC5C,MAAM,QAAQ,GAAU,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC7C,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,IAAI,CAAA;IAC5C,MAAM,UAAU,GAAuB,IAAI,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAA;IACzF,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,IAAI,IAAI,CAAA;IAE5C,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,IAAI,GAAG,CAAC,SAAS,KAAK,KAAK;QAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACjD,IAAI,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,KAAK,WAAW;QAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IAC7E,IAAI,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,KAAK,QAAQ;QAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACvE,IAAI,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,KAAK,WAAW;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC7E,IAAI,CAAC,GAAG,CAAC,cAAc,IAAI,MAAM,CAAC,KAAK,UAAU;QAAE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC5E,IAAI,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,KAAK,OAAO;QAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACjE,6EAA6E;IAC7E,+CAA+C;IAC/C,IAAI,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI,CAAC,KAAK,IAAI;QAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAEjE,OAAO;QACN,OAAO,EAAE,MAAM,CAAC,MAAM,GAAG,CAAC;QAC1B,MAAM;QACN,KAAK;QACL,WAAW;QACX,QAAQ;QACR,WAAW;QACX,UAAU;QACV,OAAO;QACP,IAAI;QACJ,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;KACpC,CAAA;AACF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAc,EAAE,OAAgB;IAClE,IAAI,CAAC,OAAO;QAAE,OAAO,MAAM,KAAK,GAAG,CAAA;IACnC,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAA;AAC1D,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare class CanonicalJsonError extends Error {
|
|
2
|
+
readonly code: 'non_finite_number' | 'unsupported_type' | 'circular_reference';
|
|
3
|
+
constructor(code: CanonicalJsonError['code'], message: string);
|
|
4
|
+
}
|
|
5
|
+
type JsonPrimitive = string | number | boolean | null;
|
|
6
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
7
|
+
[key: string]: JsonValue;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Serialize `value` per RFC 8785.
|
|
11
|
+
*
|
|
12
|
+
* Three things do the work, and each is load-bearing:
|
|
13
|
+
*
|
|
14
|
+
* 1. Object keys sort by UTF-16 code unit. JavaScript's default string sort is
|
|
15
|
+
* already a UTF-16 code-unit comparison, which is exactly what §3.2.3
|
|
16
|
+
* requires — so `.sort()` here is the specified behaviour, not a shortcut.
|
|
17
|
+
* 2. Numbers use ECMAScript `Number::toString`, which `JSON.stringify` applies.
|
|
18
|
+
* §3.2.2.3 defers to that same algorithm, so the platform is authoritative
|
|
19
|
+
* and reimplementing it would only introduce drift.
|
|
20
|
+
* 3. Strings use `JSON.stringify`'s escaping, which since ES2019 emits
|
|
21
|
+
* well-formed output (lone surrogates escaped rather than passed through) —
|
|
22
|
+
* §3.2.2.2's requirement.
|
|
23
|
+
*
|
|
24
|
+
* Non-finite numbers throw rather than becoming `null` as `JSON.stringify`
|
|
25
|
+
* would. A receipt containing `NaN` is a bug, and silently signing `null` in
|
|
26
|
+
* its place would make that bug undetectable downstream.
|
|
27
|
+
*/
|
|
28
|
+
export declare function canonicalJson(value: JsonValue): string;
|
|
29
|
+
/** Canonical UTF-8 bytes, which are what gets hashed and signed. */
|
|
30
|
+
export declare function canonicalJsonBytes(value: JsonValue): Uint8Array;
|
|
31
|
+
/** Lowercase hex SHA-256 of the canonical bytes. */
|
|
32
|
+
export declare function canonicalJsonSha256(value: JsonValue): Promise<string>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=canonical-json.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-json.d.ts","sourceRoot":"","sources":["../../../src/engine/publication-receipt/canonical-json.ts"],"names":[],"mappings":"AAYA,qBAAa,kBAAmB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAE,mBAAmB,GAAG,kBAAkB,GAAG,oBAAoB,CAAA;gBAElE,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;CAK7D;AAED,KAAK,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAA;AACrD,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAA;AAElF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEtD;AA4DD,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,UAAU,CAE/D;AAED,oDAAoD;AACpD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAU3E"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// RFC 8785 JSON Canonicalization Scheme (#342).
|
|
2
|
+
//
|
|
3
|
+
// A publication receipt is hashed and signed, and the same bytes must be
|
|
4
|
+
// reproducible by Foundry from the same object. Canonicalization is what makes
|
|
5
|
+
// that true: `JSON.stringify` alone is not stable, because object key order
|
|
6
|
+
// follows insertion order rather than a defined sort.
|
|
7
|
+
//
|
|
8
|
+
// Deliberately implemented rather than pulled from a dependency. The whole
|
|
9
|
+
// value here is that both sides agree byte-for-byte, so the algorithm should be
|
|
10
|
+
// readable, pinned, and covered by the RFC's own vectors rather than tracking
|
|
11
|
+
// someone else's semver.
|
|
12
|
+
export class CanonicalJsonError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
constructor(code, message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'CanonicalJsonError';
|
|
17
|
+
this.code = code;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Serialize `value` per RFC 8785.
|
|
22
|
+
*
|
|
23
|
+
* Three things do the work, and each is load-bearing:
|
|
24
|
+
*
|
|
25
|
+
* 1. Object keys sort by UTF-16 code unit. JavaScript's default string sort is
|
|
26
|
+
* already a UTF-16 code-unit comparison, which is exactly what §3.2.3
|
|
27
|
+
* requires — so `.sort()` here is the specified behaviour, not a shortcut.
|
|
28
|
+
* 2. Numbers use ECMAScript `Number::toString`, which `JSON.stringify` applies.
|
|
29
|
+
* §3.2.2.3 defers to that same algorithm, so the platform is authoritative
|
|
30
|
+
* and reimplementing it would only introduce drift.
|
|
31
|
+
* 3. Strings use `JSON.stringify`'s escaping, which since ES2019 emits
|
|
32
|
+
* well-formed output (lone surrogates escaped rather than passed through) —
|
|
33
|
+
* §3.2.2.2's requirement.
|
|
34
|
+
*
|
|
35
|
+
* Non-finite numbers throw rather than becoming `null` as `JSON.stringify`
|
|
36
|
+
* would. A receipt containing `NaN` is a bug, and silently signing `null` in
|
|
37
|
+
* its place would make that bug undetectable downstream.
|
|
38
|
+
*/
|
|
39
|
+
export function canonicalJson(value) {
|
|
40
|
+
return write(value, new Set());
|
|
41
|
+
}
|
|
42
|
+
function write(value, seen) {
|
|
43
|
+
if (value === null)
|
|
44
|
+
return 'null';
|
|
45
|
+
switch (typeof value) {
|
|
46
|
+
case 'boolean':
|
|
47
|
+
return value ? 'true' : 'false';
|
|
48
|
+
case 'number': {
|
|
49
|
+
if (!Number.isFinite(value)) {
|
|
50
|
+
throw new CanonicalJsonError('non_finite_number', `Cannot canonicalize non-finite number: ${String(value)}`);
|
|
51
|
+
}
|
|
52
|
+
// -0 serializes as "0" per RFC 8785 §3.2.2.3; JSON.stringify agrees.
|
|
53
|
+
return JSON.stringify(value);
|
|
54
|
+
}
|
|
55
|
+
case 'string':
|
|
56
|
+
return JSON.stringify(value);
|
|
57
|
+
case 'object':
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
throw new CanonicalJsonError('unsupported_type', `Cannot canonicalize value of type ${typeof value}`);
|
|
61
|
+
}
|
|
62
|
+
const object = value;
|
|
63
|
+
if (seen.has(object)) {
|
|
64
|
+
throw new CanonicalJsonError('circular_reference', 'Cannot canonicalize a circular structure');
|
|
65
|
+
}
|
|
66
|
+
seen.add(object);
|
|
67
|
+
try {
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
return `[${value.map((entry) => write(entry, seen)).join(',')}]`;
|
|
70
|
+
}
|
|
71
|
+
const record = value;
|
|
72
|
+
// Undefined-valued keys are omitted, matching JSON.stringify. RFC 8785
|
|
73
|
+
// has no representation for them.
|
|
74
|
+
const keys = Object.keys(record)
|
|
75
|
+
.filter((key) => record[key] !== undefined)
|
|
76
|
+
.sort();
|
|
77
|
+
const members = keys.map((key) => `${JSON.stringify(key)}:${write(record[key], seen)}`);
|
|
78
|
+
return `{${members.join(',')}}`;
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
seen.delete(object);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Canonical UTF-8 bytes, which are what gets hashed and signed. */
|
|
85
|
+
export function canonicalJsonBytes(value) {
|
|
86
|
+
return new TextEncoder().encode(canonicalJson(value));
|
|
87
|
+
}
|
|
88
|
+
/** Lowercase hex SHA-256 of the canonical bytes. */
|
|
89
|
+
export async function canonicalJsonSha256(value) {
|
|
90
|
+
// `Uint8Array.from` yields an ArrayBuffer-backed copy. TextEncoder returns
|
|
91
|
+
// `Uint8Array<ArrayBufferLike>`, which may be SharedArrayBuffer-backed and
|
|
92
|
+
// so is not a `BufferSource`. Same idiom as sha256Hex in
|
|
93
|
+
// @growth-labs/monitoring's site-agent signature helper.
|
|
94
|
+
const copy = Uint8Array.from(canonicalJsonBytes(value));
|
|
95
|
+
const digest = await globalThis.crypto.subtle.digest('SHA-256', copy);
|
|
96
|
+
return Array.from(new Uint8Array(digest))
|
|
97
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
98
|
+
.join('');
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=canonical-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-json.js","sourceRoot":"","sources":["../../../src/engine/publication-receipt/canonical-json.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const SITE_PUBLICATION_RECEIPT_SCHEMA: "site.publication.receipt.v1";
|
|
3
|
+
export declare const SITE_PUBLICATION_RECEIPT_AUDIENCE: "foundry:site-publication-receipt";
|
|
4
|
+
export declare const publicationProbeSchema: z.ZodObject<{
|
|
5
|
+
/** What was probed, e.g. `html` or `markdown`. */
|
|
6
|
+
kind: z.ZodEnum<["html", "markdown"]>;
|
|
7
|
+
url: z.ZodString;
|
|
8
|
+
status: z.ZodNumber;
|
|
9
|
+
/** SHA-256 of the exact bytes observed, so Foundry can compare. */
|
|
10
|
+
digest: z.ZodString;
|
|
11
|
+
}, "strict", z.ZodTypeAny, {
|
|
12
|
+
status: number;
|
|
13
|
+
kind: "html" | "markdown";
|
|
14
|
+
url: string;
|
|
15
|
+
digest: string;
|
|
16
|
+
}, {
|
|
17
|
+
status: number;
|
|
18
|
+
kind: "html" | "markdown";
|
|
19
|
+
url: string;
|
|
20
|
+
digest: string;
|
|
21
|
+
}>;
|
|
22
|
+
export declare const publicationProjectionCountsSchema: z.ZodObject<{
|
|
23
|
+
seo: z.ZodNumber;
|
|
24
|
+
search: z.ZodNumber;
|
|
25
|
+
sitemap: z.ZodNumber;
|
|
26
|
+
feed: z.ZodNumber;
|
|
27
|
+
}, "strict", z.ZodTypeAny, {
|
|
28
|
+
search: number;
|
|
29
|
+
seo: number;
|
|
30
|
+
sitemap: number;
|
|
31
|
+
feed: number;
|
|
32
|
+
}, {
|
|
33
|
+
search: number;
|
|
34
|
+
seo: number;
|
|
35
|
+
sitemap: number;
|
|
36
|
+
feed: number;
|
|
37
|
+
}>;
|
|
38
|
+
export declare const sitePublicationReceiptBodySchema: z.ZodEffects<z.ZodObject<{
|
|
39
|
+
schema: z.ZodLiteral<"site.publication.receipt.v1">;
|
|
40
|
+
audience: z.ZodLiteral<"foundry:site-publication-receipt">;
|
|
41
|
+
/** Success and failure are both signed; a silent failure is unverifiable. */
|
|
42
|
+
outcome: z.ZodEnum<["success", "failure"]>;
|
|
43
|
+
/** Identity of the publishing property (the site). */
|
|
44
|
+
property: z.ZodObject<{
|
|
45
|
+
id: z.ZodString;
|
|
46
|
+
origin: z.ZodString;
|
|
47
|
+
}, "strict", z.ZodTypeAny, {
|
|
48
|
+
id: string;
|
|
49
|
+
origin: string;
|
|
50
|
+
}, {
|
|
51
|
+
id: string;
|
|
52
|
+
origin: string;
|
|
53
|
+
}>;
|
|
54
|
+
/** Identity of the thing published, stable across revisions. */
|
|
55
|
+
content: z.ZodObject<{
|
|
56
|
+
stableId: z.ZodString;
|
|
57
|
+
slug: z.ZodString;
|
|
58
|
+
revisionId: z.ZodString;
|
|
59
|
+
}, "strict", z.ZodTypeAny, {
|
|
60
|
+
slug: string;
|
|
61
|
+
revisionId: string;
|
|
62
|
+
stableId: string;
|
|
63
|
+
}, {
|
|
64
|
+
slug: string;
|
|
65
|
+
revisionId: string;
|
|
66
|
+
stableId: string;
|
|
67
|
+
}>;
|
|
68
|
+
/** What produced it. */
|
|
69
|
+
source: z.ZodObject<{
|
|
70
|
+
commit: z.ZodString;
|
|
71
|
+
foundryHandoffId: z.ZodString;
|
|
72
|
+
foundryRunId: z.ZodString;
|
|
73
|
+
}, "strict", z.ZodTypeAny, {
|
|
74
|
+
commit: string;
|
|
75
|
+
foundryHandoffId: string;
|
|
76
|
+
foundryRunId: string;
|
|
77
|
+
}, {
|
|
78
|
+
commit: string;
|
|
79
|
+
foundryHandoffId: string;
|
|
80
|
+
foundryRunId: string;
|
|
81
|
+
}>;
|
|
82
|
+
/** Logical database identity — never a connection string or credential. */
|
|
83
|
+
database: z.ZodObject<{
|
|
84
|
+
logicalId: z.ZodString;
|
|
85
|
+
}, "strict", z.ZodTypeAny, {
|
|
86
|
+
logicalId: string;
|
|
87
|
+
}, {
|
|
88
|
+
logicalId: string;
|
|
89
|
+
}>;
|
|
90
|
+
/** Hash of the public media manifest, not the media itself. */
|
|
91
|
+
publicMediaManifestHash: z.ZodString;
|
|
92
|
+
projections: z.ZodObject<{
|
|
93
|
+
seo: z.ZodNumber;
|
|
94
|
+
search: z.ZodNumber;
|
|
95
|
+
sitemap: z.ZodNumber;
|
|
96
|
+
feed: z.ZodNumber;
|
|
97
|
+
}, "strict", z.ZodTypeAny, {
|
|
98
|
+
search: number;
|
|
99
|
+
seo: number;
|
|
100
|
+
sitemap: number;
|
|
101
|
+
feed: number;
|
|
102
|
+
}, {
|
|
103
|
+
search: number;
|
|
104
|
+
seo: number;
|
|
105
|
+
sitemap: number;
|
|
106
|
+
feed: number;
|
|
107
|
+
}>;
|
|
108
|
+
probes: z.ZodArray<z.ZodObject<{
|
|
109
|
+
/** What was probed, e.g. `html` or `markdown`. */
|
|
110
|
+
kind: z.ZodEnum<["html", "markdown"]>;
|
|
111
|
+
url: z.ZodString;
|
|
112
|
+
status: z.ZodNumber;
|
|
113
|
+
/** SHA-256 of the exact bytes observed, so Foundry can compare. */
|
|
114
|
+
digest: z.ZodString;
|
|
115
|
+
}, "strict", z.ZodTypeAny, {
|
|
116
|
+
status: number;
|
|
117
|
+
kind: "html" | "markdown";
|
|
118
|
+
url: string;
|
|
119
|
+
digest: string;
|
|
120
|
+
}, {
|
|
121
|
+
status: number;
|
|
122
|
+
kind: "html" | "markdown";
|
|
123
|
+
url: string;
|
|
124
|
+
digest: string;
|
|
125
|
+
}>, "many">;
|
|
126
|
+
/** How to undo this publication. */
|
|
127
|
+
rollback: z.ZodObject<{
|
|
128
|
+
pointer: z.ZodString;
|
|
129
|
+
}, "strict", z.ZodTypeAny, {
|
|
130
|
+
pointer: string;
|
|
131
|
+
}, {
|
|
132
|
+
pointer: string;
|
|
133
|
+
}>;
|
|
134
|
+
/** Replay identity. Supplied by the caller and persisted, never generated here. */
|
|
135
|
+
nonce: z.ZodString;
|
|
136
|
+
issuedAt: z.ZodString;
|
|
137
|
+
/** Present only when `outcome` is `failure`, and bounded. */
|
|
138
|
+
failureReason: z.ZodOptional<z.ZodString>;
|
|
139
|
+
}, "strict", z.ZodTypeAny, {
|
|
140
|
+
rollback: {
|
|
141
|
+
pointer: string;
|
|
142
|
+
};
|
|
143
|
+
source: {
|
|
144
|
+
commit: string;
|
|
145
|
+
foundryHandoffId: string;
|
|
146
|
+
foundryRunId: string;
|
|
147
|
+
};
|
|
148
|
+
content: {
|
|
149
|
+
slug: string;
|
|
150
|
+
revisionId: string;
|
|
151
|
+
stableId: string;
|
|
152
|
+
};
|
|
153
|
+
audience: "foundry:site-publication-receipt";
|
|
154
|
+
schema: "site.publication.receipt.v1";
|
|
155
|
+
outcome: "success" | "failure";
|
|
156
|
+
property: {
|
|
157
|
+
id: string;
|
|
158
|
+
origin: string;
|
|
159
|
+
};
|
|
160
|
+
database: {
|
|
161
|
+
logicalId: string;
|
|
162
|
+
};
|
|
163
|
+
publicMediaManifestHash: string;
|
|
164
|
+
projections: {
|
|
165
|
+
search: number;
|
|
166
|
+
seo: number;
|
|
167
|
+
sitemap: number;
|
|
168
|
+
feed: number;
|
|
169
|
+
};
|
|
170
|
+
probes: {
|
|
171
|
+
status: number;
|
|
172
|
+
kind: "html" | "markdown";
|
|
173
|
+
url: string;
|
|
174
|
+
digest: string;
|
|
175
|
+
}[];
|
|
176
|
+
nonce: string;
|
|
177
|
+
issuedAt: string;
|
|
178
|
+
failureReason?: string | undefined;
|
|
179
|
+
}, {
|
|
180
|
+
rollback: {
|
|
181
|
+
pointer: string;
|
|
182
|
+
};
|
|
183
|
+
source: {
|
|
184
|
+
commit: string;
|
|
185
|
+
foundryHandoffId: string;
|
|
186
|
+
foundryRunId: string;
|
|
187
|
+
};
|
|
188
|
+
content: {
|
|
189
|
+
slug: string;
|
|
190
|
+
revisionId: string;
|
|
191
|
+
stableId: string;
|
|
192
|
+
};
|
|
193
|
+
audience: "foundry:site-publication-receipt";
|
|
194
|
+
schema: "site.publication.receipt.v1";
|
|
195
|
+
outcome: "success" | "failure";
|
|
196
|
+
property: {
|
|
197
|
+
id: string;
|
|
198
|
+
origin: string;
|
|
199
|
+
};
|
|
200
|
+
database: {
|
|
201
|
+
logicalId: string;
|
|
202
|
+
};
|
|
203
|
+
publicMediaManifestHash: string;
|
|
204
|
+
projections: {
|
|
205
|
+
search: number;
|
|
206
|
+
seo: number;
|
|
207
|
+
sitemap: number;
|
|
208
|
+
feed: number;
|
|
209
|
+
};
|
|
210
|
+
probes: {
|
|
211
|
+
status: number;
|
|
212
|
+
kind: "html" | "markdown";
|
|
213
|
+
url: string;
|
|
214
|
+
digest: string;
|
|
215
|
+
}[];
|
|
216
|
+
nonce: string;
|
|
217
|
+
issuedAt: string;
|
|
218
|
+
failureReason?: string | undefined;
|
|
219
|
+
}>, {
|
|
220
|
+
rollback: {
|
|
221
|
+
pointer: string;
|
|
222
|
+
};
|
|
223
|
+
source: {
|
|
224
|
+
commit: string;
|
|
225
|
+
foundryHandoffId: string;
|
|
226
|
+
foundryRunId: string;
|
|
227
|
+
};
|
|
228
|
+
content: {
|
|
229
|
+
slug: string;
|
|
230
|
+
revisionId: string;
|
|
231
|
+
stableId: string;
|
|
232
|
+
};
|
|
233
|
+
audience: "foundry:site-publication-receipt";
|
|
234
|
+
schema: "site.publication.receipt.v1";
|
|
235
|
+
outcome: "success" | "failure";
|
|
236
|
+
property: {
|
|
237
|
+
id: string;
|
|
238
|
+
origin: string;
|
|
239
|
+
};
|
|
240
|
+
database: {
|
|
241
|
+
logicalId: string;
|
|
242
|
+
};
|
|
243
|
+
publicMediaManifestHash: string;
|
|
244
|
+
projections: {
|
|
245
|
+
search: number;
|
|
246
|
+
seo: number;
|
|
247
|
+
sitemap: number;
|
|
248
|
+
feed: number;
|
|
249
|
+
};
|
|
250
|
+
probes: {
|
|
251
|
+
status: number;
|
|
252
|
+
kind: "html" | "markdown";
|
|
253
|
+
url: string;
|
|
254
|
+
digest: string;
|
|
255
|
+
}[];
|
|
256
|
+
nonce: string;
|
|
257
|
+
issuedAt: string;
|
|
258
|
+
failureReason?: string | undefined;
|
|
259
|
+
}, {
|
|
260
|
+
rollback: {
|
|
261
|
+
pointer: string;
|
|
262
|
+
};
|
|
263
|
+
source: {
|
|
264
|
+
commit: string;
|
|
265
|
+
foundryHandoffId: string;
|
|
266
|
+
foundryRunId: string;
|
|
267
|
+
};
|
|
268
|
+
content: {
|
|
269
|
+
slug: string;
|
|
270
|
+
revisionId: string;
|
|
271
|
+
stableId: string;
|
|
272
|
+
};
|
|
273
|
+
audience: "foundry:site-publication-receipt";
|
|
274
|
+
schema: "site.publication.receipt.v1";
|
|
275
|
+
outcome: "success" | "failure";
|
|
276
|
+
property: {
|
|
277
|
+
id: string;
|
|
278
|
+
origin: string;
|
|
279
|
+
};
|
|
280
|
+
database: {
|
|
281
|
+
logicalId: string;
|
|
282
|
+
};
|
|
283
|
+
publicMediaManifestHash: string;
|
|
284
|
+
projections: {
|
|
285
|
+
search: number;
|
|
286
|
+
seo: number;
|
|
287
|
+
sitemap: number;
|
|
288
|
+
feed: number;
|
|
289
|
+
};
|
|
290
|
+
probes: {
|
|
291
|
+
status: number;
|
|
292
|
+
kind: "html" | "markdown";
|
|
293
|
+
url: string;
|
|
294
|
+
digest: string;
|
|
295
|
+
}[];
|
|
296
|
+
nonce: string;
|
|
297
|
+
issuedAt: string;
|
|
298
|
+
failureReason?: string | undefined;
|
|
299
|
+
}>;
|
|
300
|
+
export type SitePublicationReceiptBody = z.infer<typeof sitePublicationReceiptBodySchema>;
|
|
301
|
+
export declare class PublicationReceiptError extends Error {
|
|
302
|
+
readonly code: 'invalid_body' | 'invalid_key';
|
|
303
|
+
constructor(code: PublicationReceiptError['code'], message: string);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Validate a receipt body and return its canonical RFC 8785 form.
|
|
307
|
+
*
|
|
308
|
+
* Validation happens before canonicalization on purpose: signing an unvalidated
|
|
309
|
+
* body would produce a verifiable signature over a malformed claim, which is
|
|
310
|
+
* strictly worse than refusing, because downstream would trust it.
|
|
311
|
+
*/
|
|
312
|
+
export declare function canonicalizeReceiptBody(body: unknown): {
|
|
313
|
+
body: SitePublicationReceiptBody;
|
|
314
|
+
canonical: string;
|
|
315
|
+
};
|
|
316
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../../../src/engine/publication-receipt/receipt.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,+BAA+B,EAAG,6BAAsC,CAAA;AACrF,eAAO,MAAM,iCAAiC,EAAG,kCAA2C,CAAA;AAoB5F,eAAO,MAAM,sBAAsB;IAEjC,kDAAkD;;;;IAIlD,mEAAmE;;;;;;;;;;;;EAG3D,CAAA;AAEV,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;EAOpC,CAAA;AAEV,eAAO,MAAM,gCAAgC;;;IAK3C,6EAA6E;;IAG7E,sDAAsD;;;;;;;;;;;IAGtD,gEAAgE;;;;;;;;;;;;;;IAShE,wBAAwB;;;;;;;;;;;;;;IASxB,2EAA2E;;;;;;;;IAG3E,+DAA+D;;;;;;;;;;;;;;;;;;;QAlD/D,kDAAkD;;;;QAIlD,mEAAmE;;;;;;;;;;;;;IAoDnE,oCAAoC;;;;;;;;IAGpC,mFAAmF;;;IAInF,6DAA6D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmB5D,CAAA;AAEH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAA;AAEzF,qBAAa,uBAAwB,SAAQ,KAAK;IACjD,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,aAAa,CAAA;gBAEjC,IAAI,EAAE,uBAAuB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;CAKlE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG;IACvD,IAAI,EAAE,0BAA0B,CAAA;IAChC,SAAS,EAAE,MAAM,CAAA;CACjB,CAcA"}
|