@nakafa/aksara-contracts 0.27.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/LICENSE +185 -0
- package/README.md +73 -0
- package/dist/artifact/integrity.d.ts +29 -0
- package/dist/artifact/integrity.js +54 -0
- package/dist/artifact/limits.d.ts +26 -0
- package/dist/artifact/limits.js +46 -0
- package/dist/artifact/source.d.ts +21 -0
- package/dist/artifact/source.js +24 -0
- package/dist/artifact/spec.d.ts +148 -0
- package/dist/artifact/spec.js +92 -0
- package/dist/artifact/verify.d.ts +26 -0
- package/dist/artifact/verify.js +18 -0
- package/dist/cache/content.d.ts +52 -0
- package/dist/cache/content.js +80 -0
- package/dist/content.d.ts +168 -0
- package/dist/content.js +107 -0
- package/dist/country.d.ts +4 -0
- package/dist/country.js +8 -0
- package/dist/date.d.ts +24 -0
- package/dist/date.js +44 -0
- package/dist/decode.d.ts +4 -0
- package/dist/decode.js +8 -0
- package/dist/delivery.d.ts +4 -0
- package/dist/delivery.js +7 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +8 -0
- package/dist/graph/family.d.ts +45 -0
- package/dist/graph/family.js +85 -0
- package/dist/graph/identity.d.ts +19 -0
- package/dist/graph/identity.js +39 -0
- package/dist/graph/spec.d.ts +20 -0
- package/dist/graph/spec.js +26 -0
- package/dist/hash/text.d.ts +10 -0
- package/dist/hash/text.js +20 -0
- package/dist/ids.d.ts +27 -0
- package/dist/ids.js +63 -0
- package/dist/limits.d.ts +10 -0
- package/dist/limits.js +10 -0
- package/dist/locale.d.ts +37 -0
- package/dist/locale.js +53 -0
- package/dist/material/domain.d.ts +4 -0
- package/dist/material/domain.js +8 -0
- package/dist/preview/artifact.d.ts +198 -0
- package/dist/preview/artifact.js +20 -0
- package/dist/preview/auth.d.ts +71 -0
- package/dist/preview/auth.js +49 -0
- package/dist/preview/document.d.ts +692 -0
- package/dist/preview/document.js +90 -0
- package/dist/preview/spec.d.ts +2517 -0
- package/dist/preview/spec.js +142 -0
- package/dist/preview/target.d.ts +172 -0
- package/dist/preview/target.js +70 -0
- package/dist/program/curriculum.d.ts +69 -0
- package/dist/program/curriculum.js +181 -0
- package/dist/program/snapshot/digest.d.ts +26 -0
- package/dist/program/snapshot/digest.js +220 -0
- package/dist/program/snapshot/hash.d.ts +31 -0
- package/dist/program/snapshot/hash.js +40 -0
- package/dist/program/snapshot/row-hash.d.ts +155 -0
- package/dist/program/snapshot/row-hash.js +34 -0
- package/dist/program/snapshot/row.d.ts +139 -0
- package/dist/program/snapshot/row.js +28 -0
- package/dist/program/snapshot/spec.d.ts +36 -0
- package/dist/program/snapshot/spec.js +39 -0
- package/dist/program/spec.d.ts +85 -0
- package/dist/program/spec.js +197 -0
- package/dist/projection/article.d.ts +145 -0
- package/dist/projection/article.js +150 -0
- package/dist/projection/digest.d.ts +261 -0
- package/dist/projection/digest.js +59 -0
- package/dist/projection/hash.d.ts +3 -0
- package/dist/projection/hash.js +10 -0
- package/dist/projection/material.d.ts +109 -0
- package/dist/projection/material.js +160 -0
- package/dist/projection/page.d.ts +112 -0
- package/dist/projection/page.js +106 -0
- package/dist/projection/question.d.ts +520 -0
- package/dist/projection/question.js +109 -0
- package/dist/projection/spec.d.ts +503 -0
- package/dist/projection/spec.js +56 -0
- package/dist/projection/verify.d.ts +281 -0
- package/dist/projection/verify.js +86 -0
- package/dist/question/identity.d.ts +175 -0
- package/dist/question/identity.js +205 -0
- package/dist/question/item.d.ts +453 -0
- package/dist/question/item.js +146 -0
- package/dist/question/response.d.ts +175 -0
- package/dist/question/response.js +150 -0
- package/dist/quran/attribution.d.ts +48 -0
- package/dist/quran/attribution.js +44 -0
- package/dist/quran/identity.d.ts +105 -0
- package/dist/quran/identity.js +100 -0
- package/dist/quran/notes.d.ts +56 -0
- package/dist/quran/notes.js +142 -0
- package/dist/quran/provenance.d.ts +257 -0
- package/dist/quran/provenance.js +144 -0
- package/dist/quran/snapshot/digest.d.ts +33 -0
- package/dist/quran/snapshot/digest.js +232 -0
- package/dist/quran/snapshot/hash.d.ts +51 -0
- package/dist/quran/snapshot/hash.js +50 -0
- package/dist/quran/snapshot/row/hash.d.ts +748 -0
- package/dist/quran/snapshot/row/hash.js +115 -0
- package/dist/quran/snapshot/row.d.ts +354 -0
- package/dist/quran/snapshot/row.js +91 -0
- package/dist/quran/snapshot/spec.d.ts +50 -0
- package/dist/quran/snapshot/spec.js +66 -0
- package/dist/quran/source.d.ts +206 -0
- package/dist/quran/source.js +153 -0
- package/dist/quran/spec.d.ts +47 -0
- package/dist/quran/spec.js +41 -0
- package/dist/quran/text.d.ts +7 -0
- package/dist/quran/text.js +11 -0
- package/dist/release/canonical.d.ts +23 -0
- package/dist/release/canonical.js +29 -0
- package/dist/release/current/evidence.d.ts +424 -0
- package/dist/release/current/evidence.js +41 -0
- package/dist/release/current/runtime.d.ts +4 -0
- package/dist/release/current/runtime.js +15 -0
- package/dist/release/current/state.d.ts +567 -0
- package/dist/release/current/state.js +91 -0
- package/dist/release/digest.d.ts +54 -0
- package/dist/release/digest.js +71 -0
- package/dist/release/hash.d.ts +72 -0
- package/dist/release/hash.js +14 -0
- package/dist/release/head.d.ts +206 -0
- package/dist/release/head.js +131 -0
- package/dist/release/items.d.ts +98 -0
- package/dist/release/items.js +114 -0
- package/dist/release/lifecycle.d.ts +297 -0
- package/dist/release/lifecycle.js +97 -0
- package/dist/release/origin.d.ts +30 -0
- package/dist/release/origin.js +24 -0
- package/dist/release/policy.d.ts +45 -0
- package/dist/release/policy.js +124 -0
- package/dist/release/result/digest.d.ts +127 -0
- package/dist/release/result/digest.js +140 -0
- package/dist/release/result/spec.d.ts +4 -0
- package/dist/release/result/spec.js +5 -0
- package/dist/release/rollback/digest.d.ts +126 -0
- package/dist/release/rollback/digest.js +92 -0
- package/dist/release/rollback/spec.d.ts +1729 -0
- package/dist/release/rollback/spec.js +156 -0
- package/dist/release/route/digest.d.ts +45 -0
- package/dist/release/route/digest.js +59 -0
- package/dist/release/route/page.d.ts +56 -0
- package/dist/release/route/page.js +46 -0
- package/dist/release/route/spec.d.ts +49 -0
- package/dist/release/route/spec.js +40 -0
- package/dist/release/route/verify.d.ts +66 -0
- package/dist/release/route/verify.js +67 -0
- package/dist/release/signing.d.ts +6 -0
- package/dist/release/signing.js +38 -0
- package/dist/release/snapshot/data.d.ts +475 -0
- package/dist/release/snapshot/data.js +74 -0
- package/dist/release/snapshot/evidence-requirement.d.ts +22 -0
- package/dist/release/snapshot/evidence-requirement.js +21 -0
- package/dist/release/snapshot/evidence.d.ts +58 -0
- package/dist/release/snapshot/evidence.js +14 -0
- package/dist/release/snapshot/program-evidence.d.ts +17 -0
- package/dist/release/snapshot/program-evidence.js +39 -0
- package/dist/release/snapshot/quran-evidence.d.ts +27 -0
- package/dist/release/snapshot/quran-evidence.js +43 -0
- package/dist/release/snapshot/scope.d.ts +60 -0
- package/dist/release/snapshot/scope.js +102 -0
- package/dist/release/snapshot/spec.d.ts +193 -0
- package/dist/release/snapshot/spec.js +139 -0
- package/dist/release/snapshot/tryout-evidence.d.ts +22 -0
- package/dist/release/snapshot/tryout-evidence.js +86 -0
- package/dist/release/snapshot/verify.d.ts +710 -0
- package/dist/release/snapshot/verify.js +147 -0
- package/dist/release/spec.d.ts +491 -0
- package/dist/release/spec.js +200 -0
- package/dist/release/verify.d.ts +98 -0
- package/dist/release/verify.js +70 -0
- package/dist/renderer/compatibility.d.ts +35 -0
- package/dist/renderer/compatibility.js +48 -0
- package/dist/renderer/component.d.ts +99 -0
- package/dist/renderer/component.js +102 -0
- package/dist/renderer/contract.d.ts +144 -0
- package/dist/renderer/contract.js +122 -0
- package/dist/renderer/domain.d.ts +6 -0
- package/dist/renderer/domain.js +18 -0
- package/dist/renderer/manifest.d.ts +86 -0
- package/dist/renderer/manifest.js +49 -0
- package/dist/renderer/selection.d.ts +39 -0
- package/dist/renderer/selection.js +87 -0
- package/dist/runtime/error.d.ts +8 -0
- package/dist/runtime/error.js +20 -0
- package/dist/runtime/protected/limits.d.ts +10 -0
- package/dist/runtime/protected/limits.js +15 -0
- package/dist/runtime/protected/spec.d.ts +328 -0
- package/dist/runtime/protected/spec.js +79 -0
- package/dist/runtime/protected/verify.d.ts +97 -0
- package/dist/runtime/protected/verify.js +72 -0
- package/dist/runtime/result.d.ts +12 -0
- package/dist/runtime/result.js +17 -0
- package/dist/runtime/spec.d.ts +705 -0
- package/dist/runtime/spec.js +61 -0
- package/dist/runtime/verify.d.ts +479 -0
- package/dist/runtime/verify.js +123 -0
- package/dist/signature/spec.d.ts +53 -0
- package/dist/signature/spec.js +29 -0
- package/dist/signature/trusted.d.ts +19 -0
- package/dist/signature/trusted.js +32 -0
- package/dist/signature/verify.d.ts +10 -0
- package/dist/signature/verify.js +82 -0
- package/dist/text/order.d.ts +2 -0
- package/dist/text/order.js +10 -0
- package/dist/text/syntax.d.ts +6 -0
- package/dist/text/syntax.js +15 -0
- package/dist/transport/batch.d.ts +971 -0
- package/dist/transport/batch.js +101 -0
- package/dist/transport/failure.d.ts +168 -0
- package/dist/transport/failure.js +194 -0
- package/dist/transport/group.d.ts +2946 -0
- package/dist/transport/group.js +47 -0
- package/dist/transport/limits.d.ts +30 -0
- package/dist/transport/limits.js +30 -0
- package/dist/transport/request.d.ts +9333 -0
- package/dist/transport/request.js +121 -0
- package/dist/transport/response.d.ts +6247 -0
- package/dist/transport/response.js +181 -0
- package/dist/transport/runtime.d.ts +77 -0
- package/dist/transport/runtime.js +33 -0
- package/dist/transport/snapshot.d.ts +978 -0
- package/dist/transport/snapshot.js +70 -0
- package/dist/tryout/canonical.d.ts +2 -0
- package/dist/tryout/canonical.js +11 -0
- package/dist/tryout/catalog-hash.d.ts +134 -0
- package/dist/tryout/catalog-hash.js +203 -0
- package/dist/tryout/catalog.d.ts +376 -0
- package/dist/tryout/catalog.js +147 -0
- package/dist/tryout/content-hash.d.ts +89 -0
- package/dist/tryout/content-hash.js +72 -0
- package/dist/tryout/identity.d.ts +12 -0
- package/dist/tryout/identity.js +46 -0
- package/dist/tryout/key.d.ts +4 -0
- package/dist/tryout/key.js +8 -0
- package/dist/tryout/language.d.ts +24 -0
- package/dist/tryout/language.js +32 -0
- package/dist/tryout/locale-closure.d.ts +20 -0
- package/dist/tryout/locale-closure.js +164 -0
- package/dist/tryout/placement-closure.d.ts +5 -0
- package/dist/tryout/placement-closure.js +30 -0
- package/dist/tryout/placement-hash.d.ts +11 -0
- package/dist/tryout/placement-hash.js +55 -0
- package/dist/tryout/placement.d.ts +280 -0
- package/dist/tryout/placement.js +82 -0
- package/dist/tryout/row-hash.d.ts +28 -0
- package/dist/tryout/row-hash.js +50 -0
- package/dist/tryout/runtime/canonical.d.ts +8 -0
- package/dist/tryout/runtime/canonical.js +33 -0
- package/dist/tryout/runtime/hash.d.ts +32 -0
- package/dist/tryout/runtime/hash.js +11 -0
- package/dist/tryout/runtime/source.d.ts +42 -0
- package/dist/tryout/runtime/source.js +47 -0
- package/dist/tryout/runtime/spec.d.ts +58 -0
- package/dist/tryout/runtime/spec.js +21 -0
- package/dist/tryout/runtime/verify.d.ts +61 -0
- package/dist/tryout/runtime/verify.js +69 -0
- package/dist/tryout/snapshot/canonical.d.ts +7 -0
- package/dist/tryout/snapshot/canonical.js +17 -0
- package/dist/tryout/snapshot/hash.d.ts +8 -0
- package/dist/tryout/snapshot/hash.js +19 -0
- package/dist/tryout/snapshot/spec.d.ts +46 -0
- package/dist/tryout/snapshot/spec.js +30 -0
- package/dist/tryout/spec.d.ts +13 -0
- package/dist/tryout/spec.js +15 -0
- package/package.json +506 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ContentFamilySchema } from "#contracts/content";
|
|
3
|
+
import { ContentDeliveryClassSchema } from "#contracts/delivery";
|
|
4
|
+
import { ContentKeySchema, CorpusSourcePathSchema, Ed25519SignatureSchema, ReleaseIdSchema, Sha256HashSchema, SigningKeyIdSchema, } from "#contracts/ids";
|
|
5
|
+
import { ActiveAppLocaleListSchema, ArtifactLocaleSchema, } from "#contracts/locale";
|
|
6
|
+
import { ReleaseOriginSchema } from "#contracts/release/origin";
|
|
7
|
+
import { EMPTY_RESULT_CATALOG_DIGEST } from "#contracts/release/result/spec";
|
|
8
|
+
import { PublicationScopeSchema } from "#contracts/release/snapshot/scope";
|
|
9
|
+
import { ContentSnapshotSetSchema, hasEmptySnapshotBases, hasGitSnapshotModes, hasRollbackSnapshotModes, hasScopedSnapshotTransitions, snapshotRowCount, } from "#contracts/release/snapshot/spec";
|
|
10
|
+
import { RENDERER_CONTRACT_VERSION } from "#contracts/renderer/contract";
|
|
11
|
+
import { RendererDomainSchema } from "#contracts/renderer/domain";
|
|
12
|
+
/** Semantic wire identity of the current localized content release. */
|
|
13
|
+
export const CONTENT_RELEASE_FORMAT = "localized-content-release";
|
|
14
|
+
/** Nonnegative release inventory count authenticated by one manifest. */
|
|
15
|
+
export const ReleaseCountSchema = Schema.Finite.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThanOrEqualTo(0)));
|
|
16
|
+
/** Stable inventory and provenance fields owned by the current release. */
|
|
17
|
+
const ContentReleaseManifestFields = {
|
|
18
|
+
baseActiveAppLocales: Schema.NullOr(ActiveAppLocaleListSchema),
|
|
19
|
+
baseManifestHash: Schema.NullOr(Sha256HashSchema),
|
|
20
|
+
baseReleaseId: Schema.NullOr(ReleaseIdSchema),
|
|
21
|
+
baseResultCount: ReleaseCountSchema,
|
|
22
|
+
baseResultDigest: Sha256HashSchema,
|
|
23
|
+
deleteCount: ReleaseCountSchema,
|
|
24
|
+
itemCount: ReleaseCountSchema,
|
|
25
|
+
itemsDigest: Sha256HashSchema,
|
|
26
|
+
origin: ReleaseOriginSchema,
|
|
27
|
+
projectionCount: ReleaseCountSchema,
|
|
28
|
+
projectionDigest: Sha256HashSchema,
|
|
29
|
+
releaseId: ReleaseIdSchema,
|
|
30
|
+
rendererContractVersion: Schema.Literal(RENDERER_CONTRACT_VERSION),
|
|
31
|
+
rendererManifestHash: Sha256HashSchema,
|
|
32
|
+
resultCount: ReleaseCountSchema,
|
|
33
|
+
resultDigest: Sha256HashSchema,
|
|
34
|
+
rollbackCount: ReleaseCountSchema,
|
|
35
|
+
rollbackDigest: Sha256HashSchema,
|
|
36
|
+
routeCount: ReleaseCountSchema,
|
|
37
|
+
routeDigest: Sha256HashSchema,
|
|
38
|
+
scope: PublicationScopeSchema,
|
|
39
|
+
snapshots: ContentSnapshotSetSchema,
|
|
40
|
+
upsertCount: ReleaseCountSchema,
|
|
41
|
+
};
|
|
42
|
+
/** Checks rollback provenance against forward release identities. */
|
|
43
|
+
function hasCoherentReleaseOrigin(input) {
|
|
44
|
+
const hasBaseRelease = input.baseReleaseId !== null;
|
|
45
|
+
if (hasBaseRelease !== (input.baseManifestHash !== null) ||
|
|
46
|
+
hasBaseRelease !== (input.baseActiveAppLocales !== null) ||
|
|
47
|
+
input.baseReleaseId === input.releaseId ||
|
|
48
|
+
input.deleteCount + input.upsertCount !== input.itemCount ||
|
|
49
|
+
input.rollbackCount !== input.itemCount ||
|
|
50
|
+
!hasScopedSnapshotTransitions(input.scope, input.snapshots)) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
if (!hasBaseRelease &&
|
|
54
|
+
(input.baseResultCount !== 0 ||
|
|
55
|
+
input.baseResultDigest !== EMPTY_RESULT_CATALOG_DIGEST)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (!(hasBaseRelease || hasEmptySnapshotBases(input.snapshots))) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (input.origin.kind === "git") {
|
|
62
|
+
return hasGitSnapshotModes(input.snapshots);
|
|
63
|
+
}
|
|
64
|
+
return (input.baseReleaseId === input.origin.releaseId &&
|
|
65
|
+
input.releaseId !== input.origin.releaseId &&
|
|
66
|
+
hasRollbackSnapshotModes(input.snapshots));
|
|
67
|
+
}
|
|
68
|
+
/** One immutable artifact selected for a locale-specific content head. */
|
|
69
|
+
export const ContentUpsertSchema = Schema.Struct({
|
|
70
|
+
artifactHash: Sha256HashSchema,
|
|
71
|
+
artifactLocale: ArtifactLocaleSchema,
|
|
72
|
+
contentKey: ContentKeySchema,
|
|
73
|
+
delivery: ContentDeliveryClassSchema,
|
|
74
|
+
family: ContentFamilySchema,
|
|
75
|
+
operation: Schema.Literal("upsert"),
|
|
76
|
+
rendererDomain: RendererDomainSchema,
|
|
77
|
+
sourcePath: CorpusSourcePathSchema,
|
|
78
|
+
});
|
|
79
|
+
/** One locale-specific content head removed by an explicit tombstone. */
|
|
80
|
+
export const ContentDeleteSchema = Schema.Struct({
|
|
81
|
+
artifactLocale: ArtifactLocaleSchema,
|
|
82
|
+
contentKey: ContentKeySchema,
|
|
83
|
+
family: ContentFamilySchema,
|
|
84
|
+
operation: Schema.Literal("delete"),
|
|
85
|
+
});
|
|
86
|
+
/** Complete tagged change vocabulary accepted by the current release. */
|
|
87
|
+
export const ContentChangeSchema = Schema.Union([
|
|
88
|
+
ContentUpsertSchema,
|
|
89
|
+
ContentDeleteSchema,
|
|
90
|
+
]);
|
|
91
|
+
export const ReleaseItemIndexSchema = Schema.Finite.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThanOrEqualTo(0)));
|
|
92
|
+
/** One separately stored, ordered item authenticated by a release digest. */
|
|
93
|
+
export const ContentReleaseItemSchema = Schema.Struct({
|
|
94
|
+
change: ContentChangeSchema,
|
|
95
|
+
index: ReleaseItemIndexSchema,
|
|
96
|
+
releaseId: ReleaseIdSchema,
|
|
97
|
+
});
|
|
98
|
+
/** Deterministic desired-state transition with signed source provenance. */
|
|
99
|
+
export const ContentReleaseManifestSchema = Schema.Struct({
|
|
100
|
+
activeAppLocales: ActiveAppLocaleListSchema,
|
|
101
|
+
...ContentReleaseManifestFields,
|
|
102
|
+
format: Schema.Literal(CONTENT_RELEASE_FORMAT),
|
|
103
|
+
}).pipe(Schema.check(Schema.makeFilter(hasCoherentReleaseOrigin, {
|
|
104
|
+
message: "Expected a new release identity and a coherent source origin.",
|
|
105
|
+
})));
|
|
106
|
+
/** Immutable release manifest plus its asymmetric authenticity proof. */
|
|
107
|
+
export const SignedContentReleaseSchema = Schema.Struct({
|
|
108
|
+
keyId: SigningKeyIdSchema,
|
|
109
|
+
manifest: ContentReleaseManifestSchema,
|
|
110
|
+
manifestHash: Sha256HashSchema,
|
|
111
|
+
signature: Ed25519SignatureSchema,
|
|
112
|
+
});
|
|
113
|
+
/** Checks whether one application locale is active in a signed release. */
|
|
114
|
+
export function releaseActivatesAppLocale(release, appLocale) {
|
|
115
|
+
return release.manifest.activeAppLocales.includes(appLocale);
|
|
116
|
+
}
|
|
117
|
+
/** Signed release contract accepted only for rollback-owned operations. */
|
|
118
|
+
export const RollbackSignedContentReleaseSchema = SignedContentReleaseSchema.pipe(Schema.refine((release) => release.manifest.origin.kind === "rollback", { message: "Expected a signed rollback release." }));
|
|
119
|
+
/** Checks that every staged head has exactly one matching item and artifact. */
|
|
120
|
+
function hasCoherentVerificationCounts(input) {
|
|
121
|
+
const hasBaseRelease = input.baseReleaseId !== null;
|
|
122
|
+
return (hasBaseRelease === (input.baseManifestHash !== null) &&
|
|
123
|
+
hasBaseRelease === (input.baseActiveAppLocales !== null) &&
|
|
124
|
+
(hasBaseRelease ||
|
|
125
|
+
(input.baseResultCount === 0 &&
|
|
126
|
+
input.baseResultDigest === EMPTY_RESULT_CATALOG_DIGEST)) &&
|
|
127
|
+
input.deleteHeads + input.upsertHeads === input.itemCount &&
|
|
128
|
+
input.rollbackCount === input.itemCount &&
|
|
129
|
+
input.stagedArtifacts === input.upsertHeads &&
|
|
130
|
+
input.stagedSnapshotRows === snapshotRowCount(input.snapshots));
|
|
131
|
+
}
|
|
132
|
+
/** Pre-activation evidence proving the fully staged release is coherent. */
|
|
133
|
+
export const ReleaseVerificationEvidenceSchema = Schema.Struct({
|
|
134
|
+
activeAppLocales: ActiveAppLocaleListSchema,
|
|
135
|
+
baseActiveAppLocales: Schema.NullOr(ActiveAppLocaleListSchema),
|
|
136
|
+
baseManifestHash: Schema.NullOr(Sha256HashSchema),
|
|
137
|
+
baseReleaseId: Schema.NullOr(ReleaseIdSchema),
|
|
138
|
+
baseResultCount: ReleaseCountSchema,
|
|
139
|
+
baseResultDigest: Sha256HashSchema,
|
|
140
|
+
deleteHeads: ReleaseCountSchema,
|
|
141
|
+
itemCount: ReleaseCountSchema,
|
|
142
|
+
itemsDigest: Sha256HashSchema,
|
|
143
|
+
manifestHash: Sha256HashSchema,
|
|
144
|
+
projectionCount: ReleaseCountSchema,
|
|
145
|
+
projectionDigest: Sha256HashSchema,
|
|
146
|
+
releaseId: ReleaseIdSchema,
|
|
147
|
+
rendererContractVersion: Schema.Literal(RENDERER_CONTRACT_VERSION),
|
|
148
|
+
rendererManifestHash: Sha256HashSchema,
|
|
149
|
+
resultCount: ReleaseCountSchema,
|
|
150
|
+
resultDigest: Sha256HashSchema,
|
|
151
|
+
rollbackCount: ReleaseCountSchema,
|
|
152
|
+
rollbackDigest: Sha256HashSchema,
|
|
153
|
+
routeCount: ReleaseCountSchema,
|
|
154
|
+
routeDigest: Sha256HashSchema,
|
|
155
|
+
snapshots: ContentSnapshotSetSchema,
|
|
156
|
+
stagedArtifacts: ReleaseCountSchema,
|
|
157
|
+
stagedRoutes: ReleaseCountSchema,
|
|
158
|
+
stagedSnapshotRows: ReleaseCountSchema,
|
|
159
|
+
upsertHeads: ReleaseCountSchema,
|
|
160
|
+
}).pipe(Schema.check(Schema.makeFilter(hasCoherentVerificationCounts, {
|
|
161
|
+
message: "Expected staged head and artifact counts to match the release items.",
|
|
162
|
+
})));
|
|
163
|
+
/** Durable verification has started but has not produced final evidence yet. */
|
|
164
|
+
export const ReleaseVerificationPendingSchema = Schema.Struct({
|
|
165
|
+
manifestHash: Sha256HashSchema,
|
|
166
|
+
phase: Schema.Literal("verifying"),
|
|
167
|
+
releaseId: ReleaseIdSchema,
|
|
168
|
+
});
|
|
169
|
+
/** Durable verification completed with recomputed release evidence. */
|
|
170
|
+
export const ReleaseVerificationCompleteSchema = Schema.Struct({
|
|
171
|
+
evidence: ReleaseVerificationEvidenceSchema,
|
|
172
|
+
phase: Schema.Literal("verified"),
|
|
173
|
+
});
|
|
174
|
+
/** Bounded status returned while durable verification progresses. */
|
|
175
|
+
export const ReleaseVerificationStatusSchema = Schema.Union([
|
|
176
|
+
ReleaseVerificationPendingSchema,
|
|
177
|
+
ReleaseVerificationCompleteSchema,
|
|
178
|
+
]);
|
|
179
|
+
/** Delta evidence returned after a release is staged and activated. */
|
|
180
|
+
export const PublicationReceiptSchema = Schema.Struct({
|
|
181
|
+
activatedHeads: ReleaseCountSchema,
|
|
182
|
+
activeAppLocales: ActiveAppLocaleListSchema,
|
|
183
|
+
deletedHeads: ReleaseCountSchema,
|
|
184
|
+
manifestHash: Sha256HashSchema,
|
|
185
|
+
projectionDigest: Sha256HashSchema,
|
|
186
|
+
releaseId: ReleaseIdSchema,
|
|
187
|
+
resultCount: ReleaseCountSchema,
|
|
188
|
+
resultDigest: Sha256HashSchema,
|
|
189
|
+
routeDigest: Sha256HashSchema,
|
|
190
|
+
snapshots: ContentSnapshotSetSchema,
|
|
191
|
+
stagedArtifacts: ReleaseCountSchema,
|
|
192
|
+
stagedItems: ReleaseCountSchema,
|
|
193
|
+
stagedProjections: ReleaseCountSchema,
|
|
194
|
+
stagedRoutes: ReleaseCountSchema,
|
|
195
|
+
stagedSnapshotRows: ReleaseCountSchema,
|
|
196
|
+
}).pipe(Schema.check(Schema.makeFilter((receipt) => receipt.activatedHeads + receipt.deletedHeads === receipt.stagedItems &&
|
|
197
|
+
receipt.stagedArtifacts === receipt.activatedHeads &&
|
|
198
|
+
receipt.stagedSnapshotRows === snapshotRowCount(receipt.snapshots), {
|
|
199
|
+
message: "Expected activated head and artifact counts to match staged items.",
|
|
200
|
+
})));
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { type ContentReleaseBundle, type RollbackContentReleaseBundle } from "#contracts/release/lifecycle";
|
|
3
|
+
declare const ReleaseVerificationDecodeError_base: Schema.Class<ReleaseVerificationDecodeError, Schema.TaggedStruct<"ReleaseVerificationDecodeError", {
|
|
4
|
+
readonly message: Schema.Literal<"Release verification input does not satisfy its exact wire contract.">;
|
|
5
|
+
}>, import("effect/Cause").YieldableError>;
|
|
6
|
+
/** Unknown release input did not exactly satisfy its signed wire contract. */
|
|
7
|
+
export declare class ReleaseVerificationDecodeError extends ReleaseVerificationDecodeError_base {
|
|
8
|
+
}
|
|
9
|
+
declare const ReleaseManifestHashMismatchError_base: Schema.Class<ReleaseManifestHashMismatchError, Schema.TaggedStruct<"ReleaseManifestHashMismatchError", {
|
|
10
|
+
readonly actualHash: Schema.brand<Schema.String, "@NakafaAI/AksaraSha256Hash">;
|
|
11
|
+
readonly expectedHash: Schema.brand<Schema.String, "@NakafaAI/AksaraSha256Hash">;
|
|
12
|
+
readonly releaseId: Schema.brand<Schema.String, "@NakafaAI/AksaraReleaseId">;
|
|
13
|
+
}>, import("effect/Cause").YieldableError>;
|
|
14
|
+
/** The envelope hash does not identify its complete canonical manifest. */
|
|
15
|
+
export declare class ReleaseManifestHashMismatchError extends ReleaseManifestHashMismatchError_base {
|
|
16
|
+
}
|
|
17
|
+
declare const ReleaseBundleVerificationDecodeError_base: Schema.Class<ReleaseBundleVerificationDecodeError, Schema.TaggedStruct<"ReleaseBundleVerificationDecodeError", {
|
|
18
|
+
readonly message: Schema.Literal<"Release bundle verification input does not satisfy its exact wire contract.">;
|
|
19
|
+
}>, import("effect/Cause").YieldableError>;
|
|
20
|
+
/** Stored release recovery input does not satisfy its exact bundle contract. */
|
|
21
|
+
export declare class ReleaseBundleVerificationDecodeError extends ReleaseBundleVerificationDecodeError_base {
|
|
22
|
+
}
|
|
23
|
+
/** Strictly decodes and authenticates one complete release envelope. */
|
|
24
|
+
export declare const verifySignedContentRelease: (input: unknown) => Effect.Effect<{
|
|
25
|
+
readonly keyId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSigningKeyId">;
|
|
26
|
+
readonly manifest: {
|
|
27
|
+
readonly baseActiveAppLocales: readonly [("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">, ...(("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">)[]] | null;
|
|
28
|
+
readonly baseManifestHash: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">) | null;
|
|
29
|
+
readonly baseReleaseId: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraReleaseId">) | null;
|
|
30
|
+
readonly baseResultCount: number;
|
|
31
|
+
readonly baseResultDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
32
|
+
readonly deleteCount: number;
|
|
33
|
+
readonly itemCount: number;
|
|
34
|
+
readonly itemsDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
35
|
+
readonly origin: {
|
|
36
|
+
readonly kind: "git";
|
|
37
|
+
readonly sha: string & import("effect/Brand").Brand<"@NakafaAI/AksaraGitCommitSha">;
|
|
38
|
+
} | {
|
|
39
|
+
readonly kind: "rollback";
|
|
40
|
+
readonly releaseId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraReleaseId">;
|
|
41
|
+
};
|
|
42
|
+
readonly projectionCount: number;
|
|
43
|
+
readonly projectionDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
44
|
+
readonly releaseId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraReleaseId">;
|
|
45
|
+
readonly rendererContractVersion: "1.0.0";
|
|
46
|
+
readonly rendererManifestHash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
47
|
+
readonly resultCount: number;
|
|
48
|
+
readonly resultDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
49
|
+
readonly rollbackCount: number;
|
|
50
|
+
readonly rollbackDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
51
|
+
readonly routeCount: number;
|
|
52
|
+
readonly routeDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
53
|
+
readonly scope: {
|
|
54
|
+
readonly content?: readonly {
|
|
55
|
+
readonly artifactLocale: ("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraArtifactLocale">;
|
|
56
|
+
readonly contentKey: string & import("effect/Brand").Brand<"@NakafaAI/AksaraContentKey">;
|
|
57
|
+
readonly family: "article" | "material" | "page" | "question";
|
|
58
|
+
}[] | undefined;
|
|
59
|
+
readonly families: readonly ("article" | "material" | "page" | "question")[];
|
|
60
|
+
readonly snapshots: readonly ("program" | "quran" | "tryout")[];
|
|
61
|
+
};
|
|
62
|
+
readonly snapshots: {
|
|
63
|
+
readonly program: {
|
|
64
|
+
readonly baseSnapshotId: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">) | null;
|
|
65
|
+
readonly mode: "inherit" | "replace" | "restore";
|
|
66
|
+
readonly resultSnapshotId: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">) | null;
|
|
67
|
+
readonly rowCount: number;
|
|
68
|
+
readonly rowDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
69
|
+
};
|
|
70
|
+
readonly quran: {
|
|
71
|
+
readonly baseSnapshotId: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">) | null;
|
|
72
|
+
readonly mode: "inherit" | "replace" | "restore";
|
|
73
|
+
readonly resultSnapshotId: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">) | null;
|
|
74
|
+
readonly rowCount: number;
|
|
75
|
+
readonly rowDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
76
|
+
};
|
|
77
|
+
readonly tryout: {
|
|
78
|
+
readonly baseSnapshotId: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">) | null;
|
|
79
|
+
readonly mode: "inherit" | "replace" | "restore";
|
|
80
|
+
readonly resultSnapshotId: (string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">) | null;
|
|
81
|
+
readonly rowCount: number;
|
|
82
|
+
readonly rowDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
readonly upsertCount: number;
|
|
86
|
+
readonly activeAppLocales: readonly [("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">, ...(("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">)[]];
|
|
87
|
+
readonly format: "localized-content-release";
|
|
88
|
+
};
|
|
89
|
+
readonly manifestHash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
90
|
+
readonly signature: string & import("effect/Brand").Brand<"@NakafaAI/AksaraEd25519Signature">;
|
|
91
|
+
}, import("../signature/spec.js").PublicKeyParseError | import("../signature/spec.js").PublicKeyTypeError | import("#contracts/release/hash").ReleaseHashComputationError | ReleaseManifestHashMismatchError | ReleaseVerificationDecodeError | import("../signature/spec.js").SignatureCheckError | import("../signature/spec.js").SignatureInvalidError | import("../signature/spec.js").SigningKeyNotFoundError | import("../signature/spec.js").SigningKeyResolutionError, import("../signature/spec.js").ContentVerificationKeyResolver>;
|
|
92
|
+
/** Strictly decodes and authenticates one renderer-bound release bundle. */
|
|
93
|
+
declare function verifyBundle<A extends ContentReleaseBundle, I>(schema: Schema.Codec<A, I>, input: unknown): Effect.Effect<A, import("../errors.js").ContractDecodeError | import("../signature/spec.js").PublicKeyParseError | import("../signature/spec.js").PublicKeyTypeError | ReleaseBundleVerificationDecodeError | import("#contracts/release/hash").ReleaseHashComputationError | ReleaseManifestHashMismatchError | ReleaseVerificationDecodeError | import("../renderer/component.js").RendererAuthoringComponentDuplicateError | import("../renderer/component.js").RendererAuthoringComponentExtraError | import("../renderer/component.js").RendererAuthoringComponentMissingError | import("../renderer/component.js").RendererAuthoringComponentUnsupportedError | import("../renderer/component.js").RendererAuthoringSelectionNonCanonicalError | import("../renderer/contract.js").RendererManifestHashComputeError | import("../renderer/contract.js").RendererManifestHashMismatchError | import("../signature/spec.js").SignatureCheckError | import("../signature/spec.js").SignatureInvalidError | import("../signature/spec.js").SigningKeyNotFoundError | import("../signature/spec.js").SigningKeyResolutionError, import("../signature/spec.js").ContentVerificationKeyResolver>;
|
|
94
|
+
/** Authenticates one signed release and its frozen renderer contract together. */
|
|
95
|
+
export declare const verifyContentReleaseBundle: (input: unknown) => Effect.Effect<ContentReleaseBundle, Effect.Error<ReturnType<typeof verifyBundle>>, Effect.Services<ReturnType<typeof verifyBundle>>>;
|
|
96
|
+
/** Authenticates a renderer-bound release only when rollback owns its origin. */
|
|
97
|
+
export declare const verifyRollbackContentReleaseBundle: (input: unknown) => Effect.Effect<RollbackContentReleaseBundle, Effect.Error<ReturnType<typeof verifyBundle>>, Effect.Services<ReturnType<typeof verifyBundle>>>;
|
|
98
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { ReleaseIdSchema, Sha256HashSchema } from "#contracts/ids";
|
|
3
|
+
import { hashContentReleaseManifest } from "#contracts/release/hash";
|
|
4
|
+
import { ContentReleaseBundleSchema, RollbackContentReleaseBundleSchema, } from "#contracts/release/lifecycle";
|
|
5
|
+
import { canonicalizeContentReleaseSigningInput } from "#contracts/release/signing";
|
|
6
|
+
import { SignedContentReleaseSchema, } from "#contracts/release/spec";
|
|
7
|
+
import { validateRendererManifestHash } from "#contracts/renderer/manifest";
|
|
8
|
+
import { verifyEd25519Signature } from "#contracts/signature/verify";
|
|
9
|
+
/** Unknown release input did not exactly satisfy its signed wire contract. */
|
|
10
|
+
export class ReleaseVerificationDecodeError extends Schema.TaggedError()("ReleaseVerificationDecodeError", {
|
|
11
|
+
message: Schema.Literal("Release verification input does not satisfy its exact wire contract."),
|
|
12
|
+
}) {
|
|
13
|
+
}
|
|
14
|
+
/** The envelope hash does not identify its complete canonical manifest. */
|
|
15
|
+
export class ReleaseManifestHashMismatchError extends Schema.TaggedError()("ReleaseManifestHashMismatchError", {
|
|
16
|
+
actualHash: Sha256HashSchema,
|
|
17
|
+
expectedHash: Sha256HashSchema,
|
|
18
|
+
releaseId: ReleaseIdSchema,
|
|
19
|
+
}) {
|
|
20
|
+
}
|
|
21
|
+
/** Stored release recovery input does not satisfy its exact bundle contract. */
|
|
22
|
+
export class ReleaseBundleVerificationDecodeError extends Schema.TaggedError()("ReleaseBundleVerificationDecodeError", {
|
|
23
|
+
message: Schema.Literal("Release bundle verification input does not satisfy its exact wire contract."),
|
|
24
|
+
}) {
|
|
25
|
+
}
|
|
26
|
+
/** Confirms that a release envelope identifies its canonical manifest. */
|
|
27
|
+
function validateManifestHash(releaseId, expectedHash, actualHash) {
|
|
28
|
+
if (expectedHash === actualHash) {
|
|
29
|
+
return Effect.void;
|
|
30
|
+
}
|
|
31
|
+
return Effect.fail(new ReleaseManifestHashMismatchError({
|
|
32
|
+
actualHash,
|
|
33
|
+
expectedHash,
|
|
34
|
+
releaseId,
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
/** Authenticates one already decoded current release. */
|
|
38
|
+
const authenticateRelease = Effect.fn("AksaraContracts.authenticateRelease")(function* (release) {
|
|
39
|
+
const actualHash = yield* hashContentReleaseManifest(release.manifest);
|
|
40
|
+
yield* validateManifestHash(release.manifest.releaseId, release.manifestHash, actualHash);
|
|
41
|
+
const message = canonicalizeContentReleaseSigningInput(release.manifestHash, release.manifest);
|
|
42
|
+
yield* verifyEd25519Signature({
|
|
43
|
+
keyId: release.keyId,
|
|
44
|
+
message,
|
|
45
|
+
signature: release.signature,
|
|
46
|
+
subject: "release",
|
|
47
|
+
});
|
|
48
|
+
return release;
|
|
49
|
+
});
|
|
50
|
+
/** Strictly decodes and authenticates one complete release envelope. */
|
|
51
|
+
export const verifySignedContentRelease = Effect.fn("AksaraContracts.verifySignedContentRelease")((input) => Schema.decodeUnknownEffect(SignedContentReleaseSchema)(input, {
|
|
52
|
+
onExcessProperty: "error",
|
|
53
|
+
}).pipe(Effect.mapError(() => new ReleaseVerificationDecodeError({
|
|
54
|
+
message: "Release verification input does not satisfy its exact wire contract.",
|
|
55
|
+
})), Effect.flatMap(authenticateRelease)));
|
|
56
|
+
/** Strictly decodes and authenticates one renderer-bound release bundle. */
|
|
57
|
+
function verifyBundle(schema, input) {
|
|
58
|
+
return Schema.decodeUnknownEffect(schema)(input, {
|
|
59
|
+
onExcessProperty: "error",
|
|
60
|
+
}).pipe(Effect.mapError(() => new ReleaseBundleVerificationDecodeError({
|
|
61
|
+
message: "Release bundle verification input does not satisfy its exact wire contract.",
|
|
62
|
+
})), Effect.tap((bundle) => Effect.all([
|
|
63
|
+
verifySignedContentRelease(bundle.release),
|
|
64
|
+
validateRendererManifestHash(bundle.rendererManifest),
|
|
65
|
+
], { discard: true })));
|
|
66
|
+
}
|
|
67
|
+
/** Authenticates one signed release and its frozen renderer contract together. */
|
|
68
|
+
export const verifyContentReleaseBundle = Effect.fn("AksaraContracts.verifyContentReleaseBundle")((input) => verifyBundle(ContentReleaseBundleSchema, input));
|
|
69
|
+
/** Authenticates a renderer-bound release only when rollback owns its origin. */
|
|
70
|
+
export const verifyRollbackContentReleaseBundle = Effect.fn("AksaraContracts.verifyRollbackContentReleaseBundle")((input) => verifyBundle(RollbackContentReleaseBundleSchema, input));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { ArtifactRendererComponentMissingError, ArtifactRendererDomainUnpublishedError, ArtifactRendererVersionUnsupportedError, type ArtifactVerificationRequest, RendererContractVersionMismatchError } from "#contracts/artifact/spec";
|
|
3
|
+
import type { CompiledContentPayload } from "#contracts/content";
|
|
4
|
+
/** Verifies that a deployed live renderer can route and execute one artifact. */
|
|
5
|
+
export declare const verifyContentRendererCompatibility: (args_0: {
|
|
6
|
+
readonly payload: CompiledContentPayload;
|
|
7
|
+
readonly rendererContractVersion: ArtifactVerificationRequest["rendererContractVersion"];
|
|
8
|
+
readonly rendererManifest: unknown;
|
|
9
|
+
}) => Effect.Effect<{
|
|
10
|
+
readonly base: {
|
|
11
|
+
readonly authoringComponents: readonly {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly version: number;
|
|
14
|
+
}[];
|
|
15
|
+
readonly supportedComponents: readonly {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly version: number;
|
|
18
|
+
}[];
|
|
19
|
+
};
|
|
20
|
+
readonly domains: readonly {
|
|
21
|
+
readonly authoringComponents: readonly {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly version: number;
|
|
24
|
+
}[];
|
|
25
|
+
readonly name: "ai-ds" | "biology" | "chemistry" | "mathematics" | "physics" | "politics" | "site" | "snbt-general" | "snbt-math" | "snbt-plain" | "snbt-quant" | "tka-math";
|
|
26
|
+
readonly supportedComponents: readonly {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly version: number;
|
|
29
|
+
}[];
|
|
30
|
+
}[];
|
|
31
|
+
readonly format: "nakafa-mdx-renderer-v1";
|
|
32
|
+
readonly hash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
33
|
+
readonly publishedDomains: readonly ("ai-ds" | "biology" | "chemistry" | "mathematics" | "physics" | "politics" | "site" | "snbt-general" | "snbt-math" | "snbt-plain" | "snbt-quant" | "tka-math")[];
|
|
34
|
+
readonly rendererContractVersion: "1.0.0";
|
|
35
|
+
}, ArtifactRendererComponentMissingError | ArtifactRendererDomainUnpublishedError | ArtifactRendererVersionUnsupportedError | import("../errors.js").ContractDecodeError | import("./component.js").RendererAuthoringComponentDuplicateError | import("./component.js").RendererAuthoringComponentExtraError | import("./component.js").RendererAuthoringComponentMissingError | import("./component.js").RendererAuthoringComponentUnsupportedError | import("./component.js").RendererAuthoringSelectionNonCanonicalError | RendererContractVersionMismatchError | import("#contracts/renderer/contract").RendererDomainCapabilityMissingError | import("#contracts/renderer/contract").RendererManifestHashComputeError | import("#contracts/renderer/contract").RendererManifestHashMismatchError, never>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { ArtifactRendererComponentMissingError, ArtifactRendererDomainUnpublishedError, ArtifactRendererVersionUnsupportedError, RendererContractVersionMismatchError, } from "#contracts/artifact/spec";
|
|
3
|
+
import { selectRendererDomainCapability, } from "#contracts/renderer/contract";
|
|
4
|
+
import { validateRendererManifestHash } from "#contracts/renderer/manifest";
|
|
5
|
+
/** Confirms that base plus the selected domain implement every requirement. */
|
|
6
|
+
function validateRendererRequirements(payload, manifest) {
|
|
7
|
+
return Effect.gen(function* () {
|
|
8
|
+
const domain = yield* selectRendererDomainCapability(manifest, payload.rendererDomain);
|
|
9
|
+
const supportedComponents = [
|
|
10
|
+
...manifest.base.supportedComponents,
|
|
11
|
+
...domain.supportedComponents,
|
|
12
|
+
];
|
|
13
|
+
for (const requirement of payload.requiredComponents) {
|
|
14
|
+
const versions = supportedComponents.filter(({ name }) => name === requirement.name);
|
|
15
|
+
if (versions.length === 0) {
|
|
16
|
+
return yield* new ArtifactRendererComponentMissingError({
|
|
17
|
+
componentName: requirement.name,
|
|
18
|
+
contentKey: payload.contentKey,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (!versions.some(({ version }) => version === requirement.version)) {
|
|
22
|
+
return yield* new ArtifactRendererVersionUnsupportedError({
|
|
23
|
+
componentName: requirement.name,
|
|
24
|
+
contentKey: payload.contentKey,
|
|
25
|
+
requiredVersion: requirement.version,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/** Verifies that a deployed live renderer can route and execute one artifact. */
|
|
32
|
+
export const verifyContentRendererCompatibility = Effect.fn("AksaraContracts.verifyContentRendererCompatibility")(function* ({ payload, rendererContractVersion, rendererManifest, }) {
|
|
33
|
+
const manifest = yield* validateRendererManifestHash(rendererManifest);
|
|
34
|
+
if (!manifest.publishedDomains.includes(payload.rendererDomain)) {
|
|
35
|
+
return yield* new ArtifactRendererDomainUnpublishedError({
|
|
36
|
+
contentKey: payload.contentKey,
|
|
37
|
+
rendererDomain: payload.rendererDomain,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
if (rendererContractVersion !== manifest.rendererContractVersion) {
|
|
41
|
+
return yield* new RendererContractVersionMismatchError({
|
|
42
|
+
actualVersion: rendererContractVersion,
|
|
43
|
+
expectedVersion: manifest.rendererContractVersion,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
yield* validateRendererRequirements(payload, manifest);
|
|
47
|
+
return manifest;
|
|
48
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** Renderer names are simple identifiers, never dotted member paths. */
|
|
3
|
+
export declare const RendererComponentNameSchema: Schema.String;
|
|
4
|
+
/** One named renderer capability and its positive contract version. */
|
|
5
|
+
export declare const RendererComponentRequirementSchema: Schema.Struct<{
|
|
6
|
+
readonly name: Schema.String;
|
|
7
|
+
readonly version: Schema.Finite;
|
|
8
|
+
}>;
|
|
9
|
+
export type RendererComponentRequirement = typeof RendererComponentRequirementSchema.Type;
|
|
10
|
+
/** Canonically sorts component requirements by name and then version. */
|
|
11
|
+
export declare function sortRendererComponentRequirements(requirements: readonly RendererComponentRequirement[]): {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly version: number;
|
|
14
|
+
}[];
|
|
15
|
+
/** Canonical runtime capabilities; an empty route domain is valid. */
|
|
16
|
+
export declare const RendererSupportedComponentsSchema: Schema.$Array<Schema.Struct<{
|
|
17
|
+
readonly name: Schema.String;
|
|
18
|
+
readonly version: Schema.Finite;
|
|
19
|
+
}>>;
|
|
20
|
+
/** Canonical compiler choices; an empty route domain is valid. */
|
|
21
|
+
export declare const RendererAuthoringComponentsSchema: Schema.$Array<Schema.Struct<{
|
|
22
|
+
readonly name: Schema.String;
|
|
23
|
+
readonly version: Schema.Finite;
|
|
24
|
+
}>>;
|
|
25
|
+
/** Canonical runtime capabilities; multiple versions per name are allowed. */
|
|
26
|
+
export declare const RendererManifestSupportedComponentsSchema: Schema.$Array<Schema.Struct<{
|
|
27
|
+
readonly name: Schema.String;
|
|
28
|
+
readonly version: Schema.Finite;
|
|
29
|
+
}>>;
|
|
30
|
+
/** Canonical compiler choices; exactly one version exists for every name. */
|
|
31
|
+
export declare const RendererManifestAuthoringComponentsSchema: Schema.$Array<Schema.Struct<{
|
|
32
|
+
readonly name: Schema.String;
|
|
33
|
+
readonly version: Schema.Finite;
|
|
34
|
+
}>>;
|
|
35
|
+
/** Canonical artifact requirements; an artifact chooses one version per name. */
|
|
36
|
+
export declare const CompiledContentRequirementsSchema: Schema.$Array<Schema.Struct<{
|
|
37
|
+
readonly name: Schema.String;
|
|
38
|
+
readonly version: Schema.Finite;
|
|
39
|
+
}>>;
|
|
40
|
+
declare const RendererCapabilityStructSchema: Schema.Struct<{
|
|
41
|
+
authoringComponents: Schema.$Array<Schema.Struct<{
|
|
42
|
+
readonly name: Schema.String;
|
|
43
|
+
readonly version: Schema.Finite;
|
|
44
|
+
}>>;
|
|
45
|
+
supportedComponents: Schema.$Array<Schema.Struct<{
|
|
46
|
+
readonly name: Schema.String;
|
|
47
|
+
readonly version: Schema.Finite;
|
|
48
|
+
}>>;
|
|
49
|
+
}>;
|
|
50
|
+
type RendererCapabilityStruct = typeof RendererCapabilityStructSchema.Type;
|
|
51
|
+
/** Checks that authoring pins select every supported component exactly once. */
|
|
52
|
+
export declare function hasCompleteRendererSelection(components: RendererCapabilityStruct): boolean;
|
|
53
|
+
/** One complete component contract owned by one physical registry scope. */
|
|
54
|
+
export declare const RendererCapabilitySchema: Schema.Struct<{
|
|
55
|
+
authoringComponents: Schema.$Array<Schema.Struct<{
|
|
56
|
+
readonly name: Schema.String;
|
|
57
|
+
readonly version: Schema.Finite;
|
|
58
|
+
}>>;
|
|
59
|
+
supportedComponents: Schema.$Array<Schema.Struct<{
|
|
60
|
+
readonly name: Schema.String;
|
|
61
|
+
readonly version: Schema.Finite;
|
|
62
|
+
}>>;
|
|
63
|
+
}>;
|
|
64
|
+
export type RendererCapability = typeof RendererCapabilitySchema.Type;
|
|
65
|
+
declare const RendererAuthoringComponentDuplicateError_base: Schema.Class<RendererAuthoringComponentDuplicateError, Schema.TaggedStruct<"RendererAuthoringComponentDuplicateError", {
|
|
66
|
+
readonly componentName: Schema.String;
|
|
67
|
+
}>, import("effect/Cause").YieldableError>;
|
|
68
|
+
/** More than one authoring version was selected for one component name. */
|
|
69
|
+
export declare class RendererAuthoringComponentDuplicateError extends RendererAuthoringComponentDuplicateError_base {
|
|
70
|
+
}
|
|
71
|
+
declare const RendererAuthoringComponentMissingError_base: Schema.Class<RendererAuthoringComponentMissingError, Schema.TaggedStruct<"RendererAuthoringComponentMissingError", {
|
|
72
|
+
readonly componentName: Schema.String;
|
|
73
|
+
}>, import("effect/Cause").YieldableError>;
|
|
74
|
+
/** A runtime-supported component has no pinned authoring version. */
|
|
75
|
+
export declare class RendererAuthoringComponentMissingError extends RendererAuthoringComponentMissingError_base {
|
|
76
|
+
}
|
|
77
|
+
declare const RendererAuthoringComponentExtraError_base: Schema.Class<RendererAuthoringComponentExtraError, Schema.TaggedStruct<"RendererAuthoringComponentExtraError", {
|
|
78
|
+
readonly componentName: Schema.String;
|
|
79
|
+
}>, import("effect/Cause").YieldableError>;
|
|
80
|
+
/** Authoring selected a component name absent from runtime support. */
|
|
81
|
+
export declare class RendererAuthoringComponentExtraError extends RendererAuthoringComponentExtraError_base {
|
|
82
|
+
}
|
|
83
|
+
declare const RendererAuthoringComponentUnsupportedError_base: Schema.Class<RendererAuthoringComponentUnsupportedError, Schema.TaggedStruct<"RendererAuthoringComponentUnsupportedError", {
|
|
84
|
+
readonly componentName: Schema.String;
|
|
85
|
+
readonly version: Schema.Finite;
|
|
86
|
+
}>, import("effect/Cause").YieldableError>;
|
|
87
|
+
/** Authoring selected a version absent from runtime support. */
|
|
88
|
+
export declare class RendererAuthoringComponentUnsupportedError extends RendererAuthoringComponentUnsupportedError_base {
|
|
89
|
+
}
|
|
90
|
+
declare const RendererAuthoringSelectionNonCanonicalError_base: Schema.Class<RendererAuthoringSelectionNonCanonicalError, Schema.TaggedStruct<"RendererAuthoringSelectionNonCanonicalError", {
|
|
91
|
+
readonly componentName: Schema.String;
|
|
92
|
+
readonly index: Schema.Finite;
|
|
93
|
+
}>, import("effect/Cause").YieldableError>;
|
|
94
|
+
/** Authoring selections were not sorted by canonical component order. */
|
|
95
|
+
export declare class RendererAuthoringSelectionNonCanonicalError extends RendererAuthoringSelectionNonCanonicalError_base {
|
|
96
|
+
}
|
|
97
|
+
/** Serializes one selected compiler component set in canonical order. */
|
|
98
|
+
export declare function canonicalizeRendererAuthoringSelection(authoringComponents: readonly RendererComponentRequirement[]): string;
|
|
99
|
+
export {};
|