@topik/core 0.1.0-alpha.3 → 0.1.0-alpha.5

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 (3) hide show
  1. package/dist/index.d.mts +419 -17
  2. package/dist/index.mjs +2900 -346
  3. package/package.json +9 -10
package/dist/index.mjs CHANGED
@@ -1,12 +1,22 @@
1
1
  import { z } from "zod";
2
- import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
3
- import { access, readFile, readdir } from "node:fs/promises";
2
+ import wikiV1Schema from "@topik/schema/wiki/v1.json" with { type: "json" };
3
+ import { dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
4
4
  import { parse } from "yaml";
5
+ import { constants } from "node:fs";
6
+ import { lstat, open, readdir, realpath } from "node:fs/promises";
5
7
  import { createHash } from "node:crypto";
6
- import Markdoc from "@markdoc/markdoc";
8
+ import { analyzeTopikContent, extractTopikAssetOccurrences, isTopikGeneratedAssetName, parseTopikGeneratedAssetName, rewriteTopikAssetOccurrences, sanitizeTopikContentDiagnostic, topikLinkDiagnosticMessage, validateTopikAssetReference, validateTopikContent } from "@topik/content-schema";
7
9
  import Ajv2020 from "ajv/dist/2020.js";
10
+ import rawAssetV1Schema from "@topik/schema/asset/v1.json" with { type: "json" };
11
+ import { execFile } from "node:child_process";
12
+ import { promisify } from "node:util";
8
13
  import addFormats from "ajv-formats";
9
- import { assetSchema, guideSchema, wikiPageSchema, wikiSchema } from "@topik/schema";
14
+ import courseV1Schema from "@topik/schema/course/v1.json" with { type: "json" };
15
+ import courseModuleV1Schema from "@topik/schema/course-module/v1.json" with { type: "json" };
16
+ import coursePageV1Schema from "@topik/schema/course-page/v1.json" with { type: "json" };
17
+ import guideV1Schema from "@topik/schema/guide/v1.json" with { type: "json" };
18
+ import personV1Schema from "@topik/schema/person/v1.json" with { type: "json" };
19
+ import wikiPageV1Schema from "@topik/schema/wiki-page/v1.json" with { type: "json" };
10
20
  import { EventEmitter } from "node:events";
11
21
  import { watch as watch$1 } from "chokidar";
12
22
  //#region src/config/collection.ts
@@ -20,27 +30,125 @@ function parseCollectionConfig(raw) {
20
30
  }
21
31
  //#endregion
22
32
  //#region src/config/wiki.ts
33
+ const wikiNavIconPattern = new RegExp(wikiV1Schema.$defs.groupNode.properties.icon.pattern);
34
+ const wikiExternalHrefPattern = new RegExp(wikiV1Schema.$defs.externalTabNode.properties.href.pattern);
35
+ const pathSegment = z.string().min(1).max(256).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Slugs must be lowercase DNS-style segments");
23
36
  const wikiPagePath = z.string().min(1).max(512).regex(/^(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/(?:[a-z0-9]+(?:-[a-z0-9]+)*))*$/, "Wiki page paths must use lowercase DNS-style segments separated by '/'");
24
- const navNodeSchema = z.union([
37
+ const title = z.string().min(1).max(256);
38
+ const icon = z.string().min(1).max(256).regex(wikiNavIconPattern, "Icons must be lowercase DNS-style names").optional();
39
+ const href = z.string().max(2048).refine((value) => {
40
+ if (!wikiExternalHrefPattern.test(value)) return false;
41
+ try {
42
+ const url = new URL(value);
43
+ return url.protocol === "https:" || url.protocol === "http:";
44
+ } catch {
45
+ return false;
46
+ }
47
+ }, { message: "href must be a valid http or https URL" });
48
+ const navNodeSchema = z.lazy(() => z.union([
25
49
  wikiPagePath,
26
50
  z.object({
27
- group: z.string().min(1).max(256),
28
- slug: z.string().min(1).max(256).optional(),
29
- icon: z.string().min(1).max(256).optional(),
30
- children: z.lazy(() => z.array(navNodeSchema)).default([])
31
- }),
51
+ type: z.literal("page"),
52
+ slug: wikiPagePath,
53
+ icon,
54
+ hidden: z.boolean().optional()
55
+ }).strict(),
32
56
  z.object({
33
- tab: z.string().min(1).max(256),
34
- slug: z.string().min(1).max(256).optional(),
35
- icon: z.string().min(1).max(256).optional(),
36
- children: z.lazy(() => z.array(navNodeSchema)).default([])
37
- }),
57
+ type: z.literal("group"),
58
+ title,
59
+ slug: pathSegment.optional(),
60
+ icon,
61
+ expanded: z.boolean().optional(),
62
+ hidden: z.boolean().optional(),
63
+ children: z.array(navNodeSchema).default([])
64
+ }).strict(),
38
65
  z.object({
39
- href: z.string().max(2048).refine((v) => v.startsWith("https://") || v.startsWith("http://"), { message: "href must be an http or https URL" }),
40
- title: z.string().min(1).max(256),
41
- icon: z.string().min(1).max(256).optional()
42
- })
43
- ]);
66
+ type: z.union([z.literal("tab"), z.literal("dropdown")]),
67
+ title,
68
+ slug: pathSegment.optional(),
69
+ icon,
70
+ hidden: z.boolean().optional(),
71
+ children: z.array(navNodeSchema).default([])
72
+ }).strict(),
73
+ z.object({
74
+ type: z.union([z.literal("tab"), z.literal("dropdown")]),
75
+ title,
76
+ href,
77
+ icon,
78
+ hidden: z.boolean().optional()
79
+ }).strict(),
80
+ z.object({
81
+ type: z.literal("link"),
82
+ title,
83
+ href,
84
+ icon,
85
+ hidden: z.boolean().optional()
86
+ }).strict()
87
+ ]));
88
+ function nodeKind(node) {
89
+ if (typeof node !== "string" && node.type === "tab") return "tab";
90
+ if (typeof node !== "string" && node.type === "dropdown") return "dropdown";
91
+ return "sidebar";
92
+ }
93
+ function validateHomogeneousLevel(nodes, allowed, path, context) {
94
+ const kinds = nodes.map(nodeKind);
95
+ nodes.forEach((_node, index) => {
96
+ const kind = kinds[index];
97
+ if (!allowed.has(kind)) {
98
+ const nodePath = [...path, index];
99
+ context.addIssue({
100
+ code: "custom",
101
+ path: nodePath,
102
+ message: `Navigation ${kind} nodes are not allowed at ${formatConfigPath(nodePath)}`
103
+ });
104
+ }
105
+ });
106
+ const expectedKind = kinds[0];
107
+ kinds.forEach((kind, index) => {
108
+ if (kind === expectedKind) return;
109
+ const nodePath = [...path, index];
110
+ context.addIssue({
111
+ code: "custom",
112
+ path: nodePath,
113
+ message: `Navigation surfaces cannot be mixed at the same level: expected ${expectedKind}, found ${kind} at ${formatConfigPath(nodePath)}`
114
+ });
115
+ });
116
+ nodes.forEach((node, index) => {
117
+ if (typeof node === "string" || !("children" in node)) return;
118
+ const childPath = [
119
+ ...path,
120
+ index,
121
+ "children"
122
+ ];
123
+ if (node.type === "tab") validateHomogeneousLevel(node.children, /* @__PURE__ */ new Set(["dropdown", "sidebar"]), childPath, context);
124
+ else validateHomogeneousLevel(node.children, /* @__PURE__ */ new Set(["sidebar"]), childPath, context);
125
+ });
126
+ }
127
+ function validateUniquePageRoutes(nodes, path, context, prefix = "", routes = /* @__PURE__ */ new Map()) {
128
+ nodes.forEach((node, index) => {
129
+ const nodePath = [...path, index];
130
+ if (typeof node === "string" || node.type === "page") {
131
+ const sourcePath = joinNavigationPath(prefix, typeof node === "string" ? node : node.slug);
132
+ const route = sourcePath === "index" ? "" : sourcePath.replace(/\/index$/, "");
133
+ const previousPath = routes.get(route);
134
+ if (previousPath) context.addIssue({
135
+ code: "custom",
136
+ path: nodePath,
137
+ message: `Navigation contains duplicate page route /${route}: first defined at ${formatConfigPath(previousPath)}, duplicated at ${formatConfigPath(nodePath)}`
138
+ });
139
+ else routes.set(route, nodePath);
140
+ return;
141
+ }
142
+ if ("children" in node) validateUniquePageRoutes(node.children, [...nodePath, "children"], context, joinNavigationPath(prefix, node.slug), routes);
143
+ });
144
+ }
145
+ function formatConfigPath(path) {
146
+ return path.reduce((formatted, segment) => typeof segment === "number" ? `${formatted}[${segment}]` : formatted ? `${formatted}.${segment}` : segment, "");
147
+ }
148
+ function joinNavigationPath(prefix, path) {
149
+ if (!path) return prefix;
150
+ return prefix ? `${prefix}/${path}` : path;
151
+ }
44
152
  const hexColor = z.string().regex(/^#[0-9a-fA-F]{6}$/, "Must be a hex color (e.g. #ff73a8)");
45
153
  const themeSchema = z.object({
46
154
  colors: z.object({
@@ -54,235 +162,2323 @@ const themeSchema = z.object({
54
162
  "system"
55
163
  ]).optional() }).optional()
56
164
  });
165
+ const nameRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
166
+ const MAX_WIKI_ID_FOR_PAGE_NAMES = 46;
57
167
  const wikiConfigSchema = z.object({
58
- id: z.string().min(1).max(63).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
168
+ id: z.string().min(1).max(MAX_WIKI_ID_FOR_PAGE_NAMES, `Wiki id must be ${MAX_WIKI_ID_FOR_PAGE_NAMES} characters or fewer`).regex(nameRegex),
59
169
  title: z.string().min(1).max(256),
170
+ description: z.union([z.string().max(1024), z.null()]).optional(),
60
171
  navigation: z.array(navNodeSchema).optional(),
61
172
  theme: themeSchema.optional()
173
+ }).superRefine((config, context) => {
174
+ if (config.navigation) {
175
+ validateHomogeneousLevel(config.navigation, /* @__PURE__ */ new Set([
176
+ "tab",
177
+ "dropdown",
178
+ "sidebar"
179
+ ]), ["navigation"], context);
180
+ validateUniquePageRoutes(config.navigation, ["navigation"], context);
181
+ }
62
182
  });
63
183
  function parseWikiConfig(raw) {
64
184
  return wikiConfigSchema.parse(raw);
65
185
  }
66
186
  //#endregion
187
+ //#region src/assets/path-unicode-v17.ts
188
+ /**
189
+ * Unicode 17 path-forbidden code points, encoded as inclusive hexadecimal ranges.
190
+ *
191
+ * Generated from UnicodeData.txt, PropList.txt, and DerivedCoreProperties.txt for
192
+ * Unicode 17.0.0. The table contains general categories Cc, Cf, Cs, Co, and Cn,
193
+ * plus White_Space (except U+0020), Default_Ignorable_Code_Point, Bidi_Control,
194
+ * and Noncharacter_Code_Point.
195
+ *
196
+ * Source SHA-256:
197
+ * - UnicodeData.txt: 2e1efc1dcb59c575eedf5ccae60f95229f706ee6d031835247d843c11d96470c
198
+ * - PropList.txt: 130dcddcaadaf071008bdfce1e7743e04fdfbc910886f017d9f9ac931d8c64dd
199
+ * - DerivedCoreProperties.txt: 24c7fed1195c482faaefd5c1e7eb821c5ee1fb6de07ecdbaa64b56a99da22c08
200
+ */
201
+ const TOPIK_PATH_FORBIDDEN_V17_DATA = [
202
+ "0..1f;7f..a0;ad;34f;378..379;380..383;38b;38d;3a2;530;557..558;58b..58c;590;5c8..5cf;5eb..5ee;5f5..605;61c;6dd;70e..70f;74b..74c;7b2..7bf;7fb..7fc;82e..82f;83f;85c..85d;85f;86b..86f;890..896;8e2;984;98d..98e;991..992;9a9;9b1;9b3..9b5;9ba..9bb;9c5..9c6;9c9..9ca;9cf..9d6;9d8..9db;9de;9e4..9e5;9ff..a00;a04;a0b..a0e;a11..a12;a29;a31;a34;a37;a3a..a3b;a3d;a43..a46;a49..a4a;a4e..a50;a52..a58;a5d;a5f..a65;a77..a80;a84;a8e;a92;aa9;ab1;ab4;aba..abb;ac6;aca;ace..acf;ad1..adf;ae4..ae5;af2..af8;b00;b04;b0d..b0e;b11..b12;b29;b31;b34;b3a..b3b;b45..b46;b49..b4a;b4e..b54;b58..b5b;b5e;b64..b65;b78..b81;b84;b8b..b8d;b91;b96..b98;b9b;b9d;ba0..ba2;ba5..ba7;bab..bad;bba..bbd;bc3..bc5;bc9;bce..bcf;bd1..bd6;bd8..be5;bfb..bff;c0d;c11;c29;c3a..c3b;c45;c49;c4e..c54;c57;c5b;c5e..c5f;c64..c65;c70..c76;c8d;c91;ca9;cb4;cba..cbb;cc5;cc9;cce..cd4;cd7..cdb;cdf;ce4..ce5;cf0;cf4..cff;d0d;d11;d45;d49;d50..d53;d64..d65;d80;d",
203
+ "84;d97..d99;db2;dbc;dbe..dbf;dc7..dc9;dcb..dce;dd5;dd7;de0..de5;df0..df1;df5..e00;e3b..e3e;e5c..e80;e83;e85;e8b;ea4;ea6;ebe..ebf;ec5;ec7;ecf;eda..edb;ee0..eff;f48;f6d..f70;f98;fbd;fcd;fdb..fff;10c6;10c8..10cc;10ce..10cf;115f..1160;1249;124e..124f;1257;1259;125e..125f;1289;128e..128f;12b1;12b6..12b7;12bf;12c1;12c6..12c7;12d7;1311;1316..1317;135b..135c;137d..137f;139a..139f;13f6..13f7;13fe..13ff;1680;169d..169f;16f9..16ff;1716..171e;1737..173f;1754..175f;176d;1771;1774..177f;17b4..17b5;17de..17df;17ea..17ef;17fa..17ff;180b..180f;181a..181f;1879..187f;18ab..18af;18f6..18ff;191f;192c..192f;193c..193f;1941..1943;196e..196f;1975..197f;19ac..19af;19ca..19cf;19db..19dd;1a1c..1a1d;1a5f;1a7d..1a7e;1a8a..1a8f;1a9a..1a9f;1aae..1aaf;1ade..1adf;1aec..1aff;1b4d;1bf4..1bfb;1c38..1c3a;1c4a..1c4c;1c8b..1c8f;1cbb..1cbc;1cc8..1ccf;1cfb..1cff;1f16..1f17;1f1e..1f1f;1f46..1f47;1f4e..1f4f;1f58;1f5a;1f5c;1f5e;1f",
204
+ "7e..1f7f;1fb5;1fc5;1fd4..1fd5;1fdc;1ff0..1ff1;1ff5;1fff..200f;2028..202f;205f..206f;2072..2073;208f;209d..209f;20c2..20cf;20f1..20ff;218c..218f;242a..243f;244b..245f;2b74..2b75;2cf4..2cf8;2d26;2d28..2d2c;2d2e..2d2f;2d68..2d6e;2d71..2d7e;2d97..2d9f;2da7;2daf;2db7;2dbf;2dc7;2dcf;2dd7;2ddf;2e5e..2e7f;2e9a;2ef4..2eff;2fd6..2fef;3000;3040;3097..3098;3100..3104;3130;3164;318f;31e6..31ee;321f;a48d..a48f;a4c7..a4cf;a62c..a63f;a6f8..a6ff;a7dd..a7f0;a82d..a82f;a83a..a83f;a878..a87f;a8c6..a8cd;a8da..a8df;a954..a95e;a97d..a97f;a9ce;a9da..a9dd;a9ff;aa37..aa3f;aa4e..aa4f;aa5a..aa5b;aac3..aada;aaf7..ab00;ab07..ab08;ab0f..ab10;ab17..ab1f;ab27;ab2f;ab6c..ab6f;abee..abef;abfa..abff;d7a4..d7af;d7c7..d7ca;d7fc..f8ff;fa6e..fa6f;fada..faff;fb07..fb12;fb18..fb1c;fb37;fb3d;fb3f;fb42;fb45;fdd0..fdef;fe00..fe0f;fe1a..fe1f;fe53;fe67;fe6c..fe6f;fe75;fefd..ff00;ffa0;ffbf..ffc1;ffc8..ffc9;ffd0..ffd1;ffd8..ffd9;ffdd..",
205
+ "ffdf;ffe7;ffef..fffb;fffe..ffff;1000c;10027;1003b;1003e;1004e..1004f;1005e..1007f;100fb..100ff;10103..10106;10134..10136;1018f;1019d..1019f;101a1..101cf;101fe..1027f;1029d..1029f;102d1..102df;102fc..102ff;10324..1032c;1034b..1034f;1037b..1037f;1039e;103c4..103c7;103d6..103ff;1049e..1049f;104aa..104af;104d4..104d7;104fc..104ff;10528..1052f;10564..1056e;1057b;1058b;10593;10596;105a2;105b2;105ba;105bd..105bf;105f4..105ff;10737..1073f;10756..1075f;10768..1077f;10786;107b1;107bb..107ff;10806..10807;10809;10836;10839..1083b;1083d..1083e;10856;1089f..108a6;108b0..108df;108f3;108f6..108fa;1091c..1091e;1093a..1093e;1095a..1097f;109b8..109bb;109d0..109d1;10a04;10a07..10a0b;10a14;10a18;10a36..10a37;10a3b..10a3e;10a49..10a4f;10a59..10a5f;10aa0..10abf;10ae7..10aea;10af7..10aff;10b36..10b38;10b56..10b57;10b73..10b77;10b92..10b98;10b9d..10ba8;10bb0..10bff;10c49..10c7f;10cb3..10cbf;10cf3..10cf9;10d28..1",
206
+ "0d2f;10d3a..10d3f;10d66..10d68;10d86..10d8d;10d90..10e5f;10e7f;10eaa;10eae..10eaf;10eb2..10ec1;10ec8..10ecf;10ed9..10ef9;10f28..10f2f;10f5a..10f6f;10f8a..10faf;10fcc..10fdf;10ff7..10fff;1104e..11051;11076..1107e;110bd;110c3..110cf;110e9..110ef;110fa..110ff;11135;11148..1114f;11177..1117f;111e0;111f5..111ff;11212;11242..1127f;11287;11289;1128e;1129e;112aa..112af;112eb..112ef;112fa..112ff;11304;1130d..1130e;11311..11312;11329;11331;11334;1133a;11345..11346;11349..1134a;1134e..1134f;11351..11356;11358..1135c;11364..11365;1136d..1136f;11375..1137f;1138a;1138c..1138d;1138f;113b6;113c1;113c3..113c4;113c6;113cb;113d6;113d9..113e0;113e3..113ff;1145c;11462..1147f;114c8..114cf;114da..1157f;115b6..115b7;115de..115ff;11645..1164f;1165a..1165f;1166d..1167f;116ba..116bf;116ca..116cf;116e4..116ff;1171b..1171c;1172c..1172f;11747..117ff;1183c..1189f;118f3..118fe;11907..11908;1190a..1190b;11914;11917;1193",
207
+ "6;11939..1193a;11947..1194f;1195a..1199f;119a8..119a9;119d8..119d9;119e5..119ff;11a48..11a4f;11aa3..11aaf;11af9..11aff;11b0a..11b5f;11b68..11bbf;11be2..11bef;11bfa..11bff;11c09;11c37;11c46..11c4f;11c6d..11c6f;11c90..11c91;11ca8;11cb7..11cff;11d07;11d0a;11d37..11d39;11d3b;11d3e;11d48..11d4f;11d5a..11d5f;11d66;11d69;11d8f;11d92;11d99..11d9f;11daa..11daf;11ddc..11ddf;11dea..11edf;11ef9..11eff;11f11;11f3b..11f3d;11f5b..11faf;11fb1..11fbf;11ff2..11ffe;1239a..123ff;1246f;12475..1247f;12544..12f8f;12ff3..12fff;13430..1343f;13456..1345f;143fb..143ff;14647..160ff;1613a..167ff;16a39..16a3f;16a5f;16a6a..16a6d;16abf;16aca..16acf;16aee..16aef;16af6..16aff;16b46..16b4f;16b5a;16b62;16b78..16b7c;16b90..16d3f;16d7a..16e3f;16e9b..16e9f;16eb9..16eba;16ed4..16eff;16f4b..16f4e;16f88..16f8e;16fa0..16fdf;16fe5..16fef;16ff7..16fff;18cd6..18cfe;18d1f..18d7f;18df3..1afef;1aff4;1affc;1afff;1b123..1b131;1b133..1b14",
208
+ "f;1b153..1b154;1b156..1b163;1b168..1b16f;1b2fc..1bbff;1bc6b..1bc6f;1bc7d..1bc7f;1bc89..1bc8f;1bc9a..1bc9b;1bca0..1cbff;1ccfd..1ccff;1ceb4..1ceb9;1ced1..1cedf;1cef1..1ceff;1cf2e..1cf2f;1cf47..1cf4f;1cfc4..1cfff;1d0f6..1d0ff;1d127..1d128;1d173..1d17a;1d1eb..1d1ff;1d246..1d2bf;1d2d4..1d2df;1d2f4..1d2ff;1d357..1d35f;1d379..1d3ff;1d455;1d49d;1d4a0..1d4a1;1d4a3..1d4a4;1d4a7..1d4a8;1d4ad;1d4ba;1d4bc;1d4c4;1d506;1d50b..1d50c;1d515;1d51d;1d53a;1d53f;1d545;1d547..1d549;1d551;1d6a6..1d6a7;1d7cc..1d7cd;1da8c..1da9a;1daa0;1dab0..1deff;1df1f..1df24;1df2b..1dfff;1e007;1e019..1e01a;1e022;1e025;1e02b..1e02f;1e06e..1e08e;1e090..1e0ff;1e12d..1e12f;1e13e..1e13f;1e14a..1e14d;1e150..1e28f;1e2af..1e2bf;1e2fa..1e2fe;1e300..1e4cf;1e4fa..1e5cf;1e5fb..1e5fe;1e600..1e6bf;1e6df;1e6f6..1e6fd;1e700..1e7df;1e7e7;1e7ec;1e7ef;1e7ff;1e8c5..1e8c6;1e8d7..1e8ff;1e94c..1e94f;1e95a..1e95d;1e960..1ec70;1ecb5..1ed00;1ed3e..1edff",
209
+ ";1ee04;1ee20;1ee23;1ee25..1ee26;1ee28;1ee33;1ee38;1ee3a;1ee3c..1ee41;1ee43..1ee46;1ee48;1ee4a;1ee4c;1ee50;1ee53;1ee55..1ee56;1ee58;1ee5a;1ee5c;1ee5e;1ee60;1ee63;1ee65..1ee66;1ee6b;1ee73;1ee78;1ee7d;1ee7f;1ee8a;1ee9c..1eea0;1eea4;1eeaa;1eebc..1eeef;1eef2..1efff;1f02c..1f02f;1f094..1f09f;1f0af..1f0b0;1f0c0;1f0d0;1f0f6..1f0ff;1f1ae..1f1e5;1f203..1f20f;1f23c..1f23f;1f249..1f24f;1f252..1f25f;1f266..1f2ff;1f6d9..1f6db;1f6ed..1f6ef;1f6fd..1f6ff;1f7da..1f7df;1f7ec..1f7ef;1f7f1..1f7ff;1f80c..1f80f;1f848..1f84f;1f85a..1f85f;1f888..1f88f;1f8ae..1f8af;1f8bc..1f8bf;1f8c2..1f8cf;1f8d9..1f8ff;1fa58..1fa5f;1fa6e..1fa6f;1fa7d..1fa7f;1fa8b..1fa8d;1fac7;1fac9..1facc;1fadd..1fade;1faeb..1faee;1faf9..1faff;1fb93;1fbfb..1ffff;2a6e0..2a6ff;2b81e..2b81f;2ceae..2ceaf;2ebe1..2ebef;2ee5e..2f7ff;2fa1e..2ffff;3134b..3134f;3347a..10ffff"
210
+ ].join("");
211
+ /**
212
+ * Marks first assigned in Unicode 17. Node 22.12's Unicode 16 normalizer does
213
+ * not know their canonical combining classes, so topik-path-v1 rejects them on
214
+ * every runtime. Unicode 17 added no canonical decompositions; these are the
215
+ * complete new normalization-order boundary for NFC acceptance.
216
+ */
217
+ const TOPIK_PATH_NORMALIZATION_SENSITIVE_V17_RANGES = [
218
+ [6863, 6877],
219
+ [6880, 6891],
220
+ [69370, 69371],
221
+ [72544, 72551],
222
+ [124643, 124643],
223
+ [124646, 124646],
224
+ [124654, 124655],
225
+ [124661, 124661]
226
+ ];
227
+ const TOPIK_PATH_FORBIDDEN_V17_RANGES = TOPIK_PATH_FORBIDDEN_V17_DATA.split(";").map((record) => {
228
+ const [start, end = start] = record.split("..");
229
+ return [Number.parseInt(start, 16), Number.parseInt(end, 16)];
230
+ });
231
+ function isTopikPathCodePointForbiddenV17(codePoint) {
232
+ return isCodePointInRanges(codePoint, TOPIK_PATH_FORBIDDEN_V17_RANGES);
233
+ }
234
+ function isTopikPathNormalizationSensitiveV17(codePoint) {
235
+ return isCodePointInRanges(codePoint, TOPIK_PATH_NORMALIZATION_SENSITIVE_V17_RANGES);
236
+ }
237
+ function isCodePointInRanges(codePoint, ranges) {
238
+ let low = 0;
239
+ let high = ranges.length - 1;
240
+ while (low <= high) {
241
+ const middle = low + high >>> 1;
242
+ const [start, end] = ranges[middle];
243
+ if (codePoint < start) high = middle - 1;
244
+ else if (codePoint > end) low = middle + 1;
245
+ else return true;
246
+ }
247
+ return false;
248
+ }
249
+ //#endregion
250
+ //#region src/compile/public-errors.ts
251
+ const PUBLIC_COMPILE_ERROR_MESSAGES = {
252
+ "config-access-failed": "Configuration file could not be accessed.",
253
+ "config-invalid": "Configuration is invalid.",
254
+ "config-not-found": "Required configuration file was not found.",
255
+ "config-parse-failed": "Configuration file could not be parsed.",
256
+ "config-read-failed": "Configuration file could not be read safely.",
257
+ "file-not-regular": "Compilation input must be a regular file.",
258
+ "file-outside-compilation-root": "Compilation input resolves outside the source root.",
259
+ "frontmatter-invalid": "Document frontmatter is invalid.",
260
+ "reference-list-invalid": "Document resource references are invalid.",
261
+ "wiki-page-not-found": "A configured Wiki page file was not found."
262
+ };
263
+ function publicCompileErrorMessage(id) {
264
+ return typeof id === "string" && Object.hasOwn(PUBLIC_COMPILE_ERROR_MESSAGES, id) ? PUBLIC_COMPILE_ERROR_MESSAGES[id] : void 0;
265
+ }
266
+ /** Public compiler failure with fixed wording, stable ID, and optional safe relative location. */
267
+ var PublicCompileError = class extends Error {
268
+ id;
269
+ location;
270
+ constructor(id, location) {
271
+ super(publicCompileErrorMessage(id) ?? "Compilation failed.");
272
+ this.name = "PublicCompileError";
273
+ this.id = id;
274
+ const safeLocation = sanitizeCompileLocation(location);
275
+ if (safeLocation !== void 0) this.location = safeLocation;
276
+ }
277
+ };
278
+ function sanitizeCompileLocation(value) {
279
+ if (value === void 0 || value.length === 0 || value.length > 768) return void 0;
280
+ if (isAbsolute(value) || win32.isAbsolute(value)) return void 0;
281
+ const normalized = value.replaceAll("\\", "/");
282
+ if (normalized.split("/").some((component) => component.length === 0 || component === "." || component === "..") || containsForbiddenCodePoint(normalized) || /[?#:@%]/u.test(normalized)) return;
283
+ return posix.normalize(normalized);
284
+ }
285
+ function containsForbiddenCodePoint(value) {
286
+ for (const character of value) if (isTopikPathCodePointForbiddenV17(character.codePointAt(0) ?? 0)) return true;
287
+ return false;
288
+ }
289
+ //#endregion
290
+ //#region src/compile/files.ts
291
+ const SAFE_READ_FLAGS = constants.O_RDONLY | (typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0) | (typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0);
292
+ var FileOutsideCompilationRootError = class extends PublicCompileError {
293
+ constructor(filePath, rootDir) {
294
+ super("file-outside-compilation-root", safeRelativePath(filePath, rootDir));
295
+ this.name = "FileOutsideCompilationRootError";
296
+ }
297
+ };
298
+ var FileNotRegularError = class extends PublicCompileError {
299
+ constructor(filePath, rootDir) {
300
+ super("file-not-regular", safeRelativePath(filePath, rootDir));
301
+ this.name = "FileNotRegularError";
302
+ }
303
+ };
304
+ async function readRegularFileWithinRoot(filePath, rootDir, encoding) {
305
+ const handle = await openRegularFileWithinRoot(filePath, rootDir);
306
+ try {
307
+ return encoding == null ? await handle.readFile() : await handle.readFile({ encoding });
308
+ } finally {
309
+ await handle.close();
310
+ }
311
+ }
312
+ async function assertRegularFileWithinRoot(filePath, rootDir) {
313
+ await (await openRegularFileWithinRoot(filePath, rootDir)).close();
314
+ }
315
+ async function openRegularFileWithinRoot(filePath, rootDir) {
316
+ const authoredRoot = resolve(rootDir);
317
+ const authoredFile = resolve(filePath);
318
+ const [canonicalRoot, canonicalFile] = await Promise.all([realpath(rootDir), realpath(filePath)]);
319
+ if (!isWithinRoot(canonicalRoot, canonicalFile)) throw new FileOutsideCompilationRootError(filePath, rootDir);
320
+ if (authoredRoot !== canonicalRoot || authoredFile !== canonicalFile) throw new FileNotRegularError(filePath, rootDir);
321
+ if (!(await lstat(canonicalFile)).isFile()) throw new FileNotRegularError(filePath, rootDir);
322
+ const handle = await open(canonicalFile, SAFE_READ_FLAGS);
323
+ try {
324
+ if (!(await handle.stat()).isFile()) throw new FileNotRegularError(filePath, rootDir);
325
+ return handle;
326
+ } catch (error) {
327
+ await handle.close();
328
+ throw error;
329
+ }
330
+ }
331
+ function isWithinRoot(rootDir, filePath) {
332
+ const rel = relative(rootDir, filePath);
333
+ return rel === "" || rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel);
334
+ }
335
+ function safeRelativePath(filePath, rootDir) {
336
+ const location = relative(resolve(rootDir), resolve(filePath)).split(sep).join("/");
337
+ return location.length > 0 && !location.startsWith("../") && location !== ".." ? location : void 0;
338
+ }
339
+ //#endregion
67
340
  //#region src/compile/config.ts
68
- async function readOptionalConfigFile(dir, candidates) {
341
+ async function readOptionalConfigFileWithPath(dir, candidates) {
69
342
  for (const name of candidates) {
70
343
  const filePath = join(dir, name);
71
344
  let raw;
72
345
  try {
73
- raw = await readFile(filePath, "utf-8");
346
+ raw = await readRegularFileWithinRoot(filePath, dir, "utf-8");
74
347
  } catch (error) {
75
348
  if (error.code === "ENOENT") continue;
76
- throw new Error(`Failed to read config file ${filePath}`, { cause: error });
349
+ throw new PublicCompileError("config-read-failed", name);
77
350
  }
78
351
  try {
79
- return name.endsWith(".json") ? JSON.parse(raw) : parse(raw);
80
- } catch (error) {
81
- throw new Error(`Failed to parse config file ${filePath}`, { cause: error });
352
+ return {
353
+ path: name,
354
+ value: name.endsWith(".json") ? JSON.parse(raw) : parse(raw)
355
+ };
356
+ } catch {
357
+ throw new PublicCompileError("config-parse-failed", name);
82
358
  }
83
359
  }
84
360
  }
85
361
  async function findConfigFile(dir, candidates) {
86
362
  for (const name of candidates) try {
87
- await access(join(dir, name));
363
+ await assertRegularFileWithinRoot(join(dir, name), dir);
88
364
  return name;
89
365
  } catch (error) {
90
366
  if (error.code === "ENOENT") continue;
91
- throw new Error(`Failed to access config file ${join(dir, name)}`, { cause: error });
367
+ throw new PublicCompileError("config-access-failed", name);
92
368
  }
93
369
  return null;
94
370
  }
95
371
  //#endregion
372
+ //#region src/assets/constants.ts
373
+ const ASSET_API_VERSION = "v1";
374
+ const ASSET_TYPE = "Asset";
375
+ const ASSET_SCHEMA_ID = "https://topik.dev/schemas/asset/v1.json";
376
+ const TOPIK_JSON_VERSION = "topik-json-v1";
377
+ const TOPIK_PATH_VERSION = "topik-path-v1";
378
+ const TOPIK_ASSET_REFERENCE_VERSION = "topik-asset-reference-v1";
379
+ const TOPIK_MATERIALIZATION_VERSION = "topik-materialization-v1";
380
+ const TOPIK_ASSET_NAME_VERSION = "topik-asset-name-v1";
381
+ const TOPIK_BLOB_OUTPUT_PREFIX = "blobs";
382
+ const TOPIK_PATH_V1_DESCRIPTOR = {
383
+ collisionNormalization: "toNFKC_Casefold",
384
+ id: TOPIK_PATH_VERSION,
385
+ localReferenceEncoding: "rfc3986-utf8-upper-v1",
386
+ maxComponentUtf8Bytes: 255,
387
+ maxComponents: 64,
388
+ maxRepositoryPathUtf8Bytes: 768,
389
+ normalizationSensitiveExclusion: "unicode-17-new-marks",
390
+ storageNormalization: "NFC",
391
+ unicodeVersion: "17.0.0"
392
+ };
393
+ const TOPIK_ASSET_LIMITS = {
394
+ maxDescriptorBytes: 1048576,
395
+ maxAssetBytes: 268435456,
396
+ maxAssets: 1e4,
397
+ maxJsonDepth: 8,
398
+ maxComponentUtf8Bytes: 255,
399
+ maxComponents: 64,
400
+ maxRepositoryPathUtf8Bytes: 768
401
+ };
402
+ //#endregion
403
+ //#region src/assets/diagnostics.ts
404
+ const TOPIK_ASSET_DIAGNOSTIC_IDS = [
405
+ "TOPIK_ASSET_UNSUPPORTED_VERSION",
406
+ "TOPIK_ASSET_DUPLICATE_MEMBER",
407
+ "TOPIK_ASSET_SCHEMA_INVALID",
408
+ "TOPIK_ASSET_NON_CANONICAL",
409
+ "TOPIK_ASSET_NAME_INVALID",
410
+ "TOPIK_ASSET_NAME_COLLISION",
411
+ "TOPIK_ASSET_SOURCE_NAMESPACE_REQUIRED",
412
+ "TOPIK_ASSET_SOURCE_NAMESPACE_INVALID",
413
+ "TOPIK_ASSET_PATH_INVALID",
414
+ "TOPIK_ASSET_PATH_COLLISION",
415
+ "TOPIK_ASSET_REFERENCE_AMBIGUOUS",
416
+ "TOPIK_ASSET_REFERENCE_MALFORMED",
417
+ "TOPIK_ASSET_REFERENCE_MISSING",
418
+ "TOPIK_ASSET_FILE_MISSING",
419
+ "TOPIK_ASSET_DIGEST_MISMATCH",
420
+ "TOPIK_ASSET_SIZE_MISMATCH",
421
+ "TOPIK_ASSET_MEDIA_TYPE_MISMATCH",
422
+ "TOPIK_ASSET_FILE_TYPE_UNSUPPORTED",
423
+ "TOPIK_ASSET_ACTIVE_CONTENT_UNSUPPORTED",
424
+ "TOPIK_ASSET_REFERENCE_ACCESSIBILITY_INVALID",
425
+ "TOPIK_EXTERNAL_REFERENCE_UNSAFE",
426
+ "TOPIK_ASSET_INVENTORY_INCOMPLETE",
427
+ "TOPIK_ASSET_VERSION_INCOMPARABLE"
428
+ ];
429
+ const TOPIK_ASSET_DEFAULT_CORRELATION_ID = "cor_00000000000000000000000000";
430
+ const TOPIK_ASSET_CORRELATION_ID_PATTERN = /^cor_[0-7][0-9a-hjkmnp-tv-z]{25}$/u;
431
+ function topikAssetDiagnostic(id, message, options = {}) {
432
+ const location = options.location ?? {};
433
+ return {
434
+ id,
435
+ correlationId: options.correlationId ?? "cor_00000000000000000000000000",
436
+ severity: "error",
437
+ consequence: options.consequence ?? "block-resource",
438
+ descriptorVersion: sanitizeDescriptorVersion(options.descriptorVersion ?? "Asset/v1"),
439
+ location: {
440
+ ...location.jsonPointer === void 0 ? {} : { jsonPointer: sanitizeJsonPointer(location.jsonPointer) },
441
+ ...location.contentPosition === void 0 ? {} : { contentPosition: sanitizeAsciiField(location.contentPosition, "[redacted]") },
442
+ ...location.path === void 0 ? {} : { path: sanitizePath(location.path) },
443
+ ...location.key === void 0 ? {} : { key: sanitizeKey(location.key) },
444
+ ...location.commit === void 0 ? {} : { commit: sanitizeCommit(location.commit) }
445
+ },
446
+ recovery: options.recovery ?? "repair-source",
447
+ ...options.reason === void 0 ? {} : { reason: options.reason },
448
+ message: sanitizeMessage(message)
449
+ };
450
+ }
451
+ function correlateTopikAssetResult(result, correlationId) {
452
+ if (!TOPIK_ASSET_CORRELATION_ID_PATTERN.test(correlationId)) throw new RangeError("Correlation ID must be opaque 128-bit lowercase Crockford form");
453
+ if (result.ok) return result;
454
+ return {
455
+ ...result,
456
+ diagnostics: result.diagnostics.map((diagnostic) => ({
457
+ ...diagnostic,
458
+ correlationId
459
+ }))
460
+ };
461
+ }
462
+ const SAFE_ASCII_FIELD = /^[\x20-\x7e]{1,1024}$/u;
463
+ function sanitizeMessage(value) {
464
+ return SAFE_ASCII_FIELD.test(value) ? value : "Diagnostic detail was redacted";
465
+ }
466
+ function sanitizeAsciiField(value, replacement) {
467
+ return SAFE_ASCII_FIELD.test(value) ? value : replacement;
468
+ }
469
+ function sanitizeDescriptorVersion(value) {
470
+ return /^[A-Za-z0-9][A-Za-z0-9./_-]{0,127}$/u.test(value) ? value : "unknown-descriptor";
471
+ }
472
+ function sanitizeKey(value) {
473
+ return /^(?:[a-z0-9]+(?:-[a-z0-9]+)*|auto-v1-[a-z2-7]{51}[aq])$/u.test(value) ? value : "[redacted]";
474
+ }
475
+ function sanitizeCommit(value) {
476
+ return /^[0-9a-f]{7,64}$/u.test(value) ? value : "[redacted]";
477
+ }
478
+ function sanitizeJsonPointer(value) {
479
+ if (!value.startsWith("/")) return "/[redacted]";
480
+ return value.slice(1).split("/").every(isSafeJsonPointerSegment) ? value : "/[redacted]";
481
+ }
482
+ function isSafeJsonPointerSegment(value) {
483
+ return value.length === 0 || /^(?:[0-9]+|[a-z0-9]+(?:-[a-z0-9]+)*|auto-v1-[a-z2-7]{51}[aq])$/u.test(value) || [
484
+ "algorithm",
485
+ "apiVersion",
486
+ "attribution",
487
+ "creator",
488
+ "integrity",
489
+ "license",
490
+ "mediaType",
491
+ "name",
492
+ "spec",
493
+ "uri",
494
+ "size",
495
+ "sourceUrl",
496
+ "spdxExpression",
497
+ "text",
498
+ "title",
499
+ "type",
500
+ "url",
501
+ "value"
502
+ ].includes(value);
503
+ }
504
+ function sanitizePath(value) {
505
+ const components = value.split("/");
506
+ if (value.length === 0 || value.length > 1024 || containsUnsafeDiagnosticUnicode(value) || value.includes("?") || value.includes("#") || value.includes(":") || value.includes("%") || value.includes("\\") || value.startsWith("/") || value.startsWith("~") || /^[a-z][a-z0-9+.-]*:/iu.test(value) || value.startsWith("//") || components.some((component) => component === "." || component === "..") || /[/∕⁄⧸\⧵:꞉]/u.test(value)) return "[redacted]";
507
+ return value;
508
+ }
509
+ function containsUnsafeDiagnosticUnicode(value) {
510
+ for (const character of value) {
511
+ const codePoint = character.codePointAt(0) ?? 0;
512
+ if (isTopikPathCodePointForbiddenV17(codePoint) || isTopikPathNormalizationSensitiveV17(codePoint)) return true;
513
+ }
514
+ return false;
515
+ }
516
+ //#endregion
517
+ //#region src/assets/json.ts
518
+ var TopikJsonSyntaxError = class extends SyntaxError {
519
+ offset;
520
+ duplicatePointer;
521
+ constructor(message, offset, duplicatePointer) {
522
+ super(message);
523
+ this.offset = offset;
524
+ this.duplicatePointer = duplicatePointer;
525
+ this.name = "TopikJsonSyntaxError";
526
+ }
527
+ };
528
+ function parseStrictTopikJson(source, maxDepth = 8) {
529
+ let offset = 0;
530
+ function whitespace() {
531
+ while (offset < source.length && isJsonWhitespace(source.charCodeAt(offset))) offset++;
532
+ }
533
+ function value(depth, pointer) {
534
+ if (depth > maxDepth) throw new TopikJsonSyntaxError("JSON exceeds maximum depth", offset);
535
+ whitespace();
536
+ const character = source[offset];
537
+ if (character === "{") return object(depth, pointer);
538
+ if (character === "[") return array(depth, pointer);
539
+ if (character === "\"") return string();
540
+ if (source.startsWith("true", offset)) {
541
+ offset += 4;
542
+ return true;
543
+ }
544
+ if (source.startsWith("false", offset)) {
545
+ offset += 5;
546
+ return false;
547
+ }
548
+ if (source.startsWith("null", offset)) {
549
+ offset += 4;
550
+ return null;
551
+ }
552
+ if (character === "-" || character >= "0" && character <= "9") return number();
553
+ throw new TopikJsonSyntaxError("Expected a JSON value", offset);
554
+ }
555
+ function object(depth, pointer) {
556
+ offset++;
557
+ whitespace();
558
+ const result = Object.create(null);
559
+ const keys = /* @__PURE__ */ new Set();
560
+ if (source[offset] === "}") {
561
+ offset++;
562
+ return result;
563
+ }
564
+ while (offset < source.length) {
565
+ whitespace();
566
+ if (source[offset] !== "\"") throw new TopikJsonSyntaxError("Expected object member", offset);
567
+ const key = string();
568
+ const memberPointer = `${pointer}/${escapePointer(key)}`;
569
+ if (keys.has(key)) throw new TopikJsonSyntaxError("Duplicate JSON object member", offset, memberPointer);
570
+ keys.add(key);
571
+ whitespace();
572
+ if (source[offset] !== ":") throw new TopikJsonSyntaxError("Expected ':'", offset);
573
+ offset++;
574
+ Object.defineProperty(result, key, {
575
+ configurable: true,
576
+ enumerable: true,
577
+ value: value(depth + 1, memberPointer),
578
+ writable: true
579
+ });
580
+ whitespace();
581
+ if (source[offset] === "}") {
582
+ offset++;
583
+ return result;
584
+ }
585
+ if (source[offset] !== ",") throw new TopikJsonSyntaxError("Expected ',' or '}'", offset);
586
+ offset++;
587
+ }
588
+ throw new TopikJsonSyntaxError("Unterminated object", offset);
589
+ }
590
+ function array(depth, pointer) {
591
+ offset++;
592
+ whitespace();
593
+ const result = [];
594
+ if (source[offset] === "]") {
595
+ offset++;
596
+ return result;
597
+ }
598
+ while (offset < source.length) {
599
+ result.push(value(depth + 1, `${pointer}/${result.length}`));
600
+ whitespace();
601
+ if (source[offset] === "]") {
602
+ offset++;
603
+ return result;
604
+ }
605
+ if (source[offset] !== ",") throw new TopikJsonSyntaxError("Expected ',' or ']'", offset);
606
+ offset++;
607
+ }
608
+ throw new TopikJsonSyntaxError("Unterminated array", offset);
609
+ }
610
+ function string() {
611
+ const start = offset;
612
+ offset++;
613
+ while (offset < source.length) {
614
+ const character = source.charCodeAt(offset);
615
+ if (character === 34) {
616
+ offset++;
617
+ const parsed = JSON.parse(source.slice(start, offset));
618
+ if (hasLoneSurrogate(parsed)) throw new TopikJsonSyntaxError("Lone surrogate is not valid topik-json-v1", start);
619
+ return parsed;
620
+ }
621
+ if (character < 32) throw new TopikJsonSyntaxError("Control in JSON string", offset);
622
+ if (character === 92) {
623
+ offset++;
624
+ const escaped = source[offset];
625
+ if (escaped === "u") {
626
+ if (!/^[0-9a-fA-F]{4}$/u.test(source.slice(offset + 1, offset + 5))) throw new TopikJsonSyntaxError("Invalid Unicode escape", offset);
627
+ offset += 5;
628
+ continue;
629
+ }
630
+ if (!"\"\\/bfnrt".includes(escaped ?? "")) throw new TopikJsonSyntaxError("Invalid string escape", offset);
631
+ }
632
+ offset++;
633
+ }
634
+ throw new TopikJsonSyntaxError("Unterminated string", start);
635
+ }
636
+ function number() {
637
+ const start = offset;
638
+ while (offset < source.length && /[-+0-9.eE]/u.test(source[offset])) offset++;
639
+ const spelling = source.slice(start, offset);
640
+ if (!/^(?:0|[1-9][0-9]*)$/u.test(spelling)) throw new TopikJsonSyntaxError("topik-json-v1 permits only ordinary nonnegative integers", start);
641
+ const parsed = Number(spelling);
642
+ if (!Number.isSafeInteger(parsed)) throw new TopikJsonSyntaxError("JSON integer exceeds the safe integer range", start);
643
+ return parsed;
644
+ }
645
+ whitespace();
646
+ const parsed = value(1, "");
647
+ whitespace();
648
+ if (offset !== source.length) throw new TopikJsonSyntaxError("Trailing data after JSON value", offset);
649
+ return parsed;
650
+ }
651
+ /** RFC 8785 scalar/member rules with Topik's two-space layout and final LF. */
652
+ function serializeTopikJson(value) {
653
+ assertSerializable(value);
654
+ return `${pretty(value, 0)}\n`;
655
+ }
656
+ /** True only for values whose complete own-property graph is topik-json-v1 data. */
657
+ function isTopikJsonDataValue(value) {
658
+ try {
659
+ assertSerializable(value);
660
+ return true;
661
+ } catch {
662
+ return false;
663
+ }
664
+ }
665
+ function topikJsonDescriptor() {
666
+ return { id: TOPIK_JSON_VERSION };
667
+ }
668
+ function pretty(value, depth) {
669
+ if (value === null || typeof value === "boolean" || typeof value === "string") return JSON.stringify(value);
670
+ if (typeof value === "number") return String(value);
671
+ if (Array.isArray(value)) {
672
+ if (value.length === 0) return "[]";
673
+ const indent = " ".repeat(depth + 1);
674
+ return `[\n${value.map((entry) => `${indent}${pretty(entry, depth + 1)}`).join(",\n")}\n${" ".repeat(depth)}]`;
675
+ }
676
+ const object = value;
677
+ const keys = Object.keys(object).sort(compareUtf16);
678
+ if (keys.length === 0) return "{}";
679
+ const indent = " ".repeat(depth + 1);
680
+ return `{\n${keys.map((key) => `${indent}${JSON.stringify(key)}: ${pretty(object[key], depth + 1)}`).join(",\n")}\n${" ".repeat(depth)}}`;
681
+ }
682
+ function assertSerializable(value, seen = /* @__PURE__ */ new Set()) {
683
+ if (value === null || typeof value === "boolean") return;
684
+ if (typeof value === "string") {
685
+ if (hasLoneSurrogate(value)) throw new TypeError("topik-json-v1 rejects lone surrogates");
686
+ return;
687
+ }
688
+ if (typeof value === "number") {
689
+ if (!Number.isSafeInteger(value) || value < 0 || Object.is(value, -0)) throw new TypeError("topik-json-v1 permits only nonnegative safe integers");
690
+ return;
691
+ }
692
+ if (typeof value !== "object" || value === void 0) throw new TypeError("Value is not serializable by topik-json-v1");
693
+ if (seen.has(value)) throw new TypeError("Cyclic value is not serializable");
694
+ seen.add(value);
695
+ if (Array.isArray(value)) {
696
+ if (Object.getPrototypeOf(value) !== Array.prototype) throw new TypeError("topik-json-v1 rejects nonstandard array prototypes");
697
+ const names = Object.getOwnPropertyNames(value);
698
+ if (Object.getOwnPropertySymbols(value).length !== 0 || names.length !== value.length + 1 || names.some((name) => name !== "length" && !isArrayIndex(name, value.length))) throw new TypeError("topik-json-v1 requires dense arrays without extra properties");
699
+ for (let index = 0; index < value.length; index++) {
700
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
701
+ if (descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable) throw new TypeError("topik-json-v1 requires own array data properties");
702
+ assertSerializable(descriptor.value, seen);
703
+ }
704
+ } else {
705
+ const prototype = Object.getPrototypeOf(value);
706
+ if (prototype !== Object.prototype && prototype !== null) throw new TypeError("topik-json-v1 rejects inherited or custom object prototypes");
707
+ if (Object.getOwnPropertySymbols(value).length !== 0) throw new TypeError("topik-json-v1 rejects symbol properties");
708
+ for (const key of Object.getOwnPropertyNames(value)) {
709
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
710
+ if (hasLoneSurrogate(key) || descriptor === void 0 || !("value" in descriptor) || !descriptor.enumerable || descriptor.value === void 0) throw new TypeError("topik-json-v1 requires enumerable own data properties with defined values");
711
+ assertSerializable(descriptor.value, seen);
712
+ }
713
+ }
714
+ seen.delete(value);
715
+ }
716
+ function isArrayIndex(value, length) {
717
+ if (!/^(?:0|[1-9][0-9]*)$/u.test(value)) return false;
718
+ const index = Number(value);
719
+ return Number.isSafeInteger(index) && index >= 0 && index < length && String(index) === value;
720
+ }
721
+ function compareUtf16(left, right) {
722
+ return left < right ? -1 : left > right ? 1 : 0;
723
+ }
724
+ function hasLoneSurrogate(value) {
725
+ for (let index = 0; index < value.length; index++) {
726
+ const unit = value.charCodeAt(index);
727
+ if (unit >= 55296 && unit <= 56319) {
728
+ const next = value.charCodeAt(index + 1);
729
+ if (next < 56320 || next > 57343) return true;
730
+ index++;
731
+ } else if (unit >= 56320 && unit <= 57343) return true;
732
+ }
733
+ return false;
734
+ }
735
+ function escapePointer(value) {
736
+ return value.replaceAll("~", "~0").replaceAll("/", "~1");
737
+ }
738
+ function isJsonWhitespace(code) {
739
+ return code === 9 || code === 10 || code === 13 || code === 32;
740
+ }
741
+ //#endregion
742
+ //#region src/assets/nfkc-casefold-v17.ts
743
+ /** Unicode 17.0.0 NFKC_CF data from DerivedNormalizationProps.txt.
744
+ * Source SHA-256: 71fd6a206a2c0cdd41feb6b7f656aa31091db45e9cedc926985d718397f9e488
745
+ */
746
+ const NFKC_CASEFOLD_V17_DATA = [
747
+ "0041=0061;0042=0062;0043=0063;0044=0064;0045=0065;0046=0066;0047=0067;0048=0068;0049=0069;004a=006a;004b=006b;004c=006c;004d=006d;004e=006e;004f=006f;0050=0070;0051=0071;0052=0072;0053=0073;0054=0074;0055=0075;0056=0076;0057=0077;0058=0078;0059=0079;005a=007a;00a0=0020;00a8=0020.0308;00aa=0061;00ad=;00af=0020.0304;00b2=0032;00b3=0033;00b4=0020.0301;00b5=03bc;00b8=0020.0327;00b9=0031;00ba=006f;00bc=0031.2044.0034;00bd=0031.2044.0032;00be=0033.2044.0034;00c0=00e0;00c1=00e1;00c2=00e2;00c3=00e3;00c4=00e4;00c5=00e5;00c6=00e6;00c7=00e7;00c8=00e8;00c9=00e9;00ca=00ea;00cb=00eb;00cc=00ec;00cd=00ed;00ce=00ee;00cf=00ef;00d0=00f0;00d1=00f1;00d2=00f2;00d3=00f3;00d4=00f4;00d5=00f5;00d6=00f6;00d8=00f8;00d9=00f9;00da=00fa;00db=00fb;00dc=00fc;00dd=00fd;00de=00fe;00df=0073.0073;0100=0101;0102=0103;0104=0105;0106=0107;0108=0109;010a=010b;010c=010d;010e=010f;0110=0111;0112=0113;0114=0115;0116=0117;0118=0119;011a=011b;011c=011d;011e=011f;0120=0121;0122=0123;0124=0125;0126=0127;0128=0129;012a=012b;012c=012d",
748
+ ";012e=012f;0130=0069.0307;0132..0133=0069.006a;0134=0135;0136=0137;0139=013a;013b=013c;013d=013e;013f..0140=006c.00b7;0141=0142;0143=0144;0145=0146;0147=0148;0149=02bc.006e;014a=014b;014c=014d;014e=014f;0150=0151;0152=0153;0154=0155;0156=0157;0158=0159;015a=015b;015c=015d;015e=015f;0160=0161;0162=0163;0164=0165;0166=0167;0168=0169;016a=016b;016c=016d;016e=016f;0170=0171;0172=0173;0174=0175;0176=0177;0178=00ff;0179=017a;017b=017c;017d=017e;017f=0073;0181=0253;0182=0183;0184=0185;0186=0254;0187=0188;0189=0256;018a=0257;018b=018c;018e=01dd;018f=0259;0190=025b;0191=0192;0193=0260;0194=0263;0196=0269;0197=0268;0198=0199;019c=026f;019d=0272;019f=0275;01a0=01a1;01a2=01a3;01a4=01a5;01a6=0280;01a7=01a8;01a9=0283;01ac=01ad;01ae=0288;01af=01b0;01b1=028a;01b2=028b;01b3=01b4;01b5=01b6;01b7=0292;01b8=01b9;01bc=01bd;01c4..01c6=0064.017e;01c7..01c9=006c.006a;01ca..01cc=006e.006a;01cd=01ce;01cf=01d0;01d1=01d2;01d3=01d4;01d5=01d6;01d7=01d8;01d9=01da;01db=01dc;01de=01df;01e0=01e1;01e2=01e3;01e4=01e5;01e6",
749
+ "=01e7;01e8=01e9;01ea=01eb;01ec=01ed;01ee=01ef;01f1..01f3=0064.007a;01f4=01f5;01f6=0195;01f7=01bf;01f8=01f9;01fa=01fb;01fc=01fd;01fe=01ff;0200=0201;0202=0203;0204=0205;0206=0207;0208=0209;020a=020b;020c=020d;020e=020f;0210=0211;0212=0213;0214=0215;0216=0217;0218=0219;021a=021b;021c=021d;021e=021f;0220=019e;0222=0223;0224=0225;0226=0227;0228=0229;022a=022b;022c=022d;022e=022f;0230=0231;0232=0233;023a=2c65;023b=023c;023d=019a;023e=2c66;0241=0242;0243=0180;0244=0289;0245=028c;0246=0247;0248=0249;024a=024b;024c=024d;024e=024f;02b0=0068;02b1=0266;02b2=006a;02b3=0072;02b4=0279;02b5=027b;02b6=0281;02b7=0077;02b8=0079;02d8=0020.0306;02d9=0020.0307;02da=0020.030a;02db=0020.0328;02dc=0020.0303;02dd=0020.030b;02e0=0263;02e1=006c;02e2=0073;02e3=0078;02e4=0295;0340=0300;0341=0301;0343=0313;0344=0308.0301;0345=03b9;034f=;0370=0371;0372=0373;0374=02b9;0376=0377;037a=0020.03b9;037e=003b;037f=03f3;0384=0020.0301;0385=0020.0308.0301;0386=03ac;0387=00b7;0388=03ad;0389=03ae;038a=03af;038c=03cc;038e=03cd;03",
750
+ "8f=03ce;0391=03b1;0392=03b2;0393=03b3;0394=03b4;0395=03b5;0396=03b6;0397=03b7;0398=03b8;0399=03b9;039a=03ba;039b=03bb;039c=03bc;039d=03bd;039e=03be;039f=03bf;03a0=03c0;03a1=03c1;03a3=03c3;03a4=03c4;03a5=03c5;03a6=03c6;03a7=03c7;03a8=03c8;03a9=03c9;03aa=03ca;03ab=03cb;03c2=03c3;03cf=03d7;03d0=03b2;03d1=03b8;03d2=03c5;03d3=03cd;03d4=03cb;03d5=03c6;03d6=03c0;03d8=03d9;03da=03db;03dc=03dd;03de=03df;03e0=03e1;03e2=03e3;03e4=03e5;03e6=03e7;03e8=03e9;03ea=03eb;03ec=03ed;03ee=03ef;03f0=03ba;03f1=03c1;03f2=03c3;03f4=03b8;03f5=03b5;03f7=03f8;03f9=03c3;03fa=03fb;03fd=037b;03fe=037c;03ff=037d;0400=0450;0401=0451;0402=0452;0403=0453;0404=0454;0405=0455;0406=0456;0407=0457;0408=0458;0409=0459;040a=045a;040b=045b;040c=045c;040d=045d;040e=045e;040f=045f;0410=0430;0411=0431;0412=0432;0413=0433;0414=0434;0415=0435;0416=0436;0417=0437;0418=0438;0419=0439;041a=043a;041b=043b;041c=043c;041d=043d;041e=043e;041f=043f;0420=0440;0421=0441;0422=0442;0423=0443;0424=0444;0425=0445;0426=0446;0427=0447;0428=0448;04",
751
+ "29=0449;042a=044a;042b=044b;042c=044c;042d=044d;042e=044e;042f=044f;0460=0461;0462=0463;0464=0465;0466=0467;0468=0469;046a=046b;046c=046d;046e=046f;0470=0471;0472=0473;0474=0475;0476=0477;0478=0479;047a=047b;047c=047d;047e=047f;0480=0481;048a=048b;048c=048d;048e=048f;0490=0491;0492=0493;0494=0495;0496=0497;0498=0499;049a=049b;049c=049d;049e=049f;04a0=04a1;04a2=04a3;04a4=04a5;04a6=04a7;04a8=04a9;04aa=04ab;04ac=04ad;04ae=04af;04b0=04b1;04b2=04b3;04b4=04b5;04b6=04b7;04b8=04b9;04ba=04bb;04bc=04bd;04be=04bf;04c0=04cf;04c1=04c2;04c3=04c4;04c5=04c6;04c7=04c8;04c9=04ca;04cb=04cc;04cd=04ce;04d0=04d1;04d2=04d3;04d4=04d5;04d6=04d7;04d8=04d9;04da=04db;04dc=04dd;04de=04df;04e0=04e1;04e2=04e3;04e4=04e5;04e6=04e7;04e8=04e9;04ea=04eb;04ec=04ed;04ee=04ef;04f0=04f1;04f2=04f3;04f4=04f5;04f6=04f7;04f8=04f9;04fa=04fb;04fc=04fd;04fe=04ff;0500=0501;0502=0503;0504=0505;0506=0507;0508=0509;050a=050b;050c=050d;050e=050f;0510=0511;0512=0513;0514=0515;0516=0517;0518=0519;051a=051b;051c=051d;051e=051f;0520=0521;05",
752
+ "22=0523;0524=0525;0526=0527;0528=0529;052a=052b;052c=052d;052e=052f;0531=0561;0532=0562;0533=0563;0534=0564;0535=0565;0536=0566;0537=0567;0538=0568;0539=0569;053a=056a;053b=056b;053c=056c;053d=056d;053e=056e;053f=056f;0540=0570;0541=0571;0542=0572;0543=0573;0544=0574;0545=0575;0546=0576;0547=0577;0548=0578;0549=0579;054a=057a;054b=057b;054c=057c;054d=057d;054e=057e;054f=057f;0550=0580;0551=0581;0552=0582;0553=0583;0554=0584;0555=0585;0556=0586;0587=0565.0582;061c=;0675=0627.0674;0676=0648.0674;0677=06c7.0674;0678=064a.0674;0958=0915.093c;0959=0916.093c;095a=0917.093c;095b=091c.093c;095c=0921.093c;095d=0922.093c;095e=092b.093c;095f=092f.093c;09dc=09a1.09bc;09dd=09a2.09bc;09df=09af.09bc;0a33=0a32.0a3c;0a36=0a38.0a3c;0a59=0a16.0a3c;0a5a=0a17.0a3c;0a5b=0a1c.0a3c;0a5e=0a2b.0a3c;0b5c=0b21.0b3c;0b5d=0b22.0b3c;0e33=0e4d.0e32;0eb3=0ecd.0eb2;0edc=0eab.0e99;0edd=0eab.0ea1;0f0c=0f0b;0f43=0f42.0fb7;0f4d=0f4c.0fb7;0f52=0f51.0fb7;0f57=0f56.0fb7;0f5c=0f5b.0fb7;0f69=0f40.0fb5;0f73=0f71.0f72;0f75=0f71.0",
753
+ "f74;0f76=0fb2.0f80;0f77=0fb2.0f71.0f80;0f78=0fb3.0f80;0f79=0fb3.0f71.0f80;0f81=0f71.0f80;0f93=0f92.0fb7;0f9d=0f9c.0fb7;0fa2=0fa1.0fb7;0fa7=0fa6.0fb7;0fac=0fab.0fb7;0fb9=0f90.0fb5;10a0=2d00;10a1=2d01;10a2=2d02;10a3=2d03;10a4=2d04;10a5=2d05;10a6=2d06;10a7=2d07;10a8=2d08;10a9=2d09;10aa=2d0a;10ab=2d0b;10ac=2d0c;10ad=2d0d;10ae=2d0e;10af=2d0f;10b0=2d10;10b1=2d11;10b2=2d12;10b3=2d13;10b4=2d14;10b5=2d15;10b6=2d16;10b7=2d17;10b8=2d18;10b9=2d19;10ba=2d1a;10bb=2d1b;10bc=2d1c;10bd=2d1d;10be=2d1e;10bf=2d1f;10c0=2d20;10c1=2d21;10c2=2d22;10c3=2d23;10c4=2d24;10c5=2d25;10c7=2d27;10cd=2d2d;10fc=10dc;115f..1160=;13f8=13f0;13f9=13f1;13fa=13f2;13fb=13f3;13fc=13f4;13fd=13f5;17b4..17b5=;180b..180d=;180e=;180f=;1c80=0432;1c81=0434;1c82=043e;1c83=0441;1c84..1c85=0442;1c86=044a;1c87=0463;1c88=a64b;1c89=1c8a;1c90=10d0;1c91=10d1;1c92=10d2;1c93=10d3;1c94=10d4;1c95=10d5;1c96=10d6;1c97=10d7;1c98=10d8;1c99=10d9;1c9a=10da;1c9b=10db;1c9c=10dc;1c9d=10dd;1c9e=10de;1c9f=10df;1ca0=10e0;1ca1=10e1;1ca2=10e2;1ca3=10e3;1ca4=10",
754
+ "e4;1ca5=10e5;1ca6=10e6;1ca7=10e7;1ca8=10e8;1ca9=10e9;1caa=10ea;1cab=10eb;1cac=10ec;1cad=10ed;1cae=10ee;1caf=10ef;1cb0=10f0;1cb1=10f1;1cb2=10f2;1cb3=10f3;1cb4=10f4;1cb5=10f5;1cb6=10f6;1cb7=10f7;1cb8=10f8;1cb9=10f9;1cba=10fa;1cbd=10fd;1cbe=10fe;1cbf=10ff;1d2c=0061;1d2d=00e6;1d2e=0062;1d30=0064;1d31=0065;1d32=01dd;1d33=0067;1d34=0068;1d35=0069;1d36=006a;1d37=006b;1d38=006c;1d39=006d;1d3a=006e;1d3c=006f;1d3d=0223;1d3e=0070;1d3f=0072;1d40=0074;1d41=0075;1d42=0077;1d43=0061;1d44=0250;1d45=0251;1d46=1d02;1d47=0062;1d48=0064;1d49=0065;1d4a=0259;1d4b=025b;1d4c=025c;1d4d=0067;1d4f=006b;1d50=006d;1d51=014b;1d52=006f;1d53=0254;1d54=1d16;1d55=1d17;1d56=0070;1d57=0074;1d58=0075;1d59=1d1d;1d5a=026f;1d5b=0076;1d5c=1d25;1d5d=03b2;1d5e=03b3;1d5f=03b4;1d60=03c6;1d61=03c7;1d62=0069;1d63=0072;1d64=0075;1d65=0076;1d66=03b2;1d67=03b3;1d68=03c1;1d69=03c6;1d6a=03c7;1d78=043d;1d9b=0252;1d9c=0063;1d9d=0255;1d9e=00f0;1d9f=025c;1da0=0066;1da1=025f;1da2=0261;1da3=0265;1da4=0268;1da5=0269;1da6=026a;1da7=1d7b;1da8=02",
755
+ "9d;1da9=026d;1daa=1d85;1dab=029f;1dac=0271;1dad=0270;1dae=0272;1daf=0273;1db0=0274;1db1=0275;1db2=0278;1db3=0282;1db4=0283;1db5=01ab;1db6=0289;1db7=028a;1db8=1d1c;1db9=028b;1dba=028c;1dbb=007a;1dbc=0290;1dbd=0291;1dbe=0292;1dbf=03b8;1e00=1e01;1e02=1e03;1e04=1e05;1e06=1e07;1e08=1e09;1e0a=1e0b;1e0c=1e0d;1e0e=1e0f;1e10=1e11;1e12=1e13;1e14=1e15;1e16=1e17;1e18=1e19;1e1a=1e1b;1e1c=1e1d;1e1e=1e1f;1e20=1e21;1e22=1e23;1e24=1e25;1e26=1e27;1e28=1e29;1e2a=1e2b;1e2c=1e2d;1e2e=1e2f;1e30=1e31;1e32=1e33;1e34=1e35;1e36=1e37;1e38=1e39;1e3a=1e3b;1e3c=1e3d;1e3e=1e3f;1e40=1e41;1e42=1e43;1e44=1e45;1e46=1e47;1e48=1e49;1e4a=1e4b;1e4c=1e4d;1e4e=1e4f;1e50=1e51;1e52=1e53;1e54=1e55;1e56=1e57;1e58=1e59;1e5a=1e5b;1e5c=1e5d;1e5e=1e5f;1e60=1e61;1e62=1e63;1e64=1e65;1e66=1e67;1e68=1e69;1e6a=1e6b;1e6c=1e6d;1e6e=1e6f;1e70=1e71;1e72=1e73;1e74=1e75;1e76=1e77;1e78=1e79;1e7a=1e7b;1e7c=1e7d;1e7e=1e7f;1e80=1e81;1e82=1e83;1e84=1e85;1e86=1e87;1e88=1e89;1e8a=1e8b;1e8c=1e8d;1e8e=1e8f;1e90=1e91;1e92=1e93;1e94=1e95;1e9a=0061.02be;1e",
756
+ "9b=1e61;1e9e=0073.0073;1ea0=1ea1;1ea2=1ea3;1ea4=1ea5;1ea6=1ea7;1ea8=1ea9;1eaa=1eab;1eac=1ead;1eae=1eaf;1eb0=1eb1;1eb2=1eb3;1eb4=1eb5;1eb6=1eb7;1eb8=1eb9;1eba=1ebb;1ebc=1ebd;1ebe=1ebf;1ec0=1ec1;1ec2=1ec3;1ec4=1ec5;1ec6=1ec7;1ec8=1ec9;1eca=1ecb;1ecc=1ecd;1ece=1ecf;1ed0=1ed1;1ed2=1ed3;1ed4=1ed5;1ed6=1ed7;1ed8=1ed9;1eda=1edb;1edc=1edd;1ede=1edf;1ee0=1ee1;1ee2=1ee3;1ee4=1ee5;1ee6=1ee7;1ee8=1ee9;1eea=1eeb;1eec=1eed;1eee=1eef;1ef0=1ef1;1ef2=1ef3;1ef4=1ef5;1ef6=1ef7;1ef8=1ef9;1efa=1efb;1efc=1efd;1efe=1eff;1f08=1f00;1f09=1f01;1f0a=1f02;1f0b=1f03;1f0c=1f04;1f0d=1f05;1f0e=1f06;1f0f=1f07;1f18=1f10;1f19=1f11;1f1a=1f12;1f1b=1f13;1f1c=1f14;1f1d=1f15;1f28=1f20;1f29=1f21;1f2a=1f22;1f2b=1f23;1f2c=1f24;1f2d=1f25;1f2e=1f26;1f2f=1f27;1f38=1f30;1f39=1f31;1f3a=1f32;1f3b=1f33;1f3c=1f34;1f3d=1f35;1f3e=1f36;1f3f=1f37;1f48=1f40;1f49=1f41;1f4a=1f42;1f4b=1f43;1f4c=1f44;1f4d=1f45;1f59=1f51;1f5b=1f53;1f5d=1f55;1f5f=1f57;1f68=1f60;1f69=1f61;1f6a=1f62;1f6b=1f63;1f6c=1f64;1f6d=1f65;1f6e=1f66;1f6f=1f67;1f71=03ac;1f73=03",
757
+ "ad;1f75=03ae;1f77=03af;1f79=03cc;1f7b=03cd;1f7d=03ce;1f80=1f00.03b9;1f81=1f01.03b9;1f82=1f02.03b9;1f83=1f03.03b9;1f84=1f04.03b9;1f85=1f05.03b9;1f86=1f06.03b9;1f87=1f07.03b9;1f88=1f00.03b9;1f89=1f01.03b9;1f8a=1f02.03b9;1f8b=1f03.03b9;1f8c=1f04.03b9;1f8d=1f05.03b9;1f8e=1f06.03b9;1f8f=1f07.03b9;1f90=1f20.03b9;1f91=1f21.03b9;1f92=1f22.03b9;1f93=1f23.03b9;1f94=1f24.03b9;1f95=1f25.03b9;1f96=1f26.03b9;1f97=1f27.03b9;1f98=1f20.03b9;1f99=1f21.03b9;1f9a=1f22.03b9;1f9b=1f23.03b9;1f9c=1f24.03b9;1f9d=1f25.03b9;1f9e=1f26.03b9;1f9f=1f27.03b9;1fa0=1f60.03b9;1fa1=1f61.03b9;1fa2=1f62.03b9;1fa3=1f63.03b9;1fa4=1f64.03b9;1fa5=1f65.03b9;1fa6=1f66.03b9;1fa7=1f67.03b9;1fa8=1f60.03b9;1fa9=1f61.03b9;1faa=1f62.03b9;1fab=1f63.03b9;1fac=1f64.03b9;1fad=1f65.03b9;1fae=1f66.03b9;1faf=1f67.03b9;1fb2=1f70.03b9;1fb3=03b1.03b9;1fb4=03ac.03b9;1fb7=1fb6.03b9;1fb8=1fb0;1fb9=1fb1;1fba=1f70;1fbb=03ac;1fbc=03b1.03b9;1fbd=0020.0313;1fbe=03b9;1fbf=0020.0313;1fc0=0020.0342;1fc1=0020.0308.0342;1fc2=1f74.03b9;1fc3=03b7.03b9;1fc4=03",
758
+ "ae.03b9;1fc7=1fc6.03b9;1fc8=1f72;1fc9=03ad;1fca=1f74;1fcb=03ae;1fcc=03b7.03b9;1fcd=0020.0313.0300;1fce=0020.0313.0301;1fcf=0020.0313.0342;1fd3=0390;1fd8=1fd0;1fd9=1fd1;1fda=1f76;1fdb=03af;1fdd=0020.0314.0300;1fde=0020.0314.0301;1fdf=0020.0314.0342;1fe3=03b0;1fe8=1fe0;1fe9=1fe1;1fea=1f7a;1feb=03cd;1fec=1fe5;1fed=0020.0308.0300;1fee=0020.0308.0301;1fef=0060;1ff2=1f7c.03b9;1ff3=03c9.03b9;1ff4=03ce.03b9;1ff7=1ff6.03b9;1ff8=1f78;1ff9=03cc;1ffa=1f7c;1ffb=03ce;1ffc=03c9.03b9;1ffd=0020.0301;1ffe=0020.0314;2000..200a=0020;200b..200f=;2011=2010;2017=0020.0333;2024=002e;2025=002e.002e;2026=002e.002e.002e;202a..202e=;202f=0020;2033=2032.2032;2034=2032.2032.2032;2036=2035.2035;2037=2035.2035.2035;203c=0021.0021;203e=0020.0305;2047=003f.003f;2048=003f.0021;2049=0021.003f;2057=2032.2032.2032.2032;205f=0020;2060..2064=;2065=;2066..206f=;2070=0030;2071=0069;2074=0034;2075=0035;2076=0036;2077=0037;2078=0038;2079=0039;207a=002b;207b=2212;207c=003d;207d=0028;207e=0029;207f=006e;2080=0030;2081=0031;2082=00",
759
+ "32;2083=0033;2084=0034;2085=0035;2086=0036;2087=0037;2088=0038;2089=0039;208a=002b;208b=2212;208c=003d;208d=0028;208e=0029;2090=0061;2091=0065;2092=006f;2093=0078;2094=0259;2095=0068;2096=006b;2097=006c;2098=006d;2099=006e;209a=0070;209b=0073;209c=0074;20a8=0072.0073;2100=0061.002f.0063;2101=0061.002f.0073;2102=0063;2103=00b0.0063;2105=0063.002f.006f;2106=0063.002f.0075;2107=025b;2109=00b0.0066;210a=0067;210b..210e=0068;210f=0127;2110..2111=0069;2112..2113=006c;2115=006e;2116=006e.006f;2119=0070;211a=0071;211b..211d=0072;2120=0073.006d;2121=0074.0065.006c;2122=0074.006d;2124=007a;2126=03c9;2128=007a;212a=006b;212b=00e5;212c=0062;212d=0063;212f..2130=0065;2131=0066;2132=214e;2133=006d;2134=006f;2135=05d0;2136=05d1;2137=05d2;2138=05d3;2139=0069;213b=0066.0061.0078;213c=03c0;213d..213e=03b3;213f=03c0;2140=2211;2145..2146=0064;2147=0065;2148=0069;2149=006a;2150=0031.2044.0037;2151=0031.2044.0039;2152=0031.2044.0031.0030;2153=0031.2044.0033;2154=0032.2044.0033;2155=0031.2044.0035;2156=0032.",
760
+ "2044.0035;2157=0033.2044.0035;2158=0034.2044.0035;2159=0031.2044.0036;215a=0035.2044.0036;215b=0031.2044.0038;215c=0033.2044.0038;215d=0035.2044.0038;215e=0037.2044.0038;215f=0031.2044;2160=0069;2161=0069.0069;2162=0069.0069.0069;2163=0069.0076;2164=0076;2165=0076.0069;2166=0076.0069.0069;2167=0076.0069.0069.0069;2168=0069.0078;2169=0078;216a=0078.0069;216b=0078.0069.0069;216c=006c;216d=0063;216e=0064;216f=006d;2170=0069;2171=0069.0069;2172=0069.0069.0069;2173=0069.0076;2174=0076;2175=0076.0069;2176=0076.0069.0069;2177=0076.0069.0069.0069;2178=0069.0078;2179=0078;217a=0078.0069;217b=0078.0069.0069;217c=006c;217d=0063;217e=0064;217f=006d;2183=2184;2189=0030.2044.0033;222c=222b.222b;222d=222b.222b.222b;222f=222e.222e;2230=222e.222e.222e;2329=3008;232a=3009;2460=0031;2461=0032;2462=0033;2463=0034;2464=0035;2465=0036;2466=0037;2467=0038;2468=0039;2469=0031.0030;246a=0031.0031;246b=0031.0032;246c=0031.0033;246d=0031.0034;246e=0031.0035;246f=0031.0036;2470=0031.0037;2471=0031.0038;2472=0031.",
761
+ "0039;2473=0032.0030;2474=0028.0031.0029;2475=0028.0032.0029;2476=0028.0033.0029;2477=0028.0034.0029;2478=0028.0035.0029;2479=0028.0036.0029;247a=0028.0037.0029;247b=0028.0038.0029;247c=0028.0039.0029;247d=0028.0031.0030.0029;247e=0028.0031.0031.0029;247f=0028.0031.0032.0029;2480=0028.0031.0033.0029;2481=0028.0031.0034.0029;2482=0028.0031.0035.0029;2483=0028.0031.0036.0029;2484=0028.0031.0037.0029;2485=0028.0031.0038.0029;2486=0028.0031.0039.0029;2487=0028.0032.0030.0029;2488=0031.002e;2489=0032.002e;248a=0033.002e;248b=0034.002e;248c=0035.002e;248d=0036.002e;248e=0037.002e;248f=0038.002e;2490=0039.002e;2491=0031.0030.002e;2492=0031.0031.002e;2493=0031.0032.002e;2494=0031.0033.002e;2495=0031.0034.002e;2496=0031.0035.002e;2497=0031.0036.002e;2498=0031.0037.002e;2499=0031.0038.002e;249a=0031.0039.002e;249b=0032.0030.002e;249c=0028.0061.0029;249d=0028.0062.0029;249e=0028.0063.0029;249f=0028.0064.0029;24a0=0028.0065.0029;24a1=0028.0066.0029;24a2=0028.0067.0029;24a3=0028.0068.0029;24a4=0028.",
762
+ "0069.0029;24a5=0028.006a.0029;24a6=0028.006b.0029;24a7=0028.006c.0029;24a8=0028.006d.0029;24a9=0028.006e.0029;24aa=0028.006f.0029;24ab=0028.0070.0029;24ac=0028.0071.0029;24ad=0028.0072.0029;24ae=0028.0073.0029;24af=0028.0074.0029;24b0=0028.0075.0029;24b1=0028.0076.0029;24b2=0028.0077.0029;24b3=0028.0078.0029;24b4=0028.0079.0029;24b5=0028.007a.0029;24b6=0061;24b7=0062;24b8=0063;24b9=0064;24ba=0065;24bb=0066;24bc=0067;24bd=0068;24be=0069;24bf=006a;24c0=006b;24c1=006c;24c2=006d;24c3=006e;24c4=006f;24c5=0070;24c6=0071;24c7=0072;24c8=0073;24c9=0074;24ca=0075;24cb=0076;24cc=0077;24cd=0078;24ce=0079;24cf=007a;24d0=0061;24d1=0062;24d2=0063;24d3=0064;24d4=0065;24d5=0066;24d6=0067;24d7=0068;24d8=0069;24d9=006a;24da=006b;24db=006c;24dc=006d;24dd=006e;24de=006f;24df=0070;24e0=0071;24e1=0072;24e2=0073;24e3=0074;24e4=0075;24e5=0076;24e6=0077;24e7=0078;24e8=0079;24e9=007a;24ea=0030;2a0c=222b.222b.222b.222b;2a74=003a.003a.003d;2a75=003d.003d;2a76=003d.003d.003d;2adc=2add.0338;2c00=2c30;2c01=2c31;2c02=",
763
+ "2c32;2c03=2c33;2c04=2c34;2c05=2c35;2c06=2c36;2c07=2c37;2c08=2c38;2c09=2c39;2c0a=2c3a;2c0b=2c3b;2c0c=2c3c;2c0d=2c3d;2c0e=2c3e;2c0f=2c3f;2c10=2c40;2c11=2c41;2c12=2c42;2c13=2c43;2c14=2c44;2c15=2c45;2c16=2c46;2c17=2c47;2c18=2c48;2c19=2c49;2c1a=2c4a;2c1b=2c4b;2c1c=2c4c;2c1d=2c4d;2c1e=2c4e;2c1f=2c4f;2c20=2c50;2c21=2c51;2c22=2c52;2c23=2c53;2c24=2c54;2c25=2c55;2c26=2c56;2c27=2c57;2c28=2c58;2c29=2c59;2c2a=2c5a;2c2b=2c5b;2c2c=2c5c;2c2d=2c5d;2c2e=2c5e;2c2f=2c5f;2c60=2c61;2c62=026b;2c63=1d7d;2c64=027d;2c67=2c68;2c69=2c6a;2c6b=2c6c;2c6d=0251;2c6e=0271;2c6f=0250;2c70=0252;2c72=2c73;2c75=2c76;2c7c=006a;2c7d=0076;2c7e=023f;2c7f=0240;2c80=2c81;2c82=2c83;2c84=2c85;2c86=2c87;2c88=2c89;2c8a=2c8b;2c8c=2c8d;2c8e=2c8f;2c90=2c91;2c92=2c93;2c94=2c95;2c96=2c97;2c98=2c99;2c9a=2c9b;2c9c=2c9d;2c9e=2c9f;2ca0=2ca1;2ca2=2ca3;2ca4=2ca5;2ca6=2ca7;2ca8=2ca9;2caa=2cab;2cac=2cad;2cae=2caf;2cb0=2cb1;2cb2=2cb3;2cb4=2cb5;2cb6=2cb7;2cb8=2cb9;2cba=2cbb;2cbc=2cbd;2cbe=2cbf;2cc0=2cc1;2cc2=2cc3;2cc4=2cc5;2cc6=2cc7;2cc8=2cc9;2cca=",
764
+ "2ccb;2ccc=2ccd;2cce=2ccf;2cd0=2cd1;2cd2=2cd3;2cd4=2cd5;2cd6=2cd7;2cd8=2cd9;2cda=2cdb;2cdc=2cdd;2cde=2cdf;2ce0=2ce1;2ce2=2ce3;2ceb=2cec;2ced=2cee;2cf2=2cf3;2d6f=2d61;2e9f=6bcd;2ef3=9f9f;2f00=4e00;2f01=4e28;2f02=4e36;2f03=4e3f;2f04=4e59;2f05=4e85;2f06=4e8c;2f07=4ea0;2f08=4eba;2f09=513f;2f0a=5165;2f0b=516b;2f0c=5182;2f0d=5196;2f0e=51ab;2f0f=51e0;2f10=51f5;2f11=5200;2f12=529b;2f13=52f9;2f14=5315;2f15=531a;2f16=5338;2f17=5341;2f18=535c;2f19=5369;2f1a=5382;2f1b=53b6;2f1c=53c8;2f1d=53e3;2f1e=56d7;2f1f=571f;2f20=58eb;2f21=5902;2f22=590a;2f23=5915;2f24=5927;2f25=5973;2f26=5b50;2f27=5b80;2f28=5bf8;2f29=5c0f;2f2a=5c22;2f2b=5c38;2f2c=5c6e;2f2d=5c71;2f2e=5ddb;2f2f=5de5;2f30=5df1;2f31=5dfe;2f32=5e72;2f33=5e7a;2f34=5e7f;2f35=5ef4;2f36=5efe;2f37=5f0b;2f38=5f13;2f39=5f50;2f3a=5f61;2f3b=5f73;2f3c=5fc3;2f3d=6208;2f3e=6236;2f3f=624b;2f40=652f;2f41=6534;2f42=6587;2f43=6597;2f44=65a4;2f45=65b9;2f46=65e0;2f47=65e5;2f48=66f0;2f49=6708;2f4a=6728;2f4b=6b20;2f4c=6b62;2f4d=6b79;2f4e=6bb3;2f4f=6bcb;2f50=6bd4;2f51=",
765
+ "6bdb;2f52=6c0f;2f53=6c14;2f54=6c34;2f55=706b;2f56=722a;2f57=7236;2f58=723b;2f59=723f;2f5a=7247;2f5b=7259;2f5c=725b;2f5d=72ac;2f5e=7384;2f5f=7389;2f60=74dc;2f61=74e6;2f62=7518;2f63=751f;2f64=7528;2f65=7530;2f66=758b;2f67=7592;2f68=7676;2f69=767d;2f6a=76ae;2f6b=76bf;2f6c=76ee;2f6d=77db;2f6e=77e2;2f6f=77f3;2f70=793a;2f71=79b8;2f72=79be;2f73=7a74;2f74=7acb;2f75=7af9;2f76=7c73;2f77=7cf8;2f78=7f36;2f79=7f51;2f7a=7f8a;2f7b=7fbd;2f7c=8001;2f7d=800c;2f7e=8012;2f7f=8033;2f80=807f;2f81=8089;2f82=81e3;2f83=81ea;2f84=81f3;2f85=81fc;2f86=820c;2f87=821b;2f88=821f;2f89=826e;2f8a=8272;2f8b=8278;2f8c=864d;2f8d=866b;2f8e=8840;2f8f=884c;2f90=8863;2f91=897e;2f92=898b;2f93=89d2;2f94=8a00;2f95=8c37;2f96=8c46;2f97=8c55;2f98=8c78;2f99=8c9d;2f9a=8d64;2f9b=8d70;2f9c=8db3;2f9d=8eab;2f9e=8eca;2f9f=8f9b;2fa0=8fb0;2fa1=8fb5;2fa2=9091;2fa3=9149;2fa4=91c6;2fa5=91cc;2fa6=91d1;2fa7=9577;2fa8=9580;2fa9=961c;2faa=96b6;2fab=96b9;2fac=96e8;2fad=9751;2fae=975e;2faf=9762;2fb0=9769;2fb1=97cb;2fb2=97ed;2fb3=97f3;2fb4=9801;2fb5=",
766
+ "98a8;2fb6=98db;2fb7=98df;2fb8=9996;2fb9=9999;2fba=99ac;2fbb=9aa8;2fbc=9ad8;2fbd=9adf;2fbe=9b25;2fbf=9b2f;2fc0=9b32;2fc1=9b3c;2fc2=9b5a;2fc3=9ce5;2fc4=9e75;2fc5=9e7f;2fc6=9ea5;2fc7=9ebb;2fc8=9ec3;2fc9=9ecd;2fca=9ed1;2fcb=9ef9;2fcc=9efd;2fcd=9f0e;2fce=9f13;2fcf=9f20;2fd0=9f3b;2fd1=9f4a;2fd2=9f52;2fd3=9f8d;2fd4=9f9c;2fd5=9fa0;3000=0020;3036=3012;3038=5341;3039=5344;303a=5345;309b=0020.3099;309c=0020.309a;309f=3088.308a;30ff=30b3.30c8;3131=1100;3132=1101;3133=11aa;3134=1102;3135=11ac;3136=11ad;3137=1103;3138=1104;3139=1105;313a=11b0;313b=11b1;313c=11b2;313d=11b3;313e=11b4;313f=11b5;3140=111a;3141=1106;3142=1107;3143=1108;3144=1121;3145=1109;3146=110a;3147=110b;3148=110c;3149=110d;314a=110e;314b=110f;314c=1110;314d=1111;314e=1112;314f=1161;3150=1162;3151=1163;3152=1164;3153=1165;3154=1166;3155=1167;3156=1168;3157=1169;3158=116a;3159=116b;315a=116c;315b=116d;315c=116e;315d=116f;315e=1170;315f=1171;3160=1172;3161=1173;3162=1174;3163=1175;3164=;3165=1114;3166=1115;3167=11c7;3168=11c8;3169=11cc",
767
+ ";316a=11ce;316b=11d3;316c=11d7;316d=11d9;316e=111c;316f=11dd;3170=11df;3171=111d;3172=111e;3173=1120;3174=1122;3175=1123;3176=1127;3177=1129;3178=112b;3179=112c;317a=112d;317b=112e;317c=112f;317d=1132;317e=1136;317f=1140;3180=1147;3181=114c;3182=11f1;3183=11f2;3184=1157;3185=1158;3186=1159;3187=1184;3188=1185;3189=1188;318a=1191;318b=1192;318c=1194;318d=119e;318e=11a1;3192=4e00;3193=4e8c;3194=4e09;3195=56db;3196=4e0a;3197=4e2d;3198=4e0b;3199=7532;319a=4e59;319b=4e19;319c=4e01;319d=5929;319e=5730;319f=4eba;3200=0028.1100.0029;3201=0028.1102.0029;3202=0028.1103.0029;3203=0028.1105.0029;3204=0028.1106.0029;3205=0028.1107.0029;3206=0028.1109.0029;3207=0028.110b.0029;3208=0028.110c.0029;3209=0028.110e.0029;320a=0028.110f.0029;320b=0028.1110.0029;320c=0028.1111.0029;320d=0028.1112.0029;320e=0028.ac00.0029;320f=0028.b098.0029;3210=0028.b2e4.0029;3211=0028.b77c.0029;3212=0028.b9c8.0029;3213=0028.bc14.0029;3214=0028.c0ac.0029;3215=0028.c544.0029;3216=0028.c790.0029;3217=0028.cc28.0029;3218=0028",
768
+ ".ce74.0029;3219=0028.d0c0.0029;321a=0028.d30c.0029;321b=0028.d558.0029;321c=0028.c8fc.0029;321d=0028.c624.c804.0029;321e=0028.c624.d6c4.0029;3220=0028.4e00.0029;3221=0028.4e8c.0029;3222=0028.4e09.0029;3223=0028.56db.0029;3224=0028.4e94.0029;3225=0028.516d.0029;3226=0028.4e03.0029;3227=0028.516b.0029;3228=0028.4e5d.0029;3229=0028.5341.0029;322a=0028.6708.0029;322b=0028.706b.0029;322c=0028.6c34.0029;322d=0028.6728.0029;322e=0028.91d1.0029;322f=0028.571f.0029;3230=0028.65e5.0029;3231=0028.682a.0029;3232=0028.6709.0029;3233=0028.793e.0029;3234=0028.540d.0029;3235=0028.7279.0029;3236=0028.8ca1.0029;3237=0028.795d.0029;3238=0028.52b4.0029;3239=0028.4ee3.0029;323a=0028.547c.0029;323b=0028.5b66.0029;323c=0028.76e3.0029;323d=0028.4f01.0029;323e=0028.8cc7.0029;323f=0028.5354.0029;3240=0028.796d.0029;3241=0028.4f11.0029;3242=0028.81ea.0029;3243=0028.81f3.0029;3244=554f;3245=5e7c;3246=6587;3247=7b8f;3250=0070.0074.0065;3251=0032.0031;3252=0032.0032;3253=0032.0033;3254=0032.0034;3255=0032.0035;3256",
769
+ "=0032.0036;3257=0032.0037;3258=0032.0038;3259=0032.0039;325a=0033.0030;325b=0033.0031;325c=0033.0032;325d=0033.0033;325e=0033.0034;325f=0033.0035;3260=1100;3261=1102;3262=1103;3263=1105;3264=1106;3265=1107;3266=1109;3267=110b;3268=110c;3269=110e;326a=110f;326b=1110;326c=1111;326d=1112;326e=ac00;326f=b098;3270=b2e4;3271=b77c;3272=b9c8;3273=bc14;3274=c0ac;3275=c544;3276=c790;3277=cc28;3278=ce74;3279=d0c0;327a=d30c;327b=d558;327c=cc38.ace0;327d=c8fc.c758;327e=c6b0;3280=4e00;3281=4e8c;3282=4e09;3283=56db;3284=4e94;3285=516d;3286=4e03;3287=516b;3288=4e5d;3289=5341;328a=6708;328b=706b;328c=6c34;328d=6728;328e=91d1;328f=571f;3290=65e5;3291=682a;3292=6709;3293=793e;3294=540d;3295=7279;3296=8ca1;3297=795d;3298=52b4;3299=79d8;329a=7537;329b=5973;329c=9069;329d=512a;329e=5370;329f=6ce8;32a0=9805;32a1=4f11;32a2=5199;32a3=6b63;32a4=4e0a;32a5=4e2d;32a6=4e0b;32a7=5de6;32a8=53f3;32a9=533b;32aa=5b97;32ab=5b66;32ac=76e3;32ad=4f01;32ae=8cc7;32af=5354;32b0=591c;32b1=0033.0036;32b2=0033.0037;32b3=0033.0038",
770
+ ";32b4=0033.0039;32b5=0034.0030;32b6=0034.0031;32b7=0034.0032;32b8=0034.0033;32b9=0034.0034;32ba=0034.0035;32bb=0034.0036;32bc=0034.0037;32bd=0034.0038;32be=0034.0039;32bf=0035.0030;32c0=0031.6708;32c1=0032.6708;32c2=0033.6708;32c3=0034.6708;32c4=0035.6708;32c5=0036.6708;32c6=0037.6708;32c7=0038.6708;32c8=0039.6708;32c9=0031.0030.6708;32ca=0031.0031.6708;32cb=0031.0032.6708;32cc=0068.0067;32cd=0065.0072.0067;32ce=0065.0076;32cf=006c.0074.0064;32d0=30a2;32d1=30a4;32d2=30a6;32d3=30a8;32d4=30aa;32d5=30ab;32d6=30ad;32d7=30af;32d8=30b1;32d9=30b3;32da=30b5;32db=30b7;32dc=30b9;32dd=30bb;32de=30bd;32df=30bf;32e0=30c1;32e1=30c4;32e2=30c6;32e3=30c8;32e4=30ca;32e5=30cb;32e6=30cc;32e7=30cd;32e8=30ce;32e9=30cf;32ea=30d2;32eb=30d5;32ec=30d8;32ed=30db;32ee=30de;32ef=30df;32f0=30e0;32f1=30e1;32f2=30e2;32f3=30e4;32f4=30e6;32f5=30e8;32f6=30e9;32f7=30ea;32f8=30eb;32f9=30ec;32fa=30ed;32fb=30ef;32fc=30f0;32fd=30f1;32fe=30f2;32ff=4ee4.548c;3300=30a2.30d1.30fc.30c8;3301=30a2.30eb.30d5.30a1;3302=30a2.30f3.30da",
771
+ ".30a2;3303=30a2.30fc.30eb;3304=30a4.30cb.30f3.30b0;3305=30a4.30f3.30c1;3306=30a6.30a9.30f3;3307=30a8.30b9.30af.30fc.30c9;3308=30a8.30fc.30ab.30fc;3309=30aa.30f3.30b9;330a=30aa.30fc.30e0;330b=30ab.30a4.30ea;330c=30ab.30e9.30c3.30c8;330d=30ab.30ed.30ea.30fc;330e=30ac.30ed.30f3;330f=30ac.30f3.30de;3310=30ae.30ac;3311=30ae.30cb.30fc;3312=30ad.30e5.30ea.30fc;3313=30ae.30eb.30c0.30fc;3314=30ad.30ed;3315=30ad.30ed.30b0.30e9.30e0;3316=30ad.30ed.30e1.30fc.30c8.30eb;3317=30ad.30ed.30ef.30c3.30c8;3318=30b0.30e9.30e0;3319=30b0.30e9.30e0.30c8.30f3;331a=30af.30eb.30bc.30a4.30ed;331b=30af.30ed.30fc.30cd;331c=30b1.30fc.30b9;331d=30b3.30eb.30ca;331e=30b3.30fc.30dd;331f=30b5.30a4.30af.30eb;3320=30b5.30f3.30c1.30fc.30e0;3321=30b7.30ea.30f3.30b0;3322=30bb.30f3.30c1;3323=30bb.30f3.30c8;3324=30c0.30fc.30b9;3325=30c7.30b7;3326=30c9.30eb;3327=30c8.30f3;3328=30ca.30ce;3329=30ce.30c3.30c8;332a=30cf.30a4.30c4;332b=30d1.30fc.30bb.30f3.30c8;332c=30d1.30fc.30c4;332d=30d0.30fc.30ec.30eb;332e=30d4.30a2.30b9.30c8.30eb",
772
+ ";332f=30d4.30af.30eb;3330=30d4.30b3;3331=30d3.30eb;3332=30d5.30a1.30e9.30c3.30c9;3333=30d5.30a3.30fc.30c8;3334=30d6.30c3.30b7.30a7.30eb;3335=30d5.30e9.30f3;3336=30d8.30af.30bf.30fc.30eb;3337=30da.30bd;3338=30da.30cb.30d2;3339=30d8.30eb.30c4;333a=30da.30f3.30b9;333b=30da.30fc.30b8;333c=30d9.30fc.30bf;333d=30dd.30a4.30f3.30c8;333e=30dc.30eb.30c8;333f=30db.30f3;3340=30dd.30f3.30c9;3341=30db.30fc.30eb;3342=30db.30fc.30f3;3343=30de.30a4.30af.30ed;3344=30de.30a4.30eb;3345=30de.30c3.30cf;3346=30de.30eb.30af;3347=30de.30f3.30b7.30e7.30f3;3348=30df.30af.30ed.30f3;3349=30df.30ea;334a=30df.30ea.30d0.30fc.30eb;334b=30e1.30ac;334c=30e1.30ac.30c8.30f3;334d=30e1.30fc.30c8.30eb;334e=30e4.30fc.30c9;334f=30e4.30fc.30eb;3350=30e6.30a2.30f3;3351=30ea.30c3.30c8.30eb;3352=30ea.30e9;3353=30eb.30d4.30fc;3354=30eb.30fc.30d6.30eb;3355=30ec.30e0;3356=30ec.30f3.30c8.30b2.30f3;3357=30ef.30c3.30c8;3358=0030.70b9;3359=0031.70b9;335a=0032.70b9;335b=0033.70b9;335c=0034.70b9;335d=0035.70b9;335e=0036.70b9;335f=0037.70b9",
773
+ ";3360=0038.70b9;3361=0039.70b9;3362=0031.0030.70b9;3363=0031.0031.70b9;3364=0031.0032.70b9;3365=0031.0033.70b9;3366=0031.0034.70b9;3367=0031.0035.70b9;3368=0031.0036.70b9;3369=0031.0037.70b9;336a=0031.0038.70b9;336b=0031.0039.70b9;336c=0032.0030.70b9;336d=0032.0031.70b9;336e=0032.0032.70b9;336f=0032.0033.70b9;3370=0032.0034.70b9;3371=0068.0070.0061;3372=0064.0061;3373=0061.0075;3374=0062.0061.0072;3375=006f.0076;3376=0070.0063;3377=0064.006d;3378=0064.006d.0032;3379=0064.006d.0033;337a=0069.0075;337b=5e73.6210;337c=662d.548c;337d=5927.6b63;337e=660e.6cbb;337f=682a.5f0f.4f1a.793e;3380=0070.0061;3381=006e.0061;3382=03bc.0061;3383=006d.0061;3384=006b.0061;3385=006b.0062;3386=006d.0062;3387=0067.0062;3388=0063.0061.006c;3389=006b.0063.0061.006c;338a=0070.0066;338b=006e.0066;338c=03bc.0066;338d=03bc.0067;338e=006d.0067;338f=006b.0067;3390=0068.007a;3391=006b.0068.007a;3392=006d.0068.007a;3393=0067.0068.007a;3394=0074.0068.007a;3395=03bc.006c;3396=006d.006c;3397=0064.006c;3398=006b.006c;3399",
774
+ "=0066.006d;339a=006e.006d;339b=03bc.006d;339c=006d.006d;339d=0063.006d;339e=006b.006d;339f=006d.006d.0032;33a0=0063.006d.0032;33a1=006d.0032;33a2=006b.006d.0032;33a3=006d.006d.0033;33a4=0063.006d.0033;33a5=006d.0033;33a6=006b.006d.0033;33a7=006d.2215.0073;33a8=006d.2215.0073.0032;33a9=0070.0061;33aa=006b.0070.0061;33ab=006d.0070.0061;33ac=0067.0070.0061;33ad=0072.0061.0064;33ae=0072.0061.0064.2215.0073;33af=0072.0061.0064.2215.0073.0032;33b0=0070.0073;33b1=006e.0073;33b2=03bc.0073;33b3=006d.0073;33b4=0070.0076;33b5=006e.0076;33b6=03bc.0076;33b7=006d.0076;33b8=006b.0076;33b9=006d.0076;33ba=0070.0077;33bb=006e.0077;33bc=03bc.0077;33bd=006d.0077;33be=006b.0077;33bf=006d.0077;33c0=006b.03c9;33c1=006d.03c9;33c2=0061.002e.006d.002e;33c3=0062.0071;33c4=0063.0063;33c5=0063.0064;33c6=0063.2215.006b.0067;33c7=0063.006f.002e;33c8=0064.0062;33c9=0067.0079;33ca=0068.0061;33cb=0068.0070;33cc=0069.006e;33cd=006b.006b;33ce=006b.006d;33cf=006b.0074;33d0=006c.006d;33d1=006c.006e;33d2=006c.006f.0067;33d3",
775
+ "=006c.0078;33d4=006d.0062;33d5=006d.0069.006c;33d6=006d.006f.006c;33d7=0070.0068;33d8=0070.002e.006d.002e;33d9=0070.0070.006d;33da=0070.0072;33db=0073.0072;33dc=0073.0076;33dd=0077.0062;33de=0076.2215.006d;33df=0061.2215.006d;33e0=0031.65e5;33e1=0032.65e5;33e2=0033.65e5;33e3=0034.65e5;33e4=0035.65e5;33e5=0036.65e5;33e6=0037.65e5;33e7=0038.65e5;33e8=0039.65e5;33e9=0031.0030.65e5;33ea=0031.0031.65e5;33eb=0031.0032.65e5;33ec=0031.0033.65e5;33ed=0031.0034.65e5;33ee=0031.0035.65e5;33ef=0031.0036.65e5;33f0=0031.0037.65e5;33f1=0031.0038.65e5;33f2=0031.0039.65e5;33f3=0032.0030.65e5;33f4=0032.0031.65e5;33f5=0032.0032.65e5;33f6=0032.0033.65e5;33f7=0032.0034.65e5;33f8=0032.0035.65e5;33f9=0032.0036.65e5;33fa=0032.0037.65e5;33fb=0032.0038.65e5;33fc=0032.0039.65e5;33fd=0033.0030.65e5;33fe=0033.0031.65e5;33ff=0067.0061.006c;a640=a641;a642=a643;a644=a645;a646=a647;a648=a649;a64a=a64b;a64c=a64d;a64e=a64f;a650=a651;a652=a653;a654=a655;a656=a657;a658=a659;a65a=a65b;a65c=a65d;a65e=a65f;a660=a661;a662=a663",
776
+ ";a664=a665;a666=a667;a668=a669;a66a=a66b;a66c=a66d;a680=a681;a682=a683;a684=a685;a686=a687;a688=a689;a68a=a68b;a68c=a68d;a68e=a68f;a690=a691;a692=a693;a694=a695;a696=a697;a698=a699;a69a=a69b;a69c=044a;a69d=044c;a722=a723;a724=a725;a726=a727;a728=a729;a72a=a72b;a72c=a72d;a72e=a72f;a732=a733;a734=a735;a736=a737;a738=a739;a73a=a73b;a73c=a73d;a73e=a73f;a740=a741;a742=a743;a744=a745;a746=a747;a748=a749;a74a=a74b;a74c=a74d;a74e=a74f;a750=a751;a752=a753;a754=a755;a756=a757;a758=a759;a75a=a75b;a75c=a75d;a75e=a75f;a760=a761;a762=a763;a764=a765;a766=a767;a768=a769;a76a=a76b;a76c=a76d;a76e=a76f;a770=a76f;a779=a77a;a77b=a77c;a77d=1d79;a77e=a77f;a780=a781;a782=a783;a784=a785;a786=a787;a78b=a78c;a78d=0265;a790=a791;a792=a793;a796=a797;a798=a799;a79a=a79b;a79c=a79d;a79e=a79f;a7a0=a7a1;a7a2=a7a3;a7a4=a7a5;a7a6=a7a7;a7a8=a7a9;a7aa=0266;a7ab=025c;a7ac=0261;a7ad=026c;a7ae=026a;a7b0=029e;a7b1=0287;a7b2=029d;a7b3=ab53;a7b4=a7b5;a7b6=a7b7;a7b8=a7b9;a7ba=a7bb;a7bc=a7bd;a7be=a7bf;a7c0=a7c1;a7c2=a7c3;a7c4=a794",
777
+ ";a7c5=0282;a7c6=1d8e;a7c7=a7c8;a7c9=a7ca;a7cb=0264;a7cc=a7cd;a7ce=a7cf;a7d0=a7d1;a7d2=a7d3;a7d4=a7d5;a7d6=a7d7;a7d8=a7d9;a7da=a7db;a7dc=019b;a7f1=0073;a7f2=0063;a7f3=0066;a7f4=0071;a7f5=a7f6;a7f8=0127;a7f9=0153;ab5c=a727;ab5d=ab37;ab5e=026b;ab5f=ab52;ab69=028d;ab70=13a0;ab71=13a1;ab72=13a2;ab73=13a3;ab74=13a4;ab75=13a5;ab76=13a6;ab77=13a7;ab78=13a8;ab79=13a9;ab7a=13aa;ab7b=13ab;ab7c=13ac;ab7d=13ad;ab7e=13ae;ab7f=13af;ab80=13b0;ab81=13b1;ab82=13b2;ab83=13b3;ab84=13b4;ab85=13b5;ab86=13b6;ab87=13b7;ab88=13b8;ab89=13b9;ab8a=13ba;ab8b=13bb;ab8c=13bc;ab8d=13bd;ab8e=13be;ab8f=13bf;ab90=13c0;ab91=13c1;ab92=13c2;ab93=13c3;ab94=13c4;ab95=13c5;ab96=13c6;ab97=13c7;ab98=13c8;ab99=13c9;ab9a=13ca;ab9b=13cb;ab9c=13cc;ab9d=13cd;ab9e=13ce;ab9f=13cf;aba0=13d0;aba1=13d1;aba2=13d2;aba3=13d3;aba4=13d4;aba5=13d5;aba6=13d6;aba7=13d7;aba8=13d8;aba9=13d9;abaa=13da;abab=13db;abac=13dc;abad=13dd;abae=13de;abaf=13df;abb0=13e0;abb1=13e1;abb2=13e2;abb3=13e3;abb4=13e4;abb5=13e5;abb6=13e6;abb7=13e7;abb8=13e8;abb9=13e9",
778
+ ";abba=13ea;abbb=13eb;abbc=13ec;abbd=13ed;abbe=13ee;abbf=13ef;f900=8c48;f901=66f4;f902=8eca;f903=8cc8;f904=6ed1;f905=4e32;f906=53e5;f907..f908=9f9c;f909=5951;f90a=91d1;f90b=5587;f90c=5948;f90d=61f6;f90e=7669;f90f=7f85;f910=863f;f911=87ba;f912=88f8;f913=908f;f914=6a02;f915=6d1b;f916=70d9;f917=73de;f918=843d;f919=916a;f91a=99f1;f91b=4e82;f91c=5375;f91d=6b04;f91e=721b;f91f=862d;f920=9e1e;f921=5d50;f922=6feb;f923=85cd;f924=8964;f925=62c9;f926=81d8;f927=881f;f928=5eca;f929=6717;f92a=6d6a;f92b=72fc;f92c=90ce;f92d=4f86;f92e=51b7;f92f=52de;f930=64c4;f931=6ad3;f932=7210;f933=76e7;f934=8001;f935=8606;f936=865c;f937=8def;f938=9732;f939=9b6f;f93a=9dfa;f93b=788c;f93c=797f;f93d=7da0;f93e=83c9;f93f=9304;f940=9e7f;f941=8ad6;f942=58df;f943=5f04;f944=7c60;f945=807e;f946=7262;f947=78ca;f948=8cc2;f949=96f7;f94a=58d8;f94b=5c62;f94c=6a13;f94d=6dda;f94e=6f0f;f94f=7d2f;f950=7e37;f951=964b;f952=52d2;f953=808b;f954=51dc;f955=51cc;f956=7a1c;f957=7dbe;f958=83f1;f959=9675;f95a=8b80;f95b=62cf;f95c=6a02;f95d=8afe;f95",
779
+ "e=4e39;f95f=5be7;f960=6012;f961=7387;f962=7570;f963=5317;f964=78fb;f965=4fbf;f966=5fa9;f967=4e0d;f968=6ccc;f969=6578;f96a=7d22;f96b=53c3;f96c=585e;f96d=7701;f96e=8449;f96f=8aaa;f970=6bba;f971=8fb0;f972=6c88;f973=62fe;f974=82e5;f975=63a0;f976=7565;f977=4eae;f978=5169;f979=51c9;f97a=6881;f97b=7ce7;f97c=826f;f97d=8ad2;f97e=91cf;f97f=52f5;f980=5442;f981=5973;f982=5eec;f983=65c5;f984=6ffe;f985=792a;f986=95ad;f987=9a6a;f988=9e97;f989=9ece;f98a=529b;f98b=66c6;f98c=6b77;f98d=8f62;f98e=5e74;f98f=6190;f990=6200;f991=649a;f992=6f23;f993=7149;f994=7489;f995=79ca;f996=7df4;f997=806f;f998=8f26;f999=84ee;f99a=9023;f99b=934a;f99c=5217;f99d=52a3;f99e=54bd;f99f=70c8;f9a0=88c2;f9a1=8aaa;f9a2=5ec9;f9a3=5ff5;f9a4=637b;f9a5=6bae;f9a6=7c3e;f9a7=7375;f9a8=4ee4;f9a9=56f9;f9aa=5be7;f9ab=5dba;f9ac=601c;f9ad=73b2;f9ae=7469;f9af=7f9a;f9b0=8046;f9b1=9234;f9b2=96f6;f9b3=9748;f9b4=9818;f9b5=4f8b;f9b6=79ae;f9b7=91b4;f9b8=96b8;f9b9=60e1;f9ba=4e86;f9bb=50da;f9bc=5bee;f9bd=5c3f;f9be=6599;f9bf=6a02;f9c0=71ce;f9c1=7642;f9c",
780
+ "2=84fc;f9c3=907c;f9c4=9f8d;f9c5=6688;f9c6=962e;f9c7=5289;f9c8=677b;f9c9=67f3;f9ca=6d41;f9cb=6e9c;f9cc=7409;f9cd=7559;f9ce=786b;f9cf=7d10;f9d0=985e;f9d1=516d;f9d2=622e;f9d3=9678;f9d4=502b;f9d5=5d19;f9d6=6dea;f9d7=8f2a;f9d8=5f8b;f9d9=6144;f9da=6817;f9db=7387;f9dc=9686;f9dd=5229;f9de=540f;f9df=5c65;f9e0=6613;f9e1=674e;f9e2=68a8;f9e3=6ce5;f9e4=7406;f9e5=75e2;f9e6=7f79;f9e7=88cf;f9e8=88e1;f9e9=91cc;f9ea=96e2;f9eb=533f;f9ec=6eba;f9ed=541d;f9ee=71d0;f9ef=7498;f9f0=85fa;f9f1=96a3;f9f2=9c57;f9f3=9e9f;f9f4=6797;f9f5=6dcb;f9f6=81e8;f9f7=7acb;f9f8=7b20;f9f9=7c92;f9fa=72c0;f9fb=7099;f9fc=8b58;f9fd=4ec0;f9fe=8336;f9ff=523a;fa00=5207;fa01=5ea6;fa02=62d3;fa03=7cd6;fa04=5b85;fa05=6d1e;fa06=66b4;fa07=8f3b;fa08=884c;fa09=964d;fa0a=898b;fa0b=5ed3;fa0c=5140;fa0d=55c0;fa10=585a;fa12=6674;fa15=51de;fa16=732a;fa17=76ca;fa18=793c;fa19=795e;fa1a=7965;fa1b=798f;fa1c=9756;fa1d=7cbe;fa1e=7fbd;fa20=8612;fa22=8af8;fa25=9038;fa26=90fd;fa2a=98ef;fa2b=98fc;fa2c=9928;fa2d=9db4;fa2e=90de;fa2f=96b7;fa30=4fae;fa31=50e7;fa3",
781
+ "2=514d;fa33=52c9;fa34=52e4;fa35=5351;fa36=559d;fa37=5606;fa38=5668;fa39=5840;fa3a=58a8;fa3b=5c64;fa3c=5c6e;fa3d=6094;fa3e=6168;fa3f=618e;fa40=61f2;fa41=654f;fa42=65e2;fa43=6691;fa44=6885;fa45=6d77;fa46=6e1a;fa47=6f22;fa48=716e;fa49=722b;fa4a=7422;fa4b=7891;fa4c=793e;fa4d=7949;fa4e=7948;fa4f=7950;fa50=7956;fa51=795d;fa52=798d;fa53=798e;fa54=7a40;fa55=7a81;fa56=7bc0;fa57=7df4;fa58=7e09;fa59=7e41;fa5a=7f72;fa5b=8005;fa5c=81ed;fa5d..fa5e=8279;fa5f=8457;fa60=8910;fa61=8996;fa62=8b01;fa63=8b39;fa64=8cd3;fa65=8d08;fa66=8fb6;fa67=9038;fa68=96e3;fa69=97ff;fa6a=983b;fa6b=6075;fa6c=242ee;fa6d=8218;fa70=4e26;fa71=51b5;fa72=5168;fa73=4f80;fa74=5145;fa75=5180;fa76=52c7;fa77=52fa;fa78=559d;fa79=5555;fa7a=5599;fa7b=55e2;fa7c=585a;fa7d=58b3;fa7e=5944;fa7f=5954;fa80=5a62;fa81=5b28;fa82=5ed2;fa83=5ed9;fa84=5f69;fa85=5fad;fa86=60d8;fa87=614e;fa88=6108;fa89=618e;fa8a=6160;fa8b=61f2;fa8c=6234;fa8d=63c4;fa8e=641c;fa8f=6452;fa90=6556;fa91=6674;fa92=6717;fa93=671b;fa94=6756;fa95=6b79;fa96=6bba;fa97=6d41;fa98=6",
782
+ "edb;fa99=6ecb;fa9a=6f22;fa9b=701e;fa9c=716e;fa9d=77a7;fa9e=7235;fa9f=72af;faa0=732a;faa1=7471;faa2=7506;faa3=753b;faa4=761d;faa5=761f;faa6=76ca;faa7=76db;faa8=76f4;faa9=774a;faaa=7740;faab=78cc;faac=7ab1;faad=7bc0;faae=7c7b;faaf=7d5b;fab0=7df4;fab1=7f3e;fab2=8005;fab3=8352;fab4=83ef;fab5=8779;fab6=8941;fab7=8986;fab8=8996;fab9=8abf;faba=8af8;fabb=8acb;fabc=8b01;fabd=8afe;fabe=8aed;fabf=8b39;fac0=8b8a;fac1=8d08;fac2=8f38;fac3=9072;fac4=9199;fac5=9276;fac6=967c;fac7=96e3;fac8=9756;fac9=97db;faca=97ff;facb=980b;facc=983b;facd=9b12;face=9f9c;facf=2284a;fad0=22844;fad1=233d5;fad2=3b9d;fad3=4018;fad4=4039;fad5=25249;fad6=25cd0;fad7=27ed3;fad8=9f43;fad9=9f8e;fb00=0066.0066;fb01=0066.0069;fb02=0066.006c;fb03=0066.0066.0069;fb04=0066.0066.006c;fb05..fb06=0073.0074;fb13=0574.0576;fb14=0574.0565;fb15=0574.056b;fb16=057e.0576;fb17=0574.056d;fb1d=05d9.05b4;fb1f=05f2.05b7;fb20=05e2;fb21=05d0;fb22=05d3;fb23=05d4;fb24=05db;fb25=05dc;fb26=05dd;fb27=05e8;fb28=05ea;fb29=002b;fb2a=05e9.05c1;fb2b=05e9.05c2",
783
+ ";fb2c=05e9.05bc.05c1;fb2d=05e9.05bc.05c2;fb2e=05d0.05b7;fb2f=05d0.05b8;fb30=05d0.05bc;fb31=05d1.05bc;fb32=05d2.05bc;fb33=05d3.05bc;fb34=05d4.05bc;fb35=05d5.05bc;fb36=05d6.05bc;fb38=05d8.05bc;fb39=05d9.05bc;fb3a=05da.05bc;fb3b=05db.05bc;fb3c=05dc.05bc;fb3e=05de.05bc;fb40=05e0.05bc;fb41=05e1.05bc;fb43=05e3.05bc;fb44=05e4.05bc;fb46=05e6.05bc;fb47=05e7.05bc;fb48=05e8.05bc;fb49=05e9.05bc;fb4a=05ea.05bc;fb4b=05d5.05b9;fb4c=05d1.05bf;fb4d=05db.05bf;fb4e=05e4.05bf;fb4f=05d0.05dc;fb50..fb51=0671;fb52..fb55=067b;fb56..fb59=067e;fb5a..fb5d=0680;fb5e..fb61=067a;fb62..fb65=067f;fb66..fb69=0679;fb6a..fb6d=06a4;fb6e..fb71=06a6;fb72..fb75=0684;fb76..fb79=0683;fb7a..fb7d=0686;fb7e..fb81=0687;fb82..fb83=068d;fb84..fb85=068c;fb86..fb87=068e;fb88..fb89=0688;fb8a..fb8b=0698;fb8c..fb8d=0691;fb8e..fb91=06a9;fb92..fb95=06af;fb96..fb99=06b3;fb9a..fb9d=06b1;fb9e..fb9f=06ba;fba0..fba3=06bb;fba4..fba5=06c0;fba6..fba9=06c1;fbaa..fbad=06be;fbae..fbaf=06d2;fbb0..fbb1=06d3;fbd3..fbd6=06ad;fbd7..fbd8=06c7;fbd9..fbda=0",
784
+ "6c6;fbdb..fbdc=06c8;fbdd=06c7.0674;fbde..fbdf=06cb;fbe0..fbe1=06c5;fbe2..fbe3=06c9;fbe4..fbe7=06d0;fbe8..fbe9=0649;fbea..fbeb=0626.0627;fbec..fbed=0626.06d5;fbee..fbef=0626.0648;fbf0..fbf1=0626.06c7;fbf2..fbf3=0626.06c6;fbf4..fbf5=0626.06c8;fbf6..fbf8=0626.06d0;fbf9..fbfb=0626.0649;fbfc..fbff=06cc;fc00=0626.062c;fc01=0626.062d;fc02=0626.0645;fc03=0626.0649;fc04=0626.064a;fc05=0628.062c;fc06=0628.062d;fc07=0628.062e;fc08=0628.0645;fc09=0628.0649;fc0a=0628.064a;fc0b=062a.062c;fc0c=062a.062d;fc0d=062a.062e;fc0e=062a.0645;fc0f=062a.0649;fc10=062a.064a;fc11=062b.062c;fc12=062b.0645;fc13=062b.0649;fc14=062b.064a;fc15=062c.062d;fc16=062c.0645;fc17=062d.062c;fc18=062d.0645;fc19=062e.062c;fc1a=062e.062d;fc1b=062e.0645;fc1c=0633.062c;fc1d=0633.062d;fc1e=0633.062e;fc1f=0633.0645;fc20=0635.062d;fc21=0635.0645;fc22=0636.062c;fc23=0636.062d;fc24=0636.062e;fc25=0636.0645;fc26=0637.062d;fc27=0637.0645;fc28=0638.0645;fc29=0639.062c;fc2a=0639.0645;fc2b=063a.062c;fc2c=063a.0645;fc2d=0641.062c;fc2e=0641.0",
785
+ "62d;fc2f=0641.062e;fc30=0641.0645;fc31=0641.0649;fc32=0641.064a;fc33=0642.062d;fc34=0642.0645;fc35=0642.0649;fc36=0642.064a;fc37=0643.0627;fc38=0643.062c;fc39=0643.062d;fc3a=0643.062e;fc3b=0643.0644;fc3c=0643.0645;fc3d=0643.0649;fc3e=0643.064a;fc3f=0644.062c;fc40=0644.062d;fc41=0644.062e;fc42=0644.0645;fc43=0644.0649;fc44=0644.064a;fc45=0645.062c;fc46=0645.062d;fc47=0645.062e;fc48=0645.0645;fc49=0645.0649;fc4a=0645.064a;fc4b=0646.062c;fc4c=0646.062d;fc4d=0646.062e;fc4e=0646.0645;fc4f=0646.0649;fc50=0646.064a;fc51=0647.062c;fc52=0647.0645;fc53=0647.0649;fc54=0647.064a;fc55=064a.062c;fc56=064a.062d;fc57=064a.062e;fc58=064a.0645;fc59=064a.0649;fc5a=064a.064a;fc5b=0630.0670;fc5c=0631.0670;fc5d=0649.0670;fc5e=0020.064c.0651;fc5f=0020.064d.0651;fc60=0020.064e.0651;fc61=0020.064f.0651;fc62=0020.0650.0651;fc63=0020.0651.0670;fc64=0626.0631;fc65=0626.0632;fc66=0626.0645;fc67=0626.0646;fc68=0626.0649;fc69=0626.064a;fc6a=0628.0631;fc6b=0628.0632;fc6c=0628.0645;fc6d=0628.0646;fc6e=0628.0649;fc6f=0",
786
+ "628.064a;fc70=062a.0631;fc71=062a.0632;fc72=062a.0645;fc73=062a.0646;fc74=062a.0649;fc75=062a.064a;fc76=062b.0631;fc77=062b.0632;fc78=062b.0645;fc79=062b.0646;fc7a=062b.0649;fc7b=062b.064a;fc7c=0641.0649;fc7d=0641.064a;fc7e=0642.0649;fc7f=0642.064a;fc80=0643.0627;fc81=0643.0644;fc82=0643.0645;fc83=0643.0649;fc84=0643.064a;fc85=0644.0645;fc86=0644.0649;fc87=0644.064a;fc88=0645.0627;fc89=0645.0645;fc8a=0646.0631;fc8b=0646.0632;fc8c=0646.0645;fc8d=0646.0646;fc8e=0646.0649;fc8f=0646.064a;fc90=0649.0670;fc91=064a.0631;fc92=064a.0632;fc93=064a.0645;fc94=064a.0646;fc95=064a.0649;fc96=064a.064a;fc97=0626.062c;fc98=0626.062d;fc99=0626.062e;fc9a=0626.0645;fc9b=0626.0647;fc9c=0628.062c;fc9d=0628.062d;fc9e=0628.062e;fc9f=0628.0645;fca0=0628.0647;fca1=062a.062c;fca2=062a.062d;fca3=062a.062e;fca4=062a.0645;fca5=062a.0647;fca6=062b.0645;fca7=062c.062d;fca8=062c.0645;fca9=062d.062c;fcaa=062d.0645;fcab=062e.062c;fcac=062e.0645;fcad=0633.062c;fcae=0633.062d;fcaf=0633.062e;fcb0=0633.0645;fcb1=0635.062d;f",
787
+ "cb2=0635.062e;fcb3=0635.0645;fcb4=0636.062c;fcb5=0636.062d;fcb6=0636.062e;fcb7=0636.0645;fcb8=0637.062d;fcb9=0638.0645;fcba=0639.062c;fcbb=0639.0645;fcbc=063a.062c;fcbd=063a.0645;fcbe=0641.062c;fcbf=0641.062d;fcc0=0641.062e;fcc1=0641.0645;fcc2=0642.062d;fcc3=0642.0645;fcc4=0643.062c;fcc5=0643.062d;fcc6=0643.062e;fcc7=0643.0644;fcc8=0643.0645;fcc9=0644.062c;fcca=0644.062d;fccb=0644.062e;fccc=0644.0645;fccd=0644.0647;fcce=0645.062c;fccf=0645.062d;fcd0=0645.062e;fcd1=0645.0645;fcd2=0646.062c;fcd3=0646.062d;fcd4=0646.062e;fcd5=0646.0645;fcd6=0646.0647;fcd7=0647.062c;fcd8=0647.0645;fcd9=0647.0670;fcda=064a.062c;fcdb=064a.062d;fcdc=064a.062e;fcdd=064a.0645;fcde=064a.0647;fcdf=0626.0645;fce0=0626.0647;fce1=0628.0645;fce2=0628.0647;fce3=062a.0645;fce4=062a.0647;fce5=062b.0645;fce6=062b.0647;fce7=0633.0645;fce8=0633.0647;fce9=0634.0645;fcea=0634.0647;fceb=0643.0644;fcec=0643.0645;fced=0644.0645;fcee=0646.0645;fcef=0646.0647;fcf0=064a.0645;fcf1=064a.0647;fcf2=0640.064e.0651;fcf3=0640.064f.0651;f",
788
+ "cf4=0640.0650.0651;fcf5=0637.0649;fcf6=0637.064a;fcf7=0639.0649;fcf8=0639.064a;fcf9=063a.0649;fcfa=063a.064a;fcfb=0633.0649;fcfc=0633.064a;fcfd=0634.0649;fcfe=0634.064a;fcff=062d.0649;fd00=062d.064a;fd01=062c.0649;fd02=062c.064a;fd03=062e.0649;fd04=062e.064a;fd05=0635.0649;fd06=0635.064a;fd07=0636.0649;fd08=0636.064a;fd09=0634.062c;fd0a=0634.062d;fd0b=0634.062e;fd0c=0634.0645;fd0d=0634.0631;fd0e=0633.0631;fd0f=0635.0631;fd10=0636.0631;fd11=0637.0649;fd12=0637.064a;fd13=0639.0649;fd14=0639.064a;fd15=063a.0649;fd16=063a.064a;fd17=0633.0649;fd18=0633.064a;fd19=0634.0649;fd1a=0634.064a;fd1b=062d.0649;fd1c=062d.064a;fd1d=062c.0649;fd1e=062c.064a;fd1f=062e.0649;fd20=062e.064a;fd21=0635.0649;fd22=0635.064a;fd23=0636.0649;fd24=0636.064a;fd25=0634.062c;fd26=0634.062d;fd27=0634.062e;fd28=0634.0645;fd29=0634.0631;fd2a=0633.0631;fd2b=0635.0631;fd2c=0636.0631;fd2d=0634.062c;fd2e=0634.062d;fd2f=0634.062e;fd30=0634.0645;fd31=0633.0647;fd32=0634.0647;fd33=0637.0645;fd34=0633.062c;fd35=0633.062d;fd36=0",
789
+ "633.062e;fd37=0634.062c;fd38=0634.062d;fd39=0634.062e;fd3a=0637.0645;fd3b=0638.0645;fd3c..fd3d=0627.064b;fd50=062a.062c.0645;fd51..fd52=062a.062d.062c;fd53=062a.062d.0645;fd54=062a.062e.0645;fd55=062a.0645.062c;fd56=062a.0645.062d;fd57=062a.0645.062e;fd58..fd59=062c.0645.062d;fd5a=062d.0645.064a;fd5b=062d.0645.0649;fd5c=0633.062d.062c;fd5d=0633.062c.062d;fd5e=0633.062c.0649;fd5f..fd60=0633.0645.062d;fd61=0633.0645.062c;fd62..fd63=0633.0645.0645;fd64..fd65=0635.062d.062d;fd66=0635.0645.0645;fd67..fd68=0634.062d.0645;fd69=0634.062c.064a;fd6a..fd6b=0634.0645.062e;fd6c..fd6d=0634.0645.0645;fd6e=0636.062d.0649;fd6f..fd70=0636.062e.0645;fd71..fd72=0637.0645.062d;fd73=0637.0645.0645;fd74=0637.0645.064a;fd75=0639.062c.0645;fd76..fd77=0639.0645.0645;fd78=0639.0645.0649;fd79=063a.0645.0645;fd7a=063a.0645.064a;fd7b=063a.0645.0649;fd7c..fd7d=0641.062e.0645;fd7e=0642.0645.062d;fd7f=0642.0645.0645;fd80=0644.062d.0645;fd81=0644.062d.064a;fd82=0644.062d.0649;fd83..fd84=0644.062c.062c;fd85..fd86=0644.0",
790
+ "62e.0645;fd87..fd88=0644.0645.062d;fd89=0645.062d.062c;fd8a=0645.062d.0645;fd8b=0645.062d.064a;fd8c=0645.062c.062d;fd8d=0645.062c.0645;fd8e=0645.062e.062c;fd8f=0645.062e.0645;fd92=0645.062c.062e;fd93=0647.0645.062c;fd94=0647.0645.0645;fd95=0646.062d.0645;fd96=0646.062d.0649;fd97..fd98=0646.062c.0645;fd99=0646.062c.0649;fd9a=0646.0645.064a;fd9b=0646.0645.0649;fd9c..fd9d=064a.0645.0645;fd9e=0628.062e.064a;fd9f=062a.062c.064a;fda0=062a.062c.0649;fda1=062a.062e.064a;fda2=062a.062e.0649;fda3=062a.0645.064a;fda4=062a.0645.0649;fda5=062c.0645.064a;fda6=062c.062d.0649;fda7=062c.0645.0649;fda8=0633.062e.0649;fda9=0635.062d.064a;fdaa=0634.062d.064a;fdab=0636.062d.064a;fdac=0644.062c.064a;fdad=0644.0645.064a;fdae=064a.062d.064a;fdaf=064a.062c.064a;fdb0=064a.0645.064a;fdb1=0645.0645.064a;fdb2=0642.0645.064a;fdb3=0646.062d.064a;fdb4=0642.0645.062d;fdb5=0644.062d.0645;fdb6=0639.0645.064a;fdb7=0643.0645.064a;fdb8=0646.062c.062d;fdb9=0645.062e.064a;fdba=0644.062c.0645;fdbb=0643.0645.0645;fdbc=0644.062",
791
+ "c.0645;fdbd=0646.062c.062d;fdbe=062c.062d.064a;fdbf=062d.062c.064a;fdc0=0645.062c.064a;fdc1=0641.0645.064a;fdc2=0628.062d.064a;fdc3=0643.0645.0645;fdc4=0639.062c.0645;fdc5=0635.0645.0645;fdc6=0633.062e.064a;fdc7=0646.062c.064a;fdf0=0635.0644.06d2;fdf1=0642.0644.06d2;fdf2=0627.0644.0644.0647;fdf3=0627.0643.0628.0631;fdf4=0645.062d.0645.062f;fdf5=0635.0644.0639.0645;fdf6=0631.0633.0648.0644;fdf7=0639.0644.064a.0647;fdf8=0648.0633.0644.0645;fdf9=0635.0644.0649;fdfa=0635.0644.0649.0020.0627.0644.0644.0647.0020.0639.0644.064a.0647.0020.0648.0633.0644.0645;fdfb=062c.0644.0020.062c.0644.0627.0644.0647;fdfc=0631.06cc.0627.0644;fe00..fe0f=;fe10=002c;fe11=3001;fe12=3002;fe13=003a;fe14=003b;fe15=0021;fe16=003f;fe17=3016;fe18=3017;fe19=002e.002e.002e;fe30=002e.002e;fe31=2014;fe32=2013;fe33..fe34=005f;fe35=0028;fe36=0029;fe37=007b;fe38=007d;fe39=3014;fe3a=3015;fe3b=3010;fe3c=3011;fe3d=300a;fe3e=300b;fe3f=3008;fe40=3009;fe41=300c;fe42=300d;fe43=300e;fe44=300f;fe47=005b;fe48=005d;fe49..fe4c=0020.0305",
792
+ ";fe4d..fe4f=005f;fe50=002c;fe51=3001;fe52=002e;fe54=003b;fe55=003a;fe56=003f;fe57=0021;fe58=2014;fe59=0028;fe5a=0029;fe5b=007b;fe5c=007d;fe5d=3014;fe5e=3015;fe5f=0023;fe60=0026;fe61=002a;fe62=002b;fe63=002d;fe64=003c;fe65=003e;fe66=003d;fe68=005c;fe69=0024;fe6a=0025;fe6b=0040;fe70=0020.064b;fe71=0640.064b;fe72=0020.064c;fe74=0020.064d;fe76=0020.064e;fe77=0640.064e;fe78=0020.064f;fe79=0640.064f;fe7a=0020.0650;fe7b=0640.0650;fe7c=0020.0651;fe7d=0640.0651;fe7e=0020.0652;fe7f=0640.0652;fe80=0621;fe81..fe82=0622;fe83..fe84=0623;fe85..fe86=0624;fe87..fe88=0625;fe89..fe8c=0626;fe8d..fe8e=0627;fe8f..fe92=0628;fe93..fe94=0629;fe95..fe98=062a;fe99..fe9c=062b;fe9d..fea0=062c;fea1..fea4=062d;fea5..fea8=062e;fea9..feaa=062f;feab..feac=0630;fead..feae=0631;feaf..feb0=0632;feb1..feb4=0633;feb5..feb8=0634;feb9..febc=0635;febd..fec0=0636;fec1..fec4=0637;fec5..fec8=0638;fec9..fecc=0639;fecd..fed0=063a;fed1..fed4=0641;fed5..fed8=0642;fed9..fedc=0643;fedd..fee0=0644;fee1..fee4=0645;fee5..fee8=0646;fee9..f",
793
+ "eec=0647;feed..feee=0648;feef..fef0=0649;fef1..fef4=064a;fef5..fef6=0644.0622;fef7..fef8=0644.0623;fef9..fefa=0644.0625;fefb..fefc=0644.0627;feff=;ff01=0021;ff02=0022;ff03=0023;ff04=0024;ff05=0025;ff06=0026;ff07=0027;ff08=0028;ff09=0029;ff0a=002a;ff0b=002b;ff0c=002c;ff0d=002d;ff0e=002e;ff0f=002f;ff10=0030;ff11=0031;ff12=0032;ff13=0033;ff14=0034;ff15=0035;ff16=0036;ff17=0037;ff18=0038;ff19=0039;ff1a=003a;ff1b=003b;ff1c=003c;ff1d=003d;ff1e=003e;ff1f=003f;ff20=0040;ff21=0061;ff22=0062;ff23=0063;ff24=0064;ff25=0065;ff26=0066;ff27=0067;ff28=0068;ff29=0069;ff2a=006a;ff2b=006b;ff2c=006c;ff2d=006d;ff2e=006e;ff2f=006f;ff30=0070;ff31=0071;ff32=0072;ff33=0073;ff34=0074;ff35=0075;ff36=0076;ff37=0077;ff38=0078;ff39=0079;ff3a=007a;ff3b=005b;ff3c=005c;ff3d=005d;ff3e=005e;ff3f=005f;ff40=0060;ff41=0061;ff42=0062;ff43=0063;ff44=0064;ff45=0065;ff46=0066;ff47=0067;ff48=0068;ff49=0069;ff4a=006a;ff4b=006b;ff4c=006c;ff4d=006d;ff4e=006e;ff4f=006f;ff50=0070;ff51=0071;ff52=0072;ff53=0073;ff54=0074;ff55=0075;ff5",
794
+ "6=0076;ff57=0077;ff58=0078;ff59=0079;ff5a=007a;ff5b=007b;ff5c=007c;ff5d=007d;ff5e=007e;ff5f=2985;ff60=2986;ff61=3002;ff62=300c;ff63=300d;ff64=3001;ff65=30fb;ff66=30f2;ff67=30a1;ff68=30a3;ff69=30a5;ff6a=30a7;ff6b=30a9;ff6c=30e3;ff6d=30e5;ff6e=30e7;ff6f=30c3;ff70=30fc;ff71=30a2;ff72=30a4;ff73=30a6;ff74=30a8;ff75=30aa;ff76=30ab;ff77=30ad;ff78=30af;ff79=30b1;ff7a=30b3;ff7b=30b5;ff7c=30b7;ff7d=30b9;ff7e=30bb;ff7f=30bd;ff80=30bf;ff81=30c1;ff82=30c4;ff83=30c6;ff84=30c8;ff85=30ca;ff86=30cb;ff87=30cc;ff88=30cd;ff89=30ce;ff8a=30cf;ff8b=30d2;ff8c=30d5;ff8d=30d8;ff8e=30db;ff8f=30de;ff90=30df;ff91=30e0;ff92=30e1;ff93=30e2;ff94=30e4;ff95=30e6;ff96=30e8;ff97=30e9;ff98=30ea;ff99=30eb;ff9a=30ec;ff9b=30ed;ff9c=30ef;ff9d=30f3;ff9e=3099;ff9f=309a;ffa0=;ffa1=1100;ffa2=1101;ffa3=11aa;ffa4=1102;ffa5=11ac;ffa6=11ad;ffa7=1103;ffa8=1104;ffa9=1105;ffaa=11b0;ffab=11b1;ffac=11b2;ffad=11b3;ffae=11b4;ffaf=11b5;ffb0=111a;ffb1=1106;ffb2=1107;ffb3=1108;ffb4=1121;ffb5=1109;ffb6=110a;ffb7=110b;ffb8=110c;ffb9=110d;ffba=11",
795
+ "0e;ffbb=110f;ffbc=1110;ffbd=1111;ffbe=1112;ffc2=1161;ffc3=1162;ffc4=1163;ffc5=1164;ffc6=1165;ffc7=1166;ffca=1167;ffcb=1168;ffcc=1169;ffcd=116a;ffce=116b;ffcf=116c;ffd2=116d;ffd3=116e;ffd4=116f;ffd5=1170;ffd6=1171;ffd7=1172;ffda=1173;ffdb=1174;ffdc=1175;ffe0=00a2;ffe1=00a3;ffe2=00ac;ffe3=0020.0304;ffe4=00a6;ffe5=00a5;ffe6=20a9;ffe8=2502;ffe9=2190;ffea=2191;ffeb=2192;ffec=2193;ffed=25a0;ffee=25cb;fff0..fff8=;10400=10428;10401=10429;10402=1042a;10403=1042b;10404=1042c;10405=1042d;10406=1042e;10407=1042f;10408=10430;10409=10431;1040a=10432;1040b=10433;1040c=10434;1040d=10435;1040e=10436;1040f=10437;10410=10438;10411=10439;10412=1043a;10413=1043b;10414=1043c;10415=1043d;10416=1043e;10417=1043f;10418=10440;10419=10441;1041a=10442;1041b=10443;1041c=10444;1041d=10445;1041e=10446;1041f=10447;10420=10448;10421=10449;10422=1044a;10423=1044b;10424=1044c;10425=1044d;10426=1044e;10427=1044f;104b0=104d8;104b1=104d9;104b2=104da;104b3=104db;104b4=104dc;104b5=104dd;104b6=104de;104b7=104df;104b8=104e0;10",
796
+ "4b9=104e1;104ba=104e2;104bb=104e3;104bc=104e4;104bd=104e5;104be=104e6;104bf=104e7;104c0=104e8;104c1=104e9;104c2=104ea;104c3=104eb;104c4=104ec;104c5=104ed;104c6=104ee;104c7=104ef;104c8=104f0;104c9=104f1;104ca=104f2;104cb=104f3;104cc=104f4;104cd=104f5;104ce=104f6;104cf=104f7;104d0=104f8;104d1=104f9;104d2=104fa;104d3=104fb;10570=10597;10571=10598;10572=10599;10573=1059a;10574=1059b;10575=1059c;10576=1059d;10577=1059e;10578=1059f;10579=105a0;1057a=105a1;1057c=105a3;1057d=105a4;1057e=105a5;1057f=105a6;10580=105a7;10581=105a8;10582=105a9;10583=105aa;10584=105ab;10585=105ac;10586=105ad;10587=105ae;10588=105af;10589=105b0;1058a=105b1;1058c=105b3;1058d=105b4;1058e=105b5;1058f=105b6;10590=105b7;10591=105b8;10592=105b9;10594=105bb;10595=105bc;10781=02d0;10782=02d1;10783=00e6;10784=0299;10785=0253;10787=02a3;10788=ab66;10789=02a5;1078a=02a4;1078b=0256;1078c=0257;1078d=1d91;1078e=0258;1078f=025e;10790=02a9;10791=0264;10792=0262;10793=0260;10794=029b;10795=0127;10796=029c;10797=0267;10798=0284;10799",
797
+ "=02aa;1079a=02ab;1079b=026c;1079c=1df04;1079d=a78e;1079e=026e;1079f=1df05;107a0=028e;107a1=1df06;107a2=00f8;107a3=0276;107a4=0277;107a5=0071;107a6=027a;107a7=1df08;107a8=027d;107a9=027e;107aa=0280;107ab=02a8;107ac=02a6;107ad=ab67;107ae=02a7;107af=0288;107b0=2c71;107b2=028f;107b3=02a1;107b4=02a2;107b5=0298;107b6=01c0;107b7=01c1;107b8=01c2;107b9=1df0a;107ba=1df1e;10c80=10cc0;10c81=10cc1;10c82=10cc2;10c83=10cc3;10c84=10cc4;10c85=10cc5;10c86=10cc6;10c87=10cc7;10c88=10cc8;10c89=10cc9;10c8a=10cca;10c8b=10ccb;10c8c=10ccc;10c8d=10ccd;10c8e=10cce;10c8f=10ccf;10c90=10cd0;10c91=10cd1;10c92=10cd2;10c93=10cd3;10c94=10cd4;10c95=10cd5;10c96=10cd6;10c97=10cd7;10c98=10cd8;10c99=10cd9;10c9a=10cda;10c9b=10cdb;10c9c=10cdc;10c9d=10cdd;10c9e=10cde;10c9f=10cdf;10ca0=10ce0;10ca1=10ce1;10ca2=10ce2;10ca3=10ce3;10ca4=10ce4;10ca5=10ce5;10ca6=10ce6;10ca7=10ce7;10ca8=10ce8;10ca9=10ce9;10caa=10cea;10cab=10ceb;10cac=10cec;10cad=10ced;10cae=10cee;10caf=10cef;10cb0=10cf0;10cb1=10cf1;10cb2=10cf2;10d50=10d70;10d51=10d71;",
798
+ "10d52=10d72;10d53=10d73;10d54=10d74;10d55=10d75;10d56=10d76;10d57=10d77;10d58=10d78;10d59=10d79;10d5a=10d7a;10d5b=10d7b;10d5c=10d7c;10d5d=10d7d;10d5e=10d7e;10d5f=10d7f;10d60=10d80;10d61=10d81;10d62=10d82;10d63=10d83;10d64=10d84;10d65=10d85;118a0=118c0;118a1=118c1;118a2=118c2;118a3=118c3;118a4=118c4;118a5=118c5;118a6=118c6;118a7=118c7;118a8=118c8;118a9=118c9;118aa=118ca;118ab=118cb;118ac=118cc;118ad=118cd;118ae=118ce;118af=118cf;118b0=118d0;118b1=118d1;118b2=118d2;118b3=118d3;118b4=118d4;118b5=118d5;118b6=118d6;118b7=118d7;118b8=118d8;118b9=118d9;118ba=118da;118bb=118db;118bc=118dc;118bd=118dd;118be=118de;118bf=118df;16e40=16e60;16e41=16e61;16e42=16e62;16e43=16e63;16e44=16e64;16e45=16e65;16e46=16e66;16e47=16e67;16e48=16e68;16e49=16e69;16e4a=16e6a;16e4b=16e6b;16e4c=16e6c;16e4d=16e6d;16e4e=16e6e;16e4f=16e6f;16e50=16e70;16e51=16e71;16e52=16e72;16e53=16e73;16e54=16e74;16e55=16e75;16e56=16e76;16e57=16e77;16e58=16e78;16e59=16e79;16e5a=16e7a;16e5b=16e7b;16e5c=16e7c;16e5d=16e7d;16e5e=16e7e;16e5",
799
+ "f=16e7f;16ea0=16ebb;16ea1=16ebc;16ea2=16ebd;16ea3=16ebe;16ea4=16ebf;16ea5=16ec0;16ea6=16ec1;16ea7=16ec2;16ea8=16ec3;16ea9=16ec4;16eaa=16ec5;16eab=16ec6;16eac=16ec7;16ead=16ec8;16eae=16ec9;16eaf=16eca;16eb0=16ecb;16eb1=16ecc;16eb2=16ecd;16eb3=16ece;16eb4=16ecf;16eb5=16ed0;16eb6=16ed1;16eb7=16ed2;16eb8=16ed3;1bca0..1bca3=;1ccd6=0061;1ccd7=0062;1ccd8=0063;1ccd9=0064;1ccda=0065;1ccdb=0066;1ccdc=0067;1ccdd=0068;1ccde=0069;1ccdf=006a;1cce0=006b;1cce1=006c;1cce2=006d;1cce3=006e;1cce4=006f;1cce5=0070;1cce6=0071;1cce7=0072;1cce8=0073;1cce9=0074;1ccea=0075;1cceb=0076;1ccec=0077;1cced=0078;1ccee=0079;1ccef=007a;1ccf0=0030;1ccf1=0031;1ccf2=0032;1ccf3=0033;1ccf4=0034;1ccf5=0035;1ccf6=0036;1ccf7=0037;1ccf8=0038;1ccf9=0039;1d15e=1d157.1d165;1d15f=1d158.1d165;1d160=1d158.1d165.1d16e;1d161=1d158.1d165.1d16f;1d162=1d158.1d165.1d170;1d163=1d158.1d165.1d171;1d164=1d158.1d165.1d172;1d173..1d17a=;1d1bb=1d1b9.1d165;1d1bc=1d1ba.1d165;1d1bd=1d1b9.1d165.1d16e;1d1be=1d1ba.1d165.1d16e;1d1bf=1d1b9.1d165.1d16f;1d1c",
800
+ "0=1d1ba.1d165.1d16f;1d400=0061;1d401=0062;1d402=0063;1d403=0064;1d404=0065;1d405=0066;1d406=0067;1d407=0068;1d408=0069;1d409=006a;1d40a=006b;1d40b=006c;1d40c=006d;1d40d=006e;1d40e=006f;1d40f=0070;1d410=0071;1d411=0072;1d412=0073;1d413=0074;1d414=0075;1d415=0076;1d416=0077;1d417=0078;1d418=0079;1d419=007a;1d41a=0061;1d41b=0062;1d41c=0063;1d41d=0064;1d41e=0065;1d41f=0066;1d420=0067;1d421=0068;1d422=0069;1d423=006a;1d424=006b;1d425=006c;1d426=006d;1d427=006e;1d428=006f;1d429=0070;1d42a=0071;1d42b=0072;1d42c=0073;1d42d=0074;1d42e=0075;1d42f=0076;1d430=0077;1d431=0078;1d432=0079;1d433=007a;1d434=0061;1d435=0062;1d436=0063;1d437=0064;1d438=0065;1d439=0066;1d43a=0067;1d43b=0068;1d43c=0069;1d43d=006a;1d43e=006b;1d43f=006c;1d440=006d;1d441=006e;1d442=006f;1d443=0070;1d444=0071;1d445=0072;1d446=0073;1d447=0074;1d448=0075;1d449=0076;1d44a=0077;1d44b=0078;1d44c=0079;1d44d=007a;1d44e=0061;1d44f=0062;1d450=0063;1d451=0064;1d452=0065;1d453=0066;1d454=0067;1d456=0069;1d457=006a;1d458=006b;1d459=006c;1",
801
+ "d45a=006d;1d45b=006e;1d45c=006f;1d45d=0070;1d45e=0071;1d45f=0072;1d460=0073;1d461=0074;1d462=0075;1d463=0076;1d464=0077;1d465=0078;1d466=0079;1d467=007a;1d468=0061;1d469=0062;1d46a=0063;1d46b=0064;1d46c=0065;1d46d=0066;1d46e=0067;1d46f=0068;1d470=0069;1d471=006a;1d472=006b;1d473=006c;1d474=006d;1d475=006e;1d476=006f;1d477=0070;1d478=0071;1d479=0072;1d47a=0073;1d47b=0074;1d47c=0075;1d47d=0076;1d47e=0077;1d47f=0078;1d480=0079;1d481=007a;1d482=0061;1d483=0062;1d484=0063;1d485=0064;1d486=0065;1d487=0066;1d488=0067;1d489=0068;1d48a=0069;1d48b=006a;1d48c=006b;1d48d=006c;1d48e=006d;1d48f=006e;1d490=006f;1d491=0070;1d492=0071;1d493=0072;1d494=0073;1d495=0074;1d496=0075;1d497=0076;1d498=0077;1d499=0078;1d49a=0079;1d49b=007a;1d49c=0061;1d49e=0063;1d49f=0064;1d4a2=0067;1d4a5=006a;1d4a6=006b;1d4a9=006e;1d4aa=006f;1d4ab=0070;1d4ac=0071;1d4ae=0073;1d4af=0074;1d4b0=0075;1d4b1=0076;1d4b2=0077;1d4b3=0078;1d4b4=0079;1d4b5=007a;1d4b6=0061;1d4b7=0062;1d4b8=0063;1d4b9=0064;1d4bb=0066;1d4bd=0068;1d4be=0069;",
802
+ "1d4bf=006a;1d4c0=006b;1d4c1=006c;1d4c2=006d;1d4c3=006e;1d4c5=0070;1d4c6=0071;1d4c7=0072;1d4c8=0073;1d4c9=0074;1d4ca=0075;1d4cb=0076;1d4cc=0077;1d4cd=0078;1d4ce=0079;1d4cf=007a;1d4d0=0061;1d4d1=0062;1d4d2=0063;1d4d3=0064;1d4d4=0065;1d4d5=0066;1d4d6=0067;1d4d7=0068;1d4d8=0069;1d4d9=006a;1d4da=006b;1d4db=006c;1d4dc=006d;1d4dd=006e;1d4de=006f;1d4df=0070;1d4e0=0071;1d4e1=0072;1d4e2=0073;1d4e3=0074;1d4e4=0075;1d4e5=0076;1d4e6=0077;1d4e7=0078;1d4e8=0079;1d4e9=007a;1d4ea=0061;1d4eb=0062;1d4ec=0063;1d4ed=0064;1d4ee=0065;1d4ef=0066;1d4f0=0067;1d4f1=0068;1d4f2=0069;1d4f3=006a;1d4f4=006b;1d4f5=006c;1d4f6=006d;1d4f7=006e;1d4f8=006f;1d4f9=0070;1d4fa=0071;1d4fb=0072;1d4fc=0073;1d4fd=0074;1d4fe=0075;1d4ff=0076;1d500=0077;1d501=0078;1d502=0079;1d503=007a;1d504=0061;1d505=0062;1d507=0064;1d508=0065;1d509=0066;1d50a=0067;1d50d=006a;1d50e=006b;1d50f=006c;1d510=006d;1d511=006e;1d512=006f;1d513=0070;1d514=0071;1d516=0073;1d517=0074;1d518=0075;1d519=0076;1d51a=0077;1d51b=0078;1d51c=0079;1d51e=0061;1d51f=0062",
803
+ ";1d520=0063;1d521=0064;1d522=0065;1d523=0066;1d524=0067;1d525=0068;1d526=0069;1d527=006a;1d528=006b;1d529=006c;1d52a=006d;1d52b=006e;1d52c=006f;1d52d=0070;1d52e=0071;1d52f=0072;1d530=0073;1d531=0074;1d532=0075;1d533=0076;1d534=0077;1d535=0078;1d536=0079;1d537=007a;1d538=0061;1d539=0062;1d53b=0064;1d53c=0065;1d53d=0066;1d53e=0067;1d540=0069;1d541=006a;1d542=006b;1d543=006c;1d544=006d;1d546=006f;1d54a=0073;1d54b=0074;1d54c=0075;1d54d=0076;1d54e=0077;1d54f=0078;1d550=0079;1d552=0061;1d553=0062;1d554=0063;1d555=0064;1d556=0065;1d557=0066;1d558=0067;1d559=0068;1d55a=0069;1d55b=006a;1d55c=006b;1d55d=006c;1d55e=006d;1d55f=006e;1d560=006f;1d561=0070;1d562=0071;1d563=0072;1d564=0073;1d565=0074;1d566=0075;1d567=0076;1d568=0077;1d569=0078;1d56a=0079;1d56b=007a;1d56c=0061;1d56d=0062;1d56e=0063;1d56f=0064;1d570=0065;1d571=0066;1d572=0067;1d573=0068;1d574=0069;1d575=006a;1d576=006b;1d577=006c;1d578=006d;1d579=006e;1d57a=006f;1d57b=0070;1d57c=0071;1d57d=0072;1d57e=0073;1d57f=0074;1d580=0075;1d581=007",
804
+ "6;1d582=0077;1d583=0078;1d584=0079;1d585=007a;1d586=0061;1d587=0062;1d588=0063;1d589=0064;1d58a=0065;1d58b=0066;1d58c=0067;1d58d=0068;1d58e=0069;1d58f=006a;1d590=006b;1d591=006c;1d592=006d;1d593=006e;1d594=006f;1d595=0070;1d596=0071;1d597=0072;1d598=0073;1d599=0074;1d59a=0075;1d59b=0076;1d59c=0077;1d59d=0078;1d59e=0079;1d59f=007a;1d5a0=0061;1d5a1=0062;1d5a2=0063;1d5a3=0064;1d5a4=0065;1d5a5=0066;1d5a6=0067;1d5a7=0068;1d5a8=0069;1d5a9=006a;1d5aa=006b;1d5ab=006c;1d5ac=006d;1d5ad=006e;1d5ae=006f;1d5af=0070;1d5b0=0071;1d5b1=0072;1d5b2=0073;1d5b3=0074;1d5b4=0075;1d5b5=0076;1d5b6=0077;1d5b7=0078;1d5b8=0079;1d5b9=007a;1d5ba=0061;1d5bb=0062;1d5bc=0063;1d5bd=0064;1d5be=0065;1d5bf=0066;1d5c0=0067;1d5c1=0068;1d5c2=0069;1d5c3=006a;1d5c4=006b;1d5c5=006c;1d5c6=006d;1d5c7=006e;1d5c8=006f;1d5c9=0070;1d5ca=0071;1d5cb=0072;1d5cc=0073;1d5cd=0074;1d5ce=0075;1d5cf=0076;1d5d0=0077;1d5d1=0078;1d5d2=0079;1d5d3=007a;1d5d4=0061;1d5d5=0062;1d5d6=0063;1d5d7=0064;1d5d8=0065;1d5d9=0066;1d5da=0067;1d5db=0068;1d5dc=00",
805
+ "69;1d5dd=006a;1d5de=006b;1d5df=006c;1d5e0=006d;1d5e1=006e;1d5e2=006f;1d5e3=0070;1d5e4=0071;1d5e5=0072;1d5e6=0073;1d5e7=0074;1d5e8=0075;1d5e9=0076;1d5ea=0077;1d5eb=0078;1d5ec=0079;1d5ed=007a;1d5ee=0061;1d5ef=0062;1d5f0=0063;1d5f1=0064;1d5f2=0065;1d5f3=0066;1d5f4=0067;1d5f5=0068;1d5f6=0069;1d5f7=006a;1d5f8=006b;1d5f9=006c;1d5fa=006d;1d5fb=006e;1d5fc=006f;1d5fd=0070;1d5fe=0071;1d5ff=0072;1d600=0073;1d601=0074;1d602=0075;1d603=0076;1d604=0077;1d605=0078;1d606=0079;1d607=007a;1d608=0061;1d609=0062;1d60a=0063;1d60b=0064;1d60c=0065;1d60d=0066;1d60e=0067;1d60f=0068;1d610=0069;1d611=006a;1d612=006b;1d613=006c;1d614=006d;1d615=006e;1d616=006f;1d617=0070;1d618=0071;1d619=0072;1d61a=0073;1d61b=0074;1d61c=0075;1d61d=0076;1d61e=0077;1d61f=0078;1d620=0079;1d621=007a;1d622=0061;1d623=0062;1d624=0063;1d625=0064;1d626=0065;1d627=0066;1d628=0067;1d629=0068;1d62a=0069;1d62b=006a;1d62c=006b;1d62d=006c;1d62e=006d;1d62f=006e;1d630=006f;1d631=0070;1d632=0071;1d633=0072;1d634=0073;1d635=0074;1d636=0075;1d637=0",
806
+ "076;1d638=0077;1d639=0078;1d63a=0079;1d63b=007a;1d63c=0061;1d63d=0062;1d63e=0063;1d63f=0064;1d640=0065;1d641=0066;1d642=0067;1d643=0068;1d644=0069;1d645=006a;1d646=006b;1d647=006c;1d648=006d;1d649=006e;1d64a=006f;1d64b=0070;1d64c=0071;1d64d=0072;1d64e=0073;1d64f=0074;1d650=0075;1d651=0076;1d652=0077;1d653=0078;1d654=0079;1d655=007a;1d656=0061;1d657=0062;1d658=0063;1d659=0064;1d65a=0065;1d65b=0066;1d65c=0067;1d65d=0068;1d65e=0069;1d65f=006a;1d660=006b;1d661=006c;1d662=006d;1d663=006e;1d664=006f;1d665=0070;1d666=0071;1d667=0072;1d668=0073;1d669=0074;1d66a=0075;1d66b=0076;1d66c=0077;1d66d=0078;1d66e=0079;1d66f=007a;1d670=0061;1d671=0062;1d672=0063;1d673=0064;1d674=0065;1d675=0066;1d676=0067;1d677=0068;1d678=0069;1d679=006a;1d67a=006b;1d67b=006c;1d67c=006d;1d67d=006e;1d67e=006f;1d67f=0070;1d680=0071;1d681=0072;1d682=0073;1d683=0074;1d684=0075;1d685=0076;1d686=0077;1d687=0078;1d688=0079;1d689=007a;1d68a=0061;1d68b=0062;1d68c=0063;1d68d=0064;1d68e=0065;1d68f=0066;1d690=0067;1d691=0068;1d692=",
807
+ "0069;1d693=006a;1d694=006b;1d695=006c;1d696=006d;1d697=006e;1d698=006f;1d699=0070;1d69a=0071;1d69b=0072;1d69c=0073;1d69d=0074;1d69e=0075;1d69f=0076;1d6a0=0077;1d6a1=0078;1d6a2=0079;1d6a3=007a;1d6a4=0131;1d6a5=0237;1d6a8=03b1;1d6a9=03b2;1d6aa=03b3;1d6ab=03b4;1d6ac=03b5;1d6ad=03b6;1d6ae=03b7;1d6af=03b8;1d6b0=03b9;1d6b1=03ba;1d6b2=03bb;1d6b3=03bc;1d6b4=03bd;1d6b5=03be;1d6b6=03bf;1d6b7=03c0;1d6b8=03c1;1d6b9=03b8;1d6ba=03c3;1d6bb=03c4;1d6bc=03c5;1d6bd=03c6;1d6be=03c7;1d6bf=03c8;1d6c0=03c9;1d6c1=2207;1d6c2=03b1;1d6c3=03b2;1d6c4=03b3;1d6c5=03b4;1d6c6=03b5;1d6c7=03b6;1d6c8=03b7;1d6c9=03b8;1d6ca=03b9;1d6cb=03ba;1d6cc=03bb;1d6cd=03bc;1d6ce=03bd;1d6cf=03be;1d6d0=03bf;1d6d1=03c0;1d6d2=03c1;1d6d3..1d6d4=03c3;1d6d5=03c4;1d6d6=03c5;1d6d7=03c6;1d6d8=03c7;1d6d9=03c8;1d6da=03c9;1d6db=2202;1d6dc=03b5;1d6dd=03b8;1d6de=03ba;1d6df=03c6;1d6e0=03c1;1d6e1=03c0;1d6e2=03b1;1d6e3=03b2;1d6e4=03b3;1d6e5=03b4;1d6e6=03b5;1d6e7=03b6;1d6e8=03b7;1d6e9=03b8;1d6ea=03b9;1d6eb=03ba;1d6ec=03bb;1d6ed=03bc;1d6ee=03bd;1d6ef=03b",
808
+ "e;1d6f0=03bf;1d6f1=03c0;1d6f2=03c1;1d6f3=03b8;1d6f4=03c3;1d6f5=03c4;1d6f6=03c5;1d6f7=03c6;1d6f8=03c7;1d6f9=03c8;1d6fa=03c9;1d6fb=2207;1d6fc=03b1;1d6fd=03b2;1d6fe=03b3;1d6ff=03b4;1d700=03b5;1d701=03b6;1d702=03b7;1d703=03b8;1d704=03b9;1d705=03ba;1d706=03bb;1d707=03bc;1d708=03bd;1d709=03be;1d70a=03bf;1d70b=03c0;1d70c=03c1;1d70d..1d70e=03c3;1d70f=03c4;1d710=03c5;1d711=03c6;1d712=03c7;1d713=03c8;1d714=03c9;1d715=2202;1d716=03b5;1d717=03b8;1d718=03ba;1d719=03c6;1d71a=03c1;1d71b=03c0;1d71c=03b1;1d71d=03b2;1d71e=03b3;1d71f=03b4;1d720=03b5;1d721=03b6;1d722=03b7;1d723=03b8;1d724=03b9;1d725=03ba;1d726=03bb;1d727=03bc;1d728=03bd;1d729=03be;1d72a=03bf;1d72b=03c0;1d72c=03c1;1d72d=03b8;1d72e=03c3;1d72f=03c4;1d730=03c5;1d731=03c6;1d732=03c7;1d733=03c8;1d734=03c9;1d735=2207;1d736=03b1;1d737=03b2;1d738=03b3;1d739=03b4;1d73a=03b5;1d73b=03b6;1d73c=03b7;1d73d=03b8;1d73e=03b9;1d73f=03ba;1d740=03bb;1d741=03bc;1d742=03bd;1d743=03be;1d744=03bf;1d745=03c0;1d746=03c1;1d747..1d748=03c3;1d749=03c4;1d74a=03c5;1d74b",
809
+ "=03c6;1d74c=03c7;1d74d=03c8;1d74e=03c9;1d74f=2202;1d750=03b5;1d751=03b8;1d752=03ba;1d753=03c6;1d754=03c1;1d755=03c0;1d756=03b1;1d757=03b2;1d758=03b3;1d759=03b4;1d75a=03b5;1d75b=03b6;1d75c=03b7;1d75d=03b8;1d75e=03b9;1d75f=03ba;1d760=03bb;1d761=03bc;1d762=03bd;1d763=03be;1d764=03bf;1d765=03c0;1d766=03c1;1d767=03b8;1d768=03c3;1d769=03c4;1d76a=03c5;1d76b=03c6;1d76c=03c7;1d76d=03c8;1d76e=03c9;1d76f=2207;1d770=03b1;1d771=03b2;1d772=03b3;1d773=03b4;1d774=03b5;1d775=03b6;1d776=03b7;1d777=03b8;1d778=03b9;1d779=03ba;1d77a=03bb;1d77b=03bc;1d77c=03bd;1d77d=03be;1d77e=03bf;1d77f=03c0;1d780=03c1;1d781..1d782=03c3;1d783=03c4;1d784=03c5;1d785=03c6;1d786=03c7;1d787=03c8;1d788=03c9;1d789=2202;1d78a=03b5;1d78b=03b8;1d78c=03ba;1d78d=03c6;1d78e=03c1;1d78f=03c0;1d790=03b1;1d791=03b2;1d792=03b3;1d793=03b4;1d794=03b5;1d795=03b6;1d796=03b7;1d797=03b8;1d798=03b9;1d799=03ba;1d79a=03bb;1d79b=03bc;1d79c=03bd;1d79d=03be;1d79e=03bf;1d79f=03c0;1d7a0=03c1;1d7a1=03b8;1d7a2=03c3;1d7a3=03c4;1d7a4=03c5;1d7a5=03c6;1d7a6=03",
810
+ "c7;1d7a7=03c8;1d7a8=03c9;1d7a9=2207;1d7aa=03b1;1d7ab=03b2;1d7ac=03b3;1d7ad=03b4;1d7ae=03b5;1d7af=03b6;1d7b0=03b7;1d7b1=03b8;1d7b2=03b9;1d7b3=03ba;1d7b4=03bb;1d7b5=03bc;1d7b6=03bd;1d7b7=03be;1d7b8=03bf;1d7b9=03c0;1d7ba=03c1;1d7bb..1d7bc=03c3;1d7bd=03c4;1d7be=03c5;1d7bf=03c6;1d7c0=03c7;1d7c1=03c8;1d7c2=03c9;1d7c3=2202;1d7c4=03b5;1d7c5=03b8;1d7c6=03ba;1d7c7=03c6;1d7c8=03c1;1d7c9=03c0;1d7ca..1d7cb=03dd;1d7ce=0030;1d7cf=0031;1d7d0=0032;1d7d1=0033;1d7d2=0034;1d7d3=0035;1d7d4=0036;1d7d5=0037;1d7d6=0038;1d7d7=0039;1d7d8=0030;1d7d9=0031;1d7da=0032;1d7db=0033;1d7dc=0034;1d7dd=0035;1d7de=0036;1d7df=0037;1d7e0=0038;1d7e1=0039;1d7e2=0030;1d7e3=0031;1d7e4=0032;1d7e5=0033;1d7e6=0034;1d7e7=0035;1d7e8=0036;1d7e9=0037;1d7ea=0038;1d7eb=0039;1d7ec=0030;1d7ed=0031;1d7ee=0032;1d7ef=0033;1d7f0=0034;1d7f1=0035;1d7f2=0036;1d7f3=0037;1d7f4=0038;1d7f5=0039;1d7f6=0030;1d7f7=0031;1d7f8=0032;1d7f9=0033;1d7fa=0034;1d7fb=0035;1d7fc=0036;1d7fd=0037;1d7fe=0038;1d7ff=0039;1e030=0430;1e031=0431;1e032=0432;1e033=0433;1e03",
811
+ "4=0434;1e035=0435;1e036=0436;1e037=0437;1e038=0438;1e039=043a;1e03a=043b;1e03b=043c;1e03c=043e;1e03d=043f;1e03e=0440;1e03f=0441;1e040=0442;1e041=0443;1e042=0444;1e043=0445;1e044=0446;1e045=0447;1e046=0448;1e047=044b;1e048=044d;1e049=044e;1e04a=a689;1e04b=04d9;1e04c=0456;1e04d=0458;1e04e=04e9;1e04f=04af;1e050=04cf;1e051=0430;1e052=0431;1e053=0432;1e054=0433;1e055=0434;1e056=0435;1e057=0436;1e058=0437;1e059=0438;1e05a=043a;1e05b=043b;1e05c=043e;1e05d=043f;1e05e=0441;1e05f=0443;1e060=0444;1e061=0445;1e062=0446;1e063=0447;1e064=0448;1e065=044a;1e066=044b;1e067=0491;1e068=0456;1e069=0455;1e06a=045f;1e06b=04ab;1e06c=a651;1e06d=04b1;1e900=1e922;1e901=1e923;1e902=1e924;1e903=1e925;1e904=1e926;1e905=1e927;1e906=1e928;1e907=1e929;1e908=1e92a;1e909=1e92b;1e90a=1e92c;1e90b=1e92d;1e90c=1e92e;1e90d=1e92f;1e90e=1e930;1e90f=1e931;1e910=1e932;1e911=1e933;1e912=1e934;1e913=1e935;1e914=1e936;1e915=1e937;1e916=1e938;1e917=1e939;1e918=1e93a;1e919=1e93b;1e91a=1e93c;1e91b=1e93d;1e91c=1e93e;1e91d=1e93f;1e91e=",
812
+ "1e940;1e91f=1e941;1e920=1e942;1e921=1e943;1ee00=0627;1ee01=0628;1ee02=062c;1ee03=062f;1ee05=0648;1ee06=0632;1ee07=062d;1ee08=0637;1ee09=064a;1ee0a=0643;1ee0b=0644;1ee0c=0645;1ee0d=0646;1ee0e=0633;1ee0f=0639;1ee10=0641;1ee11=0635;1ee12=0642;1ee13=0631;1ee14=0634;1ee15=062a;1ee16=062b;1ee17=062e;1ee18=0630;1ee19=0636;1ee1a=0638;1ee1b=063a;1ee1c=066e;1ee1d=06ba;1ee1e=06a1;1ee1f=066f;1ee21=0628;1ee22=062c;1ee24=0647;1ee27=062d;1ee29=064a;1ee2a=0643;1ee2b=0644;1ee2c=0645;1ee2d=0646;1ee2e=0633;1ee2f=0639;1ee30=0641;1ee31=0635;1ee32=0642;1ee34=0634;1ee35=062a;1ee36=062b;1ee37=062e;1ee39=0636;1ee3b=063a;1ee42=062c;1ee47=062d;1ee49=064a;1ee4b=0644;1ee4d=0646;1ee4e=0633;1ee4f=0639;1ee51=0635;1ee52=0642;1ee54=0634;1ee57=062e;1ee59=0636;1ee5b=063a;1ee5d=06ba;1ee5f=066f;1ee61=0628;1ee62=062c;1ee64=0647;1ee67=062d;1ee68=0637;1ee69=064a;1ee6a=0643;1ee6c=0645;1ee6d=0646;1ee6e=0633;1ee6f=0639;1ee70=0641;1ee71=0635;1ee72=0642;1ee74=0634;1ee75=062a;1ee76=062b;1ee77=062e;1ee79=0636;1ee7a=0638;1ee7b=063a;1",
813
+ "ee7c=066e;1ee7e=06a1;1ee80=0627;1ee81=0628;1ee82=062c;1ee83=062f;1ee84=0647;1ee85=0648;1ee86=0632;1ee87=062d;1ee88=0637;1ee89=064a;1ee8b=0644;1ee8c=0645;1ee8d=0646;1ee8e=0633;1ee8f=0639;1ee90=0641;1ee91=0635;1ee92=0642;1ee93=0631;1ee94=0634;1ee95=062a;1ee96=062b;1ee97=062e;1ee98=0630;1ee99=0636;1ee9a=0638;1ee9b=063a;1eea1=0628;1eea2=062c;1eea3=062f;1eea5=0648;1eea6=0632;1eea7=062d;1eea8=0637;1eea9=064a;1eeab=0644;1eeac=0645;1eead=0646;1eeae=0633;1eeaf=0639;1eeb0=0641;1eeb1=0635;1eeb2=0642;1eeb3=0631;1eeb4=0634;1eeb5=062a;1eeb6=062b;1eeb7=062e;1eeb8=0630;1eeb9=0636;1eeba=0638;1eebb=063a;1f100=0030.002e;1f101=0030.002c;1f102=0031.002c;1f103=0032.002c;1f104=0033.002c;1f105=0034.002c;1f106=0035.002c;1f107=0036.002c;1f108=0037.002c;1f109=0038.002c;1f10a=0039.002c;1f110=0028.0061.0029;1f111=0028.0062.0029;1f112=0028.0063.0029;1f113=0028.0064.0029;1f114=0028.0065.0029;1f115=0028.0066.0029;1f116=0028.0067.0029;1f117=0028.0068.0029;1f118=0028.0069.0029;1f119=0028.006a.0029;1f11a=0028.006b.0029;",
814
+ "1f11b=0028.006c.0029;1f11c=0028.006d.0029;1f11d=0028.006e.0029;1f11e=0028.006f.0029;1f11f=0028.0070.0029;1f120=0028.0071.0029;1f121=0028.0072.0029;1f122=0028.0073.0029;1f123=0028.0074.0029;1f124=0028.0075.0029;1f125=0028.0076.0029;1f126=0028.0077.0029;1f127=0028.0078.0029;1f128=0028.0079.0029;1f129=0028.007a.0029;1f12a=3014.0073.3015;1f12b=0063;1f12c=0072;1f12d=0063.0064;1f12e=0077.007a;1f130=0061;1f131=0062;1f132=0063;1f133=0064;1f134=0065;1f135=0066;1f136=0067;1f137=0068;1f138=0069;1f139=006a;1f13a=006b;1f13b=006c;1f13c=006d;1f13d=006e;1f13e=006f;1f13f=0070;1f140=0071;1f141=0072;1f142=0073;1f143=0074;1f144=0075;1f145=0076;1f146=0077;1f147=0078;1f148=0079;1f149=007a;1f14a=0068.0076;1f14b=006d.0076;1f14c=0073.0064;1f14d=0073.0073;1f14e=0070.0070.0076;1f14f=0077.0063;1f16a=006d.0063;1f16b=006d.0064;1f16c=006d.0072;1f190=0064.006a;1f200=307b.304b;1f201=30b3.30b3;1f202=30b5;1f210=624b;1f211=5b57;1f212=53cc;1f213=30c7;1f214=4e8c;1f215=591a;1f216=89e3;1f217=5929;1f218=4ea4;1f219=6620;1f21a=",
815
+ "7121;1f21b=6599;1f21c=524d;1f21d=5f8c;1f21e=518d;1f21f=65b0;1f220=521d;1f221=7d42;1f222=751f;1f223=8ca9;1f224=58f0;1f225=5439;1f226=6f14;1f227=6295;1f228=6355;1f229=4e00;1f22a=4e09;1f22b=904a;1f22c=5de6;1f22d=4e2d;1f22e=53f3;1f22f=6307;1f230=8d70;1f231=6253;1f232=7981;1f233=7a7a;1f234=5408;1f235=6e80;1f236=6709;1f237=6708;1f238=7533;1f239=5272;1f23a=55b6;1f23b=914d;1f240=3014.672c.3015;1f241=3014.4e09.3015;1f242=3014.4e8c.3015;1f243=3014.5b89.3015;1f244=3014.70b9.3015;1f245=3014.6253.3015;1f246=3014.76d7.3015;1f247=3014.52dd.3015;1f248=3014.6557.3015;1f250=5f97;1f251=53ef;1fbf0=0030;1fbf1=0031;1fbf2=0032;1fbf3=0033;1fbf4=0034;1fbf5=0035;1fbf6=0036;1fbf7=0037;1fbf8=0038;1fbf9=0039;2f800=4e3d;2f801=4e38;2f802=4e41;2f803=20122;2f804=4f60;2f805=4fae;2f806=4fbb;2f807=5002;2f808=507a;2f809=5099;2f80a=50e7;2f80b=50cf;2f80c=349e;2f80d=2063a;2f80e=514d;2f80f=5154;2f810=5164;2f811=5177;2f812=2051c;2f813=34b9;2f814=5167;2f815=518d;2f816=2054b;2f817=5197;2f818=51a4;2f819=4ecc;2f81a=51ac;2f81b=51b5",
816
+ ";2f81c=291df;2f81d=51f5;2f81e=5203;2f81f=34df;2f820=523b;2f821=5246;2f822=5272;2f823=5277;2f824=3515;2f825=52c7;2f826=52c9;2f827=52e4;2f828=52fa;2f829=5305;2f82a=5306;2f82b=5317;2f82c=5349;2f82d=5351;2f82e=535a;2f82f=5373;2f830=537d;2f831..2f833=537f;2f834=20a2c;2f835=7070;2f836=53ca;2f837=53df;2f838=20b63;2f839=53eb;2f83a=53f1;2f83b=5406;2f83c=549e;2f83d=5438;2f83e=5448;2f83f=5468;2f840=54a2;2f841=54f6;2f842=5510;2f843=5553;2f844=5563;2f845..2f846=5584;2f847=5599;2f848=55ab;2f849=55b3;2f84a=55c2;2f84b=5716;2f84c=5606;2f84d=5717;2f84e=5651;2f84f=5674;2f850=5207;2f851=58ee;2f852=57ce;2f853=57f4;2f854=580d;2f855=578b;2f856=5832;2f857=5831;2f858=58ac;2f859=214e4;2f85a=58f2;2f85b=58f7;2f85c=5906;2f85d=591a;2f85e=5922;2f85f=5962;2f860=216a8;2f861=216ea;2f862=59ec;2f863=5a1b;2f864=5a27;2f865=59d8;2f866=5a66;2f867=36ee;2f868=36fc;2f869=5b08;2f86a..2f86b=5b3e;2f86c=219c8;2f86d=5bc3;2f86e=5bd8;2f86f=5be7;2f870=5bf3;2f871=21b18;2f872=5bff;2f873=5c06;2f874=5f53;2f875=5c22;2f876=3781;2f877=5c60;2f",
817
+ "878=5c6e;2f879=5cc0;2f87a=5c8d;2f87b=21de4;2f87c=5d43;2f87d=21de6;2f87e=5d6e;2f87f=5d6b;2f880=5d7c;2f881=5de1;2f882=5de2;2f883=382f;2f884=5dfd;2f885=5e28;2f886=5e3d;2f887=5e69;2f888=3862;2f889=22183;2f88a=387c;2f88b=5eb0;2f88c=5eb3;2f88d=5eb6;2f88e=5eca;2f88f=2a392;2f890=5efe;2f891..2f892=22331;2f893=8201;2f894..2f895=5f22;2f896=38c7;2f897=232b8;2f898=261da;2f899=5f62;2f89a=5f6b;2f89b=38e3;2f89c=5f9a;2f89d=5fcd;2f89e=5fd7;2f89f=5ff9;2f8a0=6081;2f8a1=393a;2f8a2=391c;2f8a3=6094;2f8a4=226d4;2f8a5=60c7;2f8a6=6148;2f8a7=614c;2f8a8=614e;2f8a9=614c;2f8aa=617a;2f8ab=618e;2f8ac=61b2;2f8ad=61a4;2f8ae=61af;2f8af=61de;2f8b0=61f2;2f8b1=61f6;2f8b2=6210;2f8b3=621b;2f8b4=625d;2f8b5=62b1;2f8b6=62d4;2f8b7=6350;2f8b8=22b0c;2f8b9=633d;2f8ba=62fc;2f8bb=6368;2f8bc=6383;2f8bd=63e4;2f8be=22bf1;2f8bf=6422;2f8c0=63c5;2f8c1=63a9;2f8c2=3a2e;2f8c3=6469;2f8c4=647e;2f8c5=649d;2f8c6=6477;2f8c7=3a6c;2f8c8=654f;2f8c9=656c;2f8ca=2300a;2f8cb=65e3;2f8cc=66f8;2f8cd=6649;2f8ce=3b19;2f8cf=6691;2f8d0=3b08;2f8d1=3ae4;2f8d2=519",
818
+ "2;2f8d3=5195;2f8d4=6700;2f8d5=669c;2f8d6=80ad;2f8d7=43d9;2f8d8=6717;2f8d9=671b;2f8da=6721;2f8db=675e;2f8dc=6753;2f8dd=233c3;2f8de=3b49;2f8df=67fa;2f8e0=6785;2f8e1=6852;2f8e2=6885;2f8e3=2346d;2f8e4=688e;2f8e5=681f;2f8e6=6914;2f8e7=3b9d;2f8e8=6942;2f8e9=69a3;2f8ea=69ea;2f8eb=6aa8;2f8ec=236a3;2f8ed=6adb;2f8ee=3c18;2f8ef=6b21;2f8f0=238a7;2f8f1=6b54;2f8f2=3c4e;2f8f3=6b72;2f8f4=6b9f;2f8f5=6bba;2f8f6=6bbb;2f8f7=23a8d;2f8f8=21d0b;2f8f9=23afa;2f8fa=6c4e;2f8fb=23cbc;2f8fc=6cbf;2f8fd=6ccd;2f8fe=6c67;2f8ff=6d16;2f900=6d3e;2f901=6d77;2f902=6d41;2f903=6d69;2f904=6d78;2f905=6d85;2f906=23d1e;2f907=6d34;2f908=6e2f;2f909=6e6e;2f90a=3d33;2f90b=6ecb;2f90c=6ec7;2f90d=23ed1;2f90e=6df9;2f90f=6f6e;2f910=23f5e;2f911=23f8e;2f912=6fc6;2f913=7039;2f914=701e;2f915=701b;2f916=3d96;2f917=704a;2f918=707d;2f919=7077;2f91a=70ad;2f91b=20525;2f91c=7145;2f91d=24263;2f91e=719c;2f91f=243ab;2f920=7228;2f921=7235;2f922=7250;2f923=24608;2f924=7280;2f925=7295;2f926=24735;2f927=24814;2f928=737a;2f929=738b;2f92a=3eac;2f92b=73a5;2",
819
+ "f92c..2f92d=3eb8;2f92e=7447;2f92f=745c;2f930=7471;2f931=7485;2f932=74ca;2f933=3f1b;2f934=7524;2f935=24c36;2f936=753e;2f937=24c92;2f938=7570;2f939=2219f;2f93a=7610;2f93b=24fa1;2f93c=24fb8;2f93d=25044;2f93e=3ffc;2f93f=4008;2f940=76f4;2f941=250f3;2f942=250f2;2f943=25119;2f944=25133;2f945=771e;2f946..2f947=771f;2f948=774a;2f949=4039;2f94a=778b;2f94b=4046;2f94c=4096;2f94d=2541d;2f94e=784e;2f94f=788c;2f950=78cc;2f951=40e3;2f952=25626;2f953=7956;2f954=2569a;2f955=256c5;2f956=798f;2f957=79eb;2f958=412f;2f959=7a40;2f95a=7a4a;2f95b=7a4f;2f95c=2597c;2f95d..2f95e=25aa7;2f95f=7aee;2f960=4202;2f961=25bab;2f962=7bc6;2f963=7bc9;2f964=4227;2f965=25c80;2f966=7cd2;2f967=42a0;2f968=7ce8;2f969=7ce3;2f96a=7d00;2f96b=25f86;2f96c=7d63;2f96d=4301;2f96e=7dc7;2f96f=7e02;2f970=7e45;2f971=4334;2f972=26228;2f973=26247;2f974=4359;2f975=262d9;2f976=7f7a;2f977=2633e;2f978=7f95;2f979=7ffa;2f97a=8005;2f97b=264da;2f97c=26523;2f97d=8060;2f97e=265a8;2f97f=8070;2f980=2335f;2f981=43d5;2f982=80b2;2f983=8103;2f984=440b;2f985=8",
820
+ "13e;2f986=5ab5;2f987=267a7;2f988=267b5;2f989=23393;2f98a=2339c;2f98b=8201;2f98c=8204;2f98d=8f9e;2f98e=446b;2f98f=8291;2f990=828b;2f991=829d;2f992=52b3;2f993=82b1;2f994=82b3;2f995=82bd;2f996=82e6;2f997=26b3c;2f998=82e5;2f999=831d;2f99a=8363;2f99b=83ad;2f99c=8323;2f99d=83bd;2f99e=83e7;2f99f=8457;2f9a0=8353;2f9a1=83ca;2f9a2=83cc;2f9a3=83dc;2f9a4=26c36;2f9a5=26d6b;2f9a6=26cd5;2f9a7=452b;2f9a8=84f1;2f9a9=84f3;2f9aa=8516;2f9ab=273ca;2f9ac=8564;2f9ad=26f2c;2f9ae=455d;2f9af=4561;2f9b0=26fb1;2f9b1=270d2;2f9b2=456b;2f9b3=8650;2f9b4=865c;2f9b5=8667;2f9b6=8669;2f9b7=86a9;2f9b8=8688;2f9b9=870e;2f9ba=86e2;2f9bb=8779;2f9bc=8728;2f9bd=876b;2f9be=8786;2f9bf=45d7;2f9c0=87e1;2f9c1=8801;2f9c2=45f9;2f9c3=8860;2f9c4=8863;2f9c5=27667;2f9c6=88d7;2f9c7=88de;2f9c8=4635;2f9c9=88fa;2f9ca=34bb;2f9cb=278ae;2f9cc=27966;2f9cd=46be;2f9ce=46c7;2f9cf=8aa0;2f9d0=8aed;2f9d1=8b8a;2f9d2=8c55;2f9d3=27ca8;2f9d4=8cab;2f9d5=8cc1;2f9d6=8d1b;2f9d7=8d77;2f9d8=27f2f;2f9d9=20804;2f9da=8dcb;2f9db=8dbc;2f9dc=8df0;2f9dd=208de;2f9de=8ed",
821
+ "4;2f9df=8f38;2f9e0=285d2;2f9e1=285ed;2f9e2=9094;2f9e3=90f1;2f9e4=9111;2f9e5=2872e;2f9e6=911b;2f9e7=9238;2f9e8=92d7;2f9e9=92d8;2f9ea=927c;2f9eb=93f9;2f9ec=9415;2f9ed=28bfa;2f9ee=958b;2f9ef=4995;2f9f0=95b7;2f9f1=28d77;2f9f2=49e6;2f9f3=96c3;2f9f4=5db2;2f9f5=9723;2f9f6=29145;2f9f7=2921a;2f9f8=4a6e;2f9f9=4a76;2f9fa=97e0;2f9fb=2940a;2f9fc=4ab2;2f9fd=29496;2f9fe..2f9ff=980b;2fa00=9829;2fa01=295b6;2fa02=98e2;2fa03=4b33;2fa04=9929;2fa05=99a7;2fa06=99c2;2fa07=99fe;2fa08=4bce;2fa09=29b30;2fa0a=9b12;2fa0b=9c40;2fa0c=9cfd;2fa0d=4cce;2fa0e=4ced;2fa0f=9d67;2fa10=2a0ce;2fa11=4cf8;2fa12=2a105;2fa13=2a20e;2fa14=2a291;2fa15=9ebb;2fa16=4d56;2fa17=9ef9;2fa18=9efe;2fa19=9f05;2fa1a=9f0f;2fa1b=9f16;2fa1c=9f3b;2fa1d=2a600;e0000=;e0001=;e0002..e001f=;e0020..e007f=;e0080..e00ff=;e0100..e01ef=;e01f0..e0fff="
822
+ ].join("");
823
+ //#endregion
824
+ //#region src/assets/path.ts
825
+ const encoder$1 = new TextEncoder();
826
+ const decoder$1 = new TextDecoder("utf-8", { fatal: true });
827
+ const FORBIDDEN_ASCII = /[\\%<>:"|?*]/u;
828
+ const SEPARATOR_ALIASES = /[\u2044\u2215\u29f5\u29f8\u29f9\ufe68\uff0f\uff3c]/u;
829
+ const DOS_STEM = /^(?:con|prn|aux|nul|clock\$|conin\$|conout\$|com[1-9]|lpt[1-9])$/u;
830
+ const NFKC_CASEFOLD_V17 = parseNfkcCasefoldData(NFKC_CASEFOLD_V17_DATA);
831
+ function validateTopikPath(input, options = {}) {
832
+ let path;
833
+ try {
834
+ path = typeof input === "string" ? input : decoder$1.decode(input);
835
+ } catch {
836
+ return pathFailure("", "invalid_utf8", "Path is not strict UTF-8");
837
+ }
838
+ if (!validPathCapabilities(options.capabilities)) return pathFailure(path, "capability_invalid", "Path capabilities must stay within immutable portable maxima");
839
+ if (path.length === 0 || path.includes("\0") || path.includes("")) return pathFailure(path, "forbidden_character", "Path is empty or contains a forbidden value");
840
+ if (path.startsWith("/") || path.startsWith("//") || /^[a-z]:/iu.test(path) || path.startsWith("\\\\") || /^[a-z][a-z0-9+.-]*:/iu.test(path)) return pathFailure(path, "absolute", "Path must be repository relative");
841
+ if (FORBIDDEN_ASCII.test(path)) return pathFailure(path, "forbidden_character", "Path contains a forbidden character");
842
+ if (SEPARATOR_ALIASES.test(path)) return pathFailure(path, "separator_alias", "Path contains a separator-confusable character");
843
+ if (containsTopikPathForbiddenCodePoint(path)) return pathFailure(path, "forbidden_character", "Path contains a forbidden Unicode value");
844
+ if (path.normalize("NFC") !== path) return pathFailure(path, "not_nfc", "Path must already be in NFC storage spelling");
845
+ const components = path.split("/");
846
+ if (components.some((component) => component.length === 0)) return pathFailure(path, "separator_alias", "Path contains an empty component");
847
+ if (components.some((component) => component === "." || component === "..")) return pathFailure(path, "dot_segment", "Path contains a dot segment");
848
+ const maxComponents = options.capabilities?.maxComponents ?? TOPIK_PATH_V1_DESCRIPTOR.maxComponents;
849
+ if (components.length > maxComponents) return pathFailure(path, "too_long", "Path has too many components");
850
+ const collisionComponents = [];
851
+ for (const component of components) {
852
+ if (component.startsWith(" ") || component.endsWith(" ") || component.endsWith(".")) return pathFailure(path, "forbidden_character", "Path component has unsafe edge spelling");
853
+ const maxComponentBytes = options.capabilities?.maxComponentUtf8Bytes ?? TOPIK_PATH_V1_DESCRIPTOR.maxComponentUtf8Bytes;
854
+ if (encoder$1.encode(component).byteLength > maxComponentBytes) return pathFailure(path, "too_long", "Path component exceeds the UTF-8 byte limit");
855
+ const collision = toNfkcCasefold(component);
856
+ if (collision.length === 0 || collision === "." || collision === ".." || collision.includes("/") || collision.includes("\\")) return pathFailure(path, "separator_alias", "Path component folds to an unsafe spelling");
857
+ const stem = collision.replace(/[ .]+$/u, "").split(".", 1)[0];
858
+ if (DOS_STEM.test(stem) || collision === ".git" || /^git~[1-9][0-9]*$/u.test(collision) || collision === ".topik") return pathFailure(path, "reserved_name", "Path contains a reserved component");
859
+ collisionComponents.push(collision);
860
+ }
861
+ const repositoryPath = options.bindingRoot ? `${options.bindingRoot}/${path}` : path;
862
+ const maxRepositoryBytes = options.capabilities?.maxRepositoryPathUtf8Bytes ?? TOPIK_PATH_V1_DESCRIPTOR.maxRepositoryPathUtf8Bytes;
863
+ if (encoder$1.encode(repositoryPath).byteLength > maxRepositoryBytes) return pathFailure(path, "too_long", "Complete repository path exceeds the UTF-8 byte limit");
864
+ return {
865
+ ok: true,
866
+ value: {
867
+ path,
868
+ collisionKey: collisionComponents.join("/"),
869
+ utf8Bytes: encoder$1.encode(path).length
870
+ },
871
+ diagnostics: []
872
+ };
873
+ }
874
+ function containsTopikPathForbiddenCodePoint(value) {
875
+ for (const character of value) {
876
+ const codePoint = character.codePointAt(0) ?? 0;
877
+ if (isTopikPathCodePointForbiddenV17(codePoint) || isTopikPathNormalizationSensitiveV17(codePoint)) return true;
878
+ }
879
+ return false;
880
+ }
881
+ function validPathCapabilities(capabilities) {
882
+ if (capabilities === void 0) return true;
883
+ return validOptionalLimit(capabilities.maxComponentUtf8Bytes, TOPIK_PATH_V1_DESCRIPTOR.maxComponentUtf8Bytes) && validOptionalLimit(capabilities.maxComponents, TOPIK_PATH_V1_DESCRIPTOR.maxComponents) && validOptionalLimit(capabilities.maxRepositoryPathUtf8Bytes, TOPIK_PATH_V1_DESCRIPTOR.maxRepositoryPathUtf8Bytes);
884
+ }
885
+ function validOptionalLimit(value, maximum) {
886
+ return value === void 0 || Number.isInteger(value) && value >= 1 && value <= maximum;
887
+ }
888
+ function computeTopikPathCollisionKey(path, options = {}) {
889
+ const result = validateTopikPath(path, options);
890
+ return result.ok ? {
891
+ ok: true,
892
+ value: result.value.collisionKey,
893
+ diagnostics: []
894
+ } : {
895
+ ok: false,
896
+ diagnostics: result.diagnostics
897
+ };
898
+ }
899
+ function validateTopikPathSet(paths, options = {}) {
900
+ const byCollision = /* @__PURE__ */ new Map();
901
+ const diagnostics = [];
902
+ for (const path of paths) {
903
+ const result = validateTopikPath(path, options);
904
+ if (!result.ok) {
905
+ diagnostics.push(...result.diagnostics);
906
+ continue;
907
+ }
908
+ const existing = byCollision.get(result.value.collisionKey);
909
+ if (existing !== void 0 && existing !== path) diagnostics.push(topikAssetDiagnostic("TOPIK_ASSET_PATH_COLLISION", "Paths have the same collision key", {
910
+ descriptorVersion: TOPIK_PATH_VERSION,
911
+ location: { path },
912
+ reason: "casefold_collision"
913
+ }));
914
+ else byCollision.set(result.value.collisionKey, path);
915
+ }
916
+ for (const child of byCollision.keys()) {
917
+ const components = child.split("/");
918
+ for (let count = 1; count < components.length; count++) {
919
+ if (!byCollision.has(components.slice(0, count).join("/"))) continue;
920
+ diagnostics.push(topikAssetDiagnostic("TOPIK_ASSET_PATH_COLLISION", "A portable file path cannot also be another file's parent", {
921
+ descriptorVersion: TOPIK_PATH_VERSION,
922
+ location: { path: byCollision.get(child) },
923
+ reason: "casefold_collision"
924
+ }));
925
+ break;
926
+ }
927
+ }
928
+ return diagnostics.length === 0 ? {
929
+ ok: true,
930
+ value: byCollision,
931
+ diagnostics: []
932
+ } : {
933
+ ok: false,
934
+ value: byCollision,
935
+ diagnostics
936
+ };
937
+ }
938
+ /** Unicode 17 NFKC casefold for topik-path-v1-admitted input. */
939
+ function toNfkcCasefold(value) {
940
+ let mapped = "";
941
+ for (const character of value) mapped += NFKC_CASEFOLD_V17.get(character.codePointAt(0) ?? 0) ?? character;
942
+ return mapped.normalize("NFC");
943
+ }
944
+ function pathFailure(path, reason, message) {
945
+ return {
946
+ ok: false,
947
+ diagnostics: [topikAssetDiagnostic("TOPIK_ASSET_PATH_INVALID", message, {
948
+ descriptorVersion: TOPIK_PATH_VERSION,
949
+ location: { path: safePath(path) },
950
+ reason
951
+ })]
952
+ };
953
+ }
954
+ function safePath(path) {
955
+ return JSON.stringify(path).slice(1, -1);
956
+ }
957
+ function parseNfkcCasefoldData(source) {
958
+ const mapping = /* @__PURE__ */ new Map();
959
+ const records = source.split("\n").filter((line) => !line.startsWith("#")).join("").split(";").filter(Boolean);
960
+ for (const record of records) {
961
+ const [range, encoded = ""] = record.split("=");
962
+ const [startValue, endValue = startValue] = range.split("..");
963
+ const start = Number.parseInt(startValue, 16);
964
+ const end = Number.parseInt(endValue, 16);
965
+ const replacement = encoded.split(".").filter(Boolean).map((value) => String.fromCodePoint(Number.parseInt(value, 16))).join("");
966
+ for (let codePoint = start; codePoint <= end; codePoint++) mapping.set(codePoint, replacement);
967
+ }
968
+ return mapping;
969
+ }
970
+ //#endregion
971
+ //#region src/assets/asset.ts
972
+ const encoder = new TextEncoder();
973
+ const decoder = new TextDecoder("utf-8", { fatal: true });
974
+ const ASSET_BLOB_URI_VALIDATOR = /^blobs\/[0-9a-f]{64}$/u;
975
+ const validateSchema = new Ajv2020({
976
+ strict: true,
977
+ strictRequired: false,
978
+ allErrors: true,
979
+ ownProperties: true
980
+ }).compile(rawAssetV1Schema);
981
+ function isAssetBlobUri(value) {
982
+ return typeof value === "string" && ASSET_BLOB_URI_VALIDATOR.test(value);
983
+ }
984
+ function parseAssetBlobUri(value) {
985
+ if (!isAssetBlobUri(value)) throw new TypeError("Asset blob URI is not canonical");
986
+ return value;
987
+ }
988
+ function parseGeneratedAssetName(value) {
989
+ return parseTopikGeneratedAssetName(value);
990
+ }
991
+ function parseAsset(input) {
992
+ const source = typeof input === "string" ? encoder.encode(input) : Uint8Array.from(input);
993
+ if (source.byteLength > TOPIK_ASSET_LIMITS.maxDescriptorBytes) return fail(source, "TOPIK_ASSET_SCHEMA_INVALID", "Asset descriptor exceeds the byte limit");
994
+ let text;
995
+ try {
996
+ text = decoder.decode(source);
997
+ } catch {
998
+ return fail(source, "TOPIK_ASSET_SCHEMA_INVALID", "Asset descriptor is not strict UTF-8");
999
+ }
1000
+ if (text.startsWith("")) return fail(source, "TOPIK_ASSET_SCHEMA_INVALID", "Asset descriptor must not contain a BOM");
1001
+ let value;
1002
+ try {
1003
+ value = parseStrictTopikJson(text, TOPIK_ASSET_LIMITS.maxJsonDepth);
1004
+ } catch (error) {
1005
+ return {
1006
+ ok: false,
1007
+ diagnostics: [topikAssetDiagnostic(error instanceof TopikJsonSyntaxError && error.duplicatePointer !== void 0 ? "TOPIK_ASSET_DUPLICATE_MEMBER" : "TOPIK_ASSET_SCHEMA_INVALID", error instanceof Error ? error.message : "Asset descriptor is not strict JSON", { location: { jsonPointer: error instanceof TopikJsonSyntaxError ? error.duplicatePointer ?? "/" : "/" } })],
1008
+ source
1009
+ };
1010
+ }
1011
+ const validated = validateAssetValue(value);
1012
+ if (!validated.ok) return {
1013
+ ok: false,
1014
+ diagnostics: validated.diagnostics,
1015
+ source
1016
+ };
1017
+ const canonicalText = serializeTopikJson(validated.value);
1018
+ const canonicalBytes = encoder.encode(canonicalText);
1019
+ if (!equalBytes$1(source, canonicalBytes)) return {
1020
+ ok: false,
1021
+ diagnostics: [topikAssetDiagnostic("TOPIK_ASSET_NON_CANONICAL", "Asset bytes are not canonical JSON", {
1022
+ location: { jsonPointer: "/" },
1023
+ recovery: "repair-source",
1024
+ consequence: "block-identity-and-writes"
1025
+ })],
1026
+ source
1027
+ };
1028
+ return {
1029
+ ok: true,
1030
+ value: {
1031
+ asset: validated.value,
1032
+ canonicalBytes
1033
+ },
1034
+ diagnostics: [],
1035
+ source
1036
+ };
1037
+ }
1038
+ function validateAssetValue(value) {
1039
+ if (!isTopikJsonDataValue(value)) return failure("TOPIK_ASSET_SCHEMA_INVALID", "Asset must contain only own JSON data properties");
1040
+ if (isRecord$2(value)) {
1041
+ const type = Object.hasOwn(value, "type") ? value.type : void 0;
1042
+ const apiVersion = Object.hasOwn(value, "apiVersion") ? value.apiVersion : void 0;
1043
+ if (type === "Asset" && apiVersion !== "v1") return failure("TOPIK_ASSET_UNSUPPORTED_VERSION", "Unsupported Asset apiVersion", "/apiVersion");
1044
+ const spec = Object.hasOwn(value, "spec") && isRecord$2(value.spec) ? value.spec : void 0;
1045
+ if (spec !== void 0 && Object.hasOwn(spec, "size") && typeof spec.size === "number" && spec.size > TOPIK_ASSET_LIMITS.maxAssetBytes) return failure("TOPIK_ASSET_SIZE_MISMATCH", "Asset size exceeds the portable byte limit", "/spec/size");
1046
+ }
1047
+ if (!validateSchema(value)) return {
1048
+ ok: false,
1049
+ diagnostics: (validateSchema.errors ?? []).map(schemaDiagnostic)
1050
+ };
1051
+ const asset = value;
1052
+ if (!hasMatchingAssetDigests(asset)) return {
1053
+ ok: false,
1054
+ diagnostics: [topikAssetDiagnostic("TOPIK_ASSET_DIGEST_MISMATCH", "Asset payload URI and integrity must identify the same digest", {
1055
+ consequence: "block-identity-and-writes",
1056
+ location: { jsonPointer: "/spec/integrity" },
1057
+ recovery: "verify-bytes"
1058
+ })]
1059
+ };
1060
+ return {
1061
+ ok: true,
1062
+ value: asset,
1063
+ diagnostics: []
1064
+ };
1065
+ }
1066
+ /** Enforce the compiler blob/SHA-256 invariant when both forms are present. */
1067
+ function hasMatchingAssetDigests(asset) {
1068
+ const { integrity, uri } = asset.spec;
1069
+ if (integrity === void 0 || !isAssetBlobUri(uri) || !integrity.startsWith("sha256:")) return true;
1070
+ const digest = /^sha256:([0-9a-f]{64})$/u.exec(integrity);
1071
+ return digest !== null && uri.slice(6) === digest[1];
1072
+ }
1073
+ /** Serialize one Asset to deterministic UTF-8 canonical JSON and prove its round trip. */
1074
+ function serializeAsset(value) {
1075
+ const validation = validateAssetValue(value);
1076
+ if (!validation.ok) return {
1077
+ ok: false,
1078
+ diagnostics: validation.diagnostics
1079
+ };
1080
+ const bytes = encoder.encode(serializeTopikJson(validation.value));
1081
+ const parsed = parseAsset(bytes);
1082
+ if (!parsed.ok || serializeTopikJson(parsed.value.asset) !== serializeTopikJson(validation.value)) return failure("TOPIK_ASSET_NON_CANONICAL", "Serialized Asset failed strict semantic reparse");
1083
+ return {
1084
+ ok: true,
1085
+ value: bytes,
1086
+ diagnostics: []
1087
+ };
1088
+ }
1089
+ /** Admit a compiler-materialized blob path; Asset resources may use other URI forms. */
1090
+ function validateAssetBlobUri(value) {
1091
+ const local = validateTopikPath(value);
1092
+ if (!local.ok) return {
1093
+ ok: false,
1094
+ diagnostics: local.diagnostics
1095
+ };
1096
+ if (!isAssetBlobUri(local.value.path)) return failure("TOPIK_ASSET_SCHEMA_INVALID", "Asset URI must identify a compiler-materialized payload", "/spec/uri");
1097
+ return {
1098
+ ok: true,
1099
+ value: { uri: local.value.path },
1100
+ diagnostics: []
1101
+ };
1102
+ }
1103
+ function isGeneratedAssetName(value) {
1104
+ return isTopikGeneratedAssetName(value);
1105
+ }
1106
+ function validateStableSourceNamespace(value) {
1107
+ if (containsForbiddenPortableText(value)) return failure("TOPIK_ASSET_SOURCE_NAMESPACE_INVALID", "Stable source namespace is not portable text after NFC normalization");
1108
+ const normalized = value.normalize("NFC");
1109
+ const bytes = encoder.encode(normalized);
1110
+ if (bytes.byteLength < 1 || bytes.byteLength > 1024 || containsForbiddenPortableText(normalized)) return failure("TOPIK_ASSET_SOURCE_NAMESPACE_INVALID", "Stable source namespace is not portable text after NFC normalization");
1111
+ return {
1112
+ ok: true,
1113
+ value: normalized,
1114
+ diagnostics: []
1115
+ };
1116
+ }
1117
+ function containsForbiddenPortableText(value) {
1118
+ for (const character of value) {
1119
+ const codePoint = character.codePointAt(0) ?? 0;
1120
+ if (isTopikPathCodePointForbiddenV17(codePoint) || isTopikPathNormalizationSensitiveV17(codePoint)) return true;
1121
+ }
1122
+ return false;
1123
+ }
1124
+ function generateAutomaticAssetName(options) {
1125
+ const namespace = validateStableSourceNamespace(options.stableSourceNamespace);
1126
+ if (!namespace.ok) return {
1127
+ ok: false,
1128
+ diagnostics: namespace.diagnostics
1129
+ };
1130
+ const path = validateTopikPath(options.normalizedPath);
1131
+ if (!path.ok) return {
1132
+ ok: false,
1133
+ diagnostics: path.diagnostics
1134
+ };
1135
+ const input = new Uint8Array(encoder.encode(namespace.value).byteLength + 1 + encoder.encode(path.value.path).byteLength);
1136
+ const namespaceBytes = encoder.encode(namespace.value);
1137
+ const pathBytes = encoder.encode(path.value.path);
1138
+ input.set(namespaceBytes, 0);
1139
+ input[namespaceBytes.byteLength] = 0;
1140
+ input.set(pathBytes, namespaceBytes.byteLength + 1);
1141
+ return {
1142
+ ok: true,
1143
+ value: parseGeneratedAssetName(`auto-v1-${base32(createHash("sha256").update(input).digest())}`),
1144
+ diagnostics: []
1145
+ };
1146
+ }
1147
+ function topikAssetNameDescriptor() {
1148
+ return { id: TOPIK_ASSET_NAME_VERSION };
1149
+ }
1150
+ function schemaDiagnostic(error) {
1151
+ return topikAssetDiagnostic("TOPIK_ASSET_SCHEMA_INVALID", "Asset does not match Asset/v1", { location: { jsonPointer: error.instancePath || "/" } });
1152
+ }
1153
+ function failure(id, message, jsonPointer = "/") {
1154
+ return {
1155
+ ok: false,
1156
+ diagnostics: [topikAssetDiagnostic(id, message, { location: { jsonPointer } })]
1157
+ };
1158
+ }
1159
+ function fail(source, id, message) {
1160
+ return {
1161
+ ...failure(id, message),
1162
+ source
1163
+ };
1164
+ }
1165
+ function isRecord$2(value) {
1166
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1167
+ }
1168
+ function equalBytes$1(left, right) {
1169
+ return left.byteLength === right.byteLength && left.every((byte, index) => byte === right[index]);
1170
+ }
1171
+ function base32(bytes) {
1172
+ const alphabet = "abcdefghijklmnopqrstuvwxyz234567";
1173
+ let accumulator = 0;
1174
+ let bits = 0;
1175
+ let result = "";
1176
+ for (const byte of bytes) {
1177
+ accumulator = accumulator << 8 | byte;
1178
+ bits += 8;
1179
+ while (bits >= 5) {
1180
+ bits -= 5;
1181
+ result += alphabet[accumulator >>> bits & 31];
1182
+ accumulator &= (1 << bits) - 1;
1183
+ }
1184
+ }
1185
+ if (bits > 0) result += alphabet[accumulator << 5 - bits & 31];
1186
+ return result;
1187
+ }
1188
+ //#endregion
1189
+ //#region src/assets/files.ts
1190
+ const execFileAsync = promisify(execFile);
1191
+ function validatePortableAssetFile(file) {
1192
+ const diagnostics = [];
1193
+ const path = validateTopikPath(file.path);
1194
+ if (!path.ok) diagnostics.push(...path.diagnostics);
1195
+ const basename = file.path.split("/").at(-1)?.toLowerCase();
1196
+ if ([
1197
+ ".gitattributes",
1198
+ ".gitmodules",
1199
+ ".lfsconfig"
1200
+ ].includes(basename ?? "")) diagnostics.push(unsupported(file, "Security-sensitive Git configuration cannot be asset-owned"));
1201
+ if (file.type !== "regular" || !hasPortableMode(file) || file.source !== "git" && file.linkCount !== 1 || file.hasAlternateDataStream === true || file.contentFilter !== void 0 && file.contentFilter !== "unspecified" || file.workingTreeEncoding !== void 0) diagnostics.push(unsupported(file, "Portable assets must be unfiltered regular non-executable files"));
1202
+ if (file.bytes === void 0) diagnostics.push(topikAssetDiagnostic("TOPIK_ASSET_FILE_MISSING", "File bytes are unavailable", {
1203
+ descriptorVersion: TOPIK_PATH_VERSION,
1204
+ location: { path: file.path },
1205
+ recovery: "restore-file"
1206
+ }));
1207
+ else if (file.bytes.byteLength > TOPIK_ASSET_LIMITS.maxAssetBytes) diagnostics.push(unsupported(file, "Asset bytes exceed the portable size limit"));
1208
+ else if (looksLikeGitLfsPointer(file.bytes)) diagnostics.push(unsupported(file, "Git LFS pointers and near-miss signatures are not portable bytes"));
1209
+ return diagnostics.length === 0 ? {
1210
+ ok: true,
1211
+ value: file,
1212
+ diagnostics: []
1213
+ } : {
1214
+ ok: false,
1215
+ value: file,
1216
+ diagnostics
1217
+ };
1218
+ }
1219
+ function validatePortableAssetTree(files) {
1220
+ const diagnostics = [];
1221
+ const seen = /* @__PURE__ */ new Set();
1222
+ for (const file of files) {
1223
+ if (seen.has(file.path)) {
1224
+ diagnostics.push(topikAssetDiagnostic("TOPIK_ASSET_PATH_COLLISION", "Tree repeats an exact file path", {
1225
+ descriptorVersion: TOPIK_PATH_VERSION,
1226
+ location: { path: file.path },
1227
+ reason: "casefold_collision"
1228
+ }));
1229
+ continue;
1230
+ }
1231
+ seen.add(file.path);
1232
+ const validation = validatePortableAssetFile(file);
1233
+ if (!validation.ok) diagnostics.push(...validation.diagnostics);
1234
+ }
1235
+ const paths = validateTopikPathSet(files.map((file) => file.path));
1236
+ if (!paths.ok) diagnostics.push(...paths.diagnostics);
1237
+ return diagnostics.length === 0 ? {
1238
+ ok: true,
1239
+ value: files,
1240
+ diagnostics: []
1241
+ } : {
1242
+ ok: false,
1243
+ value: files,
1244
+ diagnostics
1245
+ };
1246
+ }
1247
+ /** @internal Classify directory navigation through anchored no-follow handles. */
1248
+ async function classifyPortableNavigationPath(options) {
1249
+ const pathValidation = validateTopikPath(options.path);
1250
+ if (!pathValidation.ok) return "other";
1251
+ if (process.platform !== "linux" || typeof constants.O_NOFOLLOW !== "number" || typeof constants.O_DIRECTORY !== "number") return "other";
1252
+ const handles = [];
1253
+ try {
1254
+ const expectedRoot = await lstat(options.root, { bigint: true });
1255
+ if (!expectedRoot.isDirectory() || expectedRoot.isSymbolicLink()) return "other";
1256
+ const root = await open(options.root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1257
+ handles.push(root);
1258
+ const openedRoot = await root.stat({ bigint: true });
1259
+ if (!openedRoot.isDirectory() || expectedRoot.dev !== openedRoot.dev || expectedRoot.ino !== openedRoot.ino) return "other";
1260
+ for (const component of pathValidation.value.path.split("/")) {
1261
+ const parent = handles.at(-1);
1262
+ if (parent === void 0) return "other";
1263
+ const child = await open(procFdChild(parent.fd, component), constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1264
+ handles.push(child);
1265
+ if (!(await child.stat({ bigint: true })).isDirectory()) return "other";
1266
+ }
1267
+ return "directory";
1268
+ } catch (error) {
1269
+ return isMissingFilesystemError(error) ? "missing" : "other";
1270
+ } finally {
1271
+ for (const handle of handles.reverse()) await handle.close().catch(() => void 0);
1272
+ }
1273
+ }
1274
+ function isMissingFilesystemError(error) {
1275
+ return error?.code === "ENOENT";
1276
+ }
1277
+ /** Filesystem reader using no-follow traversal and stable same-handle identity checks. */
1278
+ async function readPortableAssetFile(options) {
1279
+ return readPortableAssetFileAnchored(options);
1280
+ }
1281
+ async function readPortableAssetFileAnchored(options, afterDirectoryOpened, afterFileRead) {
1282
+ const pathValidation = validateTopikPath(options.path);
1283
+ if (!pathValidation.ok) return {
1284
+ ok: false,
1285
+ diagnostics: pathValidation.diagnostics
1286
+ };
1287
+ if (process.platform !== "linux" || typeof constants.O_NOFOLLOW !== "number" || typeof constants.O_DIRECTORY !== "number") return {
1288
+ ok: false,
1289
+ diagnostics: [unsupportedPath(options.path, "This platform cannot prove descriptor-anchored no-follow traversal")]
1290
+ };
1291
+ const directoryHandles = [];
1292
+ const gitBoundaryFingerprints = [];
1293
+ let fileHandle;
1294
+ try {
1295
+ const repositoryBefore = await repositoryEvidenceFingerprint(options.root, options.path);
1296
+ if (!repositoryBefore.ok) return {
1297
+ ok: false,
1298
+ diagnostics: [unsupportedPath(options.path, repositoryBefore.failure)]
1299
+ };
1300
+ const gitAttributesBefore = await effectiveGitAttributeFingerprint(options.root, options.path);
1301
+ if (!gitAttributesBefore.ok) return {
1302
+ ok: false,
1303
+ diagnostics: [unsupportedPath(options.path, gitAttributesBefore.failure)]
1304
+ };
1305
+ const expectedRoot = await lstat(options.root, { bigint: true });
1306
+ if (!expectedRoot.isDirectory() || expectedRoot.isSymbolicLink()) return {
1307
+ ok: false,
1308
+ diagnostics: [unsupportedPath(options.path, "Resource root is not a stable directory")]
1309
+ };
1310
+ const rootHandle = await open(options.root, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1311
+ directoryHandles.push(rootHandle);
1312
+ const openedRoot = await rootHandle.stat({ bigint: true });
1313
+ if (!openedRoot.isDirectory() || expectedRoot.dev !== openedRoot.dev || expectedRoot.ino !== openedRoot.ino) return {
1314
+ ok: false,
1315
+ diagnostics: [unsupportedPath(options.path, "Resource root identity changed before open")]
1316
+ };
1317
+ const rootGitBoundary = await anchoredGitBoundaryFingerprint(rootHandle);
1318
+ if (!rootGitBoundary.ok) return {
1319
+ ok: false,
1320
+ diagnostics: [unsupportedPath(options.path, rootGitBoundary.failure)]
1321
+ };
1322
+ gitBoundaryFingerprints.push(rootGitBoundary.fingerprint);
1323
+ const components = options.path.split("/");
1324
+ const attributes = {};
1325
+ const rootAttributeFailure = await applyGitAttributes(rootHandle, components.join("/"), attributes);
1326
+ if (rootAttributeFailure !== void 0) return {
1327
+ ok: false,
1328
+ diagnostics: [unsupportedPath(options.path, rootAttributeFailure)]
1329
+ };
1330
+ for (let index = 0; index < components.length - 1; index++) {
1331
+ const parent = directoryHandles.at(-1);
1332
+ if (parent === void 0) throw new Error("Missing anchored parent directory handle");
1333
+ const child = await open(procFdChild(parent.fd, components[index]), constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
1334
+ directoryHandles.push(child);
1335
+ if (!(await child.stat({ bigint: true })).isDirectory()) return {
1336
+ ok: false,
1337
+ diagnostics: [unsupportedPath(options.path, "Non-directory traversal is unsupported")]
1338
+ };
1339
+ const gitBoundary = await anchoredGitBoundaryFingerprint(child);
1340
+ if (!gitBoundary.ok || gitBoundary.present) return {
1341
+ ok: false,
1342
+ diagnostics: [unsupportedPath(options.path, gitBoundary.ok ? "Nested Git worktree or submodule traversal is unsupported" : gitBoundary.failure)]
1343
+ };
1344
+ gitBoundaryFingerprints.push(gitBoundary.fingerprint);
1345
+ const attributeFailure = await applyGitAttributes(child, components.slice(index + 1).join("/"), attributes);
1346
+ if (attributeFailure !== void 0) return {
1347
+ ok: false,
1348
+ diagnostics: [unsupportedPath(options.path, attributeFailure)]
1349
+ };
1350
+ await afterDirectoryOpened?.(components.slice(0, index + 1));
1351
+ }
1352
+ if (attributes.filter !== void 0 && attributes.filter !== "unspecified" || attributes.workingTreeEncoding !== void 0 && attributes.workingTreeEncoding !== "unspecified") return {
1353
+ ok: false,
1354
+ diagnostics: [unsupportedPath(options.path, "Effective Git content filters or working-tree encoding are not portable")]
1355
+ };
1356
+ const parent = directoryHandles.at(-1);
1357
+ if (parent === void 0) throw new Error("Missing anchored parent directory handle");
1358
+ fileHandle = await open(procFdChild(parent.fd, components.at(-1) ?? ""), constants.O_RDONLY | constants.O_NOFOLLOW | (typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0));
1359
+ const before = await fileHandle.stat({ bigint: true });
1360
+ if (!before.isFile() || before.nlink !== 1n || (before.mode & 73n) !== 0n || before.size > BigInt(TOPIK_ASSET_LIMITS.maxAssetBytes) || before.size > 0n && before.blocks * 512n < before.size) return {
1361
+ ok: false,
1362
+ diagnostics: [unsupportedPath(options.path, "Unsafe filesystem file type or mode")]
1363
+ };
1364
+ const bytes = await fileHandle.readFile();
1365
+ await afterFileRead?.();
1366
+ const after = await fileHandle.stat({ bigint: true });
1367
+ if (before.dev !== after.dev || before.ino !== after.ino || before.mode !== after.mode || before.nlink !== after.nlink || before.size !== after.size || before.ctimeNs !== after.ctimeNs || before.mtimeNs !== after.mtimeNs) return {
1368
+ ok: false,
1369
+ diagnostics: [unsupportedPath(options.path, "File identity changed while hashing")]
1370
+ };
1371
+ for (let index = 0; index < directoryHandles.length; index++) {
1372
+ const gitBoundary = await anchoredGitBoundaryFingerprint(directoryHandles[index]);
1373
+ if (!gitBoundary.ok || gitBoundary.fingerprint !== gitBoundaryFingerprints[index] || index > 0 && gitBoundary.present) return {
1374
+ ok: false,
1375
+ diagnostics: [unsupportedPath(options.path, gitBoundary.ok ? "Git worktree boundary evidence changed while the file was read" : gitBoundary.failure)]
1376
+ };
1377
+ }
1378
+ const repositoryAfter = await repositoryEvidenceFingerprint(options.root, options.path);
1379
+ if (!repositoryAfter.ok || repositoryAfter.fingerprint !== repositoryBefore.fingerprint) return {
1380
+ ok: false,
1381
+ diagnostics: [unsupportedPath(options.path, repositoryAfter.ok ? "Git repository evidence changed while the file was read" : repositoryAfter.failure)]
1382
+ };
1383
+ const gitAttributesAfter = await effectiveGitAttributeFingerprint(options.root, options.path);
1384
+ if (!gitAttributesAfter.ok || gitAttributesAfter.fingerprint !== gitAttributesBefore.fingerprint) return {
1385
+ ok: false,
1386
+ diagnostics: [unsupportedPath(options.path, gitAttributesAfter.ok ? "Effective Git attribute evidence changed while it was evaluated" : gitAttributesAfter.failure)]
1387
+ };
1388
+ return validatePortableAssetFile({
1389
+ path: options.path,
1390
+ type: "regular",
1391
+ source: "filesystem",
1392
+ mode: "100644",
1393
+ bytes,
1394
+ linkCount: Number(after.nlink)
1395
+ });
1396
+ } catch (error) {
1397
+ return {
1398
+ ok: false,
1399
+ diagnostics: [error.code === "ENOENT" ? topikAssetDiagnostic("TOPIK_ASSET_FILE_MISSING", "Asset file is missing", {
1400
+ location: { path: options.path },
1401
+ recovery: "restore-file"
1402
+ }) : unsupportedPath(options.path, "File read failed without a portable no-follow proof")]
1403
+ };
1404
+ } finally {
1405
+ await fileHandle?.close().catch(() => void 0);
1406
+ await Promise.all(directoryHandles.map((handle) => handle.close().catch(() => void 0)));
1407
+ }
1408
+ }
1409
+ async function repositoryEvidenceFingerprint(root, path) {
1410
+ if (!await hasGitBoundary(root)) return {
1411
+ ok: true,
1412
+ fingerprint: "not-in-worktree"
1413
+ };
1414
+ try {
1415
+ const worktreeResult = await execFileAsync("/usr/bin/git", [
1416
+ "-C",
1417
+ root,
1418
+ "rev-parse",
1419
+ "--show-toplevel"
1420
+ ], {
1421
+ encoding: "utf8",
1422
+ maxBuffer: 65536
1423
+ });
1424
+ if ((await execFileAsync("/usr/bin/git", [
1425
+ "-C",
1426
+ root,
1427
+ "rev-parse",
1428
+ "--show-superproject-working-tree"
1429
+ ], {
1430
+ encoding: "utf8",
1431
+ maxBuffer: 65536
1432
+ })).stdout.trim().length > 0) return {
1433
+ ok: false,
1434
+ failure: "Compilation root traverses a checked-out Git submodule"
1435
+ };
1436
+ const worktree = worktreeResult.stdout.trim();
1437
+ const rootFromWorktree = relative(resolve(worktree), resolve(root));
1438
+ if (rootFromWorktree === ".." || rootFromWorktree.startsWith(`..${sep}`) || isAbsolute(rootFromWorktree)) return {
1439
+ ok: false,
1440
+ failure: "Compilation root is outside its Git worktree"
1441
+ };
1442
+ const worktreePath = [...rootFromWorktree === "" ? [] : rootFromWorktree.split(sep), ...path.split("/")].join("/");
1443
+ const indexResult = await execFileAsync("/usr/bin/git", [
1444
+ "-C",
1445
+ worktree,
1446
+ "ls-files",
1447
+ "--stage",
1448
+ "-z"
1449
+ ], {
1450
+ encoding: "utf8",
1451
+ maxBuffer: 67108864
1452
+ });
1453
+ for (const entry of indexResult.stdout.split("\0")) {
1454
+ if (entry.length === 0) continue;
1455
+ const separator = entry.indexOf(" ");
1456
+ if (separator === -1) return {
1457
+ ok: false,
1458
+ failure: "Git index evidence is malformed"
1459
+ };
1460
+ const [mode] = entry.slice(0, separator).split(" ");
1461
+ const entryPath = entry.slice(separator + 1);
1462
+ if (mode === "160000" && isPathAtOrBelow(worktreePath, entryPath)) return {
1463
+ ok: false,
1464
+ failure: "Asset path traverses a Gitlink index entry"
1465
+ };
1466
+ }
1467
+ return {
1468
+ ok: true,
1469
+ fingerprint: createHash("sha256").update(worktree).update("\0").update(rootFromWorktree).update("\0").update(indexResult.stdout).digest("hex")
1470
+ };
1471
+ } catch {
1472
+ return {
1473
+ ok: false,
1474
+ failure: "Git repository boundary could not be proven"
1475
+ };
1476
+ }
1477
+ }
1478
+ function isPathAtOrBelow(path, ancestor) {
1479
+ return path === ancestor || path.startsWith(`${ancestor}/`);
1480
+ }
1481
+ async function anchoredGitBoundaryFingerprint(directory) {
1482
+ let handle;
1483
+ try {
1484
+ handle = await open(procFdChild(directory.fd, ".git"), constants.O_RDONLY | constants.O_NOFOLLOW | (typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0));
1485
+ const before = await handle.stat({ bigint: true });
1486
+ if (!before.isFile() && !before.isDirectory()) return {
1487
+ ok: false,
1488
+ failure: "Git boundary is not a regular file or directory"
1489
+ };
1490
+ const after = await handle.stat({ bigint: true });
1491
+ if (before.dev !== after.dev || before.ino !== after.ino || before.mode !== after.mode || before.nlink !== after.nlink || before.size !== after.size || before.ctimeNs !== after.ctimeNs || before.mtimeNs !== after.mtimeNs) return {
1492
+ ok: false,
1493
+ failure: "Git boundary identity changed while it was inspected"
1494
+ };
1495
+ return {
1496
+ ok: true,
1497
+ present: true,
1498
+ fingerprint: [
1499
+ "present",
1500
+ after.dev,
1501
+ after.ino,
1502
+ after.mode,
1503
+ after.nlink,
1504
+ after.size,
1505
+ after.ctimeNs,
1506
+ after.mtimeNs
1507
+ ].join(":")
1508
+ };
1509
+ } catch (error) {
1510
+ return error.code === "ENOENT" ? {
1511
+ ok: true,
1512
+ present: false,
1513
+ fingerprint: "absent"
1514
+ } : {
1515
+ ok: false,
1516
+ failure: "Git boundary could not be inspected without following links"
1517
+ };
1518
+ } finally {
1519
+ await handle?.close().catch(() => void 0);
1520
+ }
1521
+ }
1522
+ async function effectiveGitAttributeFingerprint(root, path) {
1523
+ if (!await hasGitBoundary(root)) return {
1524
+ ok: true,
1525
+ fingerprint: "not-in-worktree"
1526
+ };
1527
+ let worktree;
1528
+ try {
1529
+ worktree = (await execFileAsync("/usr/bin/git", [
1530
+ "-C",
1531
+ root,
1532
+ "rev-parse",
1533
+ "--show-toplevel"
1534
+ ], { encoding: "utf8" })).stdout.trim();
1535
+ } catch {
1536
+ return {
1537
+ ok: false,
1538
+ failure: "Git worktree boundary could not be proven"
1539
+ };
1540
+ }
1541
+ const rootFromWorktree = relative(resolve(worktree), resolve(root));
1542
+ if (rootFromWorktree === ".." || rootFromWorktree.startsWith(`..${sep}`) || isAbsolute(rootFromWorktree)) return {
1543
+ ok: false,
1544
+ failure: "Compilation root is outside its Git worktree"
1545
+ };
1546
+ const worktreePath = [...rootFromWorktree === "" ? [] : rootFromWorktree.split(sep), ...path.split("/")].join("/");
1547
+ let stdout;
1548
+ try {
1549
+ stdout = (await execFileAsync("/usr/bin/git", [
1550
+ "-C",
1551
+ worktree,
1552
+ "check-attr",
1553
+ "-z",
1554
+ "filter",
1555
+ "working-tree-encoding",
1556
+ "--",
1557
+ worktreePath
1558
+ ], {
1559
+ encoding: "utf8",
1560
+ maxBuffer: 65536
1561
+ })).stdout;
1562
+ } catch {
1563
+ return {
1564
+ ok: false,
1565
+ failure: "Effective Git attributes could not be evaluated safely"
1566
+ };
1567
+ }
1568
+ const fields = stdout.split("\0");
1569
+ if (fields.at(-1) === "") fields.pop();
1570
+ if (fields.length !== 6) return {
1571
+ ok: false,
1572
+ failure: "Effective Git attribute output is malformed"
1573
+ };
1574
+ const values = /* @__PURE__ */ new Map();
1575
+ for (let index = 0; index < fields.length; index += 3) {
1576
+ if (fields[index] !== worktreePath) return {
1577
+ ok: false,
1578
+ failure: "Effective Git attribute path binding changed"
1579
+ };
1580
+ values.set(fields[index + 1], fields[index + 2]);
1581
+ }
1582
+ if (values.get("filter") !== "unspecified" || values.get("working-tree-encoding") !== "unspecified") return {
1583
+ ok: false,
1584
+ failure: "Effective Git content filters or working-tree encoding are not portable"
1585
+ };
1586
+ return {
1587
+ ok: true,
1588
+ fingerprint: `${values.get("filter")}\0${values.get("working-tree-encoding")}`
1589
+ };
1590
+ }
1591
+ async function hasGitBoundary(root) {
1592
+ let current = resolve(root);
1593
+ for (;;) {
1594
+ try {
1595
+ const stat = await lstat(`${current}/.git`);
1596
+ if (stat.isFile()) return true;
1597
+ if (stat.isDirectory()) try {
1598
+ if ((await lstat(`${current}/.git/HEAD`)).isFile()) return true;
1599
+ } catch (error) {
1600
+ if (error.code !== "ENOENT") return true;
1601
+ }
1602
+ } catch (error) {
1603
+ if (error.code !== "ENOENT") return true;
1604
+ }
1605
+ const parent = dirname(current);
1606
+ if (parent === current) return false;
1607
+ current = parent;
1608
+ }
1609
+ }
1610
+ async function applyGitAttributes(directory, relativePath, state) {
1611
+ let handle;
1612
+ try {
1613
+ handle = await open(procFdChild(directory.fd, ".gitattributes"), constants.O_RDONLY | constants.O_NOFOLLOW | (typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0));
1614
+ const before = await handle.stat({ bigint: true });
1615
+ if (!before.isFile() || before.nlink !== 1n) return "Git attribute evidence is not a stable regular file";
1616
+ const bytes = await handle.readFile();
1617
+ const after = await handle.stat({ bigint: true });
1618
+ if (before.dev !== after.dev || before.ino !== after.ino || before.mode !== after.mode || before.nlink !== after.nlink || before.size !== after.size || before.ctimeNs !== after.ctimeNs || before.mtimeNs !== after.mtimeNs) return "Git attribute evidence changed while it was evaluated";
1619
+ let source;
1620
+ try {
1621
+ source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
1622
+ } catch {
1623
+ return "Git attribute evidence is not strict UTF-8";
1624
+ }
1625
+ for (const rawLine of source.split(/\r?\n/u)) {
1626
+ const line = rawLine.trim();
1627
+ if (line.length === 0 || line.startsWith("#")) continue;
1628
+ const fields = line.split(/[\t ]+/u);
1629
+ const pattern = fields.shift();
1630
+ if (pattern === void 0 || pattern.startsWith("\"")) {
1631
+ if (fields.some(isPortableAttributeToken)) return "Quoted Git attribute patterns cannot be proven by the filesystem compiler";
1632
+ continue;
1633
+ }
1634
+ if (hasUnsupportedGitAttributePattern(pattern) && fields.some(isPortableAttributeToken)) return "Git attribute pattern cannot be proven by the filesystem compiler";
1635
+ if (!gitAttributePatternMatches(pattern, relativePath)) continue;
1636
+ for (const token of fields) applyAttributeToken(token, state);
1637
+ }
1638
+ return;
1639
+ } catch (error) {
1640
+ return error.code === "ENOENT" ? void 0 : "Git attribute evidence could not be read without following links";
1641
+ } finally {
1642
+ await handle?.close().catch(() => void 0);
1643
+ }
1644
+ }
1645
+ function applyAttributeToken(token, state) {
1646
+ const [rawName] = token.replace(/^[!-]/u, "").split("=", 1);
1647
+ const property = rawName === "filter" ? "filter" : rawName === "working-tree-encoding" ? "workingTreeEncoding" : void 0;
1648
+ if (property === void 0) return;
1649
+ state[property] = token.startsWith("!") ? "unspecified" : token.startsWith("-") ? "unset" : "set";
1650
+ }
1651
+ function isPortableAttributeToken(token) {
1652
+ return /^(?:[!-])?(?:filter|working-tree-encoding)(?:=|$)/u.test(token);
1653
+ }
1654
+ function hasUnsupportedGitAttributePattern(pattern) {
1655
+ return pattern.startsWith("!") || pattern.includes("\\") || pattern.includes("[") || pattern.includes("]") || pattern.includes("**");
1656
+ }
1657
+ function gitAttributePatternMatches(pattern, relativePath) {
1658
+ if (pattern.length === 0 || pattern.startsWith("!") || pattern.endsWith("/")) return false;
1659
+ const anchored = pattern.startsWith("/");
1660
+ const normalizedPattern = anchored ? pattern.slice(1) : pattern;
1661
+ const candidate = anchored || normalizedPattern.includes("/") ? relativePath : relativePath.split("/").at(-1) ?? relativePath;
1662
+ return globToRegExp(normalizedPattern).test(candidate);
1663
+ }
1664
+ function globToRegExp(pattern) {
1665
+ let source = "^";
1666
+ for (let index = 0; index < pattern.length; index++) {
1667
+ const character = pattern[index];
1668
+ if (character === "*" && pattern[index + 1] === "*") {
1669
+ source += ".*";
1670
+ index++;
1671
+ } else if (character === "*") source += "[^/]*";
1672
+ else if (character === "?") source += "[^/]";
1673
+ else source += character.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
1674
+ }
1675
+ return new RegExp(`${source}$`, "u");
1676
+ }
1677
+ function looksLikeGitLfsPointer(bytes) {
1678
+ const prefix = new TextDecoder().decode(bytes.slice(0, 1024));
1679
+ return /(?:^|\n)version\s+https?:\/\/git-lfs\.github\.com\/spec\/v1(?:\s|$)/iu.test(prefix) || /(?:^|\n)oid\s+sha256:[0-9a-f]{32,64}(?:\s|$)/iu.test(prefix) || /git-media|git-lfs/iu.test(prefix.slice(0, 256));
1680
+ }
1681
+ function unsupported(file, message) {
1682
+ return unsupportedPath(file.path, message);
1683
+ }
1684
+ function hasPortableMode(file) {
1685
+ if (file.source === "git" || file.source === "filesystem") return file.mode === "100644";
1686
+ if (file.source === "archive") return file.mode === "0644";
1687
+ return file.mode === "100644" || file.mode === "0644";
1688
+ }
1689
+ function procFdChild(parentFd, component) {
1690
+ return `/proc/self/fd/${parentFd}/${component}`;
1691
+ }
1692
+ function unsupportedPath(path, message) {
1693
+ return topikAssetDiagnostic("TOPIK_ASSET_FILE_TYPE_UNSUPPORTED", message, {
1694
+ descriptorVersion: TOPIK_PATH_VERSION,
1695
+ location: { path },
1696
+ recovery: "preserve-read-only"
1697
+ });
1698
+ }
1699
+ //#endregion
1700
+ //#region src/validate.ts
1701
+ const ajv = new Ajv2020({
1702
+ strict: true,
1703
+ discriminator: true,
1704
+ ownProperties: true
1705
+ });
1706
+ addFormats(ajv);
1707
+ const validators = /* @__PURE__ */ new Map([
1708
+ ["Course/v1", ajv.compile(courseV1Schema)],
1709
+ ["CourseModule/v1", ajv.compile(courseModuleV1Schema)],
1710
+ ["CoursePage/v1", ajv.compile(coursePageV1Schema)],
1711
+ ["Guide/v1", ajv.compile(guideV1Schema)],
1712
+ ["Person/v1", ajv.compile(personV1Schema)],
1713
+ ["Wiki/v1", ajv.compile(wikiV1Schema)],
1714
+ ["WikiPage/v1", ajv.compile(wikiPageV1Schema)]
1715
+ ]);
1716
+ function isRecord$1(value) {
1717
+ return typeof value === "object" && value !== null;
1718
+ }
1719
+ function getResourceLabel(resource) {
1720
+ return `${Object.hasOwn(resource, "type") && typeof resource.type === "string" ? resource.type : "unknown"}/${Object.hasOwn(resource, "name") && typeof resource.name === "string" ? resource.name : "unknown"}`;
1721
+ }
1722
+ /** Validate the resources produced by `compile` and the CLI export. */
1723
+ function validateResources(resources) {
1724
+ const errors = [];
1725
+ for (const resource of resources) {
1726
+ if (!isRecord$1(resource)) {
1727
+ errors.push({
1728
+ resource: "unknown/unknown",
1729
+ path: "/",
1730
+ message: "Resource must be an object"
1731
+ });
1732
+ continue;
1733
+ }
1734
+ if (!Object.hasOwn(resource, "type") || typeof resource.type !== "string") {
1735
+ errors.push({
1736
+ resource: getResourceLabel(resource),
1737
+ path: "/type",
1738
+ message: "Resource type must be a string"
1739
+ });
1740
+ continue;
1741
+ }
1742
+ if (![
1743
+ "Asset",
1744
+ "Course",
1745
+ "CourseModule",
1746
+ "CoursePage",
1747
+ "Guide",
1748
+ "Person",
1749
+ "Wiki",
1750
+ "WikiPage"
1751
+ ].includes(resource.type)) {
1752
+ errors.push({
1753
+ resource: getResourceLabel(resource),
1754
+ path: "/type",
1755
+ message: `Unsupported resource type: ${resource.type}`
1756
+ });
1757
+ continue;
1758
+ }
1759
+ if (!Object.hasOwn(resource, "apiVersion") || typeof resource.apiVersion !== "string") {
1760
+ errors.push({
1761
+ resource: getResourceLabel(resource),
1762
+ path: "/apiVersion",
1763
+ message: "Resource apiVersion must be a string"
1764
+ });
1765
+ continue;
1766
+ }
1767
+ if (resource.type === "Asset") {
1768
+ const validation = validateAssetValue(resource);
1769
+ if (!validation.ok) for (const diagnostic of validation.diagnostics) errors.push({
1770
+ id: diagnostic.id,
1771
+ resource: getResourceLabel(resource),
1772
+ path: diagnostic.location.jsonPointer ?? "/",
1773
+ message: diagnostic.message
1774
+ });
1775
+ continue;
1776
+ }
1777
+ const validate = validators.get(`${resource.type}/${resource.apiVersion}`);
1778
+ if (!validate) {
1779
+ errors.push({
1780
+ id: "resource-unsupported-version",
1781
+ resource: getResourceLabel(resource),
1782
+ path: "/apiVersion",
1783
+ message: `Unsupported ${resource.type} apiVersion: ${resource.apiVersion}`
1784
+ });
1785
+ continue;
1786
+ }
1787
+ if (!validate(resource)) for (const err of validate.errors ?? []) errors.push({
1788
+ resource: getResourceLabel(resource),
1789
+ path: err.instancePath || "/",
1790
+ message: err.message ?? "Unknown error"
1791
+ });
1792
+ }
1793
+ return {
1794
+ valid: errors.length === 0,
1795
+ errors
1796
+ };
1797
+ }
1798
+ //#endregion
1799
+ //#region src/assets/identity.ts
1800
+ function createTopikAssetSemanticRecord(assets, references) {
1801
+ return {
1802
+ descriptor: "topik-asset-semantic-v1",
1803
+ assetNames: assets.map((asset) => asset.name).sort(compareUtf8$1),
1804
+ references: [...references].sort((left, right) => compareUtf8$1(`${left.resource}\0${left.position}\0${left.slot}\0${left.name}`, `${right.resource}\0${right.position}\0${right.slot}\0${right.name}`))
1805
+ };
1806
+ }
1807
+ function createTopikMaterializationRecord(resources, payloads) {
1808
+ const resourceKeys = /* @__PURE__ */ new Set();
1809
+ const payloadPaths = /* @__PURE__ */ new Set();
1810
+ for (const input of resources) {
1811
+ const key = `${input.resource.type}/${input.resource.name}`;
1812
+ if (resourceKeys.has(key)) throw new TypeError("Materialization repeats a resource descriptor");
1813
+ resourceKeys.add(key);
1814
+ }
1815
+ for (const input of payloads) {
1816
+ if (payloadPaths.has(input.path)) throw new TypeError("Materialization repeats a payload path");
1817
+ payloadPaths.add(input.path);
1818
+ }
1819
+ return {
1820
+ descriptor: TOPIK_MATERIALIZATION_VERSION,
1821
+ resources: resources.map((input) => ({
1822
+ resource: `${input.resource.type}/${input.resource.name}`,
1823
+ path: `resources/${input.resource.type}/${input.resource.name}.json`,
1824
+ size: input.bytes.byteLength,
1825
+ sha256: sha256$1(input.bytes)
1826
+ })).sort((left, right) => compareUtf8$1(left.resource, right.resource)),
1827
+ payloads: payloads.map((input) => ({
1828
+ path: input.path,
1829
+ size: input.bytes.byteLength,
1830
+ sha256: sha256$1(input.bytes),
1831
+ assetNames: [...input.assetNames].sort(compareUtf8$1)
1832
+ })).sort((left, right) => compareUtf8$1(left.path, right.path))
1833
+ };
1834
+ }
1835
+ function validateTopikMaterializationRecord(record, resources, semantic) {
1836
+ if (!isTopikJsonDataValue(record) || !isRecord(record) || !Object.hasOwn(record, "descriptor") || typeof record.descriptor !== "string") return materializationFailure("TOPIK_ASSET_SCHEMA_INVALID", "Materialization record is malformed");
1837
+ if (record.descriptor !== "topik-materialization-v1") return materializationFailure("TOPIK_ASSET_UNSUPPORTED_VERSION", "Materialization descriptor version is unsupported", "topik-materialization-unknown");
1838
+ if (!isMaterializationRecordV1(record)) return materializationFailure("TOPIK_ASSET_SCHEMA_INVALID", "Materialization record is malformed");
1839
+ if (!hasCanonicalMaterializationStructure(record)) return materializationFailure("TOPIK_ASSET_INVENTORY_INCOMPLETE", "Materialization inventory records are noncanonical or duplicated");
1840
+ if (!validateResources(resources).valid) return materializationFailure("TOPIK_ASSET_INVENTORY_INCOMPLETE", "Materialization requires a valid complete compiled resource set");
1841
+ if (!isTopikJsonDataValue(semantic) || !isRecord(semantic) || !isSemanticRecordV1(semantic)) return materializationFailure("TOPIK_ASSET_SCHEMA_INVALID", "Semantic record is malformed");
1842
+ if (semantic.descriptor !== "topik-asset-semantic-v1") return materializationFailure("TOPIK_ASSET_UNSUPPORTED_VERSION", "Semantic descriptor version is unsupported", "topik-asset-semantic-unknown");
1843
+ const expectedSemantic = expectedSemanticRecord(resources);
1844
+ if (expectedSemantic === void 0 || serializeTopikJson(semantic) !== serializeTopikJson(expectedSemantic)) return materializationFailure("TOPIK_ASSET_INVENTORY_INCOMPLETE", "Semantic mappings do not close over the compiled Asset set");
1845
+ const expected = expectedMaterializationRecord(resources);
1846
+ if (expected === void 0 || serializeTopikJson(record) !== serializeTopikJson(expected)) return materializationFailure("TOPIK_ASSET_INVENTORY_INCOMPLETE", "Materialization does not exactly describe the compiled resource set");
1847
+ return {
1848
+ ok: true,
1849
+ value: record,
1850
+ diagnostics: []
1851
+ };
1852
+ }
1853
+ function isSemanticRecordV1(value) {
1854
+ return hasExactKeys(value, [
1855
+ "assetNames",
1856
+ "descriptor",
1857
+ "references"
1858
+ ]) && typeof value.descriptor === "string" && Array.isArray(value.assetNames) && value.assetNames.every((name) => typeof name === "string") && Array.isArray(value.references) && value.references.every((reference) => isRecord(reference) && hasExactKeys(reference, [
1859
+ "name",
1860
+ "position",
1861
+ "resource",
1862
+ "slot"
1863
+ ]) && typeof reference.name === "string" && typeof reference.position === "string" && typeof reference.resource === "string" && typeof reference.slot === "string");
1864
+ }
1865
+ function expectedSemanticRecord(resources) {
1866
+ const assets = resources.filter((resource) => resource.type === "Asset");
1867
+ const assetNames = new Set(assets.map((asset) => asset.name));
1868
+ if (assetNames.size !== assets.length) return void 0;
1869
+ const references = [];
1870
+ for (const resource of resources) {
1871
+ if (resource.type !== "Guide" && resource.type !== "WikiPage" && resource.type !== "CoursePage" || resource.spec.content.format !== "topik") continue;
1872
+ const content = resource.spec.content.value;
1873
+ if (!validateTopikContent(content, { allowCompiledAssetReferences: true }).valid) return;
1874
+ for (const occurrence of extractTopikAssetOccurrences(content)) {
1875
+ const parsedValidation = validateTopikAssetReference(occurrence.parsedReference);
1876
+ if (occurrence.kind === "external-https" || occurrence.reference.length === 0 && parsedValidation.valid && parsedValidation.kind === "external-https") continue;
1877
+ if (occurrence.kind !== "asset") return void 0;
1878
+ const name = occurrence.reference.slice(6);
1879
+ if (!assetNames.has(name)) return void 0;
1880
+ references.push({
1881
+ resource: `${resource.type}/${resource.name}`,
1882
+ position: occurrence.position,
1883
+ slot: occurrence.slot,
1884
+ name
1885
+ });
1886
+ }
1887
+ }
1888
+ const referencedNames = new Set(references.map((reference) => reference.name));
1889
+ if (assets.some((asset) => !referencedNames.has(asset.name))) return void 0;
1890
+ return createTopikAssetSemanticRecord(assets, references);
1891
+ }
1892
+ function isMaterializationRecordV1(value) {
1893
+ if (!hasExactKeys(value, [
1894
+ "descriptor",
1895
+ "payloads",
1896
+ "resources"
1897
+ ]) || !Array.isArray(value.resources) || !Array.isArray(value.payloads)) return false;
1898
+ return value.resources.every((entry) => isRecord(entry) && hasExactKeys(entry, [
1899
+ "path",
1900
+ "resource",
1901
+ "sha256",
1902
+ "size"
1903
+ ]) && typeof entry.resource === "string" && typeof entry.path === "string" && validInventorySize(entry.size, TOPIK_ASSET_LIMITS.maxDescriptorBytes) && isSha256(entry.sha256)) && value.payloads.every((entry) => isRecord(entry) && hasExactKeys(entry, [
1904
+ "assetNames",
1905
+ "path",
1906
+ "sha256",
1907
+ "size"
1908
+ ]) && typeof entry.path === "string" && validInventorySize(entry.size, TOPIK_ASSET_LIMITS.maxAssetBytes) && isSha256(entry.sha256) && Array.isArray(entry.assetNames) && entry.assetNames.every((name) => typeof name === "string"));
1909
+ }
1910
+ function hasCanonicalMaterializationStructure(record) {
1911
+ const resourceKeys = record.resources.map((entry) => entry.resource);
1912
+ const resourcePaths = record.resources.map((entry) => entry.path);
1913
+ const payloadPaths = record.payloads.map((entry) => entry.path);
1914
+ if (new Set(resourceKeys).size !== resourceKeys.length || new Set(resourcePaths).size !== resourcePaths.length || new Set(payloadPaths).size !== payloadPaths.length || !isSorted(resourceKeys) || !isSorted(payloadPaths) || record.payloads.some((entry) => new Set(entry.assetNames).size !== entry.assetNames.length || !isSorted(entry.assetNames))) return false;
1915
+ return validateTopikPathSet([...resourcePaths, ...payloadPaths]).ok;
1916
+ }
1917
+ function expectedMaterializationRecord(resources) {
1918
+ let descriptorRecord;
1919
+ try {
1920
+ descriptorRecord = createTopikMaterializationRecord(resources.map((resource) => ({
1921
+ resource,
1922
+ bytes: new TextEncoder().encode(serializeTopikJson(resource))
1923
+ })), []);
1924
+ } catch {
1925
+ return;
1926
+ }
1927
+ const payloads = /* @__PURE__ */ new Map();
1928
+ for (const resource of resources) {
1929
+ if (resource.type !== "Asset") continue;
1930
+ const { integrity, size, uri } = resource.spec;
1931
+ if (integrity === void 0 || size === void 0 || size > TOPIK_ASSET_LIMITS.maxAssetBytes || uri !== `blobs/${integrity.slice(7)}`) return;
1932
+ const sha256 = integrity.slice(7);
1933
+ if (!isSha256(sha256)) return void 0;
1934
+ const existing = payloads.get(uri);
1935
+ if (existing !== void 0) {
1936
+ if (existing.sha256 !== sha256 || existing.size !== size) return void 0;
1937
+ existing.assetNames.push(resource.name);
1938
+ } else payloads.set(uri, {
1939
+ path: uri,
1940
+ size,
1941
+ sha256,
1942
+ assetNames: [resource.name]
1943
+ });
1944
+ }
1945
+ const sortedPayloads = [...payloads.values()].map((payload) => ({
1946
+ ...payload,
1947
+ assetNames: [...payload.assetNames].sort(compareUtf8$1)
1948
+ })).sort((left, right) => compareUtf8$1(left.path, right.path));
1949
+ const expected = {
1950
+ descriptor: TOPIK_MATERIALIZATION_VERSION,
1951
+ resources: descriptorRecord.resources,
1952
+ payloads: sortedPayloads
1953
+ };
1954
+ return hasCanonicalMaterializationStructure(expected) ? expected : void 0;
1955
+ }
1956
+ function materializationFailure(id, message, descriptorVersion = TOPIK_MATERIALIZATION_VERSION) {
1957
+ return {
1958
+ ok: false,
1959
+ diagnostics: [topikAssetDiagnostic(id, message, {
1960
+ consequence: "block-identity-and-writes",
1961
+ descriptorVersion,
1962
+ recovery: "upgrade-reader"
1963
+ })]
1964
+ };
1965
+ }
1966
+ function validInventorySize(value, maximum) {
1967
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= maximum;
1968
+ }
1969
+ function isSha256(value) {
1970
+ return typeof value === "string" && /^[0-9a-f]{64}$/u.test(value);
1971
+ }
1972
+ function hasExactKeys(value, expected) {
1973
+ const keys = Object.keys(value).sort();
1974
+ return keys.length === expected.length && keys.every((key, index) => key === expected[index]);
1975
+ }
1976
+ function isRecord(value) {
1977
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1978
+ }
1979
+ function isSorted(values) {
1980
+ return values.every((value, index) => index === 0 || compareUtf8$1(values[index - 1], value) <= 0);
1981
+ }
1982
+ function digestTopikAssetSemanticRecord(record) {
1983
+ return sha256$1(new TextEncoder().encode(serializeTopikJson(record)));
1984
+ }
1985
+ function digestTopikMaterializationRecord(record) {
1986
+ return sha256$1(new TextEncoder().encode(serializeTopikJson(record)));
1987
+ }
1988
+ function compareTopikAssetIdentities(left, right) {
1989
+ if (left.semantic.descriptor !== right.semantic.descriptor || left.materialization.descriptor !== right.materialization.descriptor) return {
1990
+ ok: false,
1991
+ diagnostics: [topikAssetDiagnostic("TOPIK_ASSET_VERSION_INCOMPARABLE", "Identity descriptor versions differ", {
1992
+ consequence: "block-identity-and-writes",
1993
+ recovery: "upgrade-reader"
1994
+ })]
1995
+ };
1996
+ return {
1997
+ ok: true,
1998
+ value: {
1999
+ semanticEqual: digestTopikAssetSemanticRecord(left.semantic) === digestTopikAssetSemanticRecord(right.semantic),
2000
+ exactEqual: digestTopikMaterializationRecord(left.materialization) === digestTopikMaterializationRecord(right.materialization)
2001
+ },
2002
+ diagnostics: []
2003
+ };
2004
+ }
2005
+ function sha256$1(bytes) {
2006
+ return createHash("sha256").update(bytes).digest("hex");
2007
+ }
2008
+ function compareUtf8$1(left, right) {
2009
+ return Buffer.compare(Buffer.from(left), Buffer.from(right));
2010
+ }
2011
+ //#endregion
2012
+ //#region src/assets/media.ts
2013
+ const MARKUP_INSPECTION_LIMIT = 65536;
2014
+ const TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE = "application/x-topik-active-content";
2015
+ /** Sniff exact bytes conservatively. Unknown safe bytes remain opaque downloads. */
2016
+ function sniffPortableMediaType(bytes) {
2017
+ const has = (...values) => values.every((value, index) => bytes[index] === value);
2018
+ if (has(137, 80, 78, 71, 13, 10, 26, 10)) return "image/png";
2019
+ if (has(255, 216, 255)) return "image/jpeg";
2020
+ if (ascii(bytes, 0, 6) === "GIF87a" || ascii(bytes, 0, 6) === "GIF89a") return "image/gif";
2021
+ if (ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 4) === "WEBP") return "image/webp";
2022
+ if (ascii(bytes, 4, 8).includes("ftypavif")) return "image/avif";
2023
+ if (ascii(bytes, 0, 2) === "BM") return "image/bmp";
2024
+ if (has(0, 0, 1, 0)) return "image/x-icon";
2025
+ if (ascii(bytes, 0, 5) === "%PDF-") return "application/pdf";
2026
+ if (has(80, 75, 3, 4) || has(80, 75, 5, 6)) return "application/zip";
2027
+ if (has(31, 139)) return "application/gzip";
2028
+ if (ascii(bytes, 0, 4) === "RIFF" && ascii(bytes, 8, 4) === "WAVE") return "audio/wav";
2029
+ if (ascii(bytes, 0, 4) === "OggS") return "audio/ogg";
2030
+ if (ascii(bytes, 0, 4) === "fLaC") return "audio/flac";
2031
+ if (ascii(bytes, 0, 3) === "ID3" || bytes[0] === 255 && ((bytes[1] ?? 0) & 224) === 224) return "audio/mpeg";
2032
+ if (ascii(bytes, 4, 4) === "ftyp") return "video/mp4";
2033
+ if (has(127, 69, 76, 70) || has(77, 90) || has(254, 237, 250, 206) || has(206, 250, 237, 254) || has(254, 237, 250, 207) || has(207, 250, 237, 254) || has(202, 254, 186, 190) || has(35, 33)) return "application/x-executable";
2034
+ if (has(0, 97, 115, 109)) return "application/wasm";
2035
+ return sniffPortableMarkupType(bytes) ?? "application/octet-stream";
2036
+ }
2037
+ function isTopikActiveMediaType(mediaType) {
2038
+ return [
2039
+ "text/html",
2040
+ "image/svg+xml",
2041
+ "application/javascript",
2042
+ "text/javascript",
2043
+ "application/x-executable",
2044
+ "application/wasm",
2045
+ TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE
2046
+ ].includes(mediaType);
2047
+ }
2048
+ function isInlineMediaCompatible(mediaType, role) {
2049
+ return role.startsWith("image") ? mediaType.startsWith("image/") : /^(?:image|audio|video)\//u.test(mediaType);
2050
+ }
2051
+ function sniffPortableMarkupType(bytes) {
2052
+ const boundary = Math.min(bytes.byteLength, MARKUP_INSPECTION_LIMIT);
2053
+ const completionBytes = trailingUtf8Completion(bytes.slice(0, boundary));
2054
+ if (completionBytes > bytes.byteLength - boundary) return TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE;
2055
+ const inspectedEnd = boundary + completionBytes;
2056
+ if (completionBytes > 0) {
2057
+ const sequenceStart = trailingUtf8SequenceStart(bytes.slice(0, boundary));
2058
+ try {
2059
+ new TextDecoder("utf-8", { fatal: true }).decode(bytes.slice(sequenceStart, inspectedEnd));
2060
+ } catch {
2061
+ return TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE;
2062
+ }
2063
+ }
2064
+ let text;
2065
+ try {
2066
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes.slice(0, inspectedEnd)).toLowerCase();
2067
+ } catch {
2068
+ return;
2069
+ }
2070
+ let cursor = skipMarkupWhitespace(text, 0);
2071
+ let consumedPreamble = false;
2072
+ while (cursor < text.length) {
2073
+ if (text.startsWith("<!--", cursor)) {
2074
+ const end = text.indexOf("-->", cursor + 4);
2075
+ if (end === -1) return "text/html";
2076
+ consumedPreamble = true;
2077
+ cursor = skipMarkupWhitespace(text, end + 3);
2078
+ continue;
2079
+ }
2080
+ if (text.startsWith("<?xml", cursor)) {
2081
+ const end = text.indexOf("?>", cursor + 5);
2082
+ if (end === -1) return TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE;
2083
+ consumedPreamble = true;
2084
+ cursor = skipMarkupWhitespace(text, end + 2);
2085
+ continue;
2086
+ }
2087
+ break;
2088
+ }
2089
+ if (cursor === text.length) return bytes.byteLength > inspectedEnd && (cursor > 0 || consumedPreamble) ? TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE : void 0;
2090
+ const markup = text.slice(cursor);
2091
+ if (/<!doctype\s+svg\b/u.test(markup) || /<svg\b/u.test(markup)) return "image/svg+xml";
2092
+ if (/(?:<!doctype\s+html\b|<[a-z][a-z0-9:-]*(?:\s|>|\/))/u.test(markup) || /<[^>]+\bon[a-z][a-z0-9_-]*\s*=/u.test(markup) || /<[^>]+(?:href|src)\s*=\s*["']?\s*javascript:/u.test(markup) || /<script\b/u.test(markup)) return "text/html";
2093
+ if (markup.startsWith("<") && (consumedPreamble || bytes.byteLength > inspectedEnd)) return TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE;
2094
+ }
2095
+ function trailingUtf8Completion(bytes) {
2096
+ if (bytes.byteLength === 0) return 0;
2097
+ const start = trailingUtf8SequenceStart(bytes);
2098
+ const lead = bytes[start];
2099
+ const width = lead >= 194 && lead <= 223 ? 2 : lead >= 224 && lead <= 239 ? 3 : lead >= 240 && lead <= 244 ? 4 : 1;
2100
+ const present = bytes.byteLength - start;
2101
+ return width > present ? width - present : 0;
2102
+ }
2103
+ function trailingUtf8SequenceStart(bytes) {
2104
+ let start = bytes.byteLength - 1;
2105
+ while (start > 0 && bytes[start] >= 128 && bytes[start] <= 191) start--;
2106
+ return start;
2107
+ }
2108
+ function skipMarkupWhitespace(value, start) {
2109
+ let cursor = start;
2110
+ while (/\s/u.test(value[cursor] ?? "")) cursor++;
2111
+ return cursor;
2112
+ }
2113
+ function ascii(bytes, start, length) {
2114
+ return String.fromCharCode(...bytes.slice(start, start + length));
2115
+ }
2116
+ //#endregion
96
2117
  //#region src/compile/assets.ts
97
- const MIME_BY_EXT = {
98
- ".png": "image/png",
99
- ".jpg": "image/jpeg",
100
- ".jpeg": "image/jpeg",
101
- ".gif": "image/gif",
102
- ".svg": "image/svg+xml",
103
- ".webp": "image/webp",
104
- ".avif": "image/avif",
105
- ".ico": "image/x-icon",
106
- ".bmp": "image/bmp",
107
- ".mp4": "video/mp4",
108
- ".webm": "video/webm",
109
- ".pdf": "application/pdf",
110
- ".zip": "application/zip",
111
- ".tar": "application/x-tar",
112
- ".gz": "application/gzip",
113
- ".mp3": "audio/mpeg",
114
- ".wav": "audio/wav",
115
- ".ogg": "audio/ogg",
116
- ".m4a": "audio/mp4",
117
- ".flac": "audio/flac",
118
- ".csv": "text/csv",
119
- ".txt": "text/plain",
120
- ".doc": "application/msword",
121
- ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
122
- ".xls": "application/vnd.ms-excel",
123
- ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
124
- ".ppt": "application/vnd.ms-powerpoint",
125
- ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
2118
+ var AssetCompilationError = class extends Error {
2119
+ diagnostics;
2120
+ constructor(message, diagnostics = []) {
2121
+ super(message);
2122
+ this.diagnostics = diagnostics;
2123
+ this.name = "AssetCompilationError";
2124
+ }
126
2125
  };
127
- const ASSET_TAG_ATTRS = ["src", "href"];
128
- async function extractAssets(source, options) {
129
- const { baseDir, filePath } = options;
130
- const ast = Markdoc.parse(source);
131
- const refs = /* @__PURE__ */ new Map();
132
- const slots = /* @__PURE__ */ new Map();
133
- const collect = (url, slot, requireKnownExtension) => {
134
- if (!isLocalRef(url)) return;
135
- if (requireKnownExtension && !hasKnownAssetExtension(url)) return;
136
- if (!refs.has(url)) {
137
- const absPath = resolveAssetPath(url, {
138
- baseDir,
139
- filePath
140
- });
141
- const relFromBase = relative(baseDir, absPath).split(sep).join(posix.sep);
142
- refs.set(url, {
143
- original: url,
144
- absPath,
145
- relFromBase
2126
+ /** Discover and resolve one compilation-wide Asset set and deduplicated payload inventory. */
2127
+ async function compileAssetResources(input) {
2128
+ return compileAssetResourcesWithReader(input, readPortableAssetFile);
2129
+ }
2130
+ async function compileAssetResourcesWithReader(input, readAssetFile) {
2131
+ const receivedResources = input.resources;
2132
+ if (receivedResources.some((resource) => resource.type === "Asset")) throw new AssetCompilationError("Asset resources are compiler output and cannot be inputs", [topikAssetDiagnostic("TOPIK_ASSET_SCHEMA_INVALID", "Asset resources are compiler output and cannot be inputs")]);
2133
+ const sourceResources = receivedResources;
2134
+ const initialValidation = validateResources(sourceResources);
2135
+ if (!initialValidation.valid) throw new AssetCompilationError("Asset compilation received an invalid resource", resourceValidationDiagnostics(initialValidation.errors, sourceResources));
2136
+ const sourcePaths = /* @__PURE__ */ new Map();
2137
+ for (const [resource, source] of Object.entries(input.sourcePathsByResource)) {
2138
+ const path = requirePath(source, "Content source path is not portable");
2139
+ if (sourcePaths.has(resource)) throw new AssetCompilationError("Content source binding repeats");
2140
+ sourcePaths.set(resource, path);
2141
+ }
2142
+ const contentResources = sourceResources.filter(isContentBearingResource);
2143
+ const topikContentResources = contentResources.filter(isTopikContentResource);
2144
+ for (const resource of contentResources) if (!sourcePaths.has(resourceKey$1(resource))) throw new AssetCompilationError("Content-bearing resource lacks a source-path binding");
2145
+ for (const key of sourcePaths.keys()) if (!contentResources.some((resource) => resourceKey$1(resource) === key)) throw new AssetCompilationError("Source-path binding names a non-content resource");
2146
+ for (const resource of topikContentResources) {
2147
+ const sourcePath = sourcePaths.get(resourceKey$1(resource));
2148
+ const validation = validateTopikContent(resource.spec.content.value, { file: sourcePath });
2149
+ if (!validation.valid) throw new AssetCompilationError("Asset compilation received invalid Topik content", contentValidationDiagnostics(validation.errors, sourcePath));
2150
+ }
2151
+ const readCache = /* @__PURE__ */ new Map();
2152
+ const localPathByGeneratedName = /* @__PURE__ */ new Map();
2153
+ const replacements = /* @__PURE__ */ new Map();
2154
+ const mappings = [];
2155
+ const rolesByName = /* @__PURE__ */ new Map();
2156
+ const allPaths = new Set(sourcePaths.values());
2157
+ const protectedPaths = new Set(sourcePaths.values());
2158
+ for (const source of input.protectedSourcePaths ?? []) {
2159
+ const path = requirePath(source, "Protected compiler source path is not portable");
2160
+ allPaths.add(path);
2161
+ protectedPaths.add(path);
2162
+ }
2163
+ for (const resource of topikContentResources) {
2164
+ const key = resourceKey$1(resource);
2165
+ const sourcePath = sourcePaths.get(key);
2166
+ const resourceReplacements = /* @__PURE__ */ new Map();
2167
+ replacements.set(key, resourceReplacements);
2168
+ const occurrences = extractTopikAssetOccurrences(resource.spec.content.value, { includeGenericLinkCandidates: true });
2169
+ for (const occurrence of occurrences) {
2170
+ if (occurrence.kind === "asset" || occurrence.kind === "reserved-asset") throw referenceError("Source content cannot use the reserved Asset locator scheme", occurrence, sourcePath);
2171
+ if (occurrence.kind === "external-https") continue;
2172
+ if (occurrence.kind === "unsafe") {
2173
+ const parsedValidation = validateTopikAssetReference(occurrence.parsedReference);
2174
+ if (!parsedValidation.valid && parsedValidation.failureKind === "external" && /^https?:/iu.test(occurrence.parsedReference)) throw referenceError("Asset-capable slot contains an unsafe reference", occurrence, sourcePath, occurrence.parsedReference);
2175
+ if (occurrence.reference.length === 0 && parsedValidation.valid && parsedValidation.kind === "external-https") continue;
2176
+ if (occurrence.slot === "link.href") {
2177
+ if (parsedValidation.valid && parsedValidation.kind === "local") {
2178
+ let parsedPath;
2179
+ try {
2180
+ parsedPath = resolveOccurrencePath(sourcePath, occurrence.parsedReference);
2181
+ } catch (error) {
2182
+ if (error instanceof AssetCompilationError) continue;
2183
+ throw error;
2184
+ }
2185
+ if (protectedPaths.has(parsedPath)) continue;
2186
+ if (await readGenericNavigationCandidate(readAssetFile, input.rootDir, parsedPath) !== void 0) throw referenceError("Proven download reference is not canonical", occurrence, sourcePath);
2187
+ continue;
2188
+ }
2189
+ if (!/^https?:/iu.test(occurrence.reference)) continue;
2190
+ }
2191
+ throw referenceError("Asset-capable slot contains an unsafe reference", occurrence, sourcePath);
2192
+ }
2193
+ const ordinaryNavigation = occurrence.slot === "link.href";
2194
+ let normalizedPath;
2195
+ if (ordinaryNavigation) try {
2196
+ normalizedPath = resolveOccurrencePath(sourcePath, occurrence.reference);
2197
+ } catch (error) {
2198
+ if (error instanceof AssetCompilationError) continue;
2199
+ throw error;
2200
+ }
2201
+ else normalizedPath = resolveOccurrencePath(sourcePath, occurrence.reference);
2202
+ if (protectedPaths.has(normalizedPath)) {
2203
+ if (ordinaryNavigation) continue;
2204
+ throw ambiguousReference("Local reference conflicts with a compiler input path", normalizedPath, occurrence.position);
2205
+ }
2206
+ if (ordinaryNavigation) {
2207
+ const proof = await readGenericNavigationCandidate(readAssetFile, input.rootDir, normalizedPath);
2208
+ if (proof === void 0) continue;
2209
+ readCache.set(normalizedPath, proof);
2210
+ }
2211
+ if (input.sourceNamespace === void 0) throw new AssetCompilationError("Automatic Asset discovery requires a stable source namespace", [topikAssetDiagnostic("TOPIK_ASSET_SOURCE_NAMESPACE_REQUIRED", "Provide the versioned stable source namespace option", { location: {
2212
+ path: normalizedPath,
2213
+ contentPosition: occurrence.position
2214
+ } })]);
2215
+ const generated = generateAutomaticAssetName({
2216
+ stableSourceNamespace: input.sourceNamespace,
2217
+ normalizedPath
146
2218
  });
147
- }
148
- const list = slots.get(url) ?? [];
149
- list.push(slot);
150
- slots.set(url, list);
151
- };
152
- walk(ast, (node) => {
153
- if (node.type === "image") {
154
- const url = stringAttr(node, "src");
155
- if (url) collect(url, {
156
- node,
157
- attr: "src"
158
- }, false);
159
- } else if (node.type === "link") {
160
- const url = stringAttr(node, "href");
161
- if (url) collect(url, {
162
- node,
163
- attr: "href"
164
- }, true);
165
- } else if (node.type === "tag") for (const attr of ASSET_TAG_ATTRS) {
166
- const url = stringAttr(node, attr);
167
- if (url) collect(url, {
168
- node,
169
- attr
170
- }, true);
171
- }
172
- });
173
- if (refs.size === 0) return {
174
- content: source,
175
- assets: [],
176
- manifest: []
177
- };
178
- const urlToId = /* @__PURE__ */ new Map();
179
- const byName = /* @__PURE__ */ new Map();
180
- const refList = Array.from(refs.values());
181
- const settled = await Promise.allSettled(refList.map((ref) => toAsset(ref)));
182
- const failures = [];
183
- for (let i = 0; i < settled.length; i++) {
184
- const result = settled[i];
185
- if (result.status === "fulfilled") {
186
- urlToId.set(refList[i].original, result.value.name);
187
- byName.set(result.value.name, result.value);
188
- } else failures.push(result.reason instanceof Error ? result.reason.message : String(result.reason));
189
- }
190
- if (failures.length > 0) throw new Error(`Failed to extract ${failures.length} asset(s):\n - ${failures.join("\n - ")}`);
191
- const manifest = [];
192
- const seen = /* @__PURE__ */ new Set();
193
- for (const ref of refs.values()) {
194
- const name = urlToId.get(ref.original);
195
- if (name && !seen.has(name)) {
196
- manifest.push(name);
197
- seen.add(name);
2219
+ if (!generated.ok) throw new AssetCompilationError("Automatic Asset name could not be generated", generated.diagnostics);
2220
+ const name = generated.value;
2221
+ registerGeneratedAssetPath(localPathByGeneratedName, name, normalizedPath);
2222
+ if (localPathByGeneratedName.size > TOPIK_ASSET_LIMITS.maxAssets) throw new AssetCompilationError("Compilation exceeds its Asset limit");
2223
+ resourceReplacements.set(occurrence.position, `asset:${name}`);
2224
+ addMapping(mappings, rolesByName, key, occurrence, name);
2225
+ allPaths.add(normalizedPath);
198
2226
  }
199
2227
  }
200
- for (const [url, slotList] of slots.entries()) {
201
- const id = urlToId.get(url);
202
- if (!id) continue;
203
- for (const slot of slotList) slot.node.attributes[slot.attr] = `asset:${id}`;
2228
+ const completePathSet = validateTopikPathSet([...allPaths]);
2229
+ if (!completePathSet.ok) throw new AssetCompilationError("Compilation paths collide", completePathSet.diagnostics);
2230
+ const resolvedAssets = [];
2231
+ const payloadsByDigest = /* @__PURE__ */ new Map();
2232
+ for (const [name, sourcePath] of localPathByGeneratedName) {
2233
+ const roles = rolesByName.get(name) ?? [];
2234
+ const file = readCache.get(sourcePath) ?? await requireAssetFile(input.rootDir, sourcePath, readAssetFile);
2235
+ readCache.set(sourcePath, file);
2236
+ const digest = sha256(file.bytes);
2237
+ const integrity = `sha256:${digest}`;
2238
+ const mediaType = sniffPortableMediaType(file.bytes);
2239
+ if (isTopikActiveMediaType(mediaType)) throw activeError(name, sourcePath);
2240
+ assertRoleMediaCompatibility(name, sourcePath, mediaType, roles);
2241
+ const payloadPath = parseAssetBlobUri(`${TOPIK_BLOB_OUTPUT_PREFIX}/${digest}`);
2242
+ resolvedAssets.push({
2243
+ apiVersion: "v1",
2244
+ type: "Asset",
2245
+ name,
2246
+ spec: {
2247
+ uri: payloadPath,
2248
+ integrity,
2249
+ size: file.bytes.byteLength,
2250
+ mediaType
2251
+ }
2252
+ });
2253
+ const payload = payloadsByDigest.get(digest);
2254
+ if (payload !== void 0) {
2255
+ if (!equalBytes(payload.bytes, file.bytes)) throw new AssetCompilationError("SHA-256 payload collision was detected");
2256
+ payload.names.add(name);
2257
+ } else payloadsByDigest.set(digest, {
2258
+ bytes: file.bytes,
2259
+ mediaType,
2260
+ names: /* @__PURE__ */ new Set([name])
2261
+ });
204
2262
  }
2263
+ const resources = [...sourceResources.map((resource) => {
2264
+ if (!isContentBearingResource(resource)) return resource;
2265
+ const key = resourceKey$1(resource);
2266
+ const sourcePath = sourcePaths.get(key);
2267
+ const byPosition = replacements.get(key);
2268
+ if (byPosition === void 0 || byPosition.size === 0) return resource;
2269
+ const rewrittenContent = rewriteTopikAssetOccurrences(resource.spec.content.value, (occurrence) => byPosition.get(occurrence.position), {
2270
+ includeGenericLinkCandidates: true,
2271
+ file: sourcePath
2272
+ });
2273
+ if (!rewrittenContent.ok) throw new AssetCompilationError("Asset compilation could not rewrite invalid content", contentValidationDiagnostics(rewrittenContent.diagnostics, sourcePath));
2274
+ return {
2275
+ ...resource,
2276
+ spec: {
2277
+ ...resource.spec,
2278
+ content: {
2279
+ ...resource.spec.content,
2280
+ value: rewrittenContent.content
2281
+ }
2282
+ }
2283
+ };
2284
+ }), ...resolvedAssets].sort(compareResources);
2285
+ const finalValidation = validateResources(resources);
2286
+ if (!finalValidation.valid) throw new AssetCompilationError("Compiled resources are invalid", resourceValidationDiagnostics(finalValidation.errors, resources));
2287
+ const payloads = [...payloadsByDigest.entries()].map(([digest, payload]) => ({
2288
+ path: parseAssetBlobUri(`${TOPIK_BLOB_OUTPUT_PREFIX}/${digest}`),
2289
+ integrity: `sha256:${digest}`,
2290
+ mediaType: payload.mediaType,
2291
+ size: payload.bytes.byteLength,
2292
+ bytes: payload.bytes,
2293
+ assetNames: [...payload.names].sort(compareUtf8)
2294
+ })).sort((left, right) => compareUtf8(left.path, right.path));
2295
+ const semantic = createTopikAssetSemanticRecord(resolvedAssets, mappings);
2296
+ const materialization = createTopikMaterializationRecord(resources.map((resource) => ({
2297
+ resource,
2298
+ bytes: new TextEncoder().encode(serializeTopikJson(resource))
2299
+ })), payloads.map((payload) => ({
2300
+ path: payload.path,
2301
+ bytes: payload.bytes,
2302
+ assetNames: payload.assetNames
2303
+ })));
2304
+ const inventory = validateTopikMaterializationRecord(materialization, resources, semantic);
2305
+ if (!inventory.ok) throw new AssetCompilationError("Compiled inventory is incomplete", inventory.diagnostics);
205
2306
  return {
206
- content: Markdoc.format(ast),
207
- assets: Array.from(byName.values()),
208
- manifest
209
- };
210
- }
211
- function walk(node, fn) {
212
- fn(node);
213
- for (const child of node.children) walk(child, fn);
214
- }
215
- function stringAttr(node, attr) {
216
- const value = node.attributes?.[attr];
217
- return typeof value === "string" ? value : void 0;
218
- }
219
- function hasKnownAssetExtension(url) {
220
- const [path] = splitUrl(url);
221
- const decoded = decodeSafe(path);
222
- const m = /\.([a-z0-9]+)$/i.exec(decoded);
223
- if (!m) return false;
224
- return `.${m[1].toLowerCase()}` in MIME_BY_EXT;
225
- }
226
- function isLocalRef(url) {
227
- if (url.length === 0) return false;
228
- if (url.startsWith("#")) return false;
229
- if (url.startsWith("mailto:")) return false;
230
- if (url.startsWith("tel:")) return false;
231
- if (url.startsWith("data:")) return false;
232
- if (url.startsWith("//")) return false;
233
- if (/^[a-z][a-z0-9+.-]*:/i.test(url)) return false;
234
- return true;
235
- }
236
- function resolveAssetPath(url, { baseDir, filePath }) {
237
- const [pathPart] = splitUrl(url);
238
- const decoded = decodeSafe(pathPart);
239
- const absolute = resolve(isAbsolute(decoded) || decoded.startsWith("/") ? baseDir : dirname(filePath), decoded.replace(/^\/+/, ""));
240
- const rel = relative(baseDir, absolute);
241
- if (rel.startsWith("..") || isAbsolute(rel)) throw new Error(`Asset reference "${url}" in ${filePath} resolves outside the compilation directory`);
242
- return absolute;
243
- }
244
- function splitUrl(url) {
245
- const hashIdx = url.indexOf("#");
246
- const queryIdx = url.indexOf("?");
247
- let cut = -1;
248
- if (hashIdx >= 0) cut = hashIdx;
249
- if (queryIdx >= 0 && (cut < 0 || queryIdx < cut)) cut = queryIdx;
250
- if (cut < 0) return [url, ""];
251
- return [url.slice(0, cut), url.slice(cut)];
252
- }
253
- function decodeSafe(s) {
254
- try {
255
- return decodeURIComponent(s);
256
- } catch {
257
- return s;
2307
+ resources,
2308
+ payloads,
2309
+ semantic,
2310
+ materialization
2311
+ };
2312
+ }
2313
+ /** @internal Shared with the source test suite to prove generated-name collision handling. */
2314
+ function registerGeneratedAssetPath(paths, name, normalizedPath) {
2315
+ const previousPath = paths.get(name);
2316
+ if (previousPath !== void 0 && previousPath !== normalizedPath) throw collision("Generated Asset name collides with another normalized path", name);
2317
+ paths.set(name, normalizedPath);
2318
+ }
2319
+ function assertRoleMediaCompatibility(name, path, mediaType, roles) {
2320
+ if (roles.find((role) => role !== "download" && !isInlineMediaCompatible(mediaType, role)) === void 0) return;
2321
+ throw new AssetCompilationError("Asset media type is incompatible with its reference role", [topikAssetDiagnostic("TOPIK_ASSET_MEDIA_TYPE_MISMATCH", "Asset media type is incompatible with its reference role", { location: {
2322
+ key: name,
2323
+ path
2324
+ } })]);
2325
+ }
2326
+ function resourceValidationDiagnostics(errors, resources) {
2327
+ return errors.map((error) => {
2328
+ const descriptorVersion = safeResourceDescriptorVersion(resources.find((candidate) => typeof candidate === "object" && candidate !== null && Object.hasOwn(candidate, "type") && Object.hasOwn(candidate, "name") && `${String(candidate.type)}/${String(candidate.name)}` === error.resource));
2329
+ return topikAssetDiagnostic(isTopikAssetDiagnosticId(error.id) ? error.id : error.id === "resource-unsupported-version" ? "TOPIK_ASSET_UNSUPPORTED_VERSION" : "TOPIK_ASSET_SCHEMA_INVALID", error.id === "resource-unsupported-version" ? "Resource apiVersion is unsupported" : "Resource schema validation failed", {
2330
+ descriptorVersion,
2331
+ location: { jsonPointer: error.path }
2332
+ });
2333
+ });
2334
+ }
2335
+ function isTopikAssetDiagnosticId(value) {
2336
+ return TOPIK_ASSET_DIAGNOSTIC_IDS.some((id) => id === value);
2337
+ }
2338
+ function contentValidationDiagnostics(diagnostics, sourcePath) {
2339
+ return diagnostics.map((diagnostic) => {
2340
+ const lines = diagnostic.lines.filter((line) => line > 0);
2341
+ const unsupportedNavigation = diagnostic.id === "link-asset-navigation-unsupported";
2342
+ return topikAssetDiagnostic(unsupportedNavigation ? "TOPIK_ASSET_REFERENCE_MALFORMED" : isTopikAssetDiagnosticId(diagnostic.id) ? diagnostic.id : "TOPIK_ASSET_SCHEMA_INVALID", unsupportedNavigation ? "Navigation-only targets cannot use Asset locators" : diagnostic.message, { location: {
2343
+ path: diagnostic.file ?? sourcePath,
2344
+ ...lines.length === 0 ? {} : { contentPosition: `line ${lines.join(",")}` }
2345
+ } });
2346
+ });
2347
+ }
2348
+ const KNOWN_RESOURCE_TYPES = /* @__PURE__ */ new Set([
2349
+ "Asset",
2350
+ "Course",
2351
+ "CourseModule",
2352
+ "CoursePage",
2353
+ "Guide",
2354
+ "Person",
2355
+ "Wiki",
2356
+ "WikiPage"
2357
+ ]);
2358
+ function safeResourceDescriptorVersion(resource) {
2359
+ if (typeof resource?.type !== "string" || !KNOWN_RESOURCE_TYPES.has(resource.type)) return "unknown-resource";
2360
+ return `${resource.type}/${resource.apiVersion === "v1" ? "v1" : "unsupported"}`;
2361
+ }
2362
+ async function requireAssetFile(root, path, readAssetFile) {
2363
+ const read = await readAssetFile({
2364
+ root,
2365
+ path
2366
+ });
2367
+ if (!read.ok || read.value.bytes === void 0) throw new AssetCompilationError("Local Asset bytes could not be proven", read.diagnostics);
2368
+ return read.value;
2369
+ }
2370
+ async function readGenericNavigationCandidate(readAssetFile, root, path) {
2371
+ if (await classifyPortableNavigationPath({
2372
+ root,
2373
+ path
2374
+ }) === "directory") return void 0;
2375
+ const proof = await readAssetFile({
2376
+ root,
2377
+ path
2378
+ });
2379
+ if (!proof.ok) {
2380
+ if (proof.diagnostics.every((diagnostic) => diagnostic.id === "TOPIK_ASSET_FILE_MISSING")) return;
2381
+ throw new AssetCompilationError("Existing generic-link target failed portable Asset proof", proof.diagnostics);
258
2382
  }
2383
+ return proof.value.bytes === void 0 ? void 0 : proof.value;
259
2384
  }
260
- async function toAsset(ref) {
261
- let bytes;
262
- try {
263
- bytes = await readFile(ref.absPath);
264
- } catch (error) {
265
- const message = error instanceof Error ? error.message : String(error);
266
- throw new Error(`Asset "${ref.original}" not found at ${ref.absPath}: ${message}`, { cause: error });
267
- }
268
- const digest = createHash("sha256").update(bytes).digest();
269
- const integrity = `sha256-${digest.toString("base64")}`;
270
- const name = digest.toString("hex").slice(0, 16);
271
- const extMatch = /\.([a-z0-9]+)(?:[?#].*)?$/i.exec(ref.relFromBase);
272
- const mediaType = MIME_BY_EXT[extMatch ? `.${extMatch[1].toLowerCase()}` : ""];
273
- return {
274
- apiVersion: "v1",
275
- type: "Asset",
276
- name,
277
- spec: {
278
- uri: ref.relFromBase,
279
- integrity,
280
- ...mediaType ? { mediaType } : {}
281
- }
282
- };
2385
+ function resolveOccurrencePath(sourcePath, reference) {
2386
+ const validation = validateTopikAssetReference(reference);
2387
+ if (!validation.valid || validation.kind !== "local") throw new AssetCompilationError("Local occurrence path is invalid", [topikAssetDiagnostic("TOPIK_ASSET_PATH_INVALID", "Local occurrence path is invalid")]);
2388
+ return requirePath(posix.join(posix.dirname(sourcePath), validation.decodedPath), "Resolved Asset path is outside the compilation root");
2389
+ }
2390
+ function addMapping(mappings, rolesByName, resource, occurrence, name) {
2391
+ mappings.push({
2392
+ resource,
2393
+ position: occurrence.position,
2394
+ slot: occurrence.slot,
2395
+ name
2396
+ });
2397
+ const roles = rolesByName.get(name) ?? [];
2398
+ roles.push(occurrence.role);
2399
+ rolesByName.set(name, roles);
2400
+ if (!hasAccessibleMeaning(occurrence)) throw new AssetCompilationError("Asset occurrence lacks accessible meaning", [topikAssetDiagnostic("TOPIK_ASSET_REFERENCE_ACCESSIBILITY_INVALID", "Asset occurrence lacks schema-supported accessible meaning", { location: {
2401
+ key: name,
2402
+ contentPosition: occurrence.position
2403
+ } })]);
2404
+ }
2405
+ function hasAccessibleMeaning(occurrence) {
2406
+ if (occurrence.role === "download") return (occurrence.semantics.linkLabel?.trim().length ?? 0) > 0;
2407
+ if (occurrence.slot.startsWith("figure.")) return (occurrence.semantics.alt?.trim().length ?? 0) > 0;
2408
+ return occurrence.semantics.decorative === true || (occurrence.semantics.alt?.trim().length ?? 0) > 0;
2409
+ }
2410
+ function isContentBearingResource(resource) {
2411
+ return resource.type === "Guide" || resource.type === "WikiPage" || resource.type === "CoursePage";
2412
+ }
2413
+ function isTopikContentResource(resource) {
2414
+ return resource.spec.content.format === "topik";
2415
+ }
2416
+ function requirePath(path, message) {
2417
+ const validation = validateTopikPath(path);
2418
+ if (!validation.ok) throw new AssetCompilationError(message, validation.diagnostics);
2419
+ return validation.value.path;
2420
+ }
2421
+ function resourceKey$1(resource) {
2422
+ return `${resource.type}/${resource.name}`;
2423
+ }
2424
+ function collision(message, name) {
2425
+ return new AssetCompilationError(message, [topikAssetDiagnostic("TOPIK_ASSET_NAME_COLLISION", message, { location: { key: name } })]);
2426
+ }
2427
+ function referenceError(message, occurrence, path, effectiveReference = occurrence.reference) {
2428
+ const validation = validateTopikAssetReference(effectiveReference);
2429
+ return new AssetCompilationError(message, [topikAssetDiagnostic(occurrence.kind === "asset" || occurrence.kind === "reserved-asset" ? "TOPIK_ASSET_REFERENCE_MALFORMED" : !validation.valid && validation.failureKind === "external" ? "TOPIK_EXTERNAL_REFERENCE_UNSAFE" : "TOPIK_ASSET_REFERENCE_MALFORMED", message, { location: {
2430
+ path,
2431
+ contentPosition: occurrence.position
2432
+ } })]);
2433
+ }
2434
+ function ambiguousReference(message, path, contentPosition) {
2435
+ return new AssetCompilationError(message, [topikAssetDiagnostic("TOPIK_ASSET_REFERENCE_AMBIGUOUS", message, { location: {
2436
+ path,
2437
+ ...contentPosition === void 0 ? {} : { contentPosition }
2438
+ } })]);
2439
+ }
2440
+ function activeError(name, path) {
2441
+ return new AssetCompilationError("Asset media is active or incompatible with its slot", [topikAssetDiagnostic("TOPIK_ASSET_ACTIVE_CONTENT_UNSUPPORTED", "Asset media is active or incompatible with its slot", { location: {
2442
+ key: name,
2443
+ path
2444
+ } })]);
2445
+ }
2446
+ function sha256(bytes) {
2447
+ return createHash("sha256").update(bytes).digest("hex");
2448
+ }
2449
+ function equalBytes(left, right) {
2450
+ return left.byteLength === right.byteLength && left.every((byte, index) => byte === right[index]);
2451
+ }
2452
+ function compareResources(left, right) {
2453
+ return compareUtf8(resourceKey$1(left), resourceKey$1(right));
2454
+ }
2455
+ function compareUtf8(left, right) {
2456
+ return Buffer.compare(Buffer.from(left), Buffer.from(right));
283
2457
  }
284
2458
  //#endregion
285
2459
  //#region src/compile/shared.ts
2460
+ var CompileError = class extends Error {
2461
+ diagnostics;
2462
+ constructor(diagnostics) {
2463
+ const sanitized = diagnostics.map(sanitizeTopikContentDiagnostic);
2464
+ super(formatContentDiagnostics(sanitized));
2465
+ this.name = "CompileError";
2466
+ this.diagnostics = sanitized;
2467
+ }
2468
+ };
2469
+ function linkValidationPolicy(options) {
2470
+ return options?.links ?? "error";
2471
+ }
2472
+ function isErrorDiagnostic(diagnostic) {
2473
+ return diagnostic.level === "error" || diagnostic.level === "critical";
2474
+ }
2475
+ function hasCompileErrors(diagnostics) {
2476
+ return diagnostics.some(isErrorDiagnostic);
2477
+ }
2478
+ function throwOnCompileErrors(diagnostics) {
2479
+ const errors = diagnostics.filter(isErrorDiagnostic);
2480
+ if (errors.length > 0) throw new CompileError(errors);
2481
+ }
286
2482
  const DNS_LABEL_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
287
2483
  function parseMarkdownFrontmatter(raw, filePath) {
288
2484
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
@@ -302,9 +2498,8 @@ function parseMarkdownFrontmatter(raw, filePath) {
302
2498
  frontmatter,
303
2499
  content: match[2]
304
2500
  };
305
- } catch (error) {
306
- const message = error instanceof Error ? error.message : String(error);
307
- throw new Error(`Invalid frontmatter in ${filePath}: ${message}`, { cause: error });
2501
+ } catch {
2502
+ throw new PublicCompileError("frontmatter-invalid", filePath);
308
2503
  }
309
2504
  }
310
2505
  function extractMarkdownTitle(content, fallback) {
@@ -312,155 +2507,524 @@ function extractMarkdownTitle(content, fallback) {
312
2507
  if (match) return match[1].trim();
313
2508
  return fallback.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
314
2509
  }
315
- function parseReferenceList(value, fieldName, filePath) {
2510
+ function formatContentDiagnostics(diagnostics) {
2511
+ return diagnostics.filter(isErrorDiagnostic).map((diagnostic) => {
2512
+ const sanitized = sanitizeTopikContentDiagnostic(diagnostic);
2513
+ return `${sanitized.file ?? "content"}${sanitized.lines.length > 0 ? `:${sanitized.lines.join(",")}` : ""} ${sanitized.level} ${sanitized.id}: ${sanitized.message}`;
2514
+ }).join("\n");
2515
+ }
2516
+ function parseReferenceList(value, _fieldName, filePath) {
316
2517
  if (value == null) return;
317
- if (!Array.isArray(value)) throw new Error(`${fieldName} in ${filePath} must be an array of resource names`);
318
- const references = value.map((entry, index) => {
319
- if (typeof entry !== "string") throw new Error(`${fieldName}[${index}] in ${filePath} must be a string`);
320
- if (entry.length > 63 || !DNS_LABEL_PATTERN.test(entry)) throw new Error(`${fieldName}[${index}] in ${filePath} must be a DNS-1123 resource name`);
2518
+ if (!Array.isArray(value)) throw new PublicCompileError("reference-list-invalid", filePath);
2519
+ const references = value.map((entry) => {
2520
+ if (typeof entry !== "string") throw new PublicCompileError("reference-list-invalid", filePath);
2521
+ if (entry.length > 63 || !DNS_LABEL_PATTERN.test(entry)) throw new PublicCompileError("reference-list-invalid", filePath);
321
2522
  return entry;
322
2523
  });
323
2524
  return references.length > 0 ? references : void 0;
324
2525
  }
325
2526
  //#endregion
2527
+ //#region src/compile/links.ts
2528
+ const NON_PAGE_SCHEME = /^(?:asset|https?|mailto|tel):/i;
2529
+ const LINK_BASE = "https://topik.local";
2530
+ function validateWikiLinks(pages, policy, nonPageLinks = /* @__PURE__ */ new Set()) {
2531
+ if (policy === "off") return [];
2532
+ const level = policy === "error" ? "error" : "warning";
2533
+ const pagesBySlug = new Map(pages.map((page) => [page.slug, page]));
2534
+ const diagnostics = [];
2535
+ for (const page of pages) for (const link of page.analysis.links) {
2536
+ if (NON_PAGE_SCHEME.test(link.href)) continue;
2537
+ const target = resolveInternalTarget(link.href, page.sourcePath);
2538
+ if (!target) continue;
2539
+ const targetPage = pagesBySlug.get(target.slug);
2540
+ if (!targetPage) {
2541
+ if (nonPageLinks.has(link)) continue;
2542
+ diagnostics.push(linkDiagnostic("link-page-not-found", level, link));
2543
+ continue;
2544
+ }
2545
+ if (target.fragment && !targetPage.analysis.headings.some((heading) => heading.id === target.fragment)) diagnostics.push(linkDiagnostic("link-fragment-not-found", level, link));
2546
+ }
2547
+ return diagnostics;
2548
+ }
2549
+ function validateLocalFragments(analysis, policy) {
2550
+ if (policy === "off") return [];
2551
+ const level = policy === "error" ? "error" : "warning";
2552
+ const headingIds = new Set(analysis.headings.map((heading) => heading.id));
2553
+ const diagnostics = [];
2554
+ for (const link of analysis.links) {
2555
+ if (!link.href.startsWith("#") || link.href === "#") continue;
2556
+ const fragment = decodeFragment(link.href.slice(1));
2557
+ if (fragment && !headingIds.has(fragment)) diagnostics.push(linkDiagnostic("link-fragment-not-found", level, link));
2558
+ }
2559
+ return diagnostics;
2560
+ }
2561
+ function resolveInternalTarget(href, sourcePath) {
2562
+ let url;
2563
+ try {
2564
+ url = new URL(href, `${LINK_BASE}/${sourcePath}`);
2565
+ } catch {
2566
+ return;
2567
+ }
2568
+ if (url.origin !== LINK_BASE) return void 0;
2569
+ return {
2570
+ fragment: decodeFragment(url.hash.slice(1)),
2571
+ slug: decodePath(url.pathname)
2572
+ };
2573
+ }
2574
+ function decodeFragment(value) {
2575
+ if (!value) return "";
2576
+ try {
2577
+ return decodeURIComponent(value);
2578
+ } catch {
2579
+ return value;
2580
+ }
2581
+ }
2582
+ function decodePath(pathname) {
2583
+ const normalized = pathname.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean).map((segment) => {
2584
+ try {
2585
+ return decodeURIComponent(segment);
2586
+ } catch {
2587
+ return segment;
2588
+ }
2589
+ }).join("/").replace(/\.(?:mdx?|markdown)$/i, "");
2590
+ if (normalized === "index") return "";
2591
+ return normalized.replace(/\/index$/, "");
2592
+ }
2593
+ function linkDiagnostic(id, level, link) {
2594
+ const message = topikLinkDiagnosticMessage(id);
2595
+ if (message === void 0) throw new TypeError("Unknown link diagnostic identifier");
2596
+ return {
2597
+ id,
2598
+ type: link.kind,
2599
+ level,
2600
+ message,
2601
+ lines: link.lines,
2602
+ ...link.file ? { file: link.file } : {}
2603
+ };
2604
+ }
2605
+ //#endregion
2606
+ //#region src/wiki-navigation.ts
2607
+ const INTERNAL_WIKI_ORIGIN = "https://topik.invalid";
2608
+ /** Returns true for tab, dropdown, and group nodes containing navigation children. */
2609
+ function hasWikiNavChildren(node) {
2610
+ return "children" in node;
2611
+ }
2612
+ function isInternalWikiTab(node) {
2613
+ return node.type === "tab" && "children" in node;
2614
+ }
2615
+ function isExternalWikiTab(node) {
2616
+ return node.type === "tab" && "href" in node;
2617
+ }
2618
+ function isInternalWikiDropdown(node) {
2619
+ return node.type === "dropdown" && "children" in node;
2620
+ }
2621
+ function isExternalWikiDropdown(node) {
2622
+ return node.type === "dropdown" && "href" in node;
2623
+ }
2624
+ /** Resolves page routes, source paths, and navigation ancestry from a compiled Wiki tree. */
2625
+ function resolveWikiNavigation(navigation) {
2626
+ const pages = [];
2627
+ const pageByName = /* @__PURE__ */ new Map();
2628
+ const pageByRoute = /* @__PURE__ */ new Map();
2629
+ const visit = (nodes, prefix, ancestors) => {
2630
+ for (const node of nodes) if (node.type === "page") {
2631
+ const route = joinWikiPath(prefix, node.slug);
2632
+ const resolved = {
2633
+ page: node.page,
2634
+ node,
2635
+ route,
2636
+ sourcePath: node.sourcePath ?? joinWikiPath(prefix, node.slug || "index"),
2637
+ ancestors
2638
+ };
2639
+ if (pageByName.has(node.page)) throw new Error(`Wiki navigation contains page ${node.page} more than once`);
2640
+ if (pageByRoute.has(route)) throw new Error(`Wiki navigation contains duplicate route /${route}`);
2641
+ pages.push(resolved);
2642
+ pageByName.set(node.page, resolved);
2643
+ pageByRoute.set(route, resolved);
2644
+ } else if (hasWikiNavChildren(node)) visit(node.children, joinWikiPath(prefix, node.slug), [...ancestors, node]);
2645
+ };
2646
+ visit(navigation, "", []);
2647
+ return {
2648
+ pages,
2649
+ pageByName,
2650
+ pageByRoute
2651
+ };
2652
+ }
2653
+ /** Finds the first page node in navigation order, ignoring external destinations. */
2654
+ function findFirstWikiPage(navigation) {
2655
+ for (const node of navigation) {
2656
+ if (node.type === "page") return node;
2657
+ if (hasWikiNavChildren(node)) {
2658
+ const page = findFirstWikiPage(node.children);
2659
+ if (page) return page;
2660
+ }
2661
+ }
2662
+ return null;
2663
+ }
2664
+ /** Returns the internal containers owning a page, or an empty array for an unknown page. */
2665
+ function findWikiPageAncestors(resolved, pageName) {
2666
+ return resolved.pageByName.get(pageName)?.ancestors ?? [];
2667
+ }
2668
+ /** Resolves an internal Markdown link against the logical source path of its current page. */
2669
+ function resolveWikiContentHref(href, currentPageName, resolved) {
2670
+ if (href.startsWith("asset:")) return null;
2671
+ const currentPage = resolved.pageByName.get(currentPageName);
2672
+ if (!currentPage) return null;
2673
+ if (href.startsWith("#")) return {
2674
+ page: currentPage,
2675
+ route: currentPage.route,
2676
+ hash: href.slice(1)
2677
+ };
2678
+ let url;
2679
+ try {
2680
+ url = new URL(href, `${INTERNAL_WIKI_ORIGIN}/${currentPage.sourcePath}`);
2681
+ } catch {
2682
+ return null;
2683
+ }
2684
+ if (url.origin !== INTERNAL_WIKI_ORIGIN) return null;
2685
+ let decodedPath;
2686
+ try {
2687
+ decodedPath = decodeURIComponent(url.pathname);
2688
+ } catch {
2689
+ return null;
2690
+ }
2691
+ const normalizedPath = decodedPath.replace(/^\/+|\/+$/g, "").replace(/\.(?:mdx?|markdown)$/i, "");
2692
+ const route = normalizedPath === "index" ? "" : normalizedPath.replace(/\/index$/, "");
2693
+ const page = resolved.pageByRoute.get(route);
2694
+ return page ? {
2695
+ page,
2696
+ route,
2697
+ hash: url.hash.replace(/^#/, "")
2698
+ } : null;
2699
+ }
2700
+ function joinWikiPath(prefix, slug) {
2701
+ if (!slug) return prefix;
2702
+ return prefix ? `${prefix}/${slug}` : slug;
2703
+ }
2704
+ //#endregion
326
2705
  //#region src/compile/wiki.ts
327
2706
  async function compileWiki(options) {
2707
+ const result = await inspectWiki(options);
2708
+ throwOnCompileErrors(result.diagnostics);
2709
+ return result;
2710
+ }
2711
+ async function inspectWiki(options) {
2712
+ const discovered = await discoverWiki(options);
2713
+ const compiled = await compileAssetResources({
2714
+ rootDir: resolve(options.dir),
2715
+ resources: discovered.resources,
2716
+ sourcePathsByResource: discovered.sourcePathsByResource,
2717
+ protectedSourcePaths: discovered.consumedSourcePaths,
2718
+ ...options.assets
2719
+ });
2720
+ return {
2721
+ diagnostics: discovered.diagnostics,
2722
+ ...compiled
2723
+ };
2724
+ }
2725
+ /** @internal Discovery phase used by the mixed top-level compiler. */
2726
+ async function discoverWiki(options) {
328
2727
  const dir = resolve(options.dir);
329
- const raw = await readOptionalConfigFile(dir, [
2728
+ const loadedConfig = await readOptionalConfigFileWithPath(dir, [
330
2729
  "wiki.yaml",
331
2730
  "wiki.yml",
332
2731
  "wiki.json"
333
2732
  ]);
334
- if (raw == null) return { resources: [] };
335
- const config = parseWikiConfig(raw);
2733
+ if (loadedConfig == null) return {
2734
+ diagnostics: [],
2735
+ resources: [],
2736
+ sourcePathsByResource: {},
2737
+ consumedSourcePaths: []
2738
+ };
2739
+ let config;
2740
+ try {
2741
+ config = parseWikiConfig(loadedConfig.value);
2742
+ } catch {
2743
+ throw new PublicCompileError("config-invalid", loadedConfig.path);
2744
+ }
336
2745
  const pagePaths = config.navigation ? [...new Set(collectPagePaths(config.navigation))] : [];
337
- const resolvedFiles = await Promise.all(pagePaths.map(async (pagePath) => {
338
- const filePath = await resolveFilePath(dir, pagePath);
339
- return {
340
- filePath,
341
- raw: await readFile(filePath, "utf-8")
342
- };
343
- }));
2746
+ const resolvedFiles = await Promise.all(pagePaths.map((pagePath) => readPageFile(dir, pagePath)));
344
2747
  const resources = [];
345
- const assetsById = /* @__PURE__ */ new Map();
2748
+ const diagnostics = [];
2749
+ const pageAnalyses = [];
2750
+ const sourcePathsByResource = {};
346
2751
  for (let i = 0; i < pagePaths.length; i++) {
347
2752
  const pagePath = pagePaths[i];
348
2753
  const { filePath, raw } = resolvedFiles[i];
2754
+ const sourcePath = `${pagePath}${filePath.endsWith(".mdx") ? ".mdx" : ".md"}`;
349
2755
  const { frontmatter, content } = parseMarkdownFrontmatter(raw, pagePath);
350
- const { content: rewritten, assets, manifest } = await extractAssets(content, {
351
- baseDir: dir,
352
- filePath
2756
+ const validation = validateTopikContent(content, { file: sourcePath });
2757
+ diagnostics.push(...validation.errors);
2758
+ if (!validation.valid) continue;
2759
+ const name = pagePathToName(config.id, pagePath);
2760
+ const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(content, pagePathToTitleFallback(pagePath));
2761
+ const description = normalizeWikiPageDescription(frontmatter.description);
2762
+ const analysis = analyzeTopikContent(content, { file: sourcePath });
2763
+ diagnostics.push(...analysis.diagnostics);
2764
+ pageAnalyses.push({
2765
+ analysis,
2766
+ slug: pagePathToSlug(pagePath),
2767
+ sourcePath: pagePath
353
2768
  });
354
- for (const asset of assets) if (!assetsById.has(asset.name)) assetsById.set(asset.name, asset);
355
- const name = pagePathToName(pagePath);
356
- const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(rewritten, name);
357
2769
  const pageResource = {
358
2770
  apiVersion: "v1",
359
2771
  type: "WikiPage",
360
- name: `${config.id}-${name}`,
2772
+ name,
361
2773
  spec: {
362
2774
  wiki: config.id,
363
2775
  title,
2776
+ ...description != null ? { description } : {},
364
2777
  content: {
365
2778
  format: "topik",
366
- value: rewritten
367
- },
368
- ...manifest.length > 0 ? { assets: manifest } : {}
2779
+ value: content
2780
+ }
369
2781
  }
370
2782
  };
371
2783
  resources.push(pageResource);
2784
+ sourcePathsByResource[`WikiPage/${pageResource.name}`] = sourcePath;
372
2785
  }
373
- for (const asset of assetsById.values()) resources.push(asset);
374
2786
  const wikiResource = {
375
2787
  apiVersion: "v1",
376
2788
  type: "Wiki",
377
2789
  name: config.id,
378
2790
  spec: {
379
2791
  title: config.title,
2792
+ ...config.description != null ? { description: config.description } : {},
380
2793
  ...config.navigation ? { navigation: resolveNavigation(config.navigation, config.id) } : {},
381
2794
  ...config.theme ? { theme: config.theme } : {}
382
2795
  }
383
2796
  };
384
2797
  resources.push(wikiResource);
385
- return { resources };
2798
+ if (!hasCompileErrors(diagnostics)) {
2799
+ const nonPageLinks = await classifyWikiNonPageLinks(dir, pageAnalyses);
2800
+ diagnostics.push(...validateWikiLinks(pageAnalyses, linkValidationPolicy(options.validation), nonPageLinks));
2801
+ }
2802
+ return {
2803
+ diagnostics,
2804
+ resources,
2805
+ sourcePathsByResource,
2806
+ consumedSourcePaths: [loadedConfig.path]
2807
+ };
2808
+ }
2809
+ async function classifyWikiNonPageLinks(root, pages) {
2810
+ const nonPageLinks = /* @__PURE__ */ new Set();
2811
+ const existingByPath = /* @__PURE__ */ new Map();
2812
+ for (const page of pages) for (const link of page.analysis.links) {
2813
+ if (link.kind !== "link") continue;
2814
+ const reference = validateTopikAssetReference(link.href);
2815
+ if (!reference.valid || reference.kind !== "local") continue;
2816
+ const path = validateTopikPath(posix.join(posix.dirname(page.sourcePath), reference.decodedPath));
2817
+ if (!path.ok) continue;
2818
+ let existing = existingByPath.get(path.value.path);
2819
+ if (existing === void 0) {
2820
+ if (await classifyPortableNavigationPath({
2821
+ root,
2822
+ path: path.value.path
2823
+ }) === "directory") existing = true;
2824
+ else {
2825
+ const proof = await readPortableAssetFile({
2826
+ root,
2827
+ path: path.value.path
2828
+ });
2829
+ existing = proof.ok || proof.diagnostics.some((diagnostic) => diagnostic.id !== "TOPIK_ASSET_FILE_MISSING");
2830
+ }
2831
+ existingByPath.set(path.value.path, existing);
2832
+ }
2833
+ if (existing) nonPageLinks.add(link);
2834
+ }
2835
+ return nonPageLinks;
2836
+ }
2837
+ function normalizeWikiPageDescription(description) {
2838
+ return typeof description === "string" ? description.slice(0, 1024) : void 0;
386
2839
  }
387
- function collectPagePaths(nodes) {
2840
+ function collectPagePaths(nodes, prefix = "") {
388
2841
  const paths = [];
389
- for (const node of nodes) if (typeof node === "string") paths.push(node);
390
- else if ("group" in node || "tab" in node) paths.push(...collectPagePaths(node.children));
2842
+ for (const node of nodes) if (typeof node === "string" || node.type === "page") paths.push(joinWikiPath(prefix, typeof node === "string" ? node : node.slug));
2843
+ else if ("children" in node) paths.push(...collectPagePaths(node.children, joinWikiPath(prefix, node.slug)));
391
2844
  return paths;
392
2845
  }
393
- async function resolveFilePath(dir, pagePath) {
2846
+ async function readPageFile(dir, pagePath) {
394
2847
  for (const ext of [".mdx", ".md"]) {
395
2848
  const filePath = join(dir, pagePath + ext);
396
2849
  try {
397
- await access(filePath);
398
- return filePath;
399
- } catch {}
2850
+ return {
2851
+ filePath,
2852
+ raw: await readRegularFileWithinRoot(filePath, dir, "utf-8")
2853
+ };
2854
+ } catch (error) {
2855
+ if (error.code === "ENOENT") continue;
2856
+ throw error;
2857
+ }
400
2858
  }
401
- throw new Error(`Page not found: ${pagePath} (tried .md and .mdx in ${dir})`);
2859
+ throw new PublicCompileError("wiki-page-not-found");
402
2860
  }
403
2861
  function pagePathToSlug(pagePath) {
404
2862
  if (pagePath === "index") return "";
405
2863
  return pagePath.replace(/\/index$/, "");
406
2864
  }
407
- function pagePathToName(pagePath) {
408
- return pagePath.replace(/^\//, "").replaceAll("/", "-");
2865
+ function pagePathToName(wikiId, pagePath) {
2866
+ const normalizedPath = normalizePagePath(pagePath);
2867
+ return `${wikiId}-${createHash("sha256").update(normalizedPath).digest("hex").slice(0, 16)}`;
409
2868
  }
410
- function resolveNavigation(nodes, wikiId) {
2869
+ function resolveNavigation(nodes, wikiId, prefix = "") {
411
2870
  return nodes.map((node) => {
412
- if (typeof node === "string") return {
413
- type: "page",
414
- page: `${wikiId}-${pagePathToName(node)}`,
415
- slug: pagePathToSlug(node)
416
- };
417
- if ("group" in node || "tab" in node) {
418
- const isTab = "tab" in node;
419
- const title = isTab ? node.tab : node.group;
2871
+ if (typeof node === "string" || node.type === "page") {
2872
+ const localPath = typeof node === "string" ? node : node.slug;
2873
+ const pagePath = joinWikiPath(prefix, localPath);
420
2874
  return {
421
- type: isTab ? "tab" : "group",
422
- title,
2875
+ type: "page",
2876
+ page: pagePathToName(wikiId, pagePath),
2877
+ slug: pagePathToSlug(localPath),
2878
+ sourcePath: pagePath,
2879
+ ...typeof node !== "string" && node.icon ? { icon: node.icon } : {},
2880
+ ...typeof node !== "string" && node.hidden ? { hidden: true } : {}
2881
+ };
2882
+ }
2883
+ if ("children" in node) {
2884
+ const children = resolveNavigation(node.children, wikiId, joinWikiPath(prefix, node.slug));
2885
+ if (node.type === "group") return {
2886
+ type: "group",
2887
+ title: node.title,
2888
+ ...node.slug ? { slug: node.slug } : {},
2889
+ ...node.icon ? { icon: node.icon } : {},
2890
+ ...node.hidden ? { hidden: true } : {},
2891
+ ...node.expanded ? { expanded: true } : {},
2892
+ children
2893
+ };
2894
+ if (node.type === "tab") return {
2895
+ type: "tab",
2896
+ title: node.title,
2897
+ ...node.slug ? { slug: node.slug } : {},
2898
+ ...node.icon ? { icon: node.icon } : {},
2899
+ ...node.hidden ? { hidden: true } : {},
2900
+ children
2901
+ };
2902
+ return {
2903
+ type: "dropdown",
2904
+ title: node.title,
423
2905
  ...node.slug ? { slug: node.slug } : {},
424
2906
  ...node.icon ? { icon: node.icon } : {},
425
- children: resolveNavigation(node.children, wikiId)
2907
+ ...node.hidden ? { hidden: true } : {},
2908
+ children
426
2909
  };
427
2910
  }
2911
+ if (node.type === "tab") return {
2912
+ type: "tab",
2913
+ title: node.title,
2914
+ href: node.href,
2915
+ ...node.icon ? { icon: node.icon } : {},
2916
+ ...node.hidden ? { hidden: true } : {}
2917
+ };
2918
+ if (node.type === "dropdown") return {
2919
+ type: "dropdown",
2920
+ title: node.title,
2921
+ href: node.href,
2922
+ ...node.icon ? { icon: node.icon } : {},
2923
+ ...node.hidden ? { hidden: true } : {}
2924
+ };
428
2925
  return {
429
2926
  type: "link",
430
2927
  title: node.title,
431
2928
  href: node.href,
432
- ...node.icon ? { icon: node.icon } : {}
2929
+ ...node.icon ? { icon: node.icon } : {},
2930
+ ...node.hidden ? { hidden: true } : {}
433
2931
  };
434
2932
  });
435
2933
  }
2934
+ function normalizePagePath(pagePath) {
2935
+ return pagePath.replace(/^\//, "").replace(/\.(?:mdx?|markdown)$/i, "");
2936
+ }
2937
+ function pagePathToTitleFallback(pagePath) {
2938
+ return normalizePagePath(pagePath).replaceAll("/", "-");
2939
+ }
436
2940
  //#endregion
437
2941
  //#region src/compile/guide.ts
438
2942
  async function compileGuides(options) {
2943
+ const result = await inspectGuides(options);
2944
+ throwOnCompileErrors(result.diagnostics);
2945
+ return result;
2946
+ }
2947
+ async function inspectGuides(options) {
2948
+ const discovered = await discoverGuides(options);
2949
+ const compiled = await compileAssetResources({
2950
+ rootDir: resolve(options.dir),
2951
+ resources: discovered.resources,
2952
+ sourcePathsByResource: discovered.sourcePathsByResource,
2953
+ protectedSourcePaths: discovered.consumedSourcePaths,
2954
+ ...options.assets
2955
+ });
2956
+ return {
2957
+ diagnostics: discovered.diagnostics,
2958
+ ...compiled
2959
+ };
2960
+ }
2961
+ /** @internal Discovery phase used by the mixed top-level compiler. */
2962
+ async function discoverGuides(options) {
439
2963
  const dir = resolve(options.dir);
440
- const raw = await readOptionalConfigFile(dir, [
2964
+ const loadedConfig = await readOptionalConfigFileWithPath(dir, [
441
2965
  "collection.yaml",
442
2966
  "collection.yml",
443
2967
  "collection.json"
444
2968
  ]);
445
- if (raw == null) return { resources: [] };
446
- const config = parseCollectionConfig(raw);
2969
+ if (loadedConfig == null) return {
2970
+ diagnostics: [],
2971
+ resources: [],
2972
+ sourcePathsByResource: {},
2973
+ consumedSourcePaths: []
2974
+ };
2975
+ let config;
2976
+ try {
2977
+ config = parseCollectionConfig(loadedConfig.value);
2978
+ } catch {
2979
+ throw new PublicCompileError("config-invalid", loadedConfig.path);
2980
+ }
447
2981
  const markdownFiles = (await readdir(dir)).filter((f) => f.endsWith(".md") || f.endsWith(".mdx")).sort();
448
2982
  const resources = [];
449
- const assetsById = /* @__PURE__ */ new Map();
2983
+ const diagnostics = [];
2984
+ const sourcePathsByResource = {};
450
2985
  for (const file of markdownFiles) {
451
2986
  const filePath = join(dir, file);
452
- const { frontmatter, content } = parseMarkdownFrontmatter(await readFile(filePath, "utf-8"), file);
453
- const { content: rewritten, assets, manifest } = await extractAssets(content, {
454
- baseDir: dir,
455
- filePath
456
- });
457
- for (const asset of assets) if (!assetsById.has(asset.name)) assetsById.set(asset.name, asset);
2987
+ let rawContent;
2988
+ try {
2989
+ rawContent = await readRegularFileWithinRoot(filePath, dir, "utf-8");
2990
+ } catch (error) {
2991
+ if (error instanceof FileOutsideCompilationRootError) {
2992
+ diagnostics.push({
2993
+ id: "guide-outside-compilation-root",
2994
+ type: "Guide",
2995
+ level: "error",
2996
+ message: "Guide files must resolve within the compilation directory",
2997
+ lines: [],
2998
+ file
2999
+ });
3000
+ continue;
3001
+ }
3002
+ if (error instanceof FileNotRegularError) {
3003
+ diagnostics.push({
3004
+ id: "guide-not-regular-file",
3005
+ type: "Guide",
3006
+ level: "error",
3007
+ message: "Guide entries must be regular files",
3008
+ lines: [],
3009
+ file
3010
+ });
3011
+ continue;
3012
+ }
3013
+ throw error;
3014
+ }
3015
+ const { frontmatter, content } = parseMarkdownFrontmatter(rawContent, file);
3016
+ const validation = validateTopikContent(content, { file });
3017
+ diagnostics.push(...validation.errors);
3018
+ if (!validation.valid) continue;
458
3019
  const slug = fileToSlug(file);
459
3020
  const name = `${config.id}-${slug}`;
460
- const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(rewritten, slug);
3021
+ const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(content, slug);
461
3022
  const tags = mergeTags(config.tags, frontmatter.tags);
462
3023
  const authors = parseReferenceList(frontmatter.authors, "authors", file);
463
3024
  const description = typeof frontmatter.description === "string" ? frontmatter.description : void 0;
3025
+ const analysis = analyzeTopikContent(content, { file });
3026
+ diagnostics.push(...analysis.diagnostics);
3027
+ diagnostics.push(...validateLocalFragments(analysis, linkValidationPolicy(options.validation)));
464
3028
  const guide = {
465
3029
  apiVersion: "v1",
466
3030
  type: "Guide",
@@ -473,15 +3037,19 @@ async function compileGuides(options) {
473
3037
  ...tags.length > 0 ? { tags } : {},
474
3038
  content: {
475
3039
  format: "topik",
476
- value: rewritten
477
- },
478
- ...manifest.length > 0 ? { assets: manifest } : {}
3040
+ value: content
3041
+ }
479
3042
  }
480
3043
  };
481
3044
  resources.push(guide);
3045
+ sourcePathsByResource[`Guide/${guide.name}`] = file;
482
3046
  }
483
- for (const asset of assetsById.values()) resources.push(asset);
484
- return { resources };
3047
+ return {
3048
+ diagnostics,
3049
+ resources,
3050
+ sourcePathsByResource,
3051
+ consumedSourcePaths: [loadedConfig.path]
3052
+ };
485
3053
  }
486
3054
  function fileToSlug(filename) {
487
3055
  return filename.replace(/\.(mdx?|md)$/, "");
@@ -509,75 +3077,49 @@ const COLLECTION_CONFIG_FILES = [
509
3077
  async function compile(options) {
510
3078
  const dir = resolve(options.dir);
511
3079
  const resources = [];
3080
+ const diagnostics = [];
3081
+ const sourcePathsByResource = {};
3082
+ const protectedSourcePaths = [];
512
3083
  if (await findConfigFile(dir, WIKI_CONFIG_FILES)) {
513
- const result = await compileWiki({ dir });
3084
+ const result = await discoverWiki({
3085
+ dir,
3086
+ validation: options.validation
3087
+ });
514
3088
  resources.push(...result.resources);
3089
+ diagnostics.push(...result.diagnostics);
3090
+ Object.assign(sourcePathsByResource, result.sourcePathsByResource);
3091
+ protectedSourcePaths.push(...result.consumedSourcePaths);
515
3092
  }
516
3093
  if (await findConfigFile(dir, COLLECTION_CONFIG_FILES)) {
517
- const result = await compileGuides({ dir });
518
- resources.push(...result.resources);
519
- }
520
- return { resources };
521
- }
522
- //#endregion
523
- //#region src/validate.ts
524
- const ajv = new Ajv2020({
525
- strict: true,
526
- discriminator: true
527
- });
528
- addFormats(ajv);
529
- const validators = {
530
- Asset: ajv.compile(assetSchema),
531
- Guide: ajv.compile(guideSchema),
532
- Wiki: ajv.compile(wikiSchema),
533
- WikiPage: ajv.compile(wikiPageSchema)
534
- };
535
- function isRecord(value) {
536
- return typeof value === "object" && value !== null;
537
- }
538
- function getResourceLabel(resource) {
539
- return `${typeof resource.type === "string" ? resource.type : "unknown"}/${typeof resource.name === "string" ? resource.name : "unknown"}`;
540
- }
541
- /** Validate the resources produced by `compile` and the CLI export. */
542
- function validateResources(resources) {
543
- const errors = [];
544
- for (const resource of resources) {
545
- if (!isRecord(resource)) {
546
- errors.push({
547
- resource: "unknown/unknown",
548
- path: "/",
549
- message: "Resource must be an object"
550
- });
551
- continue;
552
- }
553
- if (typeof resource.type !== "string") {
554
- errors.push({
555
- resource: getResourceLabel(resource),
556
- path: "/type",
557
- message: "Resource type must be a string"
558
- });
559
- continue;
560
- }
561
- const validate = validators[resource.type];
562
- if (!validate) {
563
- errors.push({
564
- resource: getResourceLabel(resource),
565
- path: "/type",
566
- message: `Unsupported resource type: ${resource.type}`
567
- });
568
- continue;
569
- }
570
- if (!validate(resource)) for (const err of validate.errors ?? []) errors.push({
571
- resource: getResourceLabel(resource),
572
- path: err.instancePath || "/",
573
- message: err.message ?? "Unknown error"
3094
+ const result = await discoverGuides({
3095
+ dir,
3096
+ validation: options.validation
574
3097
  });
3098
+ resources.push(...result.resources);
3099
+ diagnostics.push(...result.diagnostics);
3100
+ Object.assign(sourcePathsByResource, result.sourcePathsByResource);
3101
+ protectedSourcePaths.push(...result.consumedSourcePaths);
575
3102
  }
3103
+ throwOnCompileErrors(diagnostics);
576
3104
  return {
577
- valid: errors.length === 0,
578
- errors
3105
+ diagnostics,
3106
+ ...await compileAssetResources({
3107
+ rootDir: dir,
3108
+ resources,
3109
+ sourcePathsByResource,
3110
+ protectedSourcePaths,
3111
+ ...options.assets
3112
+ })
579
3113
  };
580
3114
  }
3115
+ async function lint(options) {
3116
+ try {
3117
+ return { diagnostics: (await compile(options)).diagnostics };
3118
+ } catch (error) {
3119
+ if (error instanceof CompileError) return { diagnostics: error.diagnostics };
3120
+ throw error;
3121
+ }
3122
+ }
581
3123
  //#endregion
582
3124
  //#region src/watch.ts
583
3125
  function resourceKey(resource) {
@@ -587,22 +3129,33 @@ async function watch(options) {
587
3129
  const dir = resolve(options.dir);
588
3130
  const emitter = new EventEmitter();
589
3131
  const resources = /* @__PURE__ */ new Map();
590
- const initial = await compile({ dir });
3132
+ const payloads = /* @__PURE__ */ new Map();
3133
+ const initial = await compile({
3134
+ dir,
3135
+ assets: options.assets
3136
+ });
3137
+ let materialization = initial.materialization;
591
3138
  for (const resource of initial.resources) resources.set(resourceKey(resource), resource);
3139
+ for (const payload of initial.payloads) payloads.set(payload.path, payload);
592
3140
  let debounceTimer = null;
593
3141
  async function recompile() {
594
3142
  try {
595
- const result = await compile({ dir });
3143
+ const result = await compile({
3144
+ dir,
3145
+ assets: options.assets
3146
+ });
596
3147
  const newKeys = /* @__PURE__ */ new Set();
3148
+ const materializationChanged = digestTopikMaterializationRecord(materialization) !== digestTopikMaterializationRecord(result.materialization);
3149
+ materialization = result.materialization;
3150
+ payloads.clear();
3151
+ for (const payload of result.payloads) payloads.set(payload.path, payload);
597
3152
  for (const resource of result.resources) {
598
3153
  const key = resourceKey(resource);
599
3154
  newKeys.add(key);
600
3155
  const existing = resources.get(key);
601
- const serialized = JSON.stringify(resource);
602
- if (!existing || JSON.stringify(existing) !== serialized) {
603
- resources.set(key, resource);
604
- emitter.emit("update", key, resource);
605
- }
3156
+ const resourceChanged = !existing || JSON.stringify(existing) !== JSON.stringify(resource);
3157
+ resources.set(key, resource);
3158
+ if (resourceChanged || resource.type === "Asset" && materializationChanged) emitter.emit("update", key, resource);
606
3159
  }
607
3160
  for (const key of resources.keys()) if (!newKeys.has(key)) resources.delete(key);
608
3161
  } catch (error) {
@@ -615,7 +3168,7 @@ async function watch(options) {
615
3168
  }
616
3169
  const fsWatcher = watch$1(dir, {
617
3170
  ignoreInitial: true,
618
- ignored: [/(^|[/\\])\../, "**/node_modules/**"]
3171
+ ignored: [/(^|[/\\])(?:\.git|\.topik(?:-compilation-(?:generation|prior)-[^/\\]+)?)(?:[/\\]|$)/, "**/node_modules/**"]
619
3172
  });
620
3173
  fsWatcher.on("add", scheduleRecompile);
621
3174
  fsWatcher.on("change", scheduleRecompile);
@@ -625,6 +3178,7 @@ async function watch(options) {
625
3178
  });
626
3179
  const watcher = {
627
3180
  resources,
3181
+ payloads,
628
3182
  on(event, listener) {
629
3183
  emitter.on(event, listener);
630
3184
  return watcher;
@@ -641,4 +3195,4 @@ async function watch(options) {
641
3195
  return watcher;
642
3196
  }
643
3197
  //#endregion
644
- export { compile, compileGuides, compileWiki, parseCollectionConfig, parseWikiConfig, validateResources, watch };
3198
+ export { ASSET_API_VERSION, ASSET_SCHEMA_ID, ASSET_TYPE, AssetCompilationError, CompileError, PublicCompileError, TOPIK_ASSET_CORRELATION_ID_PATTERN, TOPIK_ASSET_DEFAULT_CORRELATION_ID, TOPIK_ASSET_DIAGNOSTIC_IDS, TOPIK_ASSET_LIMITS, TOPIK_ASSET_NAME_VERSION, TOPIK_ASSET_REFERENCE_VERSION, TOPIK_BLOB_OUTPUT_PREFIX, TOPIK_JSON_VERSION, TOPIK_MATERIALIZATION_VERSION, TOPIK_PATH_V1_DESCRIPTOR, TOPIK_PATH_VERSION, TOPIK_UNRESOLVED_ACTIVE_CONTENT_TYPE, TopikJsonSyntaxError, compareTopikAssetIdentities, compile, compileAssetResources, compileGuides, compileWiki, computeTopikPathCollisionKey, correlateTopikAssetResult, createTopikAssetSemanticRecord, createTopikMaterializationRecord, digestTopikAssetSemanticRecord, digestTopikMaterializationRecord, findFirstWikiPage, findWikiPageAncestors, generateAutomaticAssetName, hasWikiNavChildren, isAssetBlobUri, isErrorDiagnostic, isExternalWikiDropdown, isExternalWikiTab, isGeneratedAssetName, isInlineMediaCompatible, isInternalWikiDropdown, isInternalWikiTab, isTopikActiveMediaType, joinWikiPath, lint, looksLikeGitLfsPointer, pagePathToName, parseAsset, parseAssetBlobUri, parseCollectionConfig, parseGeneratedAssetName, parseStrictTopikJson, parseWikiConfig, publicCompileErrorMessage, readPortableAssetFile, resolveWikiContentHref, resolveWikiNavigation, serializeAsset, serializeTopikJson, sniffPortableMediaType, toNfkcCasefold, topikAssetNameDescriptor, topikJsonDescriptor, validateAssetBlobUri, validateAssetValue, validatePortableAssetFile, validatePortableAssetTree, validateResources, validateStableSourceNamespace, validateTopikMaterializationRecord, validateTopikPath, validateTopikPathSet, watch };