@dogsbay/corpus 0.2.0-beta.132 → 0.2.0-beta.134

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -11,8 +11,65 @@ export interface Corpus {
11
11
  contains?(path: string, dir: string): boolean;
12
12
  }
13
13
  export type CorpusFiles = Map<string, string | Uint8Array>;
14
+ export interface MapCorpusOptions {
15
+ /**
16
+ * Every path of the repo (relative to the same root as `files`), when the host knows more
17
+ * files than it has fetched — the playground lists a GitHub tree and fetches as it goes.
18
+ * The corpus lists them all, so the import sees the repo as it is; reading one that `files`
19
+ * does not hold returns undefined and is recorded (`missing`).
20
+ */
21
+ paths?: Iterable<string>;
22
+ }
23
+ export interface MapCorpus extends Corpus {
24
+ /** Listed paths the import read but `files` did not hold, sorted: fetch them and run it again. */
25
+ missing(): string[];
26
+ }
14
27
  /**
15
28
  * A corpus over a map of files; bytes are read as UTF-8. A key that is not a plain path
16
29
  * (`../x`, `/etc/x`, `a/./b`, a backslash) is left out: the keys come from someone else's repo.
17
30
  */
18
- export declare function corpusFromMap(files: CorpusFiles): Corpus;
31
+ export declare function corpusFromMap(files: CorpusFiles, opts?: MapCorpusOptions): MapCorpus;
32
+ /**
33
+ * What a browser conversion still needs, when the host listed more paths than it fetched:
34
+ * `read` the import read (fetch them and convert again: the result changes), `copy` the
35
+ * output copies as they are (fetch them for the finished files; nothing is converted again).
36
+ * Both empty: the result is complete. Each round can find one more level of an include or
37
+ * `require` chain, so a host caps the rounds; a result with `read` not empty is not a
38
+ * conversion (its `files` may be empty: the import could not go on without them).
39
+ */
40
+ export interface Unfetched {
41
+ read: string[];
42
+ copy: string[];
43
+ /**
44
+ * `copy` with where each file goes in `files` (its output path), in the same order: the host
45
+ * adds the fetched bytes itself, without converting again.
46
+ */
47
+ copyTo: {
48
+ from: string;
49
+ to: string;
50
+ }[];
51
+ }
52
+ /** `copyTo`'s pairs sorted by source path, and `copy` (the sources) in the same order. */
53
+ export declare function pendingCopies(pairs: {
54
+ from: string;
55
+ to: string;
56
+ }[]): Pick<Unfetched, "copy" | "copyTo">;
57
+ /**
58
+ * The ledger message for a conversion that still needs files (undefined when none), with the
59
+ * error the import stopped on, when it stopped.
60
+ */
61
+ export declare function unfetchedMessage(read: string[], stoppedOn?: unknown): string | undefined;
62
+ /**
63
+ * Each written page's source file → its path in the project, from the pages in the order they
64
+ * were written: a later page at the same path replaces an earlier one, whose file then has no
65
+ * page (its notes would otherwise open another file's page).
66
+ */
67
+ export declare function pageSources(written: Iterable<{
68
+ file: string;
69
+ path: string;
70
+ }>): Map<string, string>;
71
+ /** The ledger, each entry about a written page's source file given its `page`. */
72
+ export declare function withPages<E extends {
73
+ file?: string;
74
+ page?: string;
75
+ }>(ledger: E[], pageOf: ReadonlyMap<string, string>): E[];
package/dist/index.js CHANGED
@@ -12,17 +12,60 @@ import { isPlainPath } from "@dogsbay/detect";
12
12
  * A corpus over a map of files; bytes are read as UTF-8. A key that is not a plain path
13
13
  * (`../x`, `/etc/x`, `a/./b`, a backslash) is left out: the keys come from someone else's repo.
14
14
  */
15
- export function corpusFromMap(files) {
15
+ export function corpusFromMap(files, opts = {}) {
16
16
  const decoder = new TextDecoder();
17
- const paths = [...files.keys()].filter(isPlainPath).sort();
17
+ const paths = [...new Set([...files.keys(), ...(opts.paths ?? [])])].filter(isPlainPath).sort();
18
18
  const listed = new Set(paths);
19
+ const missing = new Set();
19
20
  return {
20
21
  paths,
21
22
  text(path) {
22
23
  if (!listed.has(path))
23
24
  return undefined;
24
25
  const v = files.get(path);
25
- return v === undefined ? undefined : typeof v === "string" ? v : decoder.decode(v);
26
+ if (v === undefined) {
27
+ missing.add(path);
28
+ return undefined;
29
+ }
30
+ return typeof v === "string" ? v : decoder.decode(v);
26
31
  },
32
+ missing: () => [...missing].sort(),
27
33
  };
28
34
  }
35
+ /** `copyTo`'s pairs sorted by source path, and `copy` (the sources) in the same order. */
36
+ export function pendingCopies(pairs) {
37
+ const copyTo = [...pairs].sort((a, b) => (a.from < b.from ? -1 : a.from > b.from ? 1 : 0));
38
+ return { copy: copyTo.map((p) => p.from), copyTo };
39
+ }
40
+ /**
41
+ * The ledger message for a conversion that still needs files (undefined when none), with the
42
+ * error the import stopped on, when it stopped.
43
+ */
44
+ export function unfetchedMessage(read, stoppedOn) {
45
+ if (!read.length)
46
+ return undefined;
47
+ const names = `${read.slice(0, 3).join(", ")}${read.length > 3 ? ", …" : ""}`;
48
+ const stopped = stoppedOn instanceof Error ? ` It stopped on: ${stoppedOn.message}` : "";
49
+ return `${read.length} file${read.length === 1 ? "" : "s"} the conversion reads ${read.length === 1 ? "was" : "were"} not given (${names}): fetch them and convert again.${stopped}`;
50
+ }
51
+ /**
52
+ * Each written page's source file → its path in the project, from the pages in the order they
53
+ * were written: a later page at the same path replaces an earlier one, whose file then has no
54
+ * page (its notes would otherwise open another file's page).
55
+ */
56
+ export function pageSources(written) {
57
+ const fileAt = new Map();
58
+ for (const { file, path } of written)
59
+ fileAt.set(path, file);
60
+ const pageOf = new Map();
61
+ for (const [path, file] of fileAt)
62
+ pageOf.set(file, path);
63
+ return pageOf;
64
+ }
65
+ /** The ledger, each entry about a written page's source file given its `page`. */
66
+ export function withPages(ledger, pageOf) {
67
+ return ledger.map((e) => {
68
+ const page = e.file === undefined ? undefined : pageOf.get(e.file);
69
+ return page === undefined ? e : { ...e, page };
70
+ });
71
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dogsbay/corpus",
3
- "version": "0.2.0-beta.132",
3
+ "version": "0.2.0-beta.134",
4
4
  "description": "A documentation repository as its importers read it, in the browser or in Node: plain paths plus text on demand, from a map of files or from a folder whose symlinks are kept inside it",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,7 +20,7 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@dogsbay/detect": "0.2.0-beta.132"
23
+ "@dogsbay/detect": "0.2.0-beta.134"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^22.20.1",