@athrio/loom 0.0.2 → 0.0.4

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.
@@ -113806,16 +113806,16 @@ function parseDotEnvContents(lines) {
113806
113806
  }
113807
113807
  function dotEnvExpand(parsed) {
113808
113808
  const newParsed = {};
113809
- for (const configKey in parsed) newParsed[configKey] = interpolate(parsed[configKey], parsed).replace(/\\\$/g, "$");
113809
+ for (const configKey in parsed) newParsed[configKey] = interpolate$1(parsed[configKey], parsed).replace(/\\\$/g, "$");
113810
113810
  return newParsed;
113811
113811
  }
113812
- function interpolate(envValue, parsed) {
113812
+ function interpolate$1(envValue, parsed) {
113813
113813
  const lastUnescapedDollarSignIndex = searchLast(envValue, /(?!(?<=\\))\$/g);
113814
113814
  if (lastUnescapedDollarSignIndex === -1) return envValue;
113815
113815
  const match = envValue.slice(lastUnescapedDollarSignIndex).match(/((?!(?<=\\))\${?([\w]+)(?::-([^}\\]*))?}?)/);
113816
113816
  if (match !== null) {
113817
113817
  const [_, group, variableName, defaultValue] = match;
113818
- return interpolate(envValue.replace(group, defaultValue || parsed[variableName] || ""), parsed);
113818
+ return interpolate$1(envValue.replace(group, defaultValue || parsed[variableName] || ""), parsed);
113819
113819
  }
113820
113820
  return envValue;
113821
113821
  }
@@ -148029,7 +148029,7 @@ var makeTocWeft = (lineText, line, range) => TocWeftSchema.make({
148029
148029
  });
148030
148030
  //#endregion
148031
148031
  //#region ../loom-lang/src/ast/LoomFault.ts
148032
- var { UnclosedDelimiter, EmptyLabel, MalformedLabel, MissingWarpValue, UnresolvedAnchor, AmbiguousAnchor, CrossLanguageAnchor, UnresolvedTocEntry } = taggedEnum();
148032
+ var { UnclosedDelimiter, EmptyLabel, MalformedLabel, MissingWarpValue, UnknownVariable, UnresolvedAnchor, AmbiguousAnchor, CrossLanguageAnchor, UnresolvedTocEntry } = taggedEnum();
148033
148033
  var error = (message) => ({
148034
148034
  severity: "error",
148035
148035
  message
@@ -148049,7 +148049,7 @@ var rule = {
148049
148049
  warpName: "must be a TypeScript identifier",
148050
148050
  anchorName: "may not contain `]`"
148051
148051
  };
148052
- var describe = (fault) => value$1(fault).pipe(tag("UnclosedDelimiter", ({ expected }) => error(`expected closing \`${expected}\``)), tag("EmptyLabel", ({ construct }) => error(`${cap(noun[construct])} cannot be empty.`)), tag("MalformedLabel", ({ construct, value }) => error(`${cap(noun[construct])} ${rule[construct]}; got \`${value}\`.`)), tag("MissingWarpValue", ({ name }) => error(`Warp \`${name}\` has no value; a Warp binds a value, as in \`${name} = …\`.`)), tag("UnresolvedAnchor", ({ name }) => error(`Unresolved anchor: no section named \`${name}\`.`)), tag("AmbiguousAnchor", ({ name, count }) => error(`Ambiguous anchor: ${count} sections are named \`${name}\`. A name anchor resolves one local section; rename to disambiguate.`)), tag("CrossLanguageAnchor", ({ name, host, found }) => error(`Cross-language transclusion: \`${name}\` is ${found}, but this section composes ${host}. A section composes one language.`)), tag("UnresolvedTocEntry", ({ title }) => error(`Unresolved contents entry: no chapter titled \`${title}\`.`)), exhaustive);
148052
+ var describe = (fault) => value$1(fault).pipe(tag("UnclosedDelimiter", ({ expected }) => error(`expected closing \`${expected}\``)), tag("EmptyLabel", ({ construct }) => error(`${cap(noun[construct])} cannot be empty.`)), tag("MalformedLabel", ({ construct, value }) => error(`${cap(noun[construct])} ${rule[construct]}; got \`${value}\`.`)), tag("MissingWarpValue", ({ name }) => error(`Warp \`${name}\` has no value; a Warp binds a value, as in \`${name} = …\`.`)), tag("UnknownVariable", ({ name }) => error(`Unknown variable: no workspace variable named \`${name}\`. Declare it under \`variables:\` in the configuration.`)), tag("UnresolvedAnchor", ({ name }) => error(`Unresolved anchor: no section named \`${name}\`.`)), tag("AmbiguousAnchor", ({ name, count }) => error(`Ambiguous anchor: ${count} sections are named \`${name}\`. A name anchor resolves one local section; rename to disambiguate.`)), tag("CrossLanguageAnchor", ({ name, host, found }) => error(`Cross-language transclusion: \`${name}\` is ${found}, but this section composes ${host}. A section composes one language.`)), tag("UnresolvedTocEntry", ({ title }) => error(`Unresolved contents entry: no chapter titled \`${title}\`.`)), exhaustive);
148053
148053
  var statusOf = (severity) => severity === "info" ? "ok" : severity;
148054
148054
  var faulty = (fault, position) => {
148055
148055
  const note = describe(fault);
@@ -148088,6 +148088,12 @@ var tokeniseFrontmatter = (weft) => {
148088
148088
  if (colon < 0) return bareFrontmatter(weft);
148089
148089
  return buildFrontmatterField(weft, line, trimSpan(lineText.slice(0, colon), base), trimSpan(lineText.slice(colon + 1), base + colon + 1));
148090
148090
  };
148091
+ var frontmatterFields = /* @__PURE__ */ new Set([
148092
+ "Package",
148093
+ "Language",
148094
+ "Status",
148095
+ "Priority"
148096
+ ]);
148091
148097
  var buildFrontmatterField = (weft, line, key, value) => {
148092
148098
  if (key.value.startsWith("Part ") && key.value.includes(", Chapter ")) return belongingField(weft, line, key, value);
148093
148099
  if (key.value.startsWith("Part ")) return FrontmatterWeftSchema.make({
@@ -148097,6 +148103,12 @@ var buildFrontmatterField = (weft, line, key, value) => {
148097
148103
  part: ordinalToken(FrontmatterPartTokenSchema, line, key, "Part "),
148098
148104
  partName: fieldToken(FrontmatterPartNameTokenSchema, line, value)
148099
148105
  });
148106
+ if (key.value === "Part") return FrontmatterWeftSchema.make({
148107
+ position: weft.position,
148108
+ source: weft.source,
148109
+ health: okHealth,
148110
+ partName: fieldToken(FrontmatterPartNameTokenSchema, line, value)
148111
+ });
148100
148112
  if (key.value.startsWith("Chapter ")) return FrontmatterWeftSchema.make({
148101
148113
  position: weft.position,
148102
148114
  source: weft.source,
@@ -148104,7 +148116,13 @@ var buildFrontmatterField = (weft, line, key, value) => {
148104
148116
  chapter: ordinalToken(FrontmatterChapterTokenSchema, line, key, "Chapter "),
148105
148117
  title: fieldToken(FrontmatterTitleTokenSchema, line, value)
148106
148118
  });
148107
- if (key.value === "Package" || key.value === "Language") return FrontmatterWeftSchema.make({
148119
+ if (key.value === "Chapter") return FrontmatterWeftSchema.make({
148120
+ position: weft.position,
148121
+ source: weft.source,
148122
+ health: okHealth,
148123
+ title: fieldToken(FrontmatterTitleTokenSchema, line, value)
148124
+ });
148125
+ if (frontmatterFields.has(key.value)) return FrontmatterWeftSchema.make({
148108
148126
  position: weft.position,
148109
148127
  source: weft.source,
148110
148128
  health: okHealth,
@@ -148926,7 +148944,9 @@ var LoomFrontmatterSchema = loomNode("LoomFrontmatter", {
148926
148944
  chapter: optional(FrontmatterChapterTokenSchema),
148927
148945
  title: optional(FrontmatterTitleTokenSchema),
148928
148946
  package: optional(FrontmatterValueTokenSchema),
148929
- language: optional(FrontmatterValueTokenSchema)
148947
+ language: optional(FrontmatterValueTokenSchema),
148948
+ status: optional(FrontmatterValueTokenSchema),
148949
+ priority: optional(FrontmatterValueTokenSchema)
148930
148950
  });
148931
148951
  var LoomDocumentSchema = loomNode("LoomDocument", {
148932
148952
  frontmatter: optional(LoomFrontmatterSchema),
@@ -149004,14 +149024,14 @@ var spanFrom = (headingPos, ...groups) => {
149004
149024
  };
149005
149025
  var makeFrontmatter = (wefts) => {
149006
149026
  if (wefts.length === 0) return none$1();
149007
- const partFields = match$13(fromNullishOr$4(wefts.find((w) => w.part !== void 0)), {
149027
+ const partFields = match$13(fromNullishOr$4(wefts.find((w) => w.part !== void 0 || w.partName !== void 0)), {
149008
149028
  onNone: () => ({}),
149009
149029
  onSome: (w) => ({
149010
149030
  part: w.part,
149011
149031
  partName: w.partName
149012
149032
  })
149013
149033
  });
149014
- const chapterFields = match$13(fromNullishOr$4(wefts.find((w) => w.chapter !== void 0)), {
149034
+ const chapterFields = match$13(fromNullishOr$4(wefts.find((w) => w.chapter !== void 0 || w.title !== void 0)), {
149015
149035
  onNone: () => ({}),
149016
149036
  onSome: (w) => ({
149017
149037
  chapter: w.chapter,
@@ -149028,7 +149048,9 @@ var makeFrontmatter = (wefts) => {
149028
149048
  ...partFields,
149029
149049
  ...chapterFields,
149030
149050
  package: getOrUndefined$2(frontmatterField(wefts, "Package")),
149031
- language: getOrUndefined$2(frontmatterField(wefts, "Language"))
149051
+ language: getOrUndefined$2(frontmatterField(wefts, "Language")),
149052
+ status: getOrUndefined$2(frontmatterField(wefts, "Status")),
149053
+ priority: getOrUndefined$2(frontmatterField(wefts, "Priority"))
149032
149054
  }));
149033
149055
  };
149034
149056
  var frontmatterField = (wefts, key) => pipe(fromNullishOr$4(wefts.find((w) => w.key !== void 0 && w.key.value === key)), flatMapNullishOr$2((w) => w.value));
@@ -149138,17 +149160,17 @@ var ProductSchema = Struct({
149138
149160
  });
149139
149161
  //#endregion
149140
149162
  //#region ../loom-lang/src/ast/ProductBuilder.ts
149141
- var ProductBuilder = class extends Service$1()("ProductBuilder", { make: succeed$6({ build: (doc, path, packageLanguage) => sync$3(() => buildProduct(doc, path, packageLanguage)) }) }) {
149163
+ var ProductBuilder = class extends Service$1()("ProductBuilder", { make: succeed$6({ build: (doc, path, packageLanguage, variables = {}) => sync$3(() => buildProduct(doc, path, packageLanguage, variables)) }) }) {
149142
149164
  static {
149143
149165
  this.layer = effect(this, this.make);
149144
149166
  }
149145
149167
  };
149146
- var buildProduct = (doc, modulePath, packageLanguage) => {
149168
+ var buildProduct = (doc, modulePath, packageLanguage, variables = {}) => {
149147
149169
  const fm = fromNullishOr$4(doc.frontmatter);
149148
149170
  const pkg = pipe(fm, flatMapNullishOr$2((f) => f.package), map$29((p) => p.value));
149149
149171
  const lang = pipe(frontmatterLanguage(fm), orElse$6(() => fromNullishOr$4(packageLanguage)), getOrElse$2(() => "plaintext"));
149150
149172
  const index = nameIndex(doc.sections, lang, pkg);
149151
- return pipe(doc.sections, map$24(buildSection(index, lang, modulePath, pkg)), reduce$11(emptyProduct, appendSection));
149173
+ return pipe(doc.sections, map$24(buildSection(index, lang, modulePath, pkg, variables)), reduce$11(emptyProduct, appendSection));
149152
149174
  };
149153
149175
  var emptyProduct = ProductSchema.make({
149154
149176
  code: [],
@@ -149158,7 +149180,6 @@ var appendSection = (product, built) => ProductSchema.make({
149158
149180
  code: [...product.code, built.code],
149159
149181
  files: [...product.files, ...toArray$3(built.file)]
149160
149182
  });
149161
- var reservedLanguage = { config: "yaml" };
149162
149183
  var specifierLanguage = (label, sink, defaultLang, pkg) => {
149163
149184
  const id = label.toLowerCase();
149164
149185
  if (id === "tangle") return match$13(pkg, {
@@ -149166,7 +149187,7 @@ var specifierLanguage = (label, sink, defaultLang, pkg) => {
149166
149187
  onSome: (p) => extensionLanguage(tangleFilePath(p, sink))
149167
149188
  });
149168
149189
  if (id === "toc") return "prose";
149169
- return reservedLanguage[id] ?? id;
149190
+ return id;
149170
149191
  };
149171
149192
  var sectionLanguage$1 = (section, defaultLang, pkg) => {
149172
149193
  const { specifier, sink } = section.heading;
@@ -149199,12 +149220,24 @@ var decodeLiteral = (text) => {
149199
149220
  const s = text.trim();
149200
149221
  return /^"(?:\\.|[^"\\])*"$/.test(s) || /^'(?:\\.|[^'\\])*'$/.test(s) ? s.slice(1, -1).replace(/\\(.)/g, (_, c) => c) : text;
149201
149222
  };
149202
- var valueWarps = (warps) => new Map(getSomes(map$24(warps, (w) => w.default ? some$13([w.name.value, {
149203
- text: decodeLiteral(w.default.value),
149204
- pos: w.default.position
149205
- }]) : none$1())));
149206
- var buildSection = (index, defaultLang, modulePath, pkg) => (section) => {
149207
- const code = buildCode(section, valueWarps(section.preamble.flatMap((w) => w.warps)), index, {
149223
+ var variablePattern = /[$][{]([A-Za-z_][A-Za-z0-9_]*)[}]/g;
149224
+ var interpolate = (text, variables) => ({
149225
+ text: text.replace(variablePattern, (whole, name) => name in variables ? variables[name] : whole),
149226
+ missing: pipe(fromIterable$15(text.matchAll(variablePattern)), map$24((match) => match[1]), filter$15((name) => !(name in variables)), head$2)
149227
+ });
149228
+ var valueWarps = (warps, variables) => new Map(getSomes(map$24(warps, (w) => map$29(fromNullishOr$4(w.default), (def) => {
149229
+ const { text, missing } = interpolate(decodeLiteral(def.value), variables);
149230
+ return [w.name.value, {
149231
+ text,
149232
+ pos: def.position,
149233
+ health: match$13(missing, {
149234
+ onNone: () => okHealth,
149235
+ onSome: (name) => faulty(UnknownVariable({ name }), def.position)
149236
+ })
149237
+ }];
149238
+ }))));
149239
+ var buildSection = (index, defaultLang, modulePath, pkg, variables) => (section) => {
149240
+ const code = buildCode(section, valueWarps(section.preamble.flatMap((w) => w.warps), variables), index, {
149208
149241
  path: modulePath,
149209
149242
  name: sectionNameOf(section)
149210
149243
  }, sectionLanguage$1(section, defaultLang, pkg));
@@ -149280,7 +149313,8 @@ var bindAnchor = (index, values, host, modulePath) => (anchor) => {
149280
149313
  const value = values.get(name);
149281
149314
  if (value !== void 0) return FragmentSchema.make({
149282
149315
  text: value.text,
149283
- origin: at
149316
+ origin: at,
149317
+ health: value.health
149284
149318
  });
149285
149319
  return matchLeft(index.get(name) ?? [], {
149286
149320
  onEmpty: () => blankFragment(anchor.position, unresolved(name, at)),
@@ -149347,13 +149381,13 @@ var LoomCorpusAstBuilder = class extends Service$1()("LoomCorpusAstBuilder", { m
149347
149381
  text: "",
149348
149382
  doc: emptyDocument("", err.message)
149349
149383
  })));
149350
- const build = (source, path, delims = defaultAnchorDelims, primaryLanguage) => gen$1(function* () {
149384
+ const build = (source, path, delims = defaultAnchorDelims, primaryLanguage, variables = {}) => gen$1(function* () {
149351
149385
  const { text, doc } = yield* parsed(source, path, delims);
149352
149386
  return {
149353
149387
  path,
149354
149388
  text,
149355
149389
  doc,
149356
- product: yield* products.build(doc, path, primaryLanguage)
149390
+ product: yield* products.build(doc, path, primaryLanguage, variables)
149357
149391
  };
149358
149392
  });
149359
149393
  return { build };
@@ -149900,25 +149934,25 @@ var AnchorSchema = Struct({
149900
149934
  });
149901
149935
  var LanguageSchema = Struct({ service: optional(String$1) });
149902
149936
  var SettingsSchema = Record$1(String$1, Record$1(String$1, Unknown));
149903
- decodeUnknownOption(Struct({
149904
- languages: Record$1(String$1, LanguageSchema),
149905
- corpus: optional(String$1),
149906
- primary: optional(String$1),
149907
- anchor: optional(AnchorSchema),
149908
- settings: optional(SettingsSchema)
149909
- }));
149937
+ var VariablesSchema = Record$1(String$1, Union([
149938
+ String$1,
149939
+ Number$1,
149940
+ Boolean$2
149941
+ ]));
149910
149942
  var WorkspaceManifestSchema = Struct({
149911
149943
  languages: optional(Record$1(String$1, LanguageSchema)),
149912
149944
  corpus: optional(String$1),
149913
149945
  primary: optional(String$1),
149914
149946
  anchor: optional(AnchorSchema),
149915
- settings: optional(SettingsSchema)
149947
+ settings: optional(SettingsSchema),
149948
+ variables: optional(VariablesSchema)
149916
149949
  });
149917
149950
  var empty = {
149918
149951
  anchor: void 0,
149919
149952
  primary: void 0,
149920
149953
  languages: [],
149921
149954
  settings: {},
149955
+ variables: {},
149922
149956
  services: {},
149923
149957
  packageRoot: void 0,
149924
149958
  workspaceRoot: void 0,
@@ -149942,6 +149976,7 @@ var readManifest = (file) => {
149942
149976
  };
149943
149977
  var serviceFor = (id, language) => language.service ?? `@athrio/loom-service-${id}`;
149944
149978
  var servicesOf = (languages) => Object.fromEntries(Object.entries(languages ?? {}).map(([id, language]) => [id, serviceFor(id, language)]));
149979
+ var variablesOf = (variables) => Object.fromEntries(Object.entries(variables ?? {}).map(([name, value]) => [name, String(value)]));
149945
149980
  var containerRoot = (dir, container, workspace) => {
149946
149981
  if (basename(dir) === container) return dirname(dir);
149947
149982
  const parent = dirname(dir);
@@ -149962,6 +149997,7 @@ var resolveFromManifest = (manifest, workspace, fromPath) => {
149962
149997
  primary: manifest.primary,
149963
149998
  languages: Object.keys(manifest.languages ?? {}),
149964
149999
  settings: manifest.settings ?? {},
150000
+ variables: variablesOf(manifest.variables),
149965
150001
  services: servicesOf(manifest.languages),
149966
150002
  packageRoot: containerRoot(dirname(fromPath), container, workspace),
149967
150003
  workspaceRoot: workspace,
@@ -150001,6 +150037,7 @@ var PackageConfig = class extends Service$1()("PackageConfig", { make: gen$1(fun
150001
150037
  return { resolve: (path) => config.resolve(path).pipe(map$19((c) => ({
150002
150038
  delims: anchorDelimsOf(c.anchor),
150003
150039
  primaryLanguage: c.primary,
150040
+ variables: c.variables,
150004
150041
  packageRoot: c.packageRoot,
150005
150042
  workspaceRoot: c.workspaceRoot,
150006
150043
  corpusDir: c.corpusDir
@@ -150063,7 +150100,9 @@ var WovenPageSchema = Struct({
150063
150100
  var WovenNavEntrySchema = Struct({
150064
150101
  number: String$1,
150065
150102
  title: String$1,
150066
- slug: String$1
150103
+ slug: String$1,
150104
+ status: optional(String$1),
150105
+ priority: optional(String$1)
150067
150106
  });
150068
150107
  var WovenPartSchema = Struct({
150069
150108
  number: String$1,
@@ -150072,9 +150111,160 @@ var WovenPartSchema = Struct({
150072
150111
  });
150073
150112
  var WovenCorpusSchema = Struct({
150074
150113
  nav: ArraySchema(WovenPartSchema),
150114
+ loose: ArraySchema(WovenNavEntrySchema),
150075
150115
  pages: ArraySchema(WovenPageSchema)
150076
150116
  });
150077
150117
  //#endregion
150118
+ //#region ../loom-lang/src/weave/TableOfContentsBuilder.ts
150119
+ var TocChapterSchema = Struct({
150120
+ number: String$1,
150121
+ title: String$1,
150122
+ status: optional(String$1),
150123
+ priority: optional(String$1),
150124
+ path: String$1
150125
+ });
150126
+ var TocPartSchema = Struct({
150127
+ number: String$1,
150128
+ name: String$1,
150129
+ description: String$1,
150130
+ chapters: ArraySchema(TocChapterSchema)
150131
+ });
150132
+ var TableOfContentsSchema = Struct({
150133
+ parts: ArraySchema(TocPartSchema),
150134
+ loose: ArraySchema(TocChapterSchema)
150135
+ });
150136
+ var TableOfContentsBuilder = class extends Service$1()("TableOfContentsBuilder", { make: succeed$6({ build: (corpus) => sync$3(() => buildContents(corpus)) }) }) {
150137
+ static {
150138
+ this.layer = effect(this, this.make);
150139
+ }
150140
+ };
150141
+ var buildContents = (corpus) => {
150142
+ const modules = fromIterable$15(corpus.modules.values());
150143
+ const descriptions = descriptionsFrom(modules);
150144
+ const chapters = getSomes(map$24(modules, chapterDeclOf));
150145
+ const parts = pipe(dedupeParts(getSomes(map$24(modules, partDeclOf))), sort$1(byPart), map$24((decl) => TocPartSchema.make({
150146
+ number: decl.number,
150147
+ name: decl.name,
150148
+ description: getOrElse$2(fromNullishOr$4(descriptions.get(identityOf(decl))), () => ""),
150149
+ chapters: chaptersUnder(chapters, identityOf(decl))
150150
+ })));
150151
+ const loose = pipe(filter$15(chapters, (chapter) => chapter.partId === void 0), map$24(toTocChapter));
150152
+ return TableOfContentsSchema.make({
150153
+ parts,
150154
+ loose
150155
+ });
150156
+ };
150157
+ var partDeclOf = (module) => pipe(fromNullishOr$4(module.doc.frontmatter), flatMap$13((frontmatter) => map$29(fromNullishOr$4(frontmatter.partName), (partName) => ({
150158
+ number: frontmatter.part?.value ?? "",
150159
+ name: partName.value
150160
+ }))));
150161
+ var chapterDeclOf = (module) => pipe(fromNullishOr$4(module.doc.frontmatter), flatMap$13((frontmatter) => map$29(fromNullishOr$4(frontmatter.title), (title) => ({
150162
+ partId: frontmatter.part?.value ?? frontmatter.partName?.value,
150163
+ number: frontmatter.chapter?.value ?? "",
150164
+ title: title.value,
150165
+ status: frontmatter.status?.value,
150166
+ priority: frontmatter.priority?.value,
150167
+ path: module.path
150168
+ }))));
150169
+ var toTocChapter = (chapter) => TocChapterSchema.make({
150170
+ number: chapter.number,
150171
+ title: chapter.title,
150172
+ status: chapter.status,
150173
+ priority: chapter.priority,
150174
+ path: chapter.path
150175
+ });
150176
+ var romanUnits = {
150177
+ I: 1,
150178
+ V: 5,
150179
+ X: 10,
150180
+ L: 50,
150181
+ C: 100,
150182
+ D: 500,
150183
+ M: 1e3
150184
+ };
150185
+ var romanValue = (roman) => {
150186
+ const units = map$24(roman.toUpperCase().split(""), (letter) => romanUnits[letter] ?? 0);
150187
+ return pipe(units, map$24((unit, index) => unit < (units[index + 1] ?? 0) ? -unit : unit), reduce$11(0, (sum, unit) => sum + unit));
150188
+ };
150189
+ var identityOf = (part) => part.number === "" ? part.name : part.number;
150190
+ var dedupeParts = (decls) => reduce$11(decls, [], (kept, decl) => some$10(kept, (seen) => identityOf(seen) === identityOf(decl)) ? kept : [...kept, decl]);
150191
+ var byPart = combineAll([
150192
+ mapInput$4(Number$5, (part) => part.number === "" ? 1 : 0),
150193
+ mapInput$4(Number$5, (part) => part.number === "" ? 0 : romanValue(part.number)),
150194
+ mapInput$4(String$5, (part) => part.name)
150195
+ ]);
150196
+ var byChapter = combineAll([
150197
+ mapInput$4(Number$5, (chapter) => chapter.number === "" ? 1 : 0),
150198
+ mapInput$4(Number$5, (chapter) => chapter.number === "" ? 0 : Number(chapter.number)),
150199
+ mapInput$4(String$5, (chapter) => chapter.title)
150200
+ ]);
150201
+ var chaptersUnder = (chapters, identity) => pipe(filter$15(chapters, (chapter) => chapter.partId === identity), sort$1(byChapter), map$24(toTocChapter));
150202
+ var partLabel = /^Part\s+(\S+)\s*[—–-]\s*/;
150203
+ var bulletMarker = /^\s*[-*]\s+\S/;
150204
+ var partNumberOf = (label) => match$13(fromNullishOr$4(partLabel.exec(label)), {
150205
+ onNone: () => label,
150206
+ onSome: (match) => match[1] ?? label
150207
+ });
150208
+ var bookEntries = (modules) => pipe(findFirst$6(modules, (module) => some$10(module.doc.sections, (section) => section.entries !== void 0)), map$29((module) => flatMap$10(module.doc.sections, (section) => section.entries ?? [])), getOrElse$2(() => []));
150209
+ var lineOf = (entry) => match$13(fromNullishOr$4(entry.part), {
150210
+ onSome: (part) => ({
150211
+ kind: "part",
150212
+ identity: partNumberOf(part.value)
150213
+ }),
150214
+ onNone: () => entry.chapter !== void 0 || bulletMarker.test(entry.source) ? { kind: "chapter" } : { kind: "prose" }
150215
+ });
150216
+ var flushed = (state) => {
150217
+ const description = state.lines.join("\n").trim();
150218
+ return match$13(state.part, {
150219
+ onNone: () => state.found,
150220
+ onSome: (part) => description.length === 0 ? state.found : new Map(state.found).set(part, description)
150221
+ });
150222
+ };
150223
+ var descriptionsFrom = (modules) => {
150224
+ return flushed(reduce$11(bookEntries(modules), {
150225
+ part: none$1(),
150226
+ open: false,
150227
+ lines: [],
150228
+ found: /* @__PURE__ */ new Map()
150229
+ }, (state, entry) => value$1(lineOf(entry)).pipe(when({ kind: "part" }, ({ identity }) => ({
150230
+ part: some$13(identity),
150231
+ open: true,
150232
+ lines: [],
150233
+ found: flushed(state)
150234
+ })), when({ kind: "chapter" }, () => ({
150235
+ ...state,
150236
+ open: false,
150237
+ lines: [],
150238
+ found: flushed(state)
150239
+ })), orElse(() => state.open ? {
150240
+ ...state,
150241
+ lines: [...state.lines, entry.source]
150242
+ } : state))));
150243
+ };
150244
+ var chapterLine = (chapter) => chapter.number === "" ? `- ${chapter.title}` : `${chapter.number}. ${chapter.title}`;
150245
+ var partBlock = (part) => {
150246
+ const heading = part.number === "" ? `### ${part.name}` : `### Part ${part.number} — ${part.name}`;
150247
+ const list = pipe(part.chapters, map$24(chapterLine), join$6("\n"));
150248
+ const description = part.description.trim();
150249
+ return description.length === 0 ? `${heading}\n\n${list}` : `${heading}\n\n${description}\n\n${list}`;
150250
+ };
150251
+ var renderContents = (toc) => pipe(toc.parts, map$24(partBlock), join$6("\n\n"));
150252
+ var tocSectionOf = (module) => findFirst$6(module.doc.sections, (section) => section.entries !== void 0);
150253
+ var spliceToc = (module, section, toc) => {
150254
+ const heading = module.text.slice(section.heading.position.start.offset, section.heading.position.end.offset).trimEnd();
150255
+ const before = module.text.slice(0, section.position.start.offset);
150256
+ const tail = module.text.slice(section.position.end.offset).replace(/^\n+/, "");
150257
+ const rebuilt = `${before}${heading}\n\n${renderContents(toc)}\n`;
150258
+ return tail.length === 0 ? rebuilt : `${rebuilt}\n${tail}`;
150259
+ };
150260
+ var rewriteBook = (corpus, path, toc) => match$13(fromNullishOr$4(corpus.modules.get(path)), {
150261
+ onNone: () => "",
150262
+ onSome: (module) => match$13(tocSectionOf(module), {
150263
+ onNone: () => module.text,
150264
+ onSome: (section) => spliceToc(module, section, toc)
150265
+ })
150266
+ });
150267
+ //#endregion
150078
150268
  //#region ../loom-lang/src/weave/WeaveBuilder.ts
150079
150269
  var WeaveBuilder = class extends Service$1()("WeaveBuilder", { make: succeed$6({ build: (corpus) => sync$3(() => buildCorpus(corpus)) }) }) {
150080
150270
  static {
@@ -150084,11 +150274,14 @@ var WeaveBuilder = class extends Service$1()("WeaveBuilder", { make: succeed$6({
150084
150274
  var buildCorpus = (corpus) => {
150085
150275
  const modules = fromIterable$15(corpus.modules.values());
150086
150276
  const indexes = titleIndexes(corpus.modules);
150087
- const nav = buildNav(modules);
150277
+ const toc = buildContents(corpus);
150278
+ const nav = map$24(toc.parts, wovenPart);
150279
+ const loose = map$24(toc.loose, wovenEntry);
150088
150280
  const partOfSlug = partIndex(nav);
150089
150281
  const pages = getSomes(map$24(modules, (module) => buildPage(module, indexes, partOfSlug)));
150090
150282
  return WovenCorpusSchema.make({
150091
150283
  nav,
150284
+ loose,
150092
150285
  pages
150093
150286
  });
150094
150287
  };
@@ -150096,59 +150289,27 @@ var slugFor = (path) => pipe(path.replace(/\.loom$/, "").split("/"), takeRight$3
150096
150289
  var slugify = (text) => text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
150097
150290
  var isBlankSource = (source) => source.trim().length === 0;
150098
150291
  var sliceContent = (text, span) => text.slice(span.start, span.end).replace(/\s+$/, "");
150099
- var bookEntries = (modules) => pipe(findFirst$6(modules, (m) => some$10(m.doc.sections, (s) => s.entries !== void 0)), map$29((m) => flatMap$10(m.doc.sections, (s) => s.entries ?? [])), getOrElse$2(() => []));
150100
- var chapterSlugs = (modules) => pipe(modules, map$24((m) => map$29(fromNullishOr$4(m.doc.frontmatter?.title), (title) => [title.value, slugFor(m.path)])), getSomes, reduce$11(/* @__PURE__ */ new Map(), (index, [title, slug]) => index.has(title) ? index : new Map(index).set(title, slug)));
150101
- var classifyEntry = (entry) => pipe(fromNullishOr$4(entry.part), map$29((p) => ({
150102
- kind: "part",
150103
- label: p.value
150104
- })), orElse$6(() => map$29(all$5({
150105
- chapter: fromNullishOr$4(entry.chapter),
150106
- title: fromNullishOr$4(entry.title)
150107
- }), ({ chapter, title }) => ({
150108
- kind: "chapter",
150109
- number: chapter.value,
150110
- title: title.value
150111
- }))), getOrElse$2(() => ({ kind: "blank" })));
150112
- var partLabel = /^Part\s+(\S+)\s*[—–-]\s*(.+)$/;
150113
- var parsePart = (label) => match$13(fromNullishOr$4(partLabel.exec(label)), {
150114
- onNone: () => ({
150115
- number: label,
150116
- name: label
150117
- }),
150118
- onSome: (m) => ({
150119
- number: m[1] ?? label,
150120
- name: (m[2] ?? label).trim()
150121
- })
150292
+ var wovenEntry = (chapter) => WovenNavEntrySchema.make({
150293
+ number: chapter.number,
150294
+ title: chapter.title,
150295
+ slug: slugFor(chapter.path),
150296
+ status: chapter.status,
150297
+ priority: chapter.priority
150298
+ });
150299
+ var wovenPart = (part) => WovenPartSchema.make({
150300
+ number: part.number,
150301
+ name: part.name,
150302
+ chapters: map$24(part.chapters, wovenEntry)
150122
150303
  });
150123
- var appendChapter = (parts, chapter) => match$13(last$2(parts), {
150124
- onNone: () => parts,
150125
- onSome: (last) => [...dropRight$2(parts, 1), {
150126
- ...last,
150127
- chapters: [...last.chapters, chapter]
150128
- }]
150129
- });
150130
- var foldEntry = (slugs) => (parts, entry) => value$1(classifyEntry(entry)).pipe(when({ kind: "part" }, ({ label }) => {
150131
- const { number, name } = parsePart(label);
150132
- return [...parts, WovenPartSchema.make({
150133
- number,
150134
- name,
150135
- chapters: []
150136
- })];
150137
- }), when({ kind: "chapter" }, ({ number, title }) => match$13(fromNullishOr$4(slugs.get(title)), {
150138
- onNone: () => parts,
150139
- onSome: (slug) => appendChapter(parts, WovenNavEntrySchema.make({
150140
- number,
150141
- title,
150142
- slug
150143
- }))
150144
- })), orElse(() => parts));
150145
- var buildNav = (modules) => reduce$11(bookEntries(modules), [], foldEntry(chapterSlugs(modules)));
150146
150304
  var partIndex = (nav) => new Map(flatMap$10(nav, (part) => map$24(part.chapters, (chapter) => [chapter.slug, part.name])));
150147
- var buildPage = (module, indexes, partOfSlug) => map$29(fromNullishOr$4(module.doc.frontmatter?.title), (title) => {
150305
+ var isBookRoot = (module) => some$10(module.doc.sections, (section) => section.entries !== void 0);
150306
+ var firstHeadingTitle = (module) => pipe(findFirst$6(module.doc.sections, (section) => section.heading.title !== void 0), flatMap$13((section) => fromNullishOr$4(section.heading.title)), map$29((title) => title.source));
150307
+ var chapterTitleOf = (module) => orElse$6(map$29(fromNullishOr$4(module.doc.frontmatter?.title), (title) => title.value), () => firstHeadingTitle(module));
150308
+ var buildPage = (module, indexes, partOfSlug) => isBookRoot(module) ? none$1() : map$29(chapterTitleOf(module), (title) => {
150148
150309
  const slug = slugFor(module.path);
150149
150310
  return WovenPageSchema.make({
150150
150311
  slug,
150151
- title: title.value,
150312
+ title,
150152
150313
  part: partOfSlug.get(slug),
150153
150314
  blocks: buildBlocks(module, indexes)
150154
150315
  });
@@ -150379,7 +150540,8 @@ var LoomCompiler = class extends Service$1()("LoomCompiler", { make: gen$1(funct
150379
150540
  const memo = yield* LoomMemo;
150380
150541
  const config = yield* PackageConfig;
150381
150542
  const weaver = yield* WeaveBuilder;
150382
- const loadOne = (source, path) => config.resolve(path).pipe(flatMap$7(({ delims, primaryLanguage }) => memo.get(path, builder.build(source, path, delims, primaryLanguage))));
150543
+ const tocBuilder = yield* TableOfContentsBuilder;
150544
+ const loadOne = (source, path) => config.resolve(path).pipe(flatMap$7(({ delims, primaryLanguage, variables }) => memo.get(path, builder.build(source, path, delims, primaryLanguage, variables))));
150383
150545
  const listFrom = (list, entry) => config.resolve(entry).pipe(map$19((settings) => getOrElse$2(fromNullishOr$4(settings.corpusDir), () => dirname(entry))), flatMap$7(list), map$19((found) => dedupe$1([entry, ...found])));
150384
150546
  const corpusPaths = (source, entry) => match$13(source.list, {
150385
150547
  onNone: () => succeed$6([entry]),
@@ -150404,6 +150566,7 @@ var LoomCompiler = class extends Service$1()("LoomCompiler", { make: gen$1(funct
150404
150566
  heading: headingLocationOf(modules, module, file.code.origin.name)
150405
150567
  })))), { concurrency: "unbounded" }).pipe(map$19(flatten$5)))),
150406
150568
  weave: (path) => buildCorpus(documents, path).pipe(flatMap$7((modules) => weaver.build({ modules }))),
150569
+ contents: (path) => buildCorpus(documents, path).pipe(flatMap$7((modules) => tocBuilder.build({ modules }).pipe(map$19((toc) => rewriteBook({ modules }, path, toc))))),
150407
150570
  diagnose: (path) => buildCorpus(documents, path).pipe(map$19((modules) => pipe(fromNullishOr$4(modules.get(path)), match$13({
150408
150571
  onNone: () => [],
150409
150572
  onSome: (module) => [...moduleDiagnostics(module), ...flatMap$10(unresolvedTocEntriesIn({ modules }, module), (title) => faulty(UnresolvedTocEntry({ title: title.value }), title.position).diagnostics)]
@@ -150432,7 +150595,7 @@ var LoomCompiler = class extends Service$1()("LoomCompiler", { make: gen$1(funct
150432
150595
  };
150433
150596
  }) }) {
150434
150597
  static {
150435
- this.layer = effect(this, this.make).pipe(provide$5(mergeAll$2(LoomCorpusAstBuilder.layer, LoomMemo.layer, WeaveBuilder.layer)));
150598
+ this.layer = effect(this, this.make).pipe(provide$5(mergeAll$2(LoomCorpusAstBuilder.layer, LoomMemo.layer, WeaveBuilder.layer, TableOfContentsBuilder.layer)));
150436
150599
  }
150437
150600
  };
150438
150601
  //#endregion
@@ -150490,4 +150653,4 @@ var removeService = (id, dir) => sync$3(() => rmSync(join(storeDir(dir), "node_m
150490
150653
  //#endregion
150491
150654
  export { get$16 as $, filter$15 as $n, ignore$3 as $t, Boolean$1 as A, mergeAll$2 as An, identity$1 as Ar, makeUnsafe$12 as At, make$33 as B, fail$11 as Bn, catchTag$2 as Bt, errorReported as C, try_ as Cn, format$5 as Cr, endUnsafe as Ct, make$30 as D, TaggedError as Dn, isNullish as Dr, offerUnsafe as Dt, Crypto as E, catchDone as En, isNotUndefined as Er, offer$2 as Et, FileDescriptor as F, MinimumLogLevel as Fn, as$1 as Ft, isStream as G, fail$12 as Gn, ensuring$3 as Gt, decodeText as H, Done as Hn, catch_$3 as Ht, FileSystem as I, _await$1 as In, asVoid as It, run$4 as J, empty$20 as Jn, fnUntraced as Jt, merge$1 as K, Reference as Kn, fail$8 as Kt, FileTypeId as L, doneUnsafe as Ln, callback$2 as Lt, TrueValues as M, provideMerge as Mn, pipeArguments as Mr, acquireRelease$1 as Mt, Path as N, succeed$8 as Nn, addFinalizer as Nt, error$1 as O, taggedEnum as On, isUndefined$1 as Or, take$7 as Ot, TypeId$33 as P, addFinalizer$1 as Pn, andThen as Pt, unwrap$1 as Q, dedupe$1 as Qn, gen$1 as Qt, Size as R, isDone$1 as Rn, catchCause$3 as Rt, errorExitCode as S, tryPromise as Sn, BaseProto as Sr, mapDone as St, make$22 as T, void_$1 as Tn, isNotNull as Tr, make$48 as Tt, empty$9 as U, UnknownError as Un, die$3 as Ut, callback as V, succeed$10 as Vn, catchTags$1 as Vt, fromChannel as W, done$2 as Wn, effectify as Wt, splitLines$1 as X, make$69 as Xn, forever$2 as Xt, runCollect as Y, getUnsafe$11 as Yn, forEach$8 as Yt, transduce as Z, Prototype as Zn, forkScoped as Zt, Terminal as _, succeed$6 as _n, map$29 as _r, tag$2 as _t, storeDir as a, match$3 as an, of$2 as ar, badArgument as at, Stdio as b, tap$2 as bn, some$13 as br, get$21 as bt, LoomCompiler as c, orElseSucceed$1 as cn, fail$14 as cr, Defect as ct, PackageConfig as d, provideService$3 as dn, fromNullishOr$4 as dr, String$1 as dt, interrupt$3 as en, fromIterable$15 as er, make$35 as et, LoomConfig as f, raceFirst as fn, fromUndefinedOr as fr, Struct as ft, QuitError as g, serviceOption$1 as gn, isSome as gr, optional as gt, dist_exports as h, scoped$2 as hn, isNone as hr, is$2 as ht, servicePackage as i, mapError$3 as in, match$11 as ir, BadArgument as it, FalseValues as j, provide$5 as jn, pipe as jr, nominal as jt, log as k, effect as kn, dual as kr, make$50 as kt, loomsUnder as l, provide$3 as ln, succeed$12 as lr, ErrorClass as lt, ReadError as m, runFork as mn, getOrUndefined$2 as mr, decodeUnknownEffect as mt, installedServices as n, logWarning as nn, groupBy$1 as nr, fromChannel$1 as nt, workspaceRoot as o, matchEffect as on, reduce$11 as or, systemError as ot, SemanticTokenSchema as p, result$3 as pn, getOrElse$2 as pr, Union as pt, mkString as q, Service$1 as qn, flatMap$7 as qt, removeService as r, map$19 as rn, map$24 as rr, isSink as rt, DocumentSource as s, orDie$2 as sn, sort$1 as sr, ArraySchema as st, addService as t, isEffect as tn, getSomes as tr, drain$1 as tt, stringSnapshot as u, provideContext$3 as un, flatMap$13 as ur, Literal as ut, isQuitError as v, suspend$4 as vn, match$13 as vr, toCodecStringTree as vt, makeRunMain as w, uninterruptible as wn, hasProperty as wr, failCauseUnsafe as wt, make$13 as x, timeoutOrElse$1 as xn, String$5 as xr, fromTransform$1 as xt, make$11 as y, sync$3 as yn, none$1 as yr, fromIterable$10 as yt, WatchBackend as z, makeUnsafe$16 as zn, catchFilter$2 as zt };
150492
150655
 
150493
- //# sourceMappingURL=LoomStore-DPhVqYEz.js.map
150656
+ //# sourceMappingURL=LoomStore-DMf2PkfP.js.map