@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.
Files changed (269) hide show
  1. package/LICENSE +185 -0
  2. package/README.md +73 -0
  3. package/dist/artifact/integrity.d.ts +29 -0
  4. package/dist/artifact/integrity.js +54 -0
  5. package/dist/artifact/limits.d.ts +26 -0
  6. package/dist/artifact/limits.js +46 -0
  7. package/dist/artifact/source.d.ts +21 -0
  8. package/dist/artifact/source.js +24 -0
  9. package/dist/artifact/spec.d.ts +148 -0
  10. package/dist/artifact/spec.js +92 -0
  11. package/dist/artifact/verify.d.ts +26 -0
  12. package/dist/artifact/verify.js +18 -0
  13. package/dist/cache/content.d.ts +52 -0
  14. package/dist/cache/content.js +80 -0
  15. package/dist/content.d.ts +168 -0
  16. package/dist/content.js +107 -0
  17. package/dist/country.d.ts +4 -0
  18. package/dist/country.js +8 -0
  19. package/dist/date.d.ts +24 -0
  20. package/dist/date.js +44 -0
  21. package/dist/decode.d.ts +4 -0
  22. package/dist/decode.js +8 -0
  23. package/dist/delivery.d.ts +4 -0
  24. package/dist/delivery.js +7 -0
  25. package/dist/errors.d.ts +10 -0
  26. package/dist/errors.js +8 -0
  27. package/dist/graph/family.d.ts +45 -0
  28. package/dist/graph/family.js +85 -0
  29. package/dist/graph/identity.d.ts +19 -0
  30. package/dist/graph/identity.js +39 -0
  31. package/dist/graph/spec.d.ts +20 -0
  32. package/dist/graph/spec.js +26 -0
  33. package/dist/hash/text.d.ts +10 -0
  34. package/dist/hash/text.js +20 -0
  35. package/dist/ids.d.ts +27 -0
  36. package/dist/ids.js +63 -0
  37. package/dist/limits.d.ts +10 -0
  38. package/dist/limits.js +10 -0
  39. package/dist/locale.d.ts +37 -0
  40. package/dist/locale.js +53 -0
  41. package/dist/material/domain.d.ts +4 -0
  42. package/dist/material/domain.js +8 -0
  43. package/dist/preview/artifact.d.ts +198 -0
  44. package/dist/preview/artifact.js +20 -0
  45. package/dist/preview/auth.d.ts +71 -0
  46. package/dist/preview/auth.js +49 -0
  47. package/dist/preview/document.d.ts +692 -0
  48. package/dist/preview/document.js +90 -0
  49. package/dist/preview/spec.d.ts +2517 -0
  50. package/dist/preview/spec.js +142 -0
  51. package/dist/preview/target.d.ts +172 -0
  52. package/dist/preview/target.js +70 -0
  53. package/dist/program/curriculum.d.ts +69 -0
  54. package/dist/program/curriculum.js +181 -0
  55. package/dist/program/snapshot/digest.d.ts +26 -0
  56. package/dist/program/snapshot/digest.js +220 -0
  57. package/dist/program/snapshot/hash.d.ts +31 -0
  58. package/dist/program/snapshot/hash.js +40 -0
  59. package/dist/program/snapshot/row-hash.d.ts +155 -0
  60. package/dist/program/snapshot/row-hash.js +34 -0
  61. package/dist/program/snapshot/row.d.ts +139 -0
  62. package/dist/program/snapshot/row.js +28 -0
  63. package/dist/program/snapshot/spec.d.ts +36 -0
  64. package/dist/program/snapshot/spec.js +39 -0
  65. package/dist/program/spec.d.ts +85 -0
  66. package/dist/program/spec.js +197 -0
  67. package/dist/projection/article.d.ts +145 -0
  68. package/dist/projection/article.js +150 -0
  69. package/dist/projection/digest.d.ts +261 -0
  70. package/dist/projection/digest.js +59 -0
  71. package/dist/projection/hash.d.ts +3 -0
  72. package/dist/projection/hash.js +10 -0
  73. package/dist/projection/material.d.ts +109 -0
  74. package/dist/projection/material.js +160 -0
  75. package/dist/projection/page.d.ts +112 -0
  76. package/dist/projection/page.js +106 -0
  77. package/dist/projection/question.d.ts +520 -0
  78. package/dist/projection/question.js +109 -0
  79. package/dist/projection/spec.d.ts +503 -0
  80. package/dist/projection/spec.js +56 -0
  81. package/dist/projection/verify.d.ts +281 -0
  82. package/dist/projection/verify.js +86 -0
  83. package/dist/question/identity.d.ts +175 -0
  84. package/dist/question/identity.js +205 -0
  85. package/dist/question/item.d.ts +453 -0
  86. package/dist/question/item.js +146 -0
  87. package/dist/question/response.d.ts +175 -0
  88. package/dist/question/response.js +150 -0
  89. package/dist/quran/attribution.d.ts +48 -0
  90. package/dist/quran/attribution.js +44 -0
  91. package/dist/quran/identity.d.ts +105 -0
  92. package/dist/quran/identity.js +100 -0
  93. package/dist/quran/notes.d.ts +56 -0
  94. package/dist/quran/notes.js +142 -0
  95. package/dist/quran/provenance.d.ts +257 -0
  96. package/dist/quran/provenance.js +144 -0
  97. package/dist/quran/snapshot/digest.d.ts +33 -0
  98. package/dist/quran/snapshot/digest.js +232 -0
  99. package/dist/quran/snapshot/hash.d.ts +51 -0
  100. package/dist/quran/snapshot/hash.js +50 -0
  101. package/dist/quran/snapshot/row/hash.d.ts +748 -0
  102. package/dist/quran/snapshot/row/hash.js +115 -0
  103. package/dist/quran/snapshot/row.d.ts +354 -0
  104. package/dist/quran/snapshot/row.js +91 -0
  105. package/dist/quran/snapshot/spec.d.ts +50 -0
  106. package/dist/quran/snapshot/spec.js +66 -0
  107. package/dist/quran/source.d.ts +206 -0
  108. package/dist/quran/source.js +153 -0
  109. package/dist/quran/spec.d.ts +47 -0
  110. package/dist/quran/spec.js +41 -0
  111. package/dist/quran/text.d.ts +7 -0
  112. package/dist/quran/text.js +11 -0
  113. package/dist/release/canonical.d.ts +23 -0
  114. package/dist/release/canonical.js +29 -0
  115. package/dist/release/current/evidence.d.ts +424 -0
  116. package/dist/release/current/evidence.js +41 -0
  117. package/dist/release/current/runtime.d.ts +4 -0
  118. package/dist/release/current/runtime.js +15 -0
  119. package/dist/release/current/state.d.ts +567 -0
  120. package/dist/release/current/state.js +91 -0
  121. package/dist/release/digest.d.ts +54 -0
  122. package/dist/release/digest.js +71 -0
  123. package/dist/release/hash.d.ts +72 -0
  124. package/dist/release/hash.js +14 -0
  125. package/dist/release/head.d.ts +206 -0
  126. package/dist/release/head.js +131 -0
  127. package/dist/release/items.d.ts +98 -0
  128. package/dist/release/items.js +114 -0
  129. package/dist/release/lifecycle.d.ts +297 -0
  130. package/dist/release/lifecycle.js +97 -0
  131. package/dist/release/origin.d.ts +30 -0
  132. package/dist/release/origin.js +24 -0
  133. package/dist/release/policy.d.ts +45 -0
  134. package/dist/release/policy.js +124 -0
  135. package/dist/release/result/digest.d.ts +127 -0
  136. package/dist/release/result/digest.js +140 -0
  137. package/dist/release/result/spec.d.ts +4 -0
  138. package/dist/release/result/spec.js +5 -0
  139. package/dist/release/rollback/digest.d.ts +126 -0
  140. package/dist/release/rollback/digest.js +92 -0
  141. package/dist/release/rollback/spec.d.ts +1729 -0
  142. package/dist/release/rollback/spec.js +156 -0
  143. package/dist/release/route/digest.d.ts +45 -0
  144. package/dist/release/route/digest.js +59 -0
  145. package/dist/release/route/page.d.ts +56 -0
  146. package/dist/release/route/page.js +46 -0
  147. package/dist/release/route/spec.d.ts +49 -0
  148. package/dist/release/route/spec.js +40 -0
  149. package/dist/release/route/verify.d.ts +66 -0
  150. package/dist/release/route/verify.js +67 -0
  151. package/dist/release/signing.d.ts +6 -0
  152. package/dist/release/signing.js +38 -0
  153. package/dist/release/snapshot/data.d.ts +475 -0
  154. package/dist/release/snapshot/data.js +74 -0
  155. package/dist/release/snapshot/evidence-requirement.d.ts +22 -0
  156. package/dist/release/snapshot/evidence-requirement.js +21 -0
  157. package/dist/release/snapshot/evidence.d.ts +58 -0
  158. package/dist/release/snapshot/evidence.js +14 -0
  159. package/dist/release/snapshot/program-evidence.d.ts +17 -0
  160. package/dist/release/snapshot/program-evidence.js +39 -0
  161. package/dist/release/snapshot/quran-evidence.d.ts +27 -0
  162. package/dist/release/snapshot/quran-evidence.js +43 -0
  163. package/dist/release/snapshot/scope.d.ts +60 -0
  164. package/dist/release/snapshot/scope.js +102 -0
  165. package/dist/release/snapshot/spec.d.ts +193 -0
  166. package/dist/release/snapshot/spec.js +139 -0
  167. package/dist/release/snapshot/tryout-evidence.d.ts +22 -0
  168. package/dist/release/snapshot/tryout-evidence.js +86 -0
  169. package/dist/release/snapshot/verify.d.ts +710 -0
  170. package/dist/release/snapshot/verify.js +147 -0
  171. package/dist/release/spec.d.ts +491 -0
  172. package/dist/release/spec.js +200 -0
  173. package/dist/release/verify.d.ts +98 -0
  174. package/dist/release/verify.js +70 -0
  175. package/dist/renderer/compatibility.d.ts +35 -0
  176. package/dist/renderer/compatibility.js +48 -0
  177. package/dist/renderer/component.d.ts +99 -0
  178. package/dist/renderer/component.js +102 -0
  179. package/dist/renderer/contract.d.ts +144 -0
  180. package/dist/renderer/contract.js +122 -0
  181. package/dist/renderer/domain.d.ts +6 -0
  182. package/dist/renderer/domain.js +18 -0
  183. package/dist/renderer/manifest.d.ts +86 -0
  184. package/dist/renderer/manifest.js +49 -0
  185. package/dist/renderer/selection.d.ts +39 -0
  186. package/dist/renderer/selection.js +87 -0
  187. package/dist/runtime/error.d.ts +8 -0
  188. package/dist/runtime/error.js +20 -0
  189. package/dist/runtime/protected/limits.d.ts +10 -0
  190. package/dist/runtime/protected/limits.js +15 -0
  191. package/dist/runtime/protected/spec.d.ts +328 -0
  192. package/dist/runtime/protected/spec.js +79 -0
  193. package/dist/runtime/protected/verify.d.ts +97 -0
  194. package/dist/runtime/protected/verify.js +72 -0
  195. package/dist/runtime/result.d.ts +12 -0
  196. package/dist/runtime/result.js +17 -0
  197. package/dist/runtime/spec.d.ts +705 -0
  198. package/dist/runtime/spec.js +61 -0
  199. package/dist/runtime/verify.d.ts +479 -0
  200. package/dist/runtime/verify.js +123 -0
  201. package/dist/signature/spec.d.ts +53 -0
  202. package/dist/signature/spec.js +29 -0
  203. package/dist/signature/trusted.d.ts +19 -0
  204. package/dist/signature/trusted.js +32 -0
  205. package/dist/signature/verify.d.ts +10 -0
  206. package/dist/signature/verify.js +82 -0
  207. package/dist/text/order.d.ts +2 -0
  208. package/dist/text/order.js +10 -0
  209. package/dist/text/syntax.d.ts +6 -0
  210. package/dist/text/syntax.js +15 -0
  211. package/dist/transport/batch.d.ts +971 -0
  212. package/dist/transport/batch.js +101 -0
  213. package/dist/transport/failure.d.ts +168 -0
  214. package/dist/transport/failure.js +194 -0
  215. package/dist/transport/group.d.ts +2946 -0
  216. package/dist/transport/group.js +47 -0
  217. package/dist/transport/limits.d.ts +30 -0
  218. package/dist/transport/limits.js +30 -0
  219. package/dist/transport/request.d.ts +9333 -0
  220. package/dist/transport/request.js +121 -0
  221. package/dist/transport/response.d.ts +6247 -0
  222. package/dist/transport/response.js +181 -0
  223. package/dist/transport/runtime.d.ts +77 -0
  224. package/dist/transport/runtime.js +33 -0
  225. package/dist/transport/snapshot.d.ts +978 -0
  226. package/dist/transport/snapshot.js +70 -0
  227. package/dist/tryout/canonical.d.ts +2 -0
  228. package/dist/tryout/canonical.js +11 -0
  229. package/dist/tryout/catalog-hash.d.ts +134 -0
  230. package/dist/tryout/catalog-hash.js +203 -0
  231. package/dist/tryout/catalog.d.ts +376 -0
  232. package/dist/tryout/catalog.js +147 -0
  233. package/dist/tryout/content-hash.d.ts +89 -0
  234. package/dist/tryout/content-hash.js +72 -0
  235. package/dist/tryout/identity.d.ts +12 -0
  236. package/dist/tryout/identity.js +46 -0
  237. package/dist/tryout/key.d.ts +4 -0
  238. package/dist/tryout/key.js +8 -0
  239. package/dist/tryout/language.d.ts +24 -0
  240. package/dist/tryout/language.js +32 -0
  241. package/dist/tryout/locale-closure.d.ts +20 -0
  242. package/dist/tryout/locale-closure.js +164 -0
  243. package/dist/tryout/placement-closure.d.ts +5 -0
  244. package/dist/tryout/placement-closure.js +30 -0
  245. package/dist/tryout/placement-hash.d.ts +11 -0
  246. package/dist/tryout/placement-hash.js +55 -0
  247. package/dist/tryout/placement.d.ts +280 -0
  248. package/dist/tryout/placement.js +82 -0
  249. package/dist/tryout/row-hash.d.ts +28 -0
  250. package/dist/tryout/row-hash.js +50 -0
  251. package/dist/tryout/runtime/canonical.d.ts +8 -0
  252. package/dist/tryout/runtime/canonical.js +33 -0
  253. package/dist/tryout/runtime/hash.d.ts +32 -0
  254. package/dist/tryout/runtime/hash.js +11 -0
  255. package/dist/tryout/runtime/source.d.ts +42 -0
  256. package/dist/tryout/runtime/source.js +47 -0
  257. package/dist/tryout/runtime/spec.d.ts +58 -0
  258. package/dist/tryout/runtime/spec.js +21 -0
  259. package/dist/tryout/runtime/verify.d.ts +61 -0
  260. package/dist/tryout/runtime/verify.js +69 -0
  261. package/dist/tryout/snapshot/canonical.d.ts +7 -0
  262. package/dist/tryout/snapshot/canonical.js +17 -0
  263. package/dist/tryout/snapshot/hash.d.ts +8 -0
  264. package/dist/tryout/snapshot/hash.js +19 -0
  265. package/dist/tryout/snapshot/spec.d.ts +46 -0
  266. package/dist/tryout/snapshot/spec.js +30 -0
  267. package/dist/tryout/spec.d.ts +13 -0
  268. package/dist/tryout/spec.js +15 -0
  269. package/package.json +506 -0
@@ -0,0 +1,139 @@
1
+ /** Signed structured snapshot transition contracts. */
2
+ import { Schema } from "effect";
3
+ import { Sha256HashSchema } from "#contracts/ids";
4
+ import { ContentSnapshotKindSchema, publicationScopeSelectsSnapshot, } from "#contracts/release/snapshot/scope";
5
+ /** Canonical digest for a release that stages no structured snapshot rows. */
6
+ export const EMPTY_SNAPSHOT_ROW_DIGEST = Sha256HashSchema.make("sha256:eb27aa7f59e41b14a3f76d951c5a50cb954a19f3f6e6c44bc21a733f606e888f");
7
+ const RowCountSchema = Schema.Int.pipe(Schema.check(Schema.isGreaterThanOrEqualTo(0)));
8
+ /** One family's immutable snapshot transition authenticated by a release. */
9
+ export const ContentSnapshotStateSchema = Schema.Struct({
10
+ baseSnapshotId: Schema.NullOr(Sha256HashSchema),
11
+ mode: Schema.Literals(["inherit", "replace", "restore"]),
12
+ resultSnapshotId: Schema.NullOr(Sha256HashSchema),
13
+ rowCount: RowCountSchema,
14
+ rowDigest: Sha256HashSchema,
15
+ }).pipe(Schema.check(Schema.makeFilter(hasCoherentSnapshotState, {
16
+ message: "Expected a coherent structured snapshot transition.",
17
+ })));
18
+ /** Fixed snapshot states prevent a release from omitting one owned family. */
19
+ export const ContentSnapshotSetSchema = Schema.Struct({
20
+ program: ContentSnapshotStateSchema,
21
+ quran: ContentSnapshotStateSchema,
22
+ tryout: ContentSnapshotStateSchema,
23
+ });
24
+ /** Requires every unselected structured family to remain inherited. */
25
+ export function hasScopedSnapshotTransitions(scope, snapshots) {
26
+ return ContentSnapshotKindSchema.literals.every((family) => publicationScopeSelectsSnapshot(scope, family) ||
27
+ snapshots[family].mode === "inherit");
28
+ }
29
+ /** Checks inheritance, replacement, and forward restore row semantics. */
30
+ export function hasCoherentSnapshotState(state) {
31
+ const changed = state.baseSnapshotId !== state.resultSnapshotId;
32
+ const empty = state.rowCount === 0 && state.rowDigest === EMPTY_SNAPSHOT_ROW_DIGEST;
33
+ if (state.mode === "inherit") {
34
+ return !changed && empty;
35
+ }
36
+ if (state.mode === "replace") {
37
+ return (changed &&
38
+ state.resultSnapshotId !== null &&
39
+ state.rowCount > 0 &&
40
+ state.rowDigest !== EMPTY_SNAPSHOT_ROW_DIGEST);
41
+ }
42
+ return changed && empty;
43
+ }
44
+ /** Builds a zero-row transition that preserves one immutable snapshot. */
45
+ export function inheritContentSnapshot(snapshotId) {
46
+ return ContentSnapshotStateSchema.make({
47
+ baseSnapshotId: snapshotId,
48
+ mode: "inherit",
49
+ resultSnapshotId: snapshotId,
50
+ rowCount: 0,
51
+ rowDigest: EMPTY_SNAPSHOT_ROW_DIGEST,
52
+ });
53
+ }
54
+ /** Builds fixed inherited states from the prior release's result identities. */
55
+ export function inheritContentSnapshots(previous) {
56
+ return ContentSnapshotSetSchema.make({
57
+ program: inheritContentSnapshot(previous?.program.resultSnapshotId ?? null),
58
+ quran: inheritContentSnapshot(previous?.quran.resultSnapshotId ?? null),
59
+ tryout: inheritContentSnapshot(previous?.tryout.resultSnapshotId ?? null),
60
+ });
61
+ }
62
+ /** Reconstructs inherited prior state from one signed transition's base IDs. */
63
+ export function baseContentSnapshots(snapshots) {
64
+ return ContentSnapshotSetSchema.make({
65
+ program: inheritContentSnapshot(snapshots.program.baseSnapshotId),
66
+ quran: inheritContentSnapshot(snapshots.quran.baseSnapshotId),
67
+ tryout: inheritContentSnapshot(snapshots.tryout.baseSnapshotId),
68
+ });
69
+ }
70
+ /** Builds a full replacement transition for one prepared snapshot. */
71
+ export function replaceContentSnapshot(input) {
72
+ return ContentSnapshotStateSchema.make({
73
+ ...input,
74
+ mode: "replace",
75
+ });
76
+ }
77
+ /** Builds a row-free forward restore to an existing or absent snapshot. */
78
+ export function restoreContentSnapshot(baseSnapshotId, resultSnapshotId) {
79
+ return ContentSnapshotStateSchema.make({
80
+ baseSnapshotId,
81
+ mode: "restore",
82
+ resultSnapshotId,
83
+ rowCount: 0,
84
+ rowDigest: EMPTY_SNAPSHOT_ROW_DIGEST,
85
+ });
86
+ }
87
+ /** Builds the retained inverse without copying immutable snapshot rows. */
88
+ export function invertContentSnapshots(snapshots) {
89
+ /** Inverts one changed family and preserves one unchanged family. */
90
+ const invert = (state) => state.baseSnapshotId === state.resultSnapshotId
91
+ ? inheritContentSnapshot(state.resultSnapshotId)
92
+ : restoreContentSnapshot(state.resultSnapshotId, state.baseSnapshotId);
93
+ return ContentSnapshotSetSchema.make({
94
+ program: invert(snapshots.program),
95
+ quran: invert(snapshots.quran),
96
+ tryout: invert(snapshots.tryout),
97
+ });
98
+ }
99
+ /** Checks that an initial release does not claim an unstated snapshot base. */
100
+ export function hasEmptySnapshotBases(snapshots) {
101
+ return Object.values(snapshots).every(({ baseSnapshotId }) => baseSnapshotId === null);
102
+ }
103
+ /** Checks that a rollback only inherits or restores immutable snapshots. */
104
+ export function hasRollbackSnapshotModes(snapshots) {
105
+ return Object.values(snapshots).every(({ mode }) => mode !== "replace");
106
+ }
107
+ /** Checks that reviewed Git sources only inherit or replace snapshots. */
108
+ export function hasGitSnapshotModes(snapshots) {
109
+ return Object.values(snapshots).every(({ mode }) => mode !== "restore");
110
+ }
111
+ /** Returns the number of rows that must be staged for this release. */
112
+ export function snapshotRowCount(snapshots) {
113
+ return (snapshots.program.rowCount +
114
+ snapshots.quran.rowCount +
115
+ snapshots.tryout.rowCount);
116
+ }
117
+ /** Serializes one snapshot transition with stable signed field order. */
118
+ export function canonicalizeContentSnapshotState(state) {
119
+ return {
120
+ baseSnapshotId: state.baseSnapshotId,
121
+ mode: state.mode,
122
+ resultSnapshotId: state.resultSnapshotId,
123
+ rowCount: state.rowCount,
124
+ rowDigest: state.rowDigest,
125
+ };
126
+ }
127
+ /** Serializes all fixed snapshot families with stable signed field order. */
128
+ export function canonicalizeContentSnapshotSet(snapshots) {
129
+ return {
130
+ program: canonicalizeContentSnapshotState(snapshots.program),
131
+ quran: canonicalizeContentSnapshotState(snapshots.quran),
132
+ tryout: canonicalizeContentSnapshotState(snapshots.tryout),
133
+ };
134
+ }
135
+ /** Compares two fixed snapshot sets through their stable signed form. */
136
+ export function hasSameContentSnapshots(left, right) {
137
+ return (JSON.stringify(canonicalizeContentSnapshotSet(left)) ===
138
+ JSON.stringify(canonicalizeContentSnapshotSet(right)));
139
+ }
@@ -0,0 +1,22 @@
1
+ import { Effect } from "effect";
2
+ import { type SnapshotRowSource } from "#contracts/release/snapshot/evidence-requirement";
3
+ /** Verifies all try-out hashes, digests, counts, routes, and identity. */
4
+ export declare const verifyTryoutSnapshotRows: <E, R>(snapshot: {
5
+ readonly family: "tryout";
6
+ readonly manifest: {
7
+ readonly activeAppLocales: readonly [("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">, ...(("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">)[]];
8
+ readonly catalogDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
9
+ readonly counts: {
10
+ readonly country: number;
11
+ readonly exam: number;
12
+ readonly section: number;
13
+ readonly set: number;
14
+ readonly track: number;
15
+ };
16
+ readonly placementCount: number;
17
+ readonly placementDigest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
18
+ readonly routeCount: number;
19
+ readonly format: "localized-tryout-snapshot";
20
+ readonly snapshotId: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
21
+ };
22
+ }, rows: SnapshotRowSource<E, R>) => Effect.Effect<number, E | import("#contracts/release/snapshot/evidence-requirement").SnapshotEvidenceError | import("#contracts/tryout/locale-closure").TryoutClosureError | import("../../tryout/row-hash.js").TryoutDigestError, R>;
@@ -0,0 +1,86 @@
1
+ import { Effect, Result, Stream } from "effect";
2
+ import { requireSnapshotEvidence, } from "#contracts/release/snapshot/evidence-requirement";
3
+ import { digestTryoutCatalog } from "#contracts/tryout/catalog-hash";
4
+ import { verifyTryoutLocaleClosure } from "#contracts/tryout/locale-closure";
5
+ import { digestTryoutPlacements } from "#contracts/tryout/placement-hash";
6
+ import { makeTryoutSnapshot } from "#contracts/tryout/snapshot/hash";
7
+ /** Selects current hierarchy rows while preserving source failures. */
8
+ function currentTryoutCatalog(rows) {
9
+ return rows.pipe(Stream.filterMap((row) => row.family === "tryout" && row.rowKind === "catalog"
10
+ ? Result.succeed(row.record)
11
+ : Result.failVoid));
12
+ }
13
+ /** Selects current placements while preserving source failures. */
14
+ function currentPlacements(rows) {
15
+ return rows.pipe(Stream.filterMap((row) => row.family === "tryout" && row.rowKind === "placement"
16
+ ? Result.succeed(row.record)
17
+ : Result.failVoid));
18
+ }
19
+ /** Derives signed per-kind and public-route counts from catalog rows. */
20
+ function summarizeTryoutCatalog(records) {
21
+ const initial = {
22
+ counts: { country: 0, exam: 0, section: 0, set: 0, track: 0 },
23
+ routeCount: 0,
24
+ };
25
+ return records.pipe(Stream.runFold(() => initial, (state, { row }) => ({
26
+ counts: {
27
+ ...state.counts,
28
+ [row.kind]: state.counts[row.kind] + 1,
29
+ },
30
+ routeCount: state.routeCount + (row.publicPath === undefined ? 0 : 1),
31
+ })));
32
+ }
33
+ /** Verifies all try-out hashes, digests, counts, routes, and identity. */
34
+ export const verifyTryoutSnapshotRows = Effect.fn("AksaraContracts.verifyTryoutSnapshotRows")(function* (snapshot, rows) {
35
+ const [placementDigest, catalogDigest, catalogEvidence] = yield* Effect.all([
36
+ digestTryoutPlacements(currentPlacements(rows)),
37
+ digestTryoutCatalog(currentTryoutCatalog(rows)),
38
+ summarizeTryoutCatalog(currentTryoutCatalog(rows)),
39
+ ]);
40
+ yield* verifyTryoutLocaleClosure({
41
+ activeAppLocales: snapshot.manifest.activeAppLocales,
42
+ catalog: currentTryoutCatalog(rows),
43
+ placements: currentPlacements(rows),
44
+ });
45
+ yield* requireSnapshotEvidence({
46
+ actual: catalogDigest.digest,
47
+ expected: snapshot.manifest.catalogDigest,
48
+ family: "tryout",
49
+ field: "catalogDigest",
50
+ });
51
+ yield* requireSnapshotEvidence({
52
+ actual: placementDigest.count,
53
+ expected: snapshot.manifest.placementCount,
54
+ family: "tryout",
55
+ field: "placementCount",
56
+ });
57
+ yield* requireSnapshotEvidence({
58
+ actual: placementDigest.digest,
59
+ expected: snapshot.manifest.placementDigest,
60
+ family: "tryout",
61
+ field: "placementDigest",
62
+ });
63
+ for (const kind of ["country", "exam", "section", "set", "track"]) {
64
+ yield* requireSnapshotEvidence({
65
+ actual: catalogEvidence.counts[kind],
66
+ expected: snapshot.manifest.counts[kind],
67
+ family: "tryout",
68
+ field: `${kind}Count`,
69
+ });
70
+ }
71
+ yield* requireSnapshotEvidence({
72
+ actual: catalogEvidence.routeCount,
73
+ expected: snapshot.manifest.routeCount,
74
+ family: "tryout",
75
+ field: "routeCount",
76
+ });
77
+ const { format: _format, snapshotId, ...facts } = snapshot.manifest;
78
+ const actualId = makeTryoutSnapshot(facts).snapshotId;
79
+ yield* requireSnapshotEvidence({
80
+ actual: actualId,
81
+ expected: snapshotId,
82
+ family: "tryout",
83
+ field: "snapshotId",
84
+ });
85
+ return catalogDigest.count + placementDigest.count;
86
+ });