@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,146 @@
1
+ import { Effect, Schema, Struct } from "effect";
2
+ import { AppLocaleCodeSchema, ArtifactLocaleSchema, artifactLocaleCode, } from "#contracts/locale";
3
+ import { QuestionResponseContentSchema, QuestionResponseSchema, } from "#contracts/question/response";
4
+ import { TryoutKeySchema } from "#contracts/tryout/key";
5
+ const PositiveOrderSchema = Schema.Int.pipe(Schema.check(Schema.isGreaterThan(0)));
6
+ /** One source-authored option before stable runtime identities are derived. */
7
+ const QuestionOptionSourceSchema = Schema.Struct({
8
+ isCorrect: Schema.Boolean,
9
+ label: QuestionResponseContentSchema,
10
+ }).mapFields(Struct.map(Schema.mutableKey));
11
+ /** Requires enough options to present one meaningful choice interaction. */
12
+ function hasAtLeastTwoOptions(options) {
13
+ return options.length >= 2;
14
+ }
15
+ /** Requires exactly one authored option to carry correctness. */
16
+ function hasOneCorrectOption(options) {
17
+ return options.filter(({ isCorrect }) => isCorrect).length === 1;
18
+ }
19
+ /** Requires several correct options while retaining at least one distractor. */
20
+ function hasSeveralCorrectOptions(options) {
21
+ const correct = options.filter(({ isCorrect }) => isCorrect).length;
22
+ return correct >= 2 && correct < options.length;
23
+ }
24
+ const QuestionOptionSourceListSchema = Schema.Array(QuestionOptionSourceSchema).pipe(Schema.mutable, Schema.check(Schema.makeFilter(hasAtLeastTwoOptions, {
25
+ message: "Expected at least two response options.",
26
+ })));
27
+ const SingleChoiceResponseSourceSchema = Schema.Struct({
28
+ kind: Schema.Literal("single-choice"),
29
+ options: QuestionOptionSourceListSchema.pipe(Schema.check(Schema.makeFilter(hasOneCorrectOption, {
30
+ message: "Single-choice responses require one correct option.",
31
+ }))),
32
+ }).mapFields(Struct.map(Schema.mutableKey));
33
+ const MultipleChoiceResponseSourceSchema = Schema.Struct({
34
+ kind: Schema.Literal("multiple-choice"),
35
+ options: QuestionOptionSourceListSchema.pipe(Schema.check(Schema.makeFilter(hasSeveralCorrectOptions, {
36
+ message: "Multiple-choice responses require several correct options and one distractor.",
37
+ }))),
38
+ }).mapFields(Struct.map(Schema.mutableKey));
39
+ const CategoryStatementSourceSchema = Schema.Struct({
40
+ correctCategoryOrder: PositiveOrderSchema,
41
+ label: QuestionResponseContentSchema,
42
+ }).mapFields(Struct.map(Schema.mutableKey));
43
+ /** Checks category cardinality and every statement's category reference. */
44
+ function hasCoherentCategoryResponse(input) {
45
+ return (input.categories.length >= 2 &&
46
+ input.statements.length > 0 &&
47
+ input.statements.every(({ correctCategoryOrder }) => correctCategoryOrder <= input.categories.length));
48
+ }
49
+ const CategoryResponseSourceSchema = Schema.Struct({
50
+ categories: Schema.Array(QuestionResponseContentSchema).pipe(Schema.mutable),
51
+ kind: Schema.Literal("category"),
52
+ statements: Schema.Array(CategoryStatementSourceSchema).pipe(Schema.mutable),
53
+ })
54
+ .mapFields(Struct.map(Schema.mutableKey))
55
+ .pipe(Schema.check(Schema.makeFilter(hasCoherentCategoryResponse, {
56
+ message: "Category responses require at least two categories and classified statements.",
57
+ })));
58
+ /** One locale-specific response authored without duplicated runtime keys. */
59
+ export const QuestionResponseSourceSchema = Schema.Union([
60
+ CategoryResponseSourceSchema,
61
+ MultipleChoiceResponseSourceSchema,
62
+ SingleChoiceResponseSourceSchema,
63
+ ]);
64
+ const QuestionResponseSourceMapSchema = Schema.Record(AppLocaleCodeSchema, Schema.optional(QuestionResponseSourceSchema));
65
+ /** Editorial blueprint coordinates used to prove assessment coverage. */
66
+ export const QuestionBlueprintSchema = Schema.Struct({
67
+ cognitiveLevel: TryoutKeySchema,
68
+ contentDomain: TryoutKeySchema,
69
+ topic: TryoutKeySchema,
70
+ });
71
+ /** Serializes response shape and answer-key positions for locale comparison. */
72
+ function responseStructure(response) {
73
+ if (response.kind === "category") {
74
+ return JSON.stringify({
75
+ categories: response.categories.length,
76
+ kind: response.kind,
77
+ statements: response.statements.map(({ correctCategoryOrder }) => correctCategoryOrder),
78
+ });
79
+ }
80
+ return JSON.stringify({
81
+ correctness: response.options.map(({ isCorrect }) => isCorrect),
82
+ kind: response.kind,
83
+ });
84
+ }
85
+ /** Requires locale siblings to preserve one response format and answer key. */
86
+ function hasCoherentLocalizedResponses(input) {
87
+ const responses = Object.values(input.responses).filter((response) => response !== undefined);
88
+ const [first] = responses;
89
+ return (first !== undefined &&
90
+ responses.every((response) => responseStructure(response) === responseStructure(first)));
91
+ }
92
+ /** Complete source-owned item, including optional shared-stimulus identity. */
93
+ export const QuestionItemSchema = Schema.Struct({
94
+ blueprint: Schema.optionalKey(QuestionBlueprintSchema),
95
+ responses: QuestionResponseSourceMapSchema,
96
+ stimulusKey: Schema.optionalKey(TryoutKeySchema),
97
+ }).pipe(Schema.check(Schema.makeFilter(hasCoherentLocalizedResponses, {
98
+ message: "Localized responses must preserve one format, structure, and answer key.",
99
+ })));
100
+ /** Returns blueprint facts in stable field order for signed canonicalizers. */
101
+ export function canonicalQuestionBlueprint(blueprint) {
102
+ return {
103
+ cognitiveLevel: blueprint.cognitiveLevel,
104
+ contentDomain: blueprint.contentDomain,
105
+ topic: blueprint.topic,
106
+ };
107
+ }
108
+ /** One item does not contain the exact artifact locale it must assess. */
109
+ export class QuestionResponseLocaleMissingError extends Schema.TaggedError()("QuestionResponseLocaleMissingError", { artifactLocale: ArtifactLocaleSchema }) {
110
+ }
111
+ /** Derives stable option keys and orders from source-authored array order. */
112
+ function freezeOptions(options) {
113
+ return options.map(({ isCorrect, label }, index) => ({
114
+ isCorrect,
115
+ label,
116
+ optionKey: `option-${index + 1}`,
117
+ order: index + 1,
118
+ }));
119
+ }
120
+ /** Resolves and freezes one exact-locale response from an authored item. */
121
+ export const questionResponseFor = Effect.fn("AksaraContracts.questionResponseFor")(function* (item, artifactLocale) {
122
+ const response = item.responses[artifactLocaleCode(artifactLocale)];
123
+ if (response === undefined) {
124
+ return yield* new QuestionResponseLocaleMissingError({ artifactLocale });
125
+ }
126
+ if (response.kind !== "category") {
127
+ return QuestionResponseSchema.make({
128
+ kind: response.kind,
129
+ options: freezeOptions(response.options),
130
+ });
131
+ }
132
+ return QuestionResponseSchema.make({
133
+ categories: response.categories.map((label, index) => ({
134
+ categoryKey: `category-${index + 1}`,
135
+ label,
136
+ order: index + 1,
137
+ })),
138
+ kind: response.kind,
139
+ statements: response.statements.map(({ correctCategoryOrder, label }, index) => ({
140
+ correctCategoryKey: `category-${correctCategoryOrder}`,
141
+ label,
142
+ order: index + 1,
143
+ statementKey: `statement-${index + 1}`,
144
+ })),
145
+ });
146
+ });
@@ -0,0 +1,175 @@
1
+ import { Schema } from "effect";
2
+ /** One semantic text or mathematics fragment in a response label. */
3
+ export declare const QuestionResponseContentPartSchema: Schema.Union<readonly [Schema.Struct<{
4
+ readonly display: Schema.Literals<readonly ["block", "inline"]>;
5
+ readonly kind: Schema.Literal<"math">;
6
+ readonly math: Schema.String;
7
+ }>, Schema.Struct<{
8
+ readonly kind: Schema.Literal<"text">;
9
+ readonly text: Schema.String;
10
+ }>]>;
11
+ export type QuestionResponseContentPart = typeof QuestionResponseContentPartSchema.Type;
12
+ /** Ordered semantic content rendered without reparsing delimiter strings. */
13
+ export declare const QuestionResponseContentSchema: Schema.NonEmptyArray<Schema.Union<readonly [Schema.Struct<{
14
+ readonly display: Schema.Literals<readonly ["block", "inline"]>;
15
+ readonly kind: Schema.Literal<"math">;
16
+ readonly math: Schema.String;
17
+ }>, Schema.Struct<{
18
+ readonly kind: Schema.Literal<"text">;
19
+ readonly text: Schema.String;
20
+ }>]>>;
21
+ export type QuestionResponseContent = typeof QuestionResponseContentSchema.Type;
22
+ /** Response formats supported by one assessment item. */
23
+ export declare const QuestionResponseKindSchema: Schema.Literals<readonly ["category", "multiple-choice", "single-choice"]>;
24
+ export type QuestionResponseKind = typeof QuestionResponseKindSchema.Type;
25
+ /** Frozen locale-specific response used from publication through review. */
26
+ export declare const QuestionResponseSchema: 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
+ export type QuestionResponse = typeof QuestionResponseSchema.Type;
85
+ /** Returns response content in stable field order for signed canonicalizers. */
86
+ export declare function canonicalQuestionResponseContent(content: QuestionResponseContent): ({
87
+ kind: "text";
88
+ text: string;
89
+ display?: never;
90
+ math?: never;
91
+ } | {
92
+ text?: never;
93
+ display: "block" | "inline";
94
+ kind: "math";
95
+ math: string;
96
+ })[];
97
+ /** Returns locale-neutral response identity without localized label content. */
98
+ export declare function canonicalQuestionResponseStructure(response: QuestionResponse): {
99
+ categories: {
100
+ categoryKey: string;
101
+ order: number;
102
+ }[];
103
+ kind: "category";
104
+ statements: {
105
+ correctCategoryKey: string;
106
+ order: number;
107
+ statementKey: string;
108
+ }[];
109
+ options?: never;
110
+ } | {
111
+ categories?: never;
112
+ statements?: never;
113
+ kind: "multiple-choice" | "single-choice";
114
+ options: {
115
+ isCorrect: boolean;
116
+ optionKey: string;
117
+ order: number;
118
+ }[];
119
+ };
120
+ /** Returns response facts in stable field order for signed canonicalizers. */
121
+ export declare function canonicalQuestionResponse(response: QuestionResponse): {
122
+ options?: never;
123
+ categories: {
124
+ categoryKey: string;
125
+ label: ({
126
+ kind: "text";
127
+ text: string;
128
+ display?: never;
129
+ math?: never;
130
+ } | {
131
+ text?: never;
132
+ display: "block" | "inline";
133
+ kind: "math";
134
+ math: string;
135
+ })[];
136
+ order: number;
137
+ }[];
138
+ kind: "category";
139
+ statements: {
140
+ correctCategoryKey: string;
141
+ label: ({
142
+ kind: "text";
143
+ text: string;
144
+ display?: never;
145
+ math?: never;
146
+ } | {
147
+ text?: never;
148
+ display: "block" | "inline";
149
+ kind: "math";
150
+ math: string;
151
+ })[];
152
+ order: number;
153
+ statementKey: string;
154
+ }[];
155
+ } | {
156
+ categories?: never;
157
+ statements?: never;
158
+ kind: "multiple-choice" | "single-choice";
159
+ options: {
160
+ isCorrect: boolean;
161
+ label: ({
162
+ kind: "text";
163
+ text: string;
164
+ display?: never;
165
+ math?: never;
166
+ } | {
167
+ text?: never;
168
+ display: "block" | "inline";
169
+ kind: "math";
170
+ math: string;
171
+ })[];
172
+ optionKey: string;
173
+ order: number;
174
+ }[];
175
+ };
@@ -0,0 +1,150 @@
1
+ import { Schema } from "effect";
2
+ const OPTION_KEY_PATTERN = /^option-[1-9]\d*$/u;
3
+ const CATEGORY_KEY_PATTERN = /^category-[1-9]\d*$/u;
4
+ const STATEMENT_KEY_PATTERN = /^statement-[1-9]\d*$/u;
5
+ const PositiveOrderSchema = Schema.Int.pipe(Schema.check(Schema.isGreaterThan(0)));
6
+ const NonEmptyContentSchema = Schema.String.pipe(Schema.check(Schema.makeFilter((value) => value.length > 0, {
7
+ message: "Response content parts must not be empty.",
8
+ })));
9
+ const QuestionResponseTextPartSchema = Schema.Struct({
10
+ kind: Schema.Literal("text"),
11
+ text: NonEmptyContentSchema,
12
+ });
13
+ const QuestionResponseMathPartSchema = Schema.Struct({
14
+ display: Schema.Literals(["block", "inline"]),
15
+ kind: Schema.Literal("math"),
16
+ math: NonEmptyContentSchema,
17
+ });
18
+ /** One semantic text or mathematics fragment in a response label. */
19
+ export const QuestionResponseContentPartSchema = Schema.Union([
20
+ QuestionResponseMathPartSchema,
21
+ QuestionResponseTextPartSchema,
22
+ ]);
23
+ /** Ordered semantic content rendered without reparsing delimiter strings. */
24
+ export const QuestionResponseContentSchema = Schema.NonEmptyArray(QuestionResponseContentPartSchema);
25
+ /** Response formats supported by one assessment item. */
26
+ export const QuestionResponseKindSchema = Schema.Literals([
27
+ "category",
28
+ "multiple-choice",
29
+ "single-choice",
30
+ ]);
31
+ const QuestionOptionSchema = Schema.Struct({
32
+ isCorrect: Schema.Boolean,
33
+ label: QuestionResponseContentSchema,
34
+ optionKey: Schema.String.pipe(Schema.check(Schema.isPattern(OPTION_KEY_PATTERN))),
35
+ order: PositiveOrderSchema,
36
+ });
37
+ /** Checks stable option identities, order, and format-specific correctness. */
38
+ function hasCanonicalOptions(options, kind) {
39
+ const correct = options.filter(({ isCorrect }) => isCorrect).length;
40
+ return (options.length >= 2 &&
41
+ options.every(({ optionKey, order }, index) => order === index + 1 && optionKey === `option-${order}`) &&
42
+ (kind === "single-choice"
43
+ ? correct === 1
44
+ : correct >= 2 && correct < options.length));
45
+ }
46
+ const SingleChoiceResponseSchema = Schema.Struct({
47
+ kind: Schema.Literal("single-choice"),
48
+ options: Schema.Array(QuestionOptionSchema),
49
+ }).pipe(Schema.check(Schema.makeFilter(({ options }) => hasCanonicalOptions(options, "single-choice"), {
50
+ message: "Single-choice responses require canonical options and one correct answer.",
51
+ })));
52
+ const MultipleChoiceResponseSchema = Schema.Struct({
53
+ kind: Schema.Literal("multiple-choice"),
54
+ options: Schema.Array(QuestionOptionSchema),
55
+ }).pipe(Schema.check(Schema.makeFilter(({ options }) => hasCanonicalOptions(options, "multiple-choice"), {
56
+ message: "Multiple-choice responses require canonical options, several correct answers, and one distractor.",
57
+ })));
58
+ const QuestionCategorySchema = Schema.Struct({
59
+ categoryKey: Schema.String.pipe(Schema.check(Schema.isPattern(CATEGORY_KEY_PATTERN))),
60
+ label: QuestionResponseContentSchema,
61
+ order: PositiveOrderSchema,
62
+ });
63
+ const QuestionCategoryStatementSchema = Schema.Struct({
64
+ correctCategoryKey: Schema.String.pipe(Schema.check(Schema.isPattern(CATEGORY_KEY_PATTERN))),
65
+ label: QuestionResponseContentSchema,
66
+ order: PositiveOrderSchema,
67
+ statementKey: Schema.String.pipe(Schema.check(Schema.isPattern(STATEMENT_KEY_PATTERN))),
68
+ });
69
+ /** Checks stable category and statement identities plus valid references. */
70
+ function hasCanonicalCategories(input) {
71
+ const categoryKeys = new Set(input.categories.map(({ categoryKey }) => categoryKey));
72
+ return (input.categories.length >= 2 &&
73
+ input.statements.length > 0 &&
74
+ input.categories.every(({ categoryKey, order }, index) => order === index + 1 && categoryKey === `category-${order}`) &&
75
+ input.statements.every(({ correctCategoryKey, order, statementKey }, index) => order === index + 1 &&
76
+ statementKey === `statement-${order}` &&
77
+ categoryKeys.has(correctCategoryKey)));
78
+ }
79
+ const CategoryResponseSchema = Schema.Struct({
80
+ categories: Schema.Array(QuestionCategorySchema),
81
+ kind: Schema.Literal("category"),
82
+ statements: Schema.Array(QuestionCategoryStatementSchema),
83
+ }).pipe(Schema.check(Schema.makeFilter(hasCanonicalCategories, {
84
+ message: "Category responses require canonical categories and classified statements.",
85
+ })));
86
+ /** Frozen locale-specific response used from publication through review. */
87
+ export const QuestionResponseSchema = Schema.Union([
88
+ CategoryResponseSchema,
89
+ MultipleChoiceResponseSchema,
90
+ SingleChoiceResponseSchema,
91
+ ]);
92
+ /** Returns response content in stable field order for signed canonicalizers. */
93
+ export function canonicalQuestionResponseContent(content) {
94
+ return content.map((part) => part.kind === "text"
95
+ ? { kind: part.kind, text: part.text }
96
+ : { display: part.display, kind: part.kind, math: part.math });
97
+ }
98
+ /** Returns locale-neutral response identity without localized label content. */
99
+ export function canonicalQuestionResponseStructure(response) {
100
+ if (response.kind === "category") {
101
+ return {
102
+ categories: response.categories.map(({ categoryKey, order }) => ({
103
+ categoryKey,
104
+ order,
105
+ })),
106
+ kind: response.kind,
107
+ statements: response.statements.map(({ correctCategoryKey, order, statementKey }) => ({
108
+ correctCategoryKey,
109
+ order,
110
+ statementKey,
111
+ })),
112
+ };
113
+ }
114
+ return {
115
+ kind: response.kind,
116
+ options: response.options.map(({ isCorrect, optionKey, order }) => ({
117
+ isCorrect,
118
+ optionKey,
119
+ order,
120
+ })),
121
+ };
122
+ }
123
+ /** Returns response facts in stable field order for signed canonicalizers. */
124
+ export function canonicalQuestionResponse(response) {
125
+ if (response.kind === "category") {
126
+ return {
127
+ categories: response.categories.map(({ categoryKey, label, order }) => ({
128
+ categoryKey,
129
+ label: canonicalQuestionResponseContent(label),
130
+ order,
131
+ })),
132
+ kind: response.kind,
133
+ statements: response.statements.map(({ correctCategoryKey, label, order, statementKey }) => ({
134
+ correctCategoryKey,
135
+ label: canonicalQuestionResponseContent(label),
136
+ order,
137
+ statementKey,
138
+ })),
139
+ };
140
+ }
141
+ return {
142
+ kind: response.kind,
143
+ options: response.options.map(({ isCorrect, label, optionKey, order }) => ({
144
+ isCorrect,
145
+ label: canonicalQuestionResponseContent(label),
146
+ optionKey,
147
+ order,
148
+ })),
149
+ };
150
+ }
@@ -0,0 +1,48 @@
1
+ import type { QuranSourceAttribution } from "#contracts/quran/source";
2
+ /** Serializes one source attribution without trusting object insertion order. */
3
+ export declare function canonicalizeQuranAttribution(attribution: QuranSourceAttribution): {
4
+ copy: {
5
+ appLocale: ("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">;
6
+ notice: string;
7
+ title: string;
8
+ }[];
9
+ id: "bubenheim-names" | "kemenag-names" | "mokhtasar-english" | "mokhtasar-german" | "quranenc-english" | "quranenc-german" | "quranenc-indonesian" | "quranenc-tafsir" | "tanzil-metadata" | "tanzil-text";
10
+ kind: "embedded" | "external";
11
+ publisher: string;
12
+ retrievedAt: string;
13
+ sourceUrl: string;
14
+ terms: {
15
+ access: "link-only";
16
+ url: string;
17
+ artifact?: never;
18
+ };
19
+ updateUrl: string;
20
+ version: string;
21
+ } | {
22
+ copy: {
23
+ appLocale: ("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">;
24
+ notice: string;
25
+ title: string;
26
+ }[];
27
+ id: "bubenheim-names" | "kemenag-names" | "mokhtasar-english" | "mokhtasar-german" | "quranenc-english" | "quranenc-german" | "quranenc-indonesian" | "quranenc-tafsir" | "tanzil-metadata" | "tanzil-text";
28
+ kind: "embedded" | "external";
29
+ publisher: string;
30
+ retrievedAt: string;
31
+ sourceUrl: string;
32
+ artifact: {
33
+ byteCount: number;
34
+ digest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
35
+ fileCount: number;
36
+ };
37
+ terms: {
38
+ access?: never;
39
+ artifact: {
40
+ byteCount: number;
41
+ digest: string & import("effect/Brand").Brand<"@NakafaAI/AksaraSha256Hash">;
42
+ fileCount: number;
43
+ };
44
+ url: string;
45
+ };
46
+ updateUrl: string;
47
+ version: string;
48
+ };
@@ -0,0 +1,44 @@
1
+ /** Serializes one source attribution without trusting object insertion order. */
2
+ export function canonicalizeQuranAttribution(attribution) {
3
+ const common = {
4
+ copy: attribution.copy.map((entry) => ({
5
+ appLocale: entry.appLocale,
6
+ notice: entry.notice,
7
+ title: entry.title,
8
+ })),
9
+ id: attribution.id,
10
+ kind: attribution.kind,
11
+ publisher: attribution.publisher,
12
+ retrievedAt: attribution.retrievedAt,
13
+ sourceUrl: attribution.sourceUrl,
14
+ };
15
+ if (attribution.kind === "external") {
16
+ return {
17
+ ...common,
18
+ terms: {
19
+ access: attribution.terms.access,
20
+ url: attribution.terms.url,
21
+ },
22
+ updateUrl: attribution.updateUrl,
23
+ version: attribution.version,
24
+ };
25
+ }
26
+ return {
27
+ artifact: {
28
+ byteCount: attribution.artifact.byteCount,
29
+ digest: attribution.artifact.digest,
30
+ fileCount: attribution.artifact.fileCount,
31
+ },
32
+ ...common,
33
+ terms: {
34
+ artifact: {
35
+ byteCount: attribution.terms.artifact.byteCount,
36
+ digest: attribution.terms.artifact.digest,
37
+ fileCount: attribution.terms.artifact.fileCount,
38
+ },
39
+ url: attribution.terms.url,
40
+ },
41
+ updateUrl: attribution.updateUrl,
42
+ version: attribution.version,
43
+ };
44
+ }
@@ -0,0 +1,105 @@
1
+ import { Schema } from "effect";
2
+ import { type AppLocaleCode, ENGLISH_APP_LOCALE_CODE, GERMAN_APP_LOCALE_CODE, INDONESIAN_APP_LOCALE_CODE } from "#contracts/locale";
3
+ /** Official source identities whose bytes are pinned inside Aksara. */
4
+ export declare const QuranEmbeddedSourceIdSchema: Schema.Literals<readonly ["tanzil-text", "tanzil-metadata", "kemenag-names", "bubenheim-names", "quranenc-english", "quranenc-indonesian", "quranenc-german", "quranenc-tafsir"]>;
5
+ export type QuranEmbeddedSourceId = typeof QuranEmbeddedSourceIdSchema.Type;
6
+ /** Official source identities that Aksara may link to but not republish. */
7
+ export declare const QuranExternalSourceIdSchema: Schema.Literals<readonly ["mokhtasar-english", "mokhtasar-german"]>;
8
+ export type QuranExternalSourceId = typeof QuranExternalSourceIdSchema.Type;
9
+ /** Exact official Quran source identities in visible attribution order. */
10
+ export declare const QuranSourceIdSchema: Schema.Literals<readonly ["tanzil-text", "tanzil-metadata", "kemenag-names", "bubenheim-names", "quranenc-english", "quranenc-indonesian", "quranenc-german", "quranenc-tafsir", "mokhtasar-english", "mokhtasar-german"]>;
11
+ export type QuranSourceId = typeof QuranSourceIdSchema.Type;
12
+ /** Canonical order of every official source supported by the contract. */
13
+ export declare const QURAN_SOURCE_IDS: readonly ["tanzil-text", "tanzil-metadata", "kemenag-names", "bubenheim-names", "quranenc-english", "quranenc-indonesian", "quranenc-german", "quranenc-tafsir", "mokhtasar-english", "mokhtasar-german"];
14
+ /** Locale-specific translation field in Quran source provenance. */
15
+ export declare const QuranTranslationProvenanceScopeSchema: Schema.TemplateLiteral<readonly [Schema.Literals<readonly ["en", "id", "de"]>, "-translation"]>;
16
+ export type QuranTranslationProvenanceScope = typeof QuranTranslationProvenanceScopeSchema.Type;
17
+ /** Locale-specific surah-name field in Quran source provenance. */
18
+ export declare const QuranNameProvenanceScopeSchema: Schema.TemplateLiteral<readonly [Schema.Literals<readonly ["en", "id", "de"]>, "-surah-name"]>;
19
+ export type QuranNameProvenanceScope = typeof QuranNameProvenanceScopeSchema.Type;
20
+ declare const QURAN_NAME_BY_LOCALE: {
21
+ readonly en: {
22
+ readonly scope: "en-surah-name";
23
+ readonly sourceId: "tanzil-metadata";
24
+ };
25
+ readonly de: {
26
+ readonly scope: "de-surah-name";
27
+ readonly sourceId: "bubenheim-names";
28
+ };
29
+ readonly id: {
30
+ readonly scope: "id-surah-name";
31
+ readonly sourceId: "kemenag-names";
32
+ };
33
+ };
34
+ declare const QURAN_TRANSLATION_BY_LOCALE: {
35
+ readonly en: {
36
+ readonly scope: "en-translation";
37
+ readonly sourceId: "quranenc-english";
38
+ };
39
+ readonly de: {
40
+ readonly scope: "de-translation";
41
+ readonly sourceId: "quranenc-german";
42
+ };
43
+ readonly id: {
44
+ readonly scope: "id-translation";
45
+ readonly sourceId: "quranenc-indonesian";
46
+ };
47
+ };
48
+ declare const QURAN_TAFSIR_BY_LOCALE: {
49
+ readonly en: {
50
+ readonly sourceId: "mokhtasar-english";
51
+ };
52
+ readonly de: {
53
+ readonly sourceId: "mokhtasar-german";
54
+ };
55
+ readonly id: {
56
+ readonly sourceId: "quranenc-tafsir";
57
+ };
58
+ };
59
+ /** Selects the reviewed surah-name source for an application locale. */
60
+ export declare function quranNameSourceId(appLocale: typeof ENGLISH_APP_LOCALE_CODE): (typeof QURAN_NAME_BY_LOCALE)[typeof ENGLISH_APP_LOCALE_CODE]["sourceId"];
61
+ /** Narrows the Indonesian surah-name source identity. */
62
+ export declare function quranNameSourceId(appLocale: typeof INDONESIAN_APP_LOCALE_CODE): (typeof QURAN_NAME_BY_LOCALE)[typeof INDONESIAN_APP_LOCALE_CODE]["sourceId"];
63
+ /** Narrows the German surah-name source identity. */
64
+ export declare function quranNameSourceId(appLocale: typeof GERMAN_APP_LOCALE_CODE): (typeof QURAN_NAME_BY_LOCALE)[typeof GERMAN_APP_LOCALE_CODE]["sourceId"];
65
+ /** Selects a surah-name source from a dynamic supported locale. */
66
+ export declare function quranNameSourceId(appLocale: AppLocaleCode): QuranEmbeddedSourceId;
67
+ /** Selects the surah-name provenance scope for an application locale. */
68
+ export declare function quranNameProvenanceScope(appLocale: typeof ENGLISH_APP_LOCALE_CODE): (typeof QURAN_NAME_BY_LOCALE)[typeof ENGLISH_APP_LOCALE_CODE]["scope"];
69
+ /** Narrows the Indonesian surah-name provenance scope. */
70
+ export declare function quranNameProvenanceScope(appLocale: typeof INDONESIAN_APP_LOCALE_CODE): (typeof QURAN_NAME_BY_LOCALE)[typeof INDONESIAN_APP_LOCALE_CODE]["scope"];
71
+ /** Narrows the German surah-name provenance scope. */
72
+ export declare function quranNameProvenanceScope(appLocale: typeof GERMAN_APP_LOCALE_CODE): (typeof QURAN_NAME_BY_LOCALE)[typeof GERMAN_APP_LOCALE_CODE]["scope"];
73
+ /** Selects a surah-name provenance scope from a dynamic locale. */
74
+ export declare function quranNameProvenanceScope(appLocale: AppLocaleCode): QuranNameProvenanceScope;
75
+ /** Selects the one pinned translation source for an application locale. */
76
+ export declare function quranTranslationSourceId(appLocale: typeof ENGLISH_APP_LOCALE_CODE): (typeof QURAN_TRANSLATION_BY_LOCALE)[typeof ENGLISH_APP_LOCALE_CODE]["sourceId"];
77
+ /** Narrows the Indonesian translation source identity. */
78
+ export declare function quranTranslationSourceId(appLocale: typeof INDONESIAN_APP_LOCALE_CODE): (typeof QURAN_TRANSLATION_BY_LOCALE)[typeof INDONESIAN_APP_LOCALE_CODE]["sourceId"];
79
+ /** Narrows the German translation source identity. */
80
+ export declare function quranTranslationSourceId(appLocale: typeof GERMAN_APP_LOCALE_CODE): (typeof QURAN_TRANSLATION_BY_LOCALE)[typeof GERMAN_APP_LOCALE_CODE]["sourceId"];
81
+ /** Selects a translation source from a dynamic supported locale. */
82
+ export declare function quranTranslationSourceId(appLocale: AppLocaleCode): QuranEmbeddedSourceId;
83
+ /** Selects the one signed Tafsir source or access record for a locale. */
84
+ export declare function quranTafsirSourceId(appLocale: typeof ENGLISH_APP_LOCALE_CODE): (typeof QURAN_TAFSIR_BY_LOCALE)[typeof ENGLISH_APP_LOCALE_CODE]["sourceId"];
85
+ /** Narrows the Indonesian Tafsir source identity. */
86
+ export declare function quranTafsirSourceId(appLocale: typeof INDONESIAN_APP_LOCALE_CODE): (typeof QURAN_TAFSIR_BY_LOCALE)[typeof INDONESIAN_APP_LOCALE_CODE]["sourceId"];
87
+ /** Narrows the German Tafsir source identity. */
88
+ export declare function quranTafsirSourceId(appLocale: typeof GERMAN_APP_LOCALE_CODE): (typeof QURAN_TAFSIR_BY_LOCALE)[typeof GERMAN_APP_LOCALE_CODE]["sourceId"];
89
+ /** Selects a Tafsir source from a dynamic supported locale. */
90
+ export declare function quranTafsirSourceId(appLocale: AppLocaleCode): QuranSourceId;
91
+ /** Selects the one provenance scope for an application locale. */
92
+ export declare function quranTranslationProvenanceScope(appLocale: typeof ENGLISH_APP_LOCALE_CODE): (typeof QURAN_TRANSLATION_BY_LOCALE)[typeof ENGLISH_APP_LOCALE_CODE]["scope"];
93
+ /** Narrows the Indonesian translation provenance scope. */
94
+ export declare function quranTranslationProvenanceScope(appLocale: typeof INDONESIAN_APP_LOCALE_CODE): (typeof QURAN_TRANSLATION_BY_LOCALE)[typeof INDONESIAN_APP_LOCALE_CODE]["scope"];
95
+ /** Narrows the German translation provenance scope. */
96
+ export declare function quranTranslationProvenanceScope(appLocale: typeof GERMAN_APP_LOCALE_CODE): (typeof QURAN_TRANSLATION_BY_LOCALE)[typeof GERMAN_APP_LOCALE_CODE]["scope"];
97
+ /** Selects a translation provenance scope from a dynamic locale. */
98
+ export declare function quranTranslationProvenanceScope(appLocale: AppLocaleCode): QuranTranslationProvenanceScope;
99
+ /** Resolves a translation provenance scope through the canonical locale map. */
100
+ export declare function quranTranslationSourceForScope(scope: QuranTranslationProvenanceScope): "quranenc-english" | "quranenc-german" | "quranenc-indonesian";
101
+ /** Resolves a surah-name provenance scope through the canonical locale map. */
102
+ export declare function quranNameSourceForScope(scope: QuranNameProvenanceScope): "bubenheim-names" | "kemenag-names" | "tanzil-metadata";
103
+ /** Selects the signed Arabic and localized translation reading sources. */
104
+ export declare function quranReadingSourceIds(appLocale: AppLocaleCode): readonly ["tanzil-text", "bubenheim-names" | "kemenag-names" | "quranenc-english" | "quranenc-german" | "quranenc-indonesian" | "quranenc-tafsir" | "tanzil-metadata" | "tanzil-text"];
105
+ export {};