@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,109 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { type AppLocale } from "#contracts/locale";
|
|
3
|
+
/** Locale-owned root namespace for public material routes. */
|
|
4
|
+
export declare const MaterialPublicNamespaceSchema: Schema.Literals<readonly ["subjects", "materi", "faecher"]>;
|
|
5
|
+
export type MaterialPublicNamespace = typeof MaterialPublicNamespaceSchema.Type;
|
|
6
|
+
/** Resolves the only public material namespace owned by one app locale. */
|
|
7
|
+
export declare function materialPublicNamespace(appLocale: AppLocale): MaterialPublicNamespace;
|
|
8
|
+
/** Stable reusable material identity preserved from Nakafa's source registry. */
|
|
9
|
+
export declare const MaterialKeySchema: Schema.brand<Schema.String, "@NakafaAI/AksaraMaterialKey">;
|
|
10
|
+
export type MaterialKey = typeof MaterialKeySchema.Type;
|
|
11
|
+
/** Stable lesson-section identity preserved across localized public routes. */
|
|
12
|
+
export declare const MaterialSectionSchema: Schema.brand<Schema.String, "@NakafaAI/AksaraMaterialSection">;
|
|
13
|
+
export type MaterialSection = typeof MaterialSectionSchema.Type;
|
|
14
|
+
/** Exact metadata contract required from newly authored material sources. */
|
|
15
|
+
export declare const MaterialMetadataSchema: Schema.Struct<{
|
|
16
|
+
readonly dateModified: Schema.optionalKey<Schema.String>;
|
|
17
|
+
readonly datePublished: Schema.String;
|
|
18
|
+
readonly authors: Schema.$Array<Schema.Struct<{
|
|
19
|
+
readonly name: Schema.String;
|
|
20
|
+
}>>;
|
|
21
|
+
readonly description: Schema.optional<Schema.String>;
|
|
22
|
+
readonly subject: Schema.optional<Schema.String>;
|
|
23
|
+
readonly title: Schema.String;
|
|
24
|
+
}>;
|
|
25
|
+
export type MaterialMetadata = typeof MaterialMetadataSchema.Type;
|
|
26
|
+
/** Non-authored material route fields preserved from Nakafa's registry. */
|
|
27
|
+
export declare const MaterialLessonRouteSchema: Schema.Struct<{
|
|
28
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
29
|
+
readonly artifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
30
|
+
readonly contentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
31
|
+
readonly graph: Schema.Struct<{
|
|
32
|
+
readonly alignmentId: Schema.String;
|
|
33
|
+
readonly assetId: Schema.String;
|
|
34
|
+
readonly conceptId: Schema.String;
|
|
35
|
+
readonly learningObjectId: Schema.String;
|
|
36
|
+
readonly lensId: Schema.String;
|
|
37
|
+
}>;
|
|
38
|
+
readonly materialKey: Schema.brand<Schema.String, "@NakafaAI/AksaraMaterialKey">;
|
|
39
|
+
readonly order: Schema.Finite;
|
|
40
|
+
readonly publicPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
41
|
+
readonly sectionKey: Schema.brand<Schema.String, "@NakafaAI/AksaraMaterialSection">;
|
|
42
|
+
readonly topicTitle: Schema.String;
|
|
43
|
+
}>;
|
|
44
|
+
export type MaterialLessonRoute = typeof MaterialLessonRouteSchema.Type;
|
|
45
|
+
/** Canonical route read model for one published material lesson body. */
|
|
46
|
+
export declare const MaterialLessonProjectionSchema: Schema.Struct<{
|
|
47
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
48
|
+
readonly artifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
49
|
+
readonly contentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
50
|
+
readonly graph: Schema.Struct<{
|
|
51
|
+
readonly alignmentId: Schema.String;
|
|
52
|
+
readonly assetId: Schema.String;
|
|
53
|
+
readonly conceptId: Schema.String;
|
|
54
|
+
readonly learningObjectId: Schema.String;
|
|
55
|
+
readonly lensId: Schema.String;
|
|
56
|
+
}>;
|
|
57
|
+
readonly materialKey: Schema.brand<Schema.String, "@NakafaAI/AksaraMaterialKey">;
|
|
58
|
+
readonly order: Schema.Finite;
|
|
59
|
+
readonly publicPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
60
|
+
readonly sectionKey: Schema.brand<Schema.String, "@NakafaAI/AksaraMaterialSection">;
|
|
61
|
+
readonly kind: Schema.Literal<"subject-lesson">;
|
|
62
|
+
readonly metadata: Schema.Struct<{
|
|
63
|
+
readonly dateModified: Schema.optionalKey<Schema.String>;
|
|
64
|
+
readonly datePublished: Schema.String;
|
|
65
|
+
readonly authors: Schema.$Array<Schema.Struct<{
|
|
66
|
+
readonly name: Schema.String;
|
|
67
|
+
}>>;
|
|
68
|
+
readonly description: Schema.optional<Schema.String>;
|
|
69
|
+
readonly subject: Schema.optional<Schema.String>;
|
|
70
|
+
readonly title: Schema.String;
|
|
71
|
+
}>;
|
|
72
|
+
readonly parentPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
73
|
+
readonly sitemap: Schema.Literal<true>;
|
|
74
|
+
readonly topicTitle: Schema.String;
|
|
75
|
+
}>;
|
|
76
|
+
export type MaterialLessonProjection = typeof MaterialLessonProjectionSchema.Type;
|
|
77
|
+
/** Combines registry-owned routing with metadata decoded from authored MDX. */
|
|
78
|
+
export declare function makeMaterialLessonProjection(route: MaterialLessonRoute, metadata: MaterialMetadata): {
|
|
79
|
+
readonly appLocale: ("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">;
|
|
80
|
+
readonly artifactLocale: ("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraArtifactLocale">;
|
|
81
|
+
readonly contentKey: string & import("effect/Brand").Brand<"@NakafaAI/AksaraContentKey">;
|
|
82
|
+
readonly graph: {
|
|
83
|
+
readonly alignmentId: string;
|
|
84
|
+
readonly assetId: string;
|
|
85
|
+
readonly conceptId: string;
|
|
86
|
+
readonly learningObjectId: string;
|
|
87
|
+
readonly lensId: string;
|
|
88
|
+
};
|
|
89
|
+
readonly materialKey: string & import("effect/Brand").Brand<"@NakafaAI/AksaraMaterialKey">;
|
|
90
|
+
readonly order: number;
|
|
91
|
+
readonly publicPath: string & import("effect/Brand").Brand<"@NakafaAI/AksaraPublicPath">;
|
|
92
|
+
readonly sectionKey: string & import("effect/Brand").Brand<"@NakafaAI/AksaraMaterialSection">;
|
|
93
|
+
readonly kind: "subject-lesson";
|
|
94
|
+
readonly metadata: {
|
|
95
|
+
readonly dateModified?: string;
|
|
96
|
+
readonly datePublished: string;
|
|
97
|
+
readonly authors: readonly {
|
|
98
|
+
readonly name: string;
|
|
99
|
+
}[];
|
|
100
|
+
readonly description?: string | undefined;
|
|
101
|
+
readonly subject?: string | undefined;
|
|
102
|
+
readonly title: string;
|
|
103
|
+
};
|
|
104
|
+
readonly parentPath: string & import("effect/Brand").Brand<"@NakafaAI/AksaraPublicPath">;
|
|
105
|
+
readonly sitemap: true;
|
|
106
|
+
readonly topicTitle: string;
|
|
107
|
+
};
|
|
108
|
+
/** Serializes one material projection with stable signed field order. */
|
|
109
|
+
export declare function canonicalizeMaterialProjection(projection: MaterialLessonProjection): string;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ContentAuthorSchema } from "#contracts/content";
|
|
3
|
+
import { withPublicationDates } from "#contracts/date";
|
|
4
|
+
import { canonicalizeLearningGraphIdentity, LearningGraphIdentitySchema, } from "#contracts/graph/spec";
|
|
5
|
+
import { ContentKeySchema, PublicPathSchema } from "#contracts/ids";
|
|
6
|
+
import { AppLocaleSchema, ArtifactLocaleSchema, } from "#contracts/locale";
|
|
7
|
+
const MATERIAL_KEY_PATTERN = /^lesson\.[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
8
|
+
const SECTION_KEY_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
9
|
+
const MaterialPublicPathSchema = PublicPathSchema.pipe(Schema.check(Schema.makeFilter((value) => value.includes("/"), {
|
|
10
|
+
message: "Expected a material lesson path with a parent route.",
|
|
11
|
+
})));
|
|
12
|
+
/** Locale-owned root namespace for public material routes. */
|
|
13
|
+
export const MaterialPublicNamespaceSchema = Schema.Literals([
|
|
14
|
+
"subjects",
|
|
15
|
+
"materi",
|
|
16
|
+
"faecher",
|
|
17
|
+
]);
|
|
18
|
+
/** Resolves the only public material namespace owned by one app locale. */
|
|
19
|
+
export function materialPublicNamespace(appLocale) {
|
|
20
|
+
if (appLocale === AppLocaleSchema.make("en")) {
|
|
21
|
+
return "subjects";
|
|
22
|
+
}
|
|
23
|
+
if (appLocale === AppLocaleSchema.make("id")) {
|
|
24
|
+
return "materi";
|
|
25
|
+
}
|
|
26
|
+
return "faecher";
|
|
27
|
+
}
|
|
28
|
+
/** Stable reusable material identity preserved from Nakafa's source registry. */
|
|
29
|
+
export const MaterialKeySchema = Schema.String.pipe(Schema.check(Schema.isPattern(MATERIAL_KEY_PATTERN, {
|
|
30
|
+
description: "Stable lesson key with domain and material segments.",
|
|
31
|
+
identifier: "MaterialKey",
|
|
32
|
+
message: "Invalid material key.",
|
|
33
|
+
})), Schema.brand("@NakafaAI/AksaraMaterialKey"));
|
|
34
|
+
/** Stable lesson-section identity preserved across localized public routes. */
|
|
35
|
+
export const MaterialSectionSchema = Schema.String.pipe(Schema.check(Schema.isPattern(SECTION_KEY_PATTERN)), Schema.brand("@NakafaAI/AksaraMaterialSection"));
|
|
36
|
+
const MaterialMetadataFields = {
|
|
37
|
+
authors: Schema.Array(ContentAuthorSchema),
|
|
38
|
+
description: Schema.optional(Schema.String),
|
|
39
|
+
subject: Schema.optional(Schema.String),
|
|
40
|
+
title: Schema.String,
|
|
41
|
+
};
|
|
42
|
+
/** Exact metadata contract required from newly authored material sources. */
|
|
43
|
+
export const MaterialMetadataSchema = withPublicationDates(MaterialMetadataFields);
|
|
44
|
+
const MaterialProjectionMetadataSchema = withPublicationDates(MaterialMetadataFields);
|
|
45
|
+
/** Stable route fields shared by material routes and published projections. */
|
|
46
|
+
const MaterialLessonRouteFields = {
|
|
47
|
+
appLocale: AppLocaleSchema,
|
|
48
|
+
artifactLocale: ArtifactLocaleSchema,
|
|
49
|
+
contentKey: ContentKeySchema,
|
|
50
|
+
graph: LearningGraphIdentitySchema,
|
|
51
|
+
materialKey: MaterialKeySchema,
|
|
52
|
+
order: Schema.Finite.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThan(0))),
|
|
53
|
+
publicPath: MaterialPublicPathSchema,
|
|
54
|
+
sectionKey: MaterialSectionSchema,
|
|
55
|
+
};
|
|
56
|
+
/** Fields shared by the filtered material projection contract. */
|
|
57
|
+
const MaterialLessonProjectionFields = {
|
|
58
|
+
...MaterialLessonRouteFields,
|
|
59
|
+
kind: Schema.Literal("subject-lesson"),
|
|
60
|
+
metadata: MaterialProjectionMetadataSchema,
|
|
61
|
+
parentPath: PublicPathSchema,
|
|
62
|
+
sitemap: Schema.Literal(true),
|
|
63
|
+
};
|
|
64
|
+
/** Returns the canonical parent route owned by one lesson public path. */
|
|
65
|
+
function materialParentPath(publicPath) {
|
|
66
|
+
return publicPath.slice(0, publicPath.lastIndexOf("/"));
|
|
67
|
+
}
|
|
68
|
+
/** Checks that a projection cannot claim an unrelated material parent. */
|
|
69
|
+
function hasCoherentParentPath(input) {
|
|
70
|
+
return input.parentPath === materialParentPath(input.publicPath);
|
|
71
|
+
}
|
|
72
|
+
/** Checks signed graph identities against stable material source keys. */
|
|
73
|
+
function hasCoherentMaterialGraph(input) {
|
|
74
|
+
const identity = input.materialKey.slice("lesson.".length);
|
|
75
|
+
const separator = identity.indexOf(".");
|
|
76
|
+
const domain = identity.slice(0, separator);
|
|
77
|
+
const topic = identity.slice(separator + 1);
|
|
78
|
+
const lens = `material:lesson:${domain}`;
|
|
79
|
+
const object = `material-section:${domain}:${topic}:${input.sectionKey}`;
|
|
80
|
+
return (input.graph.alignmentId === `alignment:${lens}:${object}` &&
|
|
81
|
+
input.graph.assetId === `asset:${input.appLocale}:${lens}:${object}` &&
|
|
82
|
+
input.graph.conceptId === `concept:${lens}:${topic}` &&
|
|
83
|
+
input.graph.learningObjectId === `lo:${object}` &&
|
|
84
|
+
input.graph.lensId === `lens:${lens}`);
|
|
85
|
+
}
|
|
86
|
+
/** Public material bodies use the same locale for routes and artifacts. */
|
|
87
|
+
function hasCoherentMaterialLocales(input) {
|
|
88
|
+
return input.appLocale === input.artifactLocale;
|
|
89
|
+
}
|
|
90
|
+
/** Checks one signed material route against its locale-owned namespace. */
|
|
91
|
+
function hasCoherentMaterialNamespace(input) {
|
|
92
|
+
return input.publicPath.startsWith(`${materialPublicNamespace(input.appLocale)}/`);
|
|
93
|
+
}
|
|
94
|
+
/** Non-authored material route fields preserved from Nakafa's registry. */
|
|
95
|
+
export const MaterialLessonRouteSchema = Schema.Struct({
|
|
96
|
+
...MaterialLessonRouteFields,
|
|
97
|
+
topicTitle: Schema.String,
|
|
98
|
+
}).pipe(Schema.check(Schema.makeFilter(hasCoherentMaterialLocales, {
|
|
99
|
+
message: "Expected public material route and artifact locales to match.",
|
|
100
|
+
})), Schema.check(Schema.makeFilter(hasCoherentMaterialNamespace, {
|
|
101
|
+
message: "Expected the material public path to use its locale-owned namespace.",
|
|
102
|
+
})), Schema.check(Schema.makeFilter(hasCoherentMaterialGraph, {
|
|
103
|
+
message: "Expected material graph identities to match its stable source keys.",
|
|
104
|
+
})));
|
|
105
|
+
/** Canonical route read model for one published material lesson body. */
|
|
106
|
+
export const MaterialLessonProjectionSchema = Schema.Struct({
|
|
107
|
+
...MaterialLessonProjectionFields,
|
|
108
|
+
topicTitle: Schema.String,
|
|
109
|
+
}).pipe(Schema.check(Schema.makeFilter(hasCoherentMaterialLocales, {
|
|
110
|
+
message: "Expected public material route and artifact locales to match.",
|
|
111
|
+
})), Schema.check(Schema.makeFilter(hasCoherentParentPath, {
|
|
112
|
+
message: "Expected the material parent path to match the lesson public path.",
|
|
113
|
+
})), Schema.check(Schema.makeFilter(hasCoherentMaterialNamespace, {
|
|
114
|
+
message: "Expected the material public path to use its locale-owned namespace.",
|
|
115
|
+
})), Schema.check(Schema.makeFilter(hasCoherentMaterialGraph, {
|
|
116
|
+
message: "Expected material graph identities to match its stable source keys.",
|
|
117
|
+
})));
|
|
118
|
+
/** Combines registry-owned routing with metadata decoded from authored MDX. */
|
|
119
|
+
export function makeMaterialLessonProjection(route, metadata) {
|
|
120
|
+
const parentPath = materialParentPath(route.publicPath);
|
|
121
|
+
return MaterialLessonProjectionSchema.make({
|
|
122
|
+
...route,
|
|
123
|
+
kind: "subject-lesson",
|
|
124
|
+
metadata,
|
|
125
|
+
parentPath: PublicPathSchema.make(parentPath),
|
|
126
|
+
sitemap: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/** Serializes one material projection with stable signed field order. */
|
|
130
|
+
export function canonicalizeMaterialProjection(projection) {
|
|
131
|
+
const metadata = {
|
|
132
|
+
authors: projection.metadata.authors.map(({ name }) => ({ name })),
|
|
133
|
+
...(projection.metadata.dateModified === undefined
|
|
134
|
+
? {}
|
|
135
|
+
: { dateModified: projection.metadata.dateModified }),
|
|
136
|
+
datePublished: projection.metadata.datePublished,
|
|
137
|
+
...(projection.metadata.description === undefined
|
|
138
|
+
? {}
|
|
139
|
+
: { description: projection.metadata.description }),
|
|
140
|
+
...(projection.metadata.subject === undefined
|
|
141
|
+
? {}
|
|
142
|
+
: { subject: projection.metadata.subject }),
|
|
143
|
+
title: projection.metadata.title,
|
|
144
|
+
};
|
|
145
|
+
return JSON.stringify({
|
|
146
|
+
appLocale: projection.appLocale,
|
|
147
|
+
artifactLocale: projection.artifactLocale,
|
|
148
|
+
contentKey: projection.contentKey,
|
|
149
|
+
graph: canonicalizeLearningGraphIdentity(projection.graph),
|
|
150
|
+
kind: projection.kind,
|
|
151
|
+
materialKey: projection.materialKey,
|
|
152
|
+
metadata,
|
|
153
|
+
order: projection.order,
|
|
154
|
+
parentPath: projection.parentPath,
|
|
155
|
+
publicPath: projection.publicPath,
|
|
156
|
+
sectionKey: projection.sectionKey,
|
|
157
|
+
sitemap: projection.sitemap,
|
|
158
|
+
topicTitle: projection.topicTitle,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { CorpusSourcePathSchema } from "#contracts/ids";
|
|
3
|
+
/** Stable source-owned identity for one reviewed public site page. */
|
|
4
|
+
export declare const PageKeySchema: Schema.brand<Schema.String, "@NakafaAI/AksaraPageKey">;
|
|
5
|
+
export type PageKey = typeof PageKeySchema.Type;
|
|
6
|
+
/** Exact metadata consumed by human, agent, and sitemap page surfaces. */
|
|
7
|
+
export declare const PageMetadataSchema: Schema.Struct<{
|
|
8
|
+
readonly dateModified: Schema.optionalKey<Schema.String>;
|
|
9
|
+
readonly datePublished: Schema.String;
|
|
10
|
+
readonly description: Schema.Trimmed;
|
|
11
|
+
readonly title: Schema.Trimmed;
|
|
12
|
+
}>;
|
|
13
|
+
export type PageMetadata = typeof PageMetadataSchema.Type;
|
|
14
|
+
/** Stable identity and locale-owned public route for one reviewed site page. */
|
|
15
|
+
export declare const PublicPageRouteSchema: Schema.Struct<{
|
|
16
|
+
appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
17
|
+
artifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
18
|
+
contentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
19
|
+
pageKey: Schema.brand<Schema.String, "@NakafaAI/AksaraPageKey">;
|
|
20
|
+
publicPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
21
|
+
}>;
|
|
22
|
+
export type PublicPageRoute = typeof PublicPageRouteSchema.Type;
|
|
23
|
+
/** Canonical signed read model for one reviewed public site page. */
|
|
24
|
+
export declare const PublicPageProjectionSchema: Schema.Struct<{
|
|
25
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
26
|
+
readonly artifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
27
|
+
readonly contentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
28
|
+
readonly pageKey: Schema.brand<Schema.String, "@NakafaAI/AksaraPageKey">;
|
|
29
|
+
readonly publicPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
30
|
+
readonly kind: Schema.Literal<"public-page">;
|
|
31
|
+
readonly metadata: Schema.Struct<{
|
|
32
|
+
readonly dateModified: Schema.optionalKey<Schema.String>;
|
|
33
|
+
readonly datePublished: Schema.String;
|
|
34
|
+
readonly description: Schema.Trimmed;
|
|
35
|
+
readonly title: Schema.Trimmed;
|
|
36
|
+
}>;
|
|
37
|
+
readonly sitemap: Schema.Literal<true>;
|
|
38
|
+
readonly sourcePath: Schema.brand<Schema.String, "@NakafaAI/AksaraCorpusSourcePath">;
|
|
39
|
+
}>;
|
|
40
|
+
export type PublicPageProjection = typeof PublicPageProjectionSchema.Type;
|
|
41
|
+
/** Exact predecessor Page bytes required for rollback and stored-state reads. */
|
|
42
|
+
export declare const HistoricalPublicPageProjectionSchema: Schema.Struct<{
|
|
43
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
44
|
+
readonly artifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
45
|
+
readonly contentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
46
|
+
readonly pageKey: Schema.brand<Schema.String, "@NakafaAI/AksaraPageKey">;
|
|
47
|
+
readonly publicPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
48
|
+
readonly kind: Schema.Literal<"public-page">;
|
|
49
|
+
readonly metadata: Schema.Struct<{
|
|
50
|
+
readonly description: Schema.Trimmed;
|
|
51
|
+
readonly lastModified: Schema.String;
|
|
52
|
+
readonly title: Schema.Trimmed;
|
|
53
|
+
}>;
|
|
54
|
+
readonly sitemap: Schema.Literal<true>;
|
|
55
|
+
readonly sourcePath: Schema.brand<Schema.String, "@NakafaAI/AksaraCorpusSourcePath">;
|
|
56
|
+
}>;
|
|
57
|
+
export type HistoricalPublicPageProjection = typeof HistoricalPublicPageProjectionSchema.Type;
|
|
58
|
+
/** Current Page values plus exact predecessor bytes needed for safe rollout. */
|
|
59
|
+
export declare const ReadablePublicPageProjectionSchema: Schema.Union<readonly [Schema.Struct<{
|
|
60
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
61
|
+
readonly artifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
62
|
+
readonly contentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
63
|
+
readonly pageKey: Schema.brand<Schema.String, "@NakafaAI/AksaraPageKey">;
|
|
64
|
+
readonly publicPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
65
|
+
readonly kind: Schema.Literal<"public-page">;
|
|
66
|
+
readonly metadata: Schema.Struct<{
|
|
67
|
+
readonly dateModified: Schema.optionalKey<Schema.String>;
|
|
68
|
+
readonly datePublished: Schema.String;
|
|
69
|
+
readonly description: Schema.Trimmed;
|
|
70
|
+
readonly title: Schema.Trimmed;
|
|
71
|
+
}>;
|
|
72
|
+
readonly sitemap: Schema.Literal<true>;
|
|
73
|
+
readonly sourcePath: Schema.brand<Schema.String, "@NakafaAI/AksaraCorpusSourcePath">;
|
|
74
|
+
}>, Schema.Struct<{
|
|
75
|
+
readonly appLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraAppLocale">;
|
|
76
|
+
readonly artifactLocale: Schema.brand<Schema.Literals<readonly ["en", "id", "de"]>, "@NakafaAI/AksaraArtifactLocale">;
|
|
77
|
+
readonly contentKey: Schema.brand<Schema.String, "@NakafaAI/AksaraContentKey">;
|
|
78
|
+
readonly pageKey: Schema.brand<Schema.String, "@NakafaAI/AksaraPageKey">;
|
|
79
|
+
readonly publicPath: Schema.brand<Schema.String, "@NakafaAI/AksaraPublicPath">;
|
|
80
|
+
readonly kind: Schema.Literal<"public-page">;
|
|
81
|
+
readonly metadata: Schema.Struct<{
|
|
82
|
+
readonly description: Schema.Trimmed;
|
|
83
|
+
readonly lastModified: Schema.String;
|
|
84
|
+
readonly title: Schema.Trimmed;
|
|
85
|
+
}>;
|
|
86
|
+
readonly sitemap: Schema.Literal<true>;
|
|
87
|
+
readonly sourcePath: Schema.brand<Schema.String, "@NakafaAI/AksaraCorpusSourcePath">;
|
|
88
|
+
}>]>;
|
|
89
|
+
export type ReadablePublicPageProjection = typeof ReadablePublicPageProjectionSchema.Type;
|
|
90
|
+
/** Combines one reviewed page route with its strict localized metadata. */
|
|
91
|
+
export declare function makePublicPageProjection(input: {
|
|
92
|
+
readonly metadata: PageMetadata;
|
|
93
|
+
readonly route: PublicPageRoute;
|
|
94
|
+
readonly sourcePath: typeof CorpusSourcePathSchema.Type;
|
|
95
|
+
}): {
|
|
96
|
+
readonly appLocale: ("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraAppLocale">;
|
|
97
|
+
readonly artifactLocale: ("de" | "en" | "id") & import("effect/Brand").Brand<"@NakafaAI/AksaraArtifactLocale">;
|
|
98
|
+
readonly contentKey: string & import("effect/Brand").Brand<"@NakafaAI/AksaraContentKey">;
|
|
99
|
+
readonly pageKey: string & import("effect/Brand").Brand<"@NakafaAI/AksaraPageKey">;
|
|
100
|
+
readonly publicPath: string & import("effect/Brand").Brand<"@NakafaAI/AksaraPublicPath">;
|
|
101
|
+
readonly kind: "public-page";
|
|
102
|
+
readonly metadata: {
|
|
103
|
+
readonly dateModified?: string;
|
|
104
|
+
readonly datePublished: string;
|
|
105
|
+
readonly description: string;
|
|
106
|
+
readonly title: string;
|
|
107
|
+
};
|
|
108
|
+
readonly sitemap: true;
|
|
109
|
+
readonly sourcePath: string & import("effect/Brand").Brand<"@NakafaAI/AksaraCorpusSourcePath">;
|
|
110
|
+
};
|
|
111
|
+
/** Serializes one public page projection with stable signed field order. */
|
|
112
|
+
export declare function canonicalizePublicPageProjection(projection: ReadablePublicPageProjection): string;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { DateOnlySchema, withPublicationDates } from "#contracts/date";
|
|
3
|
+
import { ContentKeySchema, CorpusSourcePathSchema, PublicPathSchema, } from "#contracts/ids";
|
|
4
|
+
import { AppLocaleSchema, ArtifactLocaleSchema } from "#contracts/locale";
|
|
5
|
+
import { isLowerKebab } from "#contracts/text/syntax";
|
|
6
|
+
/** Stable source-owned identity for one reviewed public site page. */
|
|
7
|
+
export const PageKeySchema = Schema.String.pipe(Schema.check(Schema.makeFilter(isLowerKebab)), Schema.brand("@NakafaAI/AksaraPageKey"));
|
|
8
|
+
/** Exact metadata consumed by human, agent, and sitemap page surfaces. */
|
|
9
|
+
export const PageMetadataSchema = withPublicationDates({
|
|
10
|
+
description: Schema.Trimmed.check(Schema.isNonEmpty()),
|
|
11
|
+
title: Schema.Trimmed.check(Schema.isNonEmpty()),
|
|
12
|
+
});
|
|
13
|
+
/** Immutable metadata retained only for authenticated historical Page reads. */
|
|
14
|
+
const HistoricalPageMetadataSchema = Schema.Struct({
|
|
15
|
+
description: Schema.Trimmed.check(Schema.isNonEmpty()),
|
|
16
|
+
lastModified: DateOnlySchema,
|
|
17
|
+
title: Schema.Trimmed.check(Schema.isNonEmpty()),
|
|
18
|
+
});
|
|
19
|
+
const PublicPageRouteFields = {
|
|
20
|
+
appLocale: AppLocaleSchema,
|
|
21
|
+
artifactLocale: ArtifactLocaleSchema,
|
|
22
|
+
contentKey: ContentKeySchema,
|
|
23
|
+
pageKey: PageKeySchema,
|
|
24
|
+
publicPath: PublicPathSchema,
|
|
25
|
+
};
|
|
26
|
+
/** Checks stable page identity separately from its locale-owned public path. */
|
|
27
|
+
function hasCoherentPageRoute(input) {
|
|
28
|
+
return input.contentKey === `pages/${input.pageKey}`;
|
|
29
|
+
}
|
|
30
|
+
/** Public page bodies use the same locale for routes and artifacts. */
|
|
31
|
+
function hasCoherentPageLocales(input) {
|
|
32
|
+
return input.appLocale === input.artifactLocale;
|
|
33
|
+
}
|
|
34
|
+
/** Stable identity and locale-owned public route for one reviewed site page. */
|
|
35
|
+
export const PublicPageRouteSchema = Schema.Struct(PublicPageRouteFields).pipe(Schema.check(Schema.makeFilter(hasCoherentPageLocales, {
|
|
36
|
+
message: "Expected public page route and artifact locales to match.",
|
|
37
|
+
})), Schema.check(Schema.makeFilter(hasCoherentPageRoute, {
|
|
38
|
+
message: "Expected the public page content key to match its page key.",
|
|
39
|
+
})));
|
|
40
|
+
/** Canonical signed read model for one reviewed public site page. */
|
|
41
|
+
export const PublicPageProjectionSchema = Schema.Struct({
|
|
42
|
+
...PublicPageRouteFields,
|
|
43
|
+
kind: Schema.Literal("public-page"),
|
|
44
|
+
metadata: PageMetadataSchema,
|
|
45
|
+
sitemap: Schema.Literal(true),
|
|
46
|
+
sourcePath: CorpusSourcePathSchema,
|
|
47
|
+
}).pipe(Schema.check(Schema.makeFilter(hasCoherentPageLocales, {
|
|
48
|
+
message: "Expected public page route and artifact locales to match.",
|
|
49
|
+
})), Schema.check(Schema.makeFilter(hasCoherentPageRoute, {
|
|
50
|
+
message: "Expected the public page content key to match its page key.",
|
|
51
|
+
})));
|
|
52
|
+
/** Exact predecessor Page bytes required for rollback and stored-state reads. */
|
|
53
|
+
export const HistoricalPublicPageProjectionSchema = Schema.Struct({
|
|
54
|
+
...PublicPageRouteFields,
|
|
55
|
+
kind: Schema.Literal("public-page"),
|
|
56
|
+
metadata: HistoricalPageMetadataSchema,
|
|
57
|
+
sitemap: Schema.Literal(true),
|
|
58
|
+
sourcePath: CorpusSourcePathSchema,
|
|
59
|
+
}).pipe(Schema.check(Schema.makeFilter(hasCoherentPageLocales, {
|
|
60
|
+
message: "Expected historical page route and artifact locales to match.",
|
|
61
|
+
})), Schema.check(Schema.makeFilter(hasCoherentPageRoute, {
|
|
62
|
+
message: "Expected the historical page content key to match its page key.",
|
|
63
|
+
})));
|
|
64
|
+
/** Current Page values plus exact predecessor bytes needed for safe rollout. */
|
|
65
|
+
export const ReadablePublicPageProjectionSchema = Schema.Union([
|
|
66
|
+
PublicPageProjectionSchema,
|
|
67
|
+
HistoricalPublicPageProjectionSchema,
|
|
68
|
+
]);
|
|
69
|
+
/** Combines one reviewed page route with its strict localized metadata. */
|
|
70
|
+
export function makePublicPageProjection(input) {
|
|
71
|
+
return PublicPageProjectionSchema.make({
|
|
72
|
+
...input.route,
|
|
73
|
+
kind: "public-page",
|
|
74
|
+
metadata: input.metadata,
|
|
75
|
+
sitemap: true,
|
|
76
|
+
sourcePath: input.sourcePath,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/** Serializes one public page projection with stable signed field order. */
|
|
80
|
+
export function canonicalizePublicPageProjection(projection) {
|
|
81
|
+
const metadata = "lastModified" in projection.metadata
|
|
82
|
+
? {
|
|
83
|
+
description: projection.metadata.description,
|
|
84
|
+
lastModified: projection.metadata.lastModified,
|
|
85
|
+
title: projection.metadata.title,
|
|
86
|
+
}
|
|
87
|
+
: {
|
|
88
|
+
...(projection.metadata.dateModified === undefined
|
|
89
|
+
? {}
|
|
90
|
+
: { dateModified: projection.metadata.dateModified }),
|
|
91
|
+
datePublished: projection.metadata.datePublished,
|
|
92
|
+
description: projection.metadata.description,
|
|
93
|
+
title: projection.metadata.title,
|
|
94
|
+
};
|
|
95
|
+
return JSON.stringify({
|
|
96
|
+
appLocale: projection.appLocale,
|
|
97
|
+
artifactLocale: projection.artifactLocale,
|
|
98
|
+
contentKey: projection.contentKey,
|
|
99
|
+
kind: projection.kind,
|
|
100
|
+
metadata,
|
|
101
|
+
pageKey: projection.pageKey,
|
|
102
|
+
publicPath: projection.publicPath,
|
|
103
|
+
sitemap: projection.sitemap,
|
|
104
|
+
sourcePath: projection.sourcePath,
|
|
105
|
+
});
|
|
106
|
+
}
|