@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,280 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** Active placement before immutable artifact hashes are bound. */
|
|
3
|
+
export declare const TryoutPlacementSourceSchema: Schema.Struct<{
|
|
4
|
+
answerArtifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
5
|
+
answerContentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
6
|
+
appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
7
|
+
blueprint: Schema.optionalKey<Schema.Struct<{
|
|
8
|
+
readonly cognitiveLevel: Schema.String;
|
|
9
|
+
readonly contentDomain: Schema.String;
|
|
10
|
+
readonly topic: Schema.String;
|
|
11
|
+
}>>;
|
|
12
|
+
countryKey: Schema.String;
|
|
13
|
+
deliveryLanguage: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraDeliveryLanguage">;
|
|
14
|
+
examKey: Schema.String;
|
|
15
|
+
languagePolicy: Schema.Union<readonly [Schema.Struct<{
|
|
16
|
+
readonly kind: Schema.Literal<"app-locale">;
|
|
17
|
+
}>, Schema.Struct<{
|
|
18
|
+
readonly kind: Schema.Literal<"fixed">;
|
|
19
|
+
readonly language: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraDeliveryLanguage">;
|
|
20
|
+
}>]>;
|
|
21
|
+
questionArtifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
22
|
+
questionContentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
23
|
+
questionOrder: Schema.Finite;
|
|
24
|
+
questionSourcePath: Schema.brand<Schema.String, "@NakafaAI/AksaraCorpusSourcePath">;
|
|
25
|
+
rendererDomain: Schema.Literals<readonly ["ai-ds", "biology", "chemistry", "mathematics", "physics", "politics", "site", "snbt-general", "snbt-math", "snbt-plain", "snbt-quant", "tka-math"]>;
|
|
26
|
+
response: Schema.Union<readonly [Schema.Struct<{
|
|
27
|
+
readonly categories: Schema.$Array<Schema.Struct<{
|
|
28
|
+
readonly categoryKey: Schema.String;
|
|
29
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
30
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
31
|
+
readonly kind: Schema.Literal<"math">;
|
|
32
|
+
readonly math: Schema.String;
|
|
33
|
+
}>, Schema.Struct<{
|
|
34
|
+
readonly kind: Schema.Literal<"text">;
|
|
35
|
+
readonly text: Schema.String;
|
|
36
|
+
}>]>>;
|
|
37
|
+
readonly order: Schema.Int;
|
|
38
|
+
}>>;
|
|
39
|
+
readonly kind: Schema.Literal<"category">;
|
|
40
|
+
readonly statements: Schema.$Array<Schema.Struct<{
|
|
41
|
+
readonly correctCategoryKey: Schema.String;
|
|
42
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
43
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
44
|
+
readonly kind: Schema.Literal<"math">;
|
|
45
|
+
readonly math: Schema.String;
|
|
46
|
+
}>, Schema.Struct<{
|
|
47
|
+
readonly kind: Schema.Literal<"text">;
|
|
48
|
+
readonly text: Schema.String;
|
|
49
|
+
}>]>>;
|
|
50
|
+
readonly order: Schema.Int;
|
|
51
|
+
readonly statementKey: Schema.String;
|
|
52
|
+
}>>;
|
|
53
|
+
}>, Schema.Struct<{
|
|
54
|
+
readonly kind: Schema.Literal<"multiple-choice">;
|
|
55
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
56
|
+
readonly isCorrect: Schema.Boolean;
|
|
57
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
58
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
59
|
+
readonly kind: Schema.Literal<"math">;
|
|
60
|
+
readonly math: Schema.String;
|
|
61
|
+
}>, Schema.Struct<{
|
|
62
|
+
readonly kind: Schema.Literal<"text">;
|
|
63
|
+
readonly text: Schema.String;
|
|
64
|
+
}>]>>;
|
|
65
|
+
readonly optionKey: Schema.String;
|
|
66
|
+
readonly order: Schema.Int;
|
|
67
|
+
}>>;
|
|
68
|
+
}>, Schema.Struct<{
|
|
69
|
+
readonly kind: Schema.Literal<"single-choice">;
|
|
70
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
71
|
+
readonly isCorrect: Schema.Boolean;
|
|
72
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
73
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
74
|
+
readonly kind: Schema.Literal<"math">;
|
|
75
|
+
readonly math: Schema.String;
|
|
76
|
+
}>, Schema.Struct<{
|
|
77
|
+
readonly kind: Schema.Literal<"text">;
|
|
78
|
+
readonly text: Schema.String;
|
|
79
|
+
}>]>>;
|
|
80
|
+
readonly optionKey: Schema.String;
|
|
81
|
+
readonly order: Schema.Int;
|
|
82
|
+
}>>;
|
|
83
|
+
}>]>;
|
|
84
|
+
scope: Schema.Literal<"server">;
|
|
85
|
+
sectionKey: Schema.String;
|
|
86
|
+
setKey: Schema.String;
|
|
87
|
+
sourceRevision: Schema.Trimmed;
|
|
88
|
+
stimulusKey: Schema.optionalKey<Schema.String>;
|
|
89
|
+
trackKey: Schema.String;
|
|
90
|
+
}>;
|
|
91
|
+
export type TryoutPlacementSource = typeof TryoutPlacementSourceSchema.Type;
|
|
92
|
+
/** Artifact-bound placement retained by signed attempt state. */
|
|
93
|
+
export declare const TryoutPlacementSchema: Schema.Struct<{
|
|
94
|
+
readonly answerArtifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
95
|
+
readonly answerContentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
96
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
97
|
+
readonly blueprint: Schema.optionalKey<Schema.Struct<{
|
|
98
|
+
readonly cognitiveLevel: Schema.String;
|
|
99
|
+
readonly contentDomain: Schema.String;
|
|
100
|
+
readonly topic: Schema.String;
|
|
101
|
+
}>>;
|
|
102
|
+
readonly countryKey: Schema.String;
|
|
103
|
+
readonly deliveryLanguage: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraDeliveryLanguage">;
|
|
104
|
+
readonly examKey: Schema.String;
|
|
105
|
+
readonly languagePolicy: Schema.Union<readonly [Schema.Struct<{
|
|
106
|
+
readonly kind: Schema.Literal<"app-locale">;
|
|
107
|
+
}>, Schema.Struct<{
|
|
108
|
+
readonly kind: Schema.Literal<"fixed">;
|
|
109
|
+
readonly language: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraDeliveryLanguage">;
|
|
110
|
+
}>]>;
|
|
111
|
+
readonly questionArtifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
112
|
+
readonly questionContentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
113
|
+
readonly questionOrder: Schema.Finite;
|
|
114
|
+
readonly questionSourcePath: Schema.brand<Schema.String, "@NakafaAI/AksaraCorpusSourcePath">;
|
|
115
|
+
readonly rendererDomain: Schema.Literals<readonly ["ai-ds", "biology", "chemistry", "mathematics", "physics", "politics", "site", "snbt-general", "snbt-math", "snbt-plain", "snbt-quant", "tka-math"]>;
|
|
116
|
+
readonly response: Schema.Union<readonly [Schema.Struct<{
|
|
117
|
+
readonly categories: Schema.$Array<Schema.Struct<{
|
|
118
|
+
readonly categoryKey: Schema.String;
|
|
119
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
120
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
121
|
+
readonly kind: Schema.Literal<"math">;
|
|
122
|
+
readonly math: Schema.String;
|
|
123
|
+
}>, Schema.Struct<{
|
|
124
|
+
readonly kind: Schema.Literal<"text">;
|
|
125
|
+
readonly text: Schema.String;
|
|
126
|
+
}>]>>;
|
|
127
|
+
readonly order: Schema.Int;
|
|
128
|
+
}>>;
|
|
129
|
+
readonly kind: Schema.Literal<"category">;
|
|
130
|
+
readonly statements: Schema.$Array<Schema.Struct<{
|
|
131
|
+
readonly correctCategoryKey: Schema.String;
|
|
132
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
133
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
134
|
+
readonly kind: Schema.Literal<"math">;
|
|
135
|
+
readonly math: Schema.String;
|
|
136
|
+
}>, Schema.Struct<{
|
|
137
|
+
readonly kind: Schema.Literal<"text">;
|
|
138
|
+
readonly text: Schema.String;
|
|
139
|
+
}>]>>;
|
|
140
|
+
readonly order: Schema.Int;
|
|
141
|
+
readonly statementKey: Schema.String;
|
|
142
|
+
}>>;
|
|
143
|
+
}>, Schema.Struct<{
|
|
144
|
+
readonly kind: Schema.Literal<"multiple-choice">;
|
|
145
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
146
|
+
readonly isCorrect: Schema.Boolean;
|
|
147
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
148
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
149
|
+
readonly kind: Schema.Literal<"math">;
|
|
150
|
+
readonly math: Schema.String;
|
|
151
|
+
}>, Schema.Struct<{
|
|
152
|
+
readonly kind: Schema.Literal<"text">;
|
|
153
|
+
readonly text: Schema.String;
|
|
154
|
+
}>]>>;
|
|
155
|
+
readonly optionKey: Schema.String;
|
|
156
|
+
readonly order: Schema.Int;
|
|
157
|
+
}>>;
|
|
158
|
+
}>, Schema.Struct<{
|
|
159
|
+
readonly kind: Schema.Literal<"single-choice">;
|
|
160
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
161
|
+
readonly isCorrect: Schema.Boolean;
|
|
162
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
163
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
164
|
+
readonly kind: Schema.Literal<"math">;
|
|
165
|
+
readonly math: Schema.String;
|
|
166
|
+
}>, Schema.Struct<{
|
|
167
|
+
readonly kind: Schema.Literal<"text">;
|
|
168
|
+
readonly text: Schema.String;
|
|
169
|
+
}>]>>;
|
|
170
|
+
readonly optionKey: Schema.String;
|
|
171
|
+
readonly order: Schema.Int;
|
|
172
|
+
}>>;
|
|
173
|
+
}>]>;
|
|
174
|
+
readonly scope: Schema.Literal<"server">;
|
|
175
|
+
readonly sectionKey: Schema.String;
|
|
176
|
+
readonly setKey: Schema.String;
|
|
177
|
+
readonly sourceRevision: Schema.Trimmed;
|
|
178
|
+
readonly stimulusKey: Schema.optionalKey<Schema.String>;
|
|
179
|
+
readonly trackKey: Schema.String;
|
|
180
|
+
readonly answerArtifactHash: Schema.brand<Schema.String, "@NakafaAI/AksaraSha256Hash">;
|
|
181
|
+
readonly contentHash: Schema.brand<Schema.String, "@NakafaAI/AksaraTryoutContentHash">;
|
|
182
|
+
readonly questionArtifactHash: Schema.brand<Schema.String, "@NakafaAI/AksaraSha256Hash">;
|
|
183
|
+
}>;
|
|
184
|
+
export type TryoutPlacement = typeof TryoutPlacementSchema.Type;
|
|
185
|
+
/** Hashed immutable placement accepted by snapshot publication. */
|
|
186
|
+
export declare const TryoutPlacementRecordSchema: Schema.Struct<{
|
|
187
|
+
readonly row: Schema.Struct<{
|
|
188
|
+
readonly answerArtifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
189
|
+
readonly answerContentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
190
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
191
|
+
readonly blueprint: Schema.optionalKey<Schema.Struct<{
|
|
192
|
+
readonly cognitiveLevel: Schema.String;
|
|
193
|
+
readonly contentDomain: Schema.String;
|
|
194
|
+
readonly topic: Schema.String;
|
|
195
|
+
}>>;
|
|
196
|
+
readonly countryKey: Schema.String;
|
|
197
|
+
readonly deliveryLanguage: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraDeliveryLanguage">;
|
|
198
|
+
readonly examKey: Schema.String;
|
|
199
|
+
readonly languagePolicy: Schema.Union<readonly [Schema.Struct<{
|
|
200
|
+
readonly kind: Schema.Literal<"app-locale">;
|
|
201
|
+
}>, Schema.Struct<{
|
|
202
|
+
readonly kind: Schema.Literal<"fixed">;
|
|
203
|
+
readonly language: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraDeliveryLanguage">;
|
|
204
|
+
}>]>;
|
|
205
|
+
readonly questionArtifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
206
|
+
readonly questionContentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
207
|
+
readonly questionOrder: Schema.Finite;
|
|
208
|
+
readonly questionSourcePath: Schema.brand<Schema.String, "@NakafaAI/AksaraCorpusSourcePath">;
|
|
209
|
+
readonly rendererDomain: Schema.Literals<readonly ["ai-ds", "biology", "chemistry", "mathematics", "physics", "politics", "site", "snbt-general", "snbt-math", "snbt-plain", "snbt-quant", "tka-math"]>;
|
|
210
|
+
readonly response: Schema.Union<readonly [Schema.Struct<{
|
|
211
|
+
readonly categories: Schema.$Array<Schema.Struct<{
|
|
212
|
+
readonly categoryKey: Schema.String;
|
|
213
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
214
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
215
|
+
readonly kind: Schema.Literal<"math">;
|
|
216
|
+
readonly math: Schema.String;
|
|
217
|
+
}>, Schema.Struct<{
|
|
218
|
+
readonly kind: Schema.Literal<"text">;
|
|
219
|
+
readonly text: Schema.String;
|
|
220
|
+
}>]>>;
|
|
221
|
+
readonly order: Schema.Int;
|
|
222
|
+
}>>;
|
|
223
|
+
readonly kind: Schema.Literal<"category">;
|
|
224
|
+
readonly statements: Schema.$Array<Schema.Struct<{
|
|
225
|
+
readonly correctCategoryKey: Schema.String;
|
|
226
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
227
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
228
|
+
readonly kind: Schema.Literal<"math">;
|
|
229
|
+
readonly math: Schema.String;
|
|
230
|
+
}>, Schema.Struct<{
|
|
231
|
+
readonly kind: Schema.Literal<"text">;
|
|
232
|
+
readonly text: Schema.String;
|
|
233
|
+
}>]>>;
|
|
234
|
+
readonly order: Schema.Int;
|
|
235
|
+
readonly statementKey: Schema.String;
|
|
236
|
+
}>>;
|
|
237
|
+
}>, Schema.Struct<{
|
|
238
|
+
readonly kind: Schema.Literal<"multiple-choice">;
|
|
239
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
240
|
+
readonly isCorrect: Schema.Boolean;
|
|
241
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
242
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
243
|
+
readonly kind: Schema.Literal<"math">;
|
|
244
|
+
readonly math: Schema.String;
|
|
245
|
+
}>, Schema.Struct<{
|
|
246
|
+
readonly kind: Schema.Literal<"text">;
|
|
247
|
+
readonly text: Schema.String;
|
|
248
|
+
}>]>>;
|
|
249
|
+
readonly optionKey: Schema.String;
|
|
250
|
+
readonly order: Schema.Int;
|
|
251
|
+
}>>;
|
|
252
|
+
}>, Schema.Struct<{
|
|
253
|
+
readonly kind: Schema.Literal<"single-choice">;
|
|
254
|
+
readonly options: Schema.$Array<Schema.Struct<{
|
|
255
|
+
readonly isCorrect: Schema.Boolean;
|
|
256
|
+
readonly label: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
|
|
257
|
+
readonly display: Schema.Literals<readonly ["block", "inline"]>;
|
|
258
|
+
readonly kind: Schema.Literal<"math">;
|
|
259
|
+
readonly math: Schema.String;
|
|
260
|
+
}>, Schema.Struct<{
|
|
261
|
+
readonly kind: Schema.Literal<"text">;
|
|
262
|
+
readonly text: Schema.String;
|
|
263
|
+
}>]>>;
|
|
264
|
+
readonly optionKey: Schema.String;
|
|
265
|
+
readonly order: Schema.Int;
|
|
266
|
+
}>>;
|
|
267
|
+
}>]>;
|
|
268
|
+
readonly scope: Schema.Literal<"server">;
|
|
269
|
+
readonly sectionKey: Schema.String;
|
|
270
|
+
readonly setKey: Schema.String;
|
|
271
|
+
readonly sourceRevision: Schema.Trimmed;
|
|
272
|
+
readonly stimulusKey: Schema.optionalKey<Schema.String>;
|
|
273
|
+
readonly trackKey: Schema.String;
|
|
274
|
+
readonly answerArtifactHash: Schema.brand<Schema.String, "@NakafaAI/AksaraSha256Hash">;
|
|
275
|
+
readonly contentHash: Schema.brand<Schema.String, "@NakafaAI/AksaraTryoutContentHash">;
|
|
276
|
+
readonly questionArtifactHash: Schema.brand<Schema.String, "@NakafaAI/AksaraSha256Hash">;
|
|
277
|
+
}>;
|
|
278
|
+
readonly rowHash: Schema.brand<Schema.String, "@NakafaAI/AksaraSha256Hash">;
|
|
279
|
+
}>;
|
|
280
|
+
export type TryoutPlacementRecord = typeof TryoutPlacementRecordSchema.Type;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ContentKeySchema, CorpusSourcePathSchema, Sha256HashSchema, } from "#contracts/ids";
|
|
3
|
+
import { AppLocaleSchema, ArtifactLocaleSchema, DeliveryLanguageSchema, } from "#contracts/locale";
|
|
4
|
+
import { QuestionKeySchema, questionKeyParts, } from "#contracts/question/identity";
|
|
5
|
+
import { QuestionBlueprintSchema } from "#contracts/question/item";
|
|
6
|
+
import { QuestionResponseSchema } from "#contracts/question/response";
|
|
7
|
+
import { RendererDomainSchema } from "#contracts/renderer/domain";
|
|
8
|
+
import { TryoutKeySchema } from "#contracts/tryout/key";
|
|
9
|
+
import { AssessmentLanguagePolicySchema, deliveryLanguageForPolicy, } from "#contracts/tryout/language";
|
|
10
|
+
import { TryoutContentHashSchema, TryoutSourceRevisionSchema, } from "#contracts/tryout/spec";
|
|
11
|
+
const PositiveCountSchema = Schema.Finite.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThan(0)));
|
|
12
|
+
const PlacementFields = {
|
|
13
|
+
answerArtifactLocale: ArtifactLocaleSchema,
|
|
14
|
+
answerContentKey: ContentKeySchema,
|
|
15
|
+
appLocale: AppLocaleSchema,
|
|
16
|
+
blueprint: Schema.optionalKey(QuestionBlueprintSchema),
|
|
17
|
+
countryKey: TryoutKeySchema,
|
|
18
|
+
deliveryLanguage: DeliveryLanguageSchema,
|
|
19
|
+
examKey: TryoutKeySchema,
|
|
20
|
+
languagePolicy: AssessmentLanguagePolicySchema,
|
|
21
|
+
questionArtifactLocale: ArtifactLocaleSchema,
|
|
22
|
+
questionContentKey: ContentKeySchema,
|
|
23
|
+
questionOrder: PositiveCountSchema,
|
|
24
|
+
questionSourcePath: CorpusSourcePathSchema,
|
|
25
|
+
rendererDomain: RendererDomainSchema,
|
|
26
|
+
response: QuestionResponseSchema,
|
|
27
|
+
scope: Schema.Literal("server"),
|
|
28
|
+
sectionKey: TryoutKeySchema,
|
|
29
|
+
setKey: TryoutKeySchema,
|
|
30
|
+
sourceRevision: TryoutSourceRevisionSchema,
|
|
31
|
+
stimulusKey: Schema.optionalKey(TryoutKeySchema),
|
|
32
|
+
trackKey: TryoutKeySchema,
|
|
33
|
+
};
|
|
34
|
+
/** Checks that content keys, source path, and authored order agree. */
|
|
35
|
+
function hasCoherentPlacementKeys(input) {
|
|
36
|
+
const questionSuffix = "/question";
|
|
37
|
+
if (!input.questionContentKey.endsWith(questionSuffix)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
const questionRoot = input.questionContentKey.slice(0, -questionSuffix.length);
|
|
41
|
+
if (!Schema.is(QuestionKeySchema)(questionRoot)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const parts = questionKeyParts(questionRoot);
|
|
45
|
+
return (input.answerContentKey === `${questionRoot}/answer` &&
|
|
46
|
+
parts.countryKey === input.countryKey &&
|
|
47
|
+
parts.examKey === input.examKey &&
|
|
48
|
+
parts.sectionKey === input.sectionKey &&
|
|
49
|
+
parts.setKey === input.setKey &&
|
|
50
|
+
parts.questionNumber === input.questionOrder &&
|
|
51
|
+
input.questionSourcePath === `packages/corpus/${questionRoot}` &&
|
|
52
|
+
input.questionContentKey === `${questionRoot}/question`);
|
|
53
|
+
}
|
|
54
|
+
/** Checks app, delivery, and artifact language identity for one placement. */
|
|
55
|
+
function hasCoherentPlacementLanguages(input) {
|
|
56
|
+
const expectedDeliveryLanguage = deliveryLanguageForPolicy(input.languagePolicy, input.appLocale);
|
|
57
|
+
return (input.deliveryLanguage === expectedDeliveryLanguage &&
|
|
58
|
+
input.questionArtifactLocale === expectedDeliveryLanguage &&
|
|
59
|
+
input.answerArtifactLocale === input.appLocale);
|
|
60
|
+
}
|
|
61
|
+
/** Active placement before immutable artifact hashes are bound. */
|
|
62
|
+
export const TryoutPlacementSourceSchema = Schema.Struct(PlacementFields).pipe(Schema.check(Schema.makeFilter(hasCoherentPlacementKeys, {
|
|
63
|
+
message: "Placement source, content keys, and authored order must agree.",
|
|
64
|
+
})), Schema.check(Schema.makeFilter(hasCoherentPlacementLanguages, {
|
|
65
|
+
message: "Placement app, delivery, question, and answer languages must agree.",
|
|
66
|
+
})));
|
|
67
|
+
/** Artifact-bound placement retained by signed attempt state. */
|
|
68
|
+
export const TryoutPlacementSchema = Schema.Struct({
|
|
69
|
+
...PlacementFields,
|
|
70
|
+
answerArtifactHash: Sha256HashSchema,
|
|
71
|
+
contentHash: TryoutContentHashSchema,
|
|
72
|
+
questionArtifactHash: Sha256HashSchema,
|
|
73
|
+
}).pipe(Schema.check(Schema.makeFilter(hasCoherentPlacementKeys, {
|
|
74
|
+
message: "Placement source, content keys, and authored order must agree.",
|
|
75
|
+
})), Schema.check(Schema.makeFilter(hasCoherentPlacementLanguages, {
|
|
76
|
+
message: "Placement app, delivery, question, and answer languages must agree.",
|
|
77
|
+
})));
|
|
78
|
+
/** Hashed immutable placement accepted by snapshot publication. */
|
|
79
|
+
export const TryoutPlacementRecordSchema = Schema.Struct({
|
|
80
|
+
row: TryoutPlacementSchema,
|
|
81
|
+
rowHash: Sha256HashSchema,
|
|
82
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import { Sha256HashSchema } from "#contracts/ids";
|
|
3
|
+
declare const TryoutDigestError_base: Schema.Class<TryoutDigestError, Schema.TaggedStruct<"TryoutDigestError", {
|
|
4
|
+
readonly code: Schema.Literals<readonly ["integrity", "order"]>;
|
|
5
|
+
readonly identity: Schema.String;
|
|
6
|
+
}>, import("effect/Cause").YieldableError>;
|
|
7
|
+
/** A snapshot stream is duplicated, unsorted, or tampered. */
|
|
8
|
+
export declare class TryoutDigestError extends TryoutDigestError_base {
|
|
9
|
+
}
|
|
10
|
+
interface DigestRecord<Row> {
|
|
11
|
+
readonly row: Row;
|
|
12
|
+
readonly rowHash: typeof Sha256HashSchema.Type;
|
|
13
|
+
}
|
|
14
|
+
/** Digests one canonically ordered record stream with constant memory. */
|
|
15
|
+
export declare const digestTryoutRecords: <E, R, Row>(input: {
|
|
16
|
+
/** Serializes one row into its exact digest bytes. */
|
|
17
|
+
readonly canonicalize: (row: Row) => string;
|
|
18
|
+
readonly domain: string;
|
|
19
|
+
/** Returns one stable ordering identity for a row. */
|
|
20
|
+
readonly identity: (row: Row) => string;
|
|
21
|
+
readonly records: Stream.Stream<DigestRecord<Row>, E, R>;
|
|
22
|
+
/** Recomputes the authenticated row hash for integrity checking. */
|
|
23
|
+
readonly rowHash: (row: Row) => typeof Sha256HashSchema.Type;
|
|
24
|
+
}) => Effect.Effect<{
|
|
25
|
+
count: number;
|
|
26
|
+
digest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
27
|
+
}, E | TryoutDigestError, R>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { Effect, Schema, Stream } from "effect";
|
|
3
|
+
import { Sha256HashSchema } from "#contracts/ids";
|
|
4
|
+
import { compareCodeUnits } from "#contracts/text/order";
|
|
5
|
+
/** A snapshot stream is duplicated, unsorted, or tampered. */
|
|
6
|
+
export class TryoutDigestError extends Schema.TaggedError()("TryoutDigestError", {
|
|
7
|
+
code: Schema.Literals(["integrity", "order"]),
|
|
8
|
+
identity: Schema.String,
|
|
9
|
+
}) {
|
|
10
|
+
}
|
|
11
|
+
/** Keeps one stream digest and previous identity private to its replay. */
|
|
12
|
+
class TryoutDigestState {
|
|
13
|
+
#hash;
|
|
14
|
+
count = 0;
|
|
15
|
+
previous;
|
|
16
|
+
/** Creates one fresh domain-separated incremental digest. */
|
|
17
|
+
constructor(domain) {
|
|
18
|
+
this.#hash = createHash("sha256").update(domain).update("\n");
|
|
19
|
+
}
|
|
20
|
+
/** Adds one verified canonical record to the digest. */
|
|
21
|
+
update(canonical, identity) {
|
|
22
|
+
this.#hash.update(canonical).update("\n");
|
|
23
|
+
this.count += 1;
|
|
24
|
+
this.previous = identity;
|
|
25
|
+
}
|
|
26
|
+
/** Consumes the hash and returns its branded value. */
|
|
27
|
+
digest() {
|
|
28
|
+
return Sha256HashSchema.make(`sha256:${this.#hash.digest("hex")}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Verifies and adds one immutable record without retaining prior rows. */
|
|
32
|
+
function updateDigest(state, record, identity, expectedHash, canonical) {
|
|
33
|
+
if (record.rowHash !== expectedHash) {
|
|
34
|
+
return Effect.fail(new TryoutDigestError({ code: "integrity", identity }));
|
|
35
|
+
}
|
|
36
|
+
if (state.previous !== undefined &&
|
|
37
|
+
compareCodeUnits(state.previous, identity) >= 0) {
|
|
38
|
+
return Effect.fail(new TryoutDigestError({ code: "order", identity }));
|
|
39
|
+
}
|
|
40
|
+
state.update(canonical, identity);
|
|
41
|
+
return Effect.succeed(state);
|
|
42
|
+
}
|
|
43
|
+
/** Digests one canonically ordered record stream with constant memory. */
|
|
44
|
+
export const digestTryoutRecords = Effect.fn("AksaraContracts.digestTryoutRecords")(function* (input) {
|
|
45
|
+
const state = yield* input.records.pipe(Stream.runFoldEffect(() => new TryoutDigestState(input.domain), (current, record) => {
|
|
46
|
+
const canonical = input.canonicalize(record.row);
|
|
47
|
+
return updateDigest(current, record, input.identity(record.row), input.rowHash(record.row), `${canonical}\0${record.rowHash}`);
|
|
48
|
+
}));
|
|
49
|
+
return { count: state.count, digest: state.digest() };
|
|
50
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Sha256Hash } from "#contracts/ids";
|
|
2
|
+
import type { TryoutRuntimeBundlePayload } from "#contracts/tryout/runtime/spec";
|
|
3
|
+
/** Ed25519 domain separating runtime bundles from every other signed object. */
|
|
4
|
+
export declare const TRYOUT_RUNTIME_BUNDLE_SIGNATURE_DOMAIN = "nakafa.aksara.tryout-runtime-bundle";
|
|
5
|
+
/** Serializes one complete runtime payload with stable signed field order. */
|
|
6
|
+
export declare function canonicalizeTryoutRuntimeBundlePayload(payload: TryoutRuntimeBundlePayload): string;
|
|
7
|
+
/** Returns domain-separated canonical bytes covered by bundle Ed25519. */
|
|
8
|
+
export declare function canonicalizeTryoutRuntimeBundleSigningInput(bundleHash: Sha256Hash, payload: TryoutRuntimeBundlePayload): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TRYOUT_RUNTIME_BUNDLE_FORMAT } from "#contracts/tryout/runtime/spec";
|
|
2
|
+
/** Ed25519 domain separating runtime bundles from every other signed object. */
|
|
3
|
+
export const TRYOUT_RUNTIME_BUNDLE_SIGNATURE_DOMAIN = "nakafa.aksara.tryout-runtime-bundle";
|
|
4
|
+
/** Serializes one complete runtime payload with stable signed field order. */
|
|
5
|
+
export function canonicalizeTryoutRuntimeBundlePayload(payload) {
|
|
6
|
+
return JSON.stringify({
|
|
7
|
+
format: TRYOUT_RUNTIME_BUNDLE_FORMAT,
|
|
8
|
+
rendererManifestHash: payload.rendererManifestHash,
|
|
9
|
+
snapshot: {
|
|
10
|
+
activeAppLocales: payload.snapshot.activeAppLocales,
|
|
11
|
+
catalogDigest: payload.snapshot.catalogDigest,
|
|
12
|
+
counts: {
|
|
13
|
+
country: payload.snapshot.counts.country,
|
|
14
|
+
exam: payload.snapshot.counts.exam,
|
|
15
|
+
section: payload.snapshot.counts.section,
|
|
16
|
+
set: payload.snapshot.counts.set,
|
|
17
|
+
track: payload.snapshot.counts.track,
|
|
18
|
+
},
|
|
19
|
+
format: payload.snapshot.format,
|
|
20
|
+
placementCount: payload.snapshot.placementCount,
|
|
21
|
+
placementDigest: payload.snapshot.placementDigest,
|
|
22
|
+
routeCount: payload.snapshot.routeCount,
|
|
23
|
+
snapshotId: payload.snapshot.snapshotId,
|
|
24
|
+
},
|
|
25
|
+
sourceGitSha: payload.sourceGitSha,
|
|
26
|
+
sourceManifestHash: payload.sourceManifestHash,
|
|
27
|
+
sourceReleaseId: payload.sourceReleaseId,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/** Returns domain-separated canonical bytes covered by bundle Ed25519. */
|
|
31
|
+
export function canonicalizeTryoutRuntimeBundleSigningInput(bundleHash, payload) {
|
|
32
|
+
return `${TRYOUT_RUNTIME_BUNDLE_SIGNATURE_DOMAIN}\n${bundleHash}\n${canonicalizeTryoutRuntimeBundlePayload(payload)}`;
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
declare const TryoutRuntimeBundleHashComputationError_base: Schema.Class<TryoutRuntimeBundleHashComputationError, Schema.TaggedStruct<"TryoutRuntimeBundleHashComputationError", {
|
|
3
|
+
readonly sourceReleaseId: Schema.brand<Schema.String, "@NakafaAI/AksaraReleaseId">;
|
|
4
|
+
}>, import("effect/Cause").YieldableError>;
|
|
5
|
+
/** SHA-256 computation failed before bundle authenticity was established. */
|
|
6
|
+
export declare class TryoutRuntimeBundleHashComputationError extends TryoutRuntimeBundleHashComputationError_base {
|
|
7
|
+
}
|
|
8
|
+
/** Computes the immutable identity of one canonical runtime bundle payload. */
|
|
9
|
+
export declare const hashTryoutRuntimeBundlePayload: (payload: {
|
|
10
|
+
readonly format: "signed-tryout-runtime-bundle";
|
|
11
|
+
readonly rendererManifestHash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
12
|
+
readonly snapshot: {
|
|
13
|
+
readonly activeAppLocales: readonly [("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">, ...(("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">)[]];
|
|
14
|
+
readonly catalogDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
15
|
+
readonly counts: {
|
|
16
|
+
readonly country: number;
|
|
17
|
+
readonly exam: number;
|
|
18
|
+
readonly section: number;
|
|
19
|
+
readonly set: number;
|
|
20
|
+
readonly track: number;
|
|
21
|
+
};
|
|
22
|
+
readonly placementCount: number;
|
|
23
|
+
readonly placementDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
24
|
+
readonly routeCount: number;
|
|
25
|
+
readonly format: "localized-tryout-snapshot";
|
|
26
|
+
readonly snapshotId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
27
|
+
};
|
|
28
|
+
readonly sourceGitSha: string & import("effect/Brand").Brand<"@NakafaAI/AksaraGitCommitSha">;
|
|
29
|
+
readonly sourceManifestHash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
30
|
+
readonly sourceReleaseId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraReleaseId">;
|
|
31
|
+
}) => Effect.Effect<string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">, TryoutRuntimeBundleHashComputationError, never>;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { hashText } from "#contracts/hash/text";
|
|
3
|
+
import { ReleaseIdSchema } from "#contracts/ids";
|
|
4
|
+
import { canonicalizeTryoutRuntimeBundlePayload } from "#contracts/tryout/runtime/canonical";
|
|
5
|
+
/** SHA-256 computation failed before bundle authenticity was established. */
|
|
6
|
+
export class TryoutRuntimeBundleHashComputationError extends Schema.TaggedError()("TryoutRuntimeBundleHashComputationError", { sourceReleaseId: ReleaseIdSchema }) {
|
|
7
|
+
}
|
|
8
|
+
/** Computes the immutable identity of one canonical runtime bundle payload. */
|
|
9
|
+
export const hashTryoutRuntimeBundlePayload = Effect.fn("AksaraContracts.hashTryoutRuntimeBundlePayload")((payload) => hashText(canonicalizeTryoutRuntimeBundlePayload(payload)).pipe(Effect.mapError(() => new TryoutRuntimeBundleHashComputationError({
|
|
10
|
+
sourceReleaseId: payload.sourceReleaseId,
|
|
11
|
+
}))));
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { SignedContentRelease } from "#contracts/release/spec";
|
|
3
|
+
import type { SignedTryoutRuntimeBundle } from "#contracts/tryout/runtime/spec";
|
|
4
|
+
declare const TryoutRuntimeBundleSourceError_base: Schema.Class<TryoutRuntimeBundleSourceError, Schema.TaggedStruct<"TryoutRuntimeBundleSourceError", {
|
|
5
|
+
readonly reason: Schema.Literals<readonly ["release", "manifest", "revision", "renderer", "snapshot"]>;
|
|
6
|
+
}>, import("effect/Cause").YieldableError>;
|
|
7
|
+
/** A runtime bundle does not describe a pair authenticated by its Git release. */
|
|
8
|
+
export declare class TryoutRuntimeBundleSourceError extends TryoutRuntimeBundleSourceError_base {
|
|
9
|
+
}
|
|
10
|
+
/** Authenticates a runtime pair against its Git source or signed recovery link. */
|
|
11
|
+
export declare const verifyTryoutRuntimeBundleSource: (input: {
|
|
12
|
+
readonly bundle: SignedTryoutRuntimeBundle;
|
|
13
|
+
readonly release: SignedContentRelease;
|
|
14
|
+
}) => Effect.Effect<{
|
|
15
|
+
readonly bundleHash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
16
|
+
readonly keyId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSigningKeyId">;
|
|
17
|
+
readonly payload: {
|
|
18
|
+
readonly format: "signed-tryout-runtime-bundle";
|
|
19
|
+
readonly rendererManifestHash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
20
|
+
readonly snapshot: {
|
|
21
|
+
readonly activeAppLocales: readonly [("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">, ...(("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">)[]];
|
|
22
|
+
readonly catalogDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
23
|
+
readonly counts: {
|
|
24
|
+
readonly country: number;
|
|
25
|
+
readonly exam: number;
|
|
26
|
+
readonly section: number;
|
|
27
|
+
readonly set: number;
|
|
28
|
+
readonly track: number;
|
|
29
|
+
};
|
|
30
|
+
readonly placementCount: number;
|
|
31
|
+
readonly placementDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
32
|
+
readonly routeCount: number;
|
|
33
|
+
readonly format: "localized-tryout-snapshot";
|
|
34
|
+
readonly snapshotId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
35
|
+
};
|
|
36
|
+
readonly sourceGitSha: string & import("effect/Brand").Brand<"@NakafaAI/AksaraGitCommitSha">;
|
|
37
|
+
readonly sourceManifestHash: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
|
|
38
|
+
readonly sourceReleaseId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraReleaseId">;
|
|
39
|
+
};
|
|
40
|
+
readonly signature: string & import("effect/Brand").Brand<"@NakafaAI/AksaraEd25519Signature">;
|
|
41
|
+
}, TryoutRuntimeBundleSourceError, never>;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
/** A runtime bundle does not describe a pair authenticated by its Git release. */
|
|
3
|
+
export class TryoutRuntimeBundleSourceError extends Schema.TaggedError()("TryoutRuntimeBundleSourceError", {
|
|
4
|
+
reason: Schema.Literals([
|
|
5
|
+
"release",
|
|
6
|
+
"manifest",
|
|
7
|
+
"revision",
|
|
8
|
+
"renderer",
|
|
9
|
+
"snapshot",
|
|
10
|
+
]),
|
|
11
|
+
}) {
|
|
12
|
+
}
|
|
13
|
+
/** Authenticates a runtime pair against its Git source or signed recovery link. */
|
|
14
|
+
export const verifyTryoutRuntimeBundleSource = Effect.fn("AksaraContracts.verifyTryoutRuntimeBundleSource")(function* (input) {
|
|
15
|
+
const { bundle, release } = input;
|
|
16
|
+
const { manifest } = release;
|
|
17
|
+
const sourceReleaseId = manifest.origin.kind === "git"
|
|
18
|
+
? manifest.releaseId
|
|
19
|
+
: manifest.origin.releaseId;
|
|
20
|
+
if (sourceReleaseId !== bundle.payload.sourceReleaseId) {
|
|
21
|
+
return yield* new TryoutRuntimeBundleSourceError({ reason: "release" });
|
|
22
|
+
}
|
|
23
|
+
const sourceManifestHash = manifest.origin.kind === "git"
|
|
24
|
+
? release.manifestHash
|
|
25
|
+
: manifest.baseManifestHash;
|
|
26
|
+
if (sourceManifestHash !== bundle.payload.sourceManifestHash) {
|
|
27
|
+
return yield* new TryoutRuntimeBundleSourceError({ reason: "manifest" });
|
|
28
|
+
}
|
|
29
|
+
if (manifest.origin.kind === "git" &&
|
|
30
|
+
manifest.origin.sha !== bundle.payload.sourceGitSha) {
|
|
31
|
+
return yield* new TryoutRuntimeBundleSourceError({ reason: "revision" });
|
|
32
|
+
}
|
|
33
|
+
if (manifest.rendererManifestHash !== bundle.payload.rendererManifestHash) {
|
|
34
|
+
return yield* new TryoutRuntimeBundleSourceError({ reason: "renderer" });
|
|
35
|
+
}
|
|
36
|
+
const { snapshotId } = bundle.payload.snapshot;
|
|
37
|
+
const transition = manifest.snapshots.tryout;
|
|
38
|
+
const isResult = snapshotId === transition.resultSnapshotId;
|
|
39
|
+
const isRetainedBase = manifest.origin.kind === "git" &&
|
|
40
|
+
transition.mode === "replace" &&
|
|
41
|
+
transition.baseSnapshotId !== null &&
|
|
42
|
+
snapshotId === transition.baseSnapshotId;
|
|
43
|
+
if (!(isResult || isRetainedBase)) {
|
|
44
|
+
return yield* new TryoutRuntimeBundleSourceError({ reason: "snapshot" });
|
|
45
|
+
}
|
|
46
|
+
return bundle;
|
|
47
|
+
});
|