@topik/core 0.1.0-alpha.4 → 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 +374 -16
  2. package/dist/index.mjs +2572 -354
  3. package/package.json +6 -6
package/dist/index.mjs CHANGED
@@ -1,12 +1,22 @@
1
1
  import { z } from "zod";
2
- import { WIKI_EXTERNAL_HREF_PATTERN, WIKI_NAV_ICON_PATTERN, assetSchema, guideSchema, joinWikiPath, wikiPageSchema, wikiSchema } from "@topik/schema";
3
- import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
4
- 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";
5
4
  import { parse } from "yaml";
5
+ import { constants } from "node:fs";
6
+ import { lstat, open, readdir, realpath } from "node:fs/promises";
6
7
  import { createHash } from "node:crypto";
7
- import { analyzeTopikContent, formatTopikContent, parseTopikContent, validateTopikContent } from "@topik/content-schema";
8
+ import { analyzeTopikContent, extractTopikAssetOccurrences, isTopikGeneratedAssetName, parseTopikGeneratedAssetName, rewriteTopikAssetOccurrences, sanitizeTopikContentDiagnostic, topikLinkDiagnosticMessage, validateTopikAssetReference, validateTopikContent } from "@topik/content-schema";
8
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";
9
13
  import addFormats from "ajv-formats";
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,8 +30,8 @@ function parseCollectionConfig(raw) {
20
30
  }
21
31
  //#endregion
22
32
  //#region src/config/wiki.ts
23
- const wikiNavIconPattern = new RegExp(WIKI_NAV_ICON_PATTERN);
24
- const wikiExternalHrefPattern = new RegExp(WIKI_EXTERNAL_HREF_PATTERN);
33
+ const wikiNavIconPattern = new RegExp(wikiV1Schema.$defs.groupNode.properties.icon.pattern);
34
+ const wikiExternalHrefPattern = new RegExp(wikiV1Schema.$defs.externalTabNode.properties.href.pattern);
25
35
  const pathSegment = z.string().min(1).max(256).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Slugs must be lowercase DNS-style segments");
26
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 '/'");
27
37
  const title = z.string().min(1).max(256);
@@ -174,236 +184,2286 @@ function parseWikiConfig(raw) {
174
184
  return wikiConfigSchema.parse(raw);
175
185
  }
176
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
177
340
  //#region src/compile/config.ts
178
- async function readOptionalConfigFile(dir, candidates) {
341
+ async function readOptionalConfigFileWithPath(dir, candidates) {
179
342
  for (const name of candidates) {
180
343
  const filePath = join(dir, name);
181
344
  let raw;
182
345
  try {
183
- raw = await readFile(filePath, "utf-8");
184
- } catch (error) {
185
- if (error.code === "ENOENT") continue;
186
- throw new Error(`Failed to read config file ${filePath}`, { cause: error });
346
+ raw = await readRegularFileWithinRoot(filePath, dir, "utf-8");
347
+ } catch (error) {
348
+ if (error.code === "ENOENT") continue;
349
+ throw new PublicCompileError("config-read-failed", name);
350
+ }
351
+ try {
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);
358
+ }
359
+ }
360
+ }
361
+ async function findConfigFile(dir, candidates) {
362
+ for (const name of candidates) try {
363
+ await assertRegularFileWithinRoot(join(dir, name), dir);
364
+ return name;
365
+ } catch (error) {
366
+ if (error.code === "ENOENT") continue;
367
+ throw new PublicCompileError("config-access-failed", name);
368
+ }
369
+ return null;
370
+ }
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;
187
2062
  }
188
- try {
189
- return name.endsWith(".json") ? JSON.parse(raw) : parse(raw);
190
- } catch (error) {
191
- throw new Error(`Failed to parse config file ${filePath}`, { cause: error });
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;
192
2086
  }
2087
+ break;
193
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;
194
2094
  }
195
- async function findConfigFile(dir, candidates) {
196
- for (const name of candidates) try {
197
- await access(join(dir, name));
198
- return name;
199
- } catch (error) {
200
- if (error.code === "ENOENT") continue;
201
- throw new Error(`Failed to access config file ${join(dir, name)}`, { cause: error });
202
- }
203
- return null;
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));
204
2115
  }
205
2116
  //#endregion
206
2117
  //#region src/compile/assets.ts
207
- const MIME_BY_EXT = {
208
- ".png": "image/png",
209
- ".jpg": "image/jpeg",
210
- ".jpeg": "image/jpeg",
211
- ".gif": "image/gif",
212
- ".svg": "image/svg+xml",
213
- ".webp": "image/webp",
214
- ".avif": "image/avif",
215
- ".ico": "image/x-icon",
216
- ".bmp": "image/bmp",
217
- ".mp4": "video/mp4",
218
- ".webm": "video/webm",
219
- ".pdf": "application/pdf",
220
- ".zip": "application/zip",
221
- ".tar": "application/x-tar",
222
- ".gz": "application/gzip",
223
- ".mp3": "audio/mpeg",
224
- ".wav": "audio/wav",
225
- ".ogg": "audio/ogg",
226
- ".m4a": "audio/mp4",
227
- ".flac": "audio/flac",
228
- ".csv": "text/csv",
229
- ".txt": "text/plain",
230
- ".doc": "application/msword",
231
- ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
232
- ".xls": "application/vnd.ms-excel",
233
- ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
234
- ".ppt": "application/vnd.ms-powerpoint",
235
- ".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
+ }
236
2125
  };
237
- const ASSET_TAG_ATTRS = [
238
- "src",
239
- "darkSrc",
240
- "href"
241
- ];
242
- async function extractAssets(source, options) {
243
- const { baseDir, filePath } = options;
244
- const ast = parseTopikContent(source);
245
- const refs = /* @__PURE__ */ new Map();
246
- const slots = /* @__PURE__ */ new Map();
247
- const collect = (url, slot, requireKnownExtension) => {
248
- if (!isLocalRef(url)) return;
249
- if (requireKnownExtension && !hasKnownAssetExtension(url)) return;
250
- if (!refs.has(url)) {
251
- const absPath = resolveAssetPath(url, {
252
- baseDir,
253
- filePath
254
- });
255
- const relFromBase = relative(baseDir, absPath).split(sep).join(posix.sep);
256
- refs.set(url, {
257
- original: url,
258
- absPath,
259
- 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
260
2218
  });
261
- }
262
- const list = slots.get(url) ?? [];
263
- list.push(slot);
264
- slots.set(url, list);
265
- };
266
- walk(ast, (node) => {
267
- if (node.type === "image") {
268
- const url = stringAttr(node, "src");
269
- if (url) collect(url, {
270
- node,
271
- attr: "src"
272
- }, false);
273
- } else if (node.type === "link") {
274
- const url = stringAttr(node, "href");
275
- if (url) collect(url, {
276
- node,
277
- attr: "href"
278
- }, true);
279
- } else if (node.type === "tag") for (const attr of ASSET_TAG_ATTRS) {
280
- const url = stringAttr(node, attr);
281
- if (url) collect(url, {
282
- node,
283
- attr
284
- }, true);
285
- }
286
- });
287
- if (refs.size === 0) return {
288
- content: source,
289
- assets: [],
290
- manifest: []
291
- };
292
- const urlToId = /* @__PURE__ */ new Map();
293
- const byName = /* @__PURE__ */ new Map();
294
- const refList = Array.from(refs.values());
295
- const settled = await Promise.allSettled(refList.map((ref) => toAsset(ref)));
296
- const failures = [];
297
- for (let i = 0; i < settled.length; i++) {
298
- const result = settled[i];
299
- if (result.status === "fulfilled") {
300
- urlToId.set(refList[i].original, result.value.name);
301
- byName.set(result.value.name, result.value);
302
- } else failures.push(result.reason instanceof Error ? result.reason.message : String(result.reason));
303
- }
304
- if (failures.length > 0) throw new Error(`Failed to extract ${failures.length} asset(s):\n - ${failures.join("\n - ")}`);
305
- const manifest = [];
306
- const seen = /* @__PURE__ */ new Set();
307
- for (const ref of refs.values()) {
308
- const name = urlToId.get(ref.original);
309
- if (name && !seen.has(name)) {
310
- manifest.push(name);
311
- 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);
312
2226
  }
313
2227
  }
314
- for (const [url, slotList] of slots.entries()) {
315
- const id = urlToId.get(url);
316
- if (!id) continue;
317
- 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
+ });
318
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);
319
2306
  return {
320
- content: formatTopikContent(ast),
321
- assets: Array.from(byName.values()),
322
- manifest
323
- };
324
- }
325
- function walk(node, fn) {
326
- fn(node);
327
- for (const child of node.children) walk(child, fn);
328
- }
329
- function stringAttr(node, attr) {
330
- const value = node.attributes?.[attr];
331
- return typeof value === "string" ? value : void 0;
332
- }
333
- function hasKnownAssetExtension(url) {
334
- const [path] = splitUrl(url);
335
- const decoded = decodeSafe(path);
336
- const m = /\.([a-z0-9]+)$/i.exec(decoded);
337
- if (!m) return false;
338
- return `.${m[1].toLowerCase()}` in MIME_BY_EXT;
339
- }
340
- function isLocalRef(url) {
341
- if (url.length === 0) return false;
342
- if (url.startsWith("#")) return false;
343
- if (url.startsWith("mailto:")) return false;
344
- if (url.startsWith("tel:")) return false;
345
- if (url.startsWith("data:")) return false;
346
- if (url.startsWith("//")) return false;
347
- if (/^[a-z][a-z0-9+.-]*:/i.test(url)) return false;
348
- return true;
349
- }
350
- function resolveAssetPath(url, { baseDir, filePath }) {
351
- const [pathPart] = splitUrl(url);
352
- const decoded = decodeSafe(pathPart);
353
- const absolute = resolve(isAbsolute(decoded) || decoded.startsWith("/") ? baseDir : dirname(filePath), decoded.replace(/^\/+/, ""));
354
- const rel = relative(baseDir, absolute);
355
- if (rel.startsWith("..") || isAbsolute(rel)) throw new Error(`Asset reference "${url}" in ${filePath} resolves outside the compilation directory`);
356
- return absolute;
357
- }
358
- function splitUrl(url) {
359
- const hashIdx = url.indexOf("#");
360
- const queryIdx = url.indexOf("?");
361
- let cut = -1;
362
- if (hashIdx >= 0) cut = hashIdx;
363
- if (queryIdx >= 0 && (cut < 0 || queryIdx < cut)) cut = queryIdx;
364
- if (cut < 0) return [url, ""];
365
- return [url.slice(0, cut), url.slice(cut)];
366
- }
367
- function decodeSafe(s) {
368
- try {
369
- return decodeURIComponent(s);
370
- } catch {
371
- 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);
372
2382
  }
2383
+ return proof.value.bytes === void 0 ? void 0 : proof.value;
373
2384
  }
374
- async function toAsset(ref) {
375
- let bytes;
376
- try {
377
- bytes = await readFile(ref.absPath);
378
- } catch (error) {
379
- const message = error instanceof Error ? error.message : String(error);
380
- throw new Error(`Asset "${ref.original}" not found at ${ref.absPath}: ${message}`, { cause: error });
381
- }
382
- const digest = createHash("sha256").update(bytes).digest();
383
- const integrity = `sha256-${digest.toString("base64")}`;
384
- const name = digest.toString("hex").slice(0, 16);
385
- const extMatch = /\.([a-z0-9]+)(?:[?#].*)?$/i.exec(ref.relFromBase);
386
- const ext = extMatch ? `.${extMatch[1].toLowerCase()}` : "";
387
- const mediaType = MIME_BY_EXT[ext];
388
- return {
389
- apiVersion: "v1",
390
- type: "Asset",
391
- name,
392
- spec: {
393
- uri: ref.relFromBase,
394
- integrity,
395
- ...mediaType ? { mediaType } : {}
396
- }
397
- };
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));
398
2457
  }
399
2458
  //#endregion
400
2459
  //#region src/compile/shared.ts
401
2460
  var CompileError = class extends Error {
402
2461
  diagnostics;
403
2462
  constructor(diagnostics) {
404
- super(formatContentDiagnostics(diagnostics));
405
- this.diagnostics = diagnostics;
2463
+ const sanitized = diagnostics.map(sanitizeTopikContentDiagnostic);
2464
+ super(formatContentDiagnostics(sanitized));
406
2465
  this.name = "CompileError";
2466
+ this.diagnostics = sanitized;
407
2467
  }
408
2468
  };
409
2469
  function linkValidationPolicy(options) {
@@ -438,9 +2498,8 @@ function parseMarkdownFrontmatter(raw, filePath) {
438
2498
  frontmatter,
439
2499
  content: match[2]
440
2500
  };
441
- } catch (error) {
442
- const message = error instanceof Error ? error.message : String(error);
443
- throw new Error(`Invalid frontmatter in ${filePath}: ${message}`, { cause: error });
2501
+ } catch {
2502
+ throw new PublicCompileError("frontmatter-invalid", filePath);
444
2503
  }
445
2504
  }
446
2505
  function extractMarkdownTitle(content, fallback) {
@@ -450,15 +2509,16 @@ function extractMarkdownTitle(content, fallback) {
450
2509
  }
451
2510
  function formatContentDiagnostics(diagnostics) {
452
2511
  return diagnostics.filter(isErrorDiagnostic).map((diagnostic) => {
453
- return `${diagnostic.file ?? "content"}${diagnostic.lines.length > 0 ? `:${diagnostic.lines.join(",")}` : ""} ${diagnostic.level} ${diagnostic.id}: ${diagnostic.message}`;
2512
+ const sanitized = sanitizeTopikContentDiagnostic(diagnostic);
2513
+ return `${sanitized.file ?? "content"}${sanitized.lines.length > 0 ? `:${sanitized.lines.join(",")}` : ""} ${sanitized.level} ${sanitized.id}: ${sanitized.message}`;
454
2514
  }).join("\n");
455
2515
  }
456
- function parseReferenceList(value, fieldName, filePath) {
2516
+ function parseReferenceList(value, _fieldName, filePath) {
457
2517
  if (value == null) return;
458
- if (!Array.isArray(value)) throw new Error(`${fieldName} in ${filePath} must be an array of resource names`);
459
- const references = value.map((entry, index) => {
460
- if (typeof entry !== "string") throw new Error(`${fieldName}[${index}] in ${filePath} must be a string`);
461
- 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);
462
2522
  return entry;
463
2523
  });
464
2524
  return references.length > 0 ? references : void 0;
@@ -467,7 +2527,7 @@ function parseReferenceList(value, fieldName, filePath) {
467
2527
  //#region src/compile/links.ts
468
2528
  const NON_PAGE_SCHEME = /^(?:asset|https?|mailto|tel):/i;
469
2529
  const LINK_BASE = "https://topik.local";
470
- function validateWikiLinks(pages, policy) {
2530
+ function validateWikiLinks(pages, policy, nonPageLinks = /* @__PURE__ */ new Set()) {
471
2531
  if (policy === "off") return [];
472
2532
  const level = policy === "error" ? "error" : "warning";
473
2533
  const pagesBySlug = new Map(pages.map((page) => [page.slug, page]));
@@ -478,10 +2538,11 @@ function validateWikiLinks(pages, policy) {
478
2538
  if (!target) continue;
479
2539
  const targetPage = pagesBySlug.get(target.slug);
480
2540
  if (!targetPage) {
481
- diagnostics.push(linkDiagnostic("link-page-not-found", level, `Internal link '${link.href}' resolves to missing page '/${target.slug}'.`, link));
2541
+ if (nonPageLinks.has(link)) continue;
2542
+ diagnostics.push(linkDiagnostic("link-page-not-found", level, link));
482
2543
  continue;
483
2544
  }
484
- if (target.fragment && !targetPage.analysis.headings.some((heading) => heading.id === target.fragment)) diagnostics.push(linkDiagnostic("link-fragment-not-found", level, `Internal link '${link.href}' references missing heading '#${target.fragment}' on '/${target.slug}'.`, link));
2545
+ if (target.fragment && !targetPage.analysis.headings.some((heading) => heading.id === target.fragment)) diagnostics.push(linkDiagnostic("link-fragment-not-found", level, link));
485
2546
  }
486
2547
  return diagnostics;
487
2548
  }
@@ -493,7 +2554,7 @@ function validateLocalFragments(analysis, policy) {
493
2554
  for (const link of analysis.links) {
494
2555
  if (!link.href.startsWith("#") || link.href === "#") continue;
495
2556
  const fragment = decodeFragment(link.href.slice(1));
496
- if (fragment && !headingIds.has(fragment)) diagnostics.push(linkDiagnostic("link-fragment-not-found", level, `Link '${link.href}' references a missing heading in this document.`, link));
2557
+ if (fragment && !headingIds.has(fragment)) diagnostics.push(linkDiagnostic("link-fragment-not-found", level, link));
497
2558
  }
498
2559
  return diagnostics;
499
2560
  }
@@ -529,7 +2590,9 @@ function decodePath(pathname) {
529
2590
  if (normalized === "index") return "";
530
2591
  return normalized.replace(/\/index$/, "");
531
2592
  }
532
- function linkDiagnostic(id, level, message, link) {
2593
+ function linkDiagnostic(id, level, link) {
2594
+ const message = topikLinkDiagnosticMessage(id);
2595
+ if (message === void 0) throw new TypeError("Unknown link diagnostic identifier");
533
2596
  return {
534
2597
  id,
535
2598
  type: link.kind,
@@ -540,6 +2603,105 @@ function linkDiagnostic(id, level, message, link) {
540
2603
  };
541
2604
  }
542
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
543
2705
  //#region src/compile/wiki.ts
544
2706
  async function compileWiki(options) {
545
2707
  const result = await inspectWiki(options);
@@ -547,45 +2709,57 @@ async function compileWiki(options) {
547
2709
  return result;
548
2710
  }
549
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) {
550
2727
  const dir = resolve(options.dir);
551
- const raw = await readOptionalConfigFile(dir, [
2728
+ const loadedConfig = await readOptionalConfigFileWithPath(dir, [
552
2729
  "wiki.yaml",
553
2730
  "wiki.yml",
554
2731
  "wiki.json"
555
2732
  ]);
556
- if (raw == null) return {
2733
+ if (loadedConfig == null) return {
557
2734
  diagnostics: [],
558
- resources: []
2735
+ resources: [],
2736
+ sourcePathsByResource: {},
2737
+ consumedSourcePaths: []
559
2738
  };
560
- const config = parseWikiConfig(raw);
2739
+ let config;
2740
+ try {
2741
+ config = parseWikiConfig(loadedConfig.value);
2742
+ } catch {
2743
+ throw new PublicCompileError("config-invalid", loadedConfig.path);
2744
+ }
561
2745
  const pagePaths = config.navigation ? [...new Set(collectPagePaths(config.navigation))] : [];
562
- const resolvedFiles = await Promise.all(pagePaths.map(async (pagePath) => {
563
- const filePath = await resolveFilePath(dir, pagePath);
564
- return {
565
- filePath,
566
- raw: await readFile(filePath, "utf-8")
567
- };
568
- }));
2746
+ const resolvedFiles = await Promise.all(pagePaths.map((pagePath) => readPageFile(dir, pagePath)));
569
2747
  const resources = [];
570
2748
  const diagnostics = [];
571
2749
  const pageAnalyses = [];
572
- const assetsById = /* @__PURE__ */ new Map();
2750
+ const sourcePathsByResource = {};
573
2751
  for (let i = 0; i < pagePaths.length; i++) {
574
2752
  const pagePath = pagePaths[i];
575
2753
  const { filePath, raw } = resolvedFiles[i];
2754
+ const sourcePath = `${pagePath}${filePath.endsWith(".mdx") ? ".mdx" : ".md"}`;
576
2755
  const { frontmatter, content } = parseMarkdownFrontmatter(raw, pagePath);
577
- const validation = validateTopikContent(content, { file: filePath });
2756
+ const validation = validateTopikContent(content, { file: sourcePath });
578
2757
  diagnostics.push(...validation.errors);
579
2758
  if (!validation.valid) continue;
580
- const { content: rewritten, assets, manifest } = await extractAssets(content, {
581
- baseDir: dir,
582
- filePath
583
- });
584
- for (const asset of assets) if (!assetsById.has(asset.name)) assetsById.set(asset.name, asset);
585
2759
  const name = pagePathToName(config.id, pagePath);
586
- const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(rewritten, pagePathToTitleFallback(pagePath));
2760
+ const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(content, pagePathToTitleFallback(pagePath));
587
2761
  const description = normalizeWikiPageDescription(frontmatter.description);
588
- const analysis = analyzeTopikContent(rewritten, { file: filePath });
2762
+ const analysis = analyzeTopikContent(content, { file: sourcePath });
589
2763
  diagnostics.push(...analysis.diagnostics);
590
2764
  pageAnalyses.push({
591
2765
  analysis,
@@ -602,14 +2776,13 @@ async function inspectWiki(options) {
602
2776
  ...description != null ? { description } : {},
603
2777
  content: {
604
2778
  format: "topik",
605
- value: rewritten
606
- },
607
- ...manifest.length > 0 ? { assets: manifest } : {}
2779
+ value: content
2780
+ }
608
2781
  }
609
2782
  };
610
2783
  resources.push(pageResource);
2784
+ sourcePathsByResource[`WikiPage/${pageResource.name}`] = sourcePath;
611
2785
  }
612
- for (const asset of assetsById.values()) resources.push(asset);
613
2786
  const wikiResource = {
614
2787
  apiVersion: "v1",
615
2788
  type: "Wiki",
@@ -622,12 +2795,45 @@ async function inspectWiki(options) {
622
2795
  }
623
2796
  };
624
2797
  resources.push(wikiResource);
625
- if (!hasCompileErrors(diagnostics)) diagnostics.push(...validateWikiLinks(pageAnalyses, linkValidationPolicy(options.validation)));
2798
+ if (!hasCompileErrors(diagnostics)) {
2799
+ const nonPageLinks = await classifyWikiNonPageLinks(dir, pageAnalyses);
2800
+ diagnostics.push(...validateWikiLinks(pageAnalyses, linkValidationPolicy(options.validation), nonPageLinks));
2801
+ }
626
2802
  return {
627
2803
  diagnostics,
628
- resources
2804
+ resources,
2805
+ sourcePathsByResource,
2806
+ consumedSourcePaths: [loadedConfig.path]
629
2807
  };
630
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
+ }
631
2837
  function normalizeWikiPageDescription(description) {
632
2838
  return typeof description === "string" ? description.slice(0, 1024) : void 0;
633
2839
  }
@@ -637,15 +2843,20 @@ function collectPagePaths(nodes, prefix = "") {
637
2843
  else if ("children" in node) paths.push(...collectPagePaths(node.children, joinWikiPath(prefix, node.slug)));
638
2844
  return paths;
639
2845
  }
640
- async function resolveFilePath(dir, pagePath) {
2846
+ async function readPageFile(dir, pagePath) {
641
2847
  for (const ext of [".mdx", ".md"]) {
642
2848
  const filePath = join(dir, pagePath + ext);
643
2849
  try {
644
- await access(filePath);
645
- return filePath;
646
- } 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
+ }
647
2858
  }
648
- throw new Error(`Page not found: ${pagePath} (tried .md and .mdx in ${dir})`);
2859
+ throw new PublicCompileError("wiki-page-not-found");
649
2860
  }
650
2861
  function pagePathToSlug(pagePath) {
651
2862
  if (pagePath === "index") return "";
@@ -734,39 +2945,84 @@ async function compileGuides(options) {
734
2945
  return result;
735
2946
  }
736
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) {
737
2963
  const dir = resolve(options.dir);
738
- const raw = await readOptionalConfigFile(dir, [
2964
+ const loadedConfig = await readOptionalConfigFileWithPath(dir, [
739
2965
  "collection.yaml",
740
2966
  "collection.yml",
741
2967
  "collection.json"
742
2968
  ]);
743
- if (raw == null) return {
2969
+ if (loadedConfig == null) return {
744
2970
  diagnostics: [],
745
- resources: []
2971
+ resources: [],
2972
+ sourcePathsByResource: {},
2973
+ consumedSourcePaths: []
746
2974
  };
747
- const config = parseCollectionConfig(raw);
2975
+ let config;
2976
+ try {
2977
+ config = parseCollectionConfig(loadedConfig.value);
2978
+ } catch {
2979
+ throw new PublicCompileError("config-invalid", loadedConfig.path);
2980
+ }
748
2981
  const markdownFiles = (await readdir(dir)).filter((f) => f.endsWith(".md") || f.endsWith(".mdx")).sort();
749
2982
  const resources = [];
750
2983
  const diagnostics = [];
751
- const assetsById = /* @__PURE__ */ new Map();
2984
+ const sourcePathsByResource = {};
752
2985
  for (const file of markdownFiles) {
753
2986
  const filePath = join(dir, file);
754
- const { frontmatter, content } = parseMarkdownFrontmatter(await readFile(filePath, "utf-8"), file);
755
- const validation = validateTopikContent(content, { file: filePath });
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 });
756
3017
  diagnostics.push(...validation.errors);
757
3018
  if (!validation.valid) continue;
758
- const { content: rewritten, assets, manifest } = await extractAssets(content, {
759
- baseDir: dir,
760
- filePath
761
- });
762
- for (const asset of assets) if (!assetsById.has(asset.name)) assetsById.set(asset.name, asset);
763
3019
  const slug = fileToSlug(file);
764
3020
  const name = `${config.id}-${slug}`;
765
- const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(rewritten, slug);
3021
+ const title = typeof frontmatter.title === "string" ? frontmatter.title : extractMarkdownTitle(content, slug);
766
3022
  const tags = mergeTags(config.tags, frontmatter.tags);
767
3023
  const authors = parseReferenceList(frontmatter.authors, "authors", file);
768
3024
  const description = typeof frontmatter.description === "string" ? frontmatter.description : void 0;
769
- const analysis = analyzeTopikContent(rewritten, { file: filePath });
3025
+ const analysis = analyzeTopikContent(content, { file });
770
3026
  diagnostics.push(...analysis.diagnostics);
771
3027
  diagnostics.push(...validateLocalFragments(analysis, linkValidationPolicy(options.validation)));
772
3028
  const guide = {
@@ -781,17 +3037,18 @@ async function inspectGuides(options) {
781
3037
  ...tags.length > 0 ? { tags } : {},
782
3038
  content: {
783
3039
  format: "topik",
784
- value: rewritten
785
- },
786
- ...manifest.length > 0 ? { assets: manifest } : {}
3040
+ value: content
3041
+ }
787
3042
  }
788
3043
  };
789
3044
  resources.push(guide);
3045
+ sourcePathsByResource[`Guide/${guide.name}`] = file;
790
3046
  }
791
- for (const asset of assetsById.values()) resources.push(asset);
792
3047
  return {
793
3048
  diagnostics,
794
- resources
3049
+ resources,
3050
+ sourcePathsByResource,
3051
+ consumedSourcePaths: [loadedConfig.path]
795
3052
  };
796
3053
  }
797
3054
  function fileToSlug(filename) {
@@ -821,98 +3078,47 @@ async function compile(options) {
821
3078
  const dir = resolve(options.dir);
822
3079
  const resources = [];
823
3080
  const diagnostics = [];
3081
+ const sourcePathsByResource = {};
3082
+ const protectedSourcePaths = [];
824
3083
  if (await findConfigFile(dir, WIKI_CONFIG_FILES)) {
825
- const result = await compileWiki({
3084
+ const result = await discoverWiki({
826
3085
  dir,
827
3086
  validation: options.validation
828
3087
  });
829
3088
  resources.push(...result.resources);
830
3089
  diagnostics.push(...result.diagnostics);
3090
+ Object.assign(sourcePathsByResource, result.sourcePathsByResource);
3091
+ protectedSourcePaths.push(...result.consumedSourcePaths);
831
3092
  }
832
3093
  if (await findConfigFile(dir, COLLECTION_CONFIG_FILES)) {
833
- const result = await compileGuides({
3094
+ const result = await discoverGuides({
834
3095
  dir,
835
3096
  validation: options.validation
836
3097
  });
837
3098
  resources.push(...result.resources);
838
3099
  diagnostics.push(...result.diagnostics);
3100
+ Object.assign(sourcePathsByResource, result.sourcePathsByResource);
3101
+ protectedSourcePaths.push(...result.consumedSourcePaths);
839
3102
  }
3103
+ throwOnCompileErrors(diagnostics);
840
3104
  return {
841
3105
  diagnostics,
842
- resources
3106
+ ...await compileAssetResources({
3107
+ rootDir: dir,
3108
+ resources,
3109
+ sourcePathsByResource,
3110
+ protectedSourcePaths,
3111
+ ...options.assets
3112
+ })
843
3113
  };
844
3114
  }
845
3115
  async function lint(options) {
846
- const dir = resolve(options.dir);
847
- const diagnostics = [];
848
- if (await findConfigFile(dir, WIKI_CONFIG_FILES)) diagnostics.push(...(await inspectWiki({
849
- dir,
850
- validation: options.validation
851
- })).diagnostics);
852
- if (await findConfigFile(dir, COLLECTION_CONFIG_FILES)) diagnostics.push(...(await inspectGuides({
853
- dir,
854
- validation: options.validation
855
- })).diagnostics);
856
- return { diagnostics };
857
- }
858
- //#endregion
859
- //#region src/validate.ts
860
- const ajv = new Ajv2020({
861
- strict: true,
862
- discriminator: true
863
- });
864
- addFormats(ajv);
865
- const validators = {
866
- Asset: ajv.compile(assetSchema),
867
- Guide: ajv.compile(guideSchema),
868
- Wiki: ajv.compile(wikiSchema),
869
- WikiPage: ajv.compile(wikiPageSchema)
870
- };
871
- function isRecord(value) {
872
- return typeof value === "object" && value !== null;
873
- }
874
- function getResourceLabel(resource) {
875
- return `${typeof resource.type === "string" ? resource.type : "unknown"}/${typeof resource.name === "string" ? resource.name : "unknown"}`;
876
- }
877
- /** Validate the resources produced by `compile` and the CLI export. */
878
- function validateResources(resources) {
879
- const errors = [];
880
- for (const resource of resources) {
881
- if (!isRecord(resource)) {
882
- errors.push({
883
- resource: "unknown/unknown",
884
- path: "/",
885
- message: "Resource must be an object"
886
- });
887
- continue;
888
- }
889
- if (typeof resource.type !== "string") {
890
- errors.push({
891
- resource: getResourceLabel(resource),
892
- path: "/type",
893
- message: "Resource type must be a string"
894
- });
895
- continue;
896
- }
897
- const validate = validators[resource.type];
898
- if (!validate) {
899
- errors.push({
900
- resource: getResourceLabel(resource),
901
- path: "/type",
902
- message: `Unsupported resource type: ${resource.type}`
903
- });
904
- continue;
905
- }
906
- if (!validate(resource)) for (const err of validate.errors ?? []) errors.push({
907
- resource: getResourceLabel(resource),
908
- path: err.instancePath || "/",
909
- message: err.message ?? "Unknown error"
910
- });
3116
+ try {
3117
+ return { diagnostics: (await compile(options)).diagnostics };
3118
+ } catch (error) {
3119
+ if (error instanceof CompileError) return { diagnostics: error.diagnostics };
3120
+ throw error;
911
3121
  }
912
- return {
913
- valid: errors.length === 0,
914
- errors
915
- };
916
3122
  }
917
3123
  //#endregion
918
3124
  //#region src/watch.ts
@@ -923,22 +3129,33 @@ async function watch(options) {
923
3129
  const dir = resolve(options.dir);
924
3130
  const emitter = new EventEmitter();
925
3131
  const resources = /* @__PURE__ */ new Map();
926
- 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;
927
3138
  for (const resource of initial.resources) resources.set(resourceKey(resource), resource);
3139
+ for (const payload of initial.payloads) payloads.set(payload.path, payload);
928
3140
  let debounceTimer = null;
929
3141
  async function recompile() {
930
3142
  try {
931
- const result = await compile({ dir });
3143
+ const result = await compile({
3144
+ dir,
3145
+ assets: options.assets
3146
+ });
932
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);
933
3152
  for (const resource of result.resources) {
934
3153
  const key = resourceKey(resource);
935
3154
  newKeys.add(key);
936
3155
  const existing = resources.get(key);
937
- const serialized = JSON.stringify(resource);
938
- if (!existing || JSON.stringify(existing) !== serialized) {
939
- resources.set(key, resource);
940
- emitter.emit("update", key, resource);
941
- }
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);
942
3159
  }
943
3160
  for (const key of resources.keys()) if (!newKeys.has(key)) resources.delete(key);
944
3161
  } catch (error) {
@@ -951,7 +3168,7 @@ async function watch(options) {
951
3168
  }
952
3169
  const fsWatcher = watch$1(dir, {
953
3170
  ignoreInitial: true,
954
- ignored: [/(^|[/\\])\../, "**/node_modules/**"]
3171
+ ignored: [/(^|[/\\])(?:\.git|\.topik(?:-compilation-(?:generation|prior)-[^/\\]+)?)(?:[/\\]|$)/, "**/node_modules/**"]
955
3172
  });
956
3173
  fsWatcher.on("add", scheduleRecompile);
957
3174
  fsWatcher.on("change", scheduleRecompile);
@@ -961,6 +3178,7 @@ async function watch(options) {
961
3178
  });
962
3179
  const watcher = {
963
3180
  resources,
3181
+ payloads,
964
3182
  on(event, listener) {
965
3183
  emitter.on(event, listener);
966
3184
  return watcher;
@@ -977,4 +3195,4 @@ async function watch(options) {
977
3195
  return watcher;
978
3196
  }
979
3197
  //#endregion
980
- export { CompileError, compile, compileGuides, compileWiki, isErrorDiagnostic, lint, pagePathToName, 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 };