@ascribed/astro 0.2.1-next.11 → 0.2.1-next.13
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/README.md +3 -1
- package/dist/dev.js +9 -0
- package/dist/dev.js.map +1 -1
- package/dist/files.d.ts +8 -2
- package/dist/files.js +21 -5
- package/dist/files.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +44 -10
- package/dist/index.js.map +1 -1
- package/dist/names.d.ts +5 -0
- package/dist/names.js +5 -0
- package/dist/names.js.map +1 -1
- package/dist/permalinks.d.ts +54 -0
- package/dist/permalinks.js +72 -0
- package/dist/permalinks.js.map +1 -0
- package/dist/project.d.ts +10 -0
- package/dist/project.js +11 -0
- package/dist/project.js.map +1 -1
- package/dist/review/diff.d.ts +13 -0
- package/dist/review/diff.js +34 -1
- package/dist/review/diff.js.map +1 -1
- package/dist/review/server.d.ts +13 -0
- package/dist/review/server.js +44 -0
- package/dist/review/server.js.map +1 -1
- package/dist/run.d.ts +5 -4
- package/dist/run.js +12 -5
- package/dist/run.js.map +1 -1
- package/dist/shapes.d.ts +8 -1
- package/dist/shapes.js.map +1 -1
- package/dist/toolbar/app.js +98 -0
- package/dist/toolbar/app.js.map +1 -1
- package/package.json +15 -8
- package/src/MissingPermalink.astro +56 -0
- package/src/Permalink.astro +77 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Permalinks' addresses: a page at `<path><id>` for each permalink the build
|
|
2
|
+
// publishes, which sends the reader on to its heading, and for each retired
|
|
3
|
+
// one with nowhere to lead, which says why it was removed. Ascribe writes the list,
|
|
4
|
+
// `_ascribe/permalinks.json` in the site output (output-layout contract §1.2);
|
|
5
|
+
// the integration adds the route (`Permalink.astro`) and builds its pages
|
|
6
|
+
// from it, in `astro build` and `astro dev`.
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { normalizeBase } from "./project.js";
|
|
10
|
+
/** The list's format version this reads. */
|
|
11
|
+
const VERSION = 1;
|
|
12
|
+
/** The list's text in a build's site output, or `undefined` when there isn't one. */
|
|
13
|
+
export function permalinksText(siteRoot) {
|
|
14
|
+
try {
|
|
15
|
+
return readFileSync(listFile(siteRoot), "utf8");
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function listFile(siteRoot) {
|
|
22
|
+
return path.join(siteRoot, "_ascribe", "permalinks.json");
|
|
23
|
+
}
|
|
24
|
+
/** The list in a build's site output, or `undefined` when there isn't one. */
|
|
25
|
+
export function readPermalinks(siteRoot) {
|
|
26
|
+
const file = listFile(siteRoot);
|
|
27
|
+
const text = permalinksText(siteRoot);
|
|
28
|
+
if (text === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
const list = JSON.parse(text);
|
|
31
|
+
if (list.version !== VERSION) {
|
|
32
|
+
throw new Error(`@ascribed/astro: ${file} is version ${String(list.version)} of the permalinks list, and this version of the integration reads version ${VERSION}. Update @ascribed/astro and @ascribed/cli together.`);
|
|
33
|
+
}
|
|
34
|
+
return list;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The route pattern for the addresses, as Astro takes it: under `base`, which
|
|
38
|
+
* Astro adds. Throws when the list's path isn't under `base`, since an Astro
|
|
39
|
+
* site serves nothing outside it.
|
|
40
|
+
*/
|
|
41
|
+
export function permalinkPattern(listPath, base) {
|
|
42
|
+
const root = normalizeBase(base);
|
|
43
|
+
const at = normalizeBase(listPath);
|
|
44
|
+
if (!at.startsWith(root)) {
|
|
45
|
+
throw new Error(`@ascribed/astro: permalinks are published at "${at}", which isn't under Astro's \`base\`, "${base}", and an Astro site serves nothing outside it. Set [consumer] permalink-path in ascribe.toml to a path under "${root}".`);
|
|
46
|
+
}
|
|
47
|
+
return `/${at.slice(root.length)}[id]`;
|
|
48
|
+
}
|
|
49
|
+
/** Where a permalink leads: its page's route, and the heading's anchor. */
|
|
50
|
+
function destination(entry) {
|
|
51
|
+
return entry.fragment === undefined ? entry.route : `${entry.route}#${entry.fragment}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `getStaticPaths` for the route: one page for each permalink in the site
|
|
55
|
+
* output's list, and one for each retired permalink it lists as removed.
|
|
56
|
+
*/
|
|
57
|
+
export function forwardingPaths(siteRoot) {
|
|
58
|
+
const list = readPermalinks(siteRoot);
|
|
59
|
+
if (list === undefined)
|
|
60
|
+
return [];
|
|
61
|
+
const dimensions = list.dimensions ?? {};
|
|
62
|
+
const forwarding = Object.entries(list.permalinks).map(([id, entry]) => ({
|
|
63
|
+
params: { id },
|
|
64
|
+
props: { to: destination(entry), dimensions },
|
|
65
|
+
}));
|
|
66
|
+
const removed = Object.entries(list.removed ?? {}).map(([id, entry]) => ({
|
|
67
|
+
params: { id },
|
|
68
|
+
props: { reason: entry.reason },
|
|
69
|
+
}));
|
|
70
|
+
return [...forwarding, ...removed];
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=permalinks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permalinks.js","sourceRoot":"","sources":["../src/permalinks.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,oFAAoF;AACpF,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,4CAA4C;AAC5C,MAAM,OAAO,GAAG,CAAC,CAAC;AA4BlB,qFAAqF;AACrF,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB;IAChC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC;AAC5D,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAkB,CAAC;IAC/C,IAAI,IAAI,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,oBAAoB,IAAI,eAAe,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,8EAA8E,OAAO,sDAAsD,CACvM,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,IAAY;IAC7D,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,iDAAiD,EAAE,2CAA2C,IAAI,kHAAkH,IAAI,IAAI,CAC7N,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;AACzC,CAAC;AAED,2EAA2E;AAC3E,SAAS,WAAW,CAAC,KAA2C;IAC9D,OAAO,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;AACzF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC;IACzC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,EAAE,EAAE,EAAE;QACd,KAAK,EAAE,EAAE,EAAE,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,UAAU,EAAgB;KAC5D,CAAC,CAAC,CAAC;IACJ,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvE,MAAM,EAAE,EAAE,EAAE,EAAE;QACd,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAgB;KAC9C,CAAC,CAAC,CAAC;IACJ,OAAO,CAAC,GAAG,UAAU,EAAE,GAAG,OAAO,CAAC,CAAC;AACrC,CAAC","sourcesContent":["// Permalinks' addresses: a page at `<path><id>` for each permalink the build\n// publishes, which sends the reader on to its heading, and for each retired\n// one with nowhere to lead, which says why it was removed. Ascribe writes the list,\n// `_ascribe/permalinks.json` in the site output (output-layout contract §1.2);\n// the integration adds the route (`Permalink.astro`) and builds its pages\n// from it, in `astro build` and `astro dev`.\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { normalizeBase } from \"./project.js\";\n\n/** The list's format version this reads. */\nconst VERSION = 1;\n\n/** `_ascribe/permalinks.json`. */\nexport interface PermalinkList {\n version: number;\n /** Where the addresses are published, from the site's root, ending in `/`. */\n path: string;\n /** The content model's dimensions, each with its values: the parameters an address takes. */\n dimensions?: Record<string, string[]>;\n /**\n * Each permalink's destination. An alias names the permalink it leads to,\n * and a retired permalink that leads somewhere its reason.\n */\n permalinks: Record<\n string,\n { route: string; fragment?: string; source: string; alias?: string; retired?: string }\n >;\n /** The retired permalinks with nowhere to lead in this build, each with why. */\n removed?: Record<string, { reason: string }>;\n}\n\n/**\n * What an address's page needs: where it sends the reader, and the\n * parameters it takes; or, for a retired permalink with nowhere to lead,\n * why it was removed.\n */\nexport type Forwarding = { to: string; dimensions: Record<string, string[]> } | { reason: string };\n\n/** The list's text in a build's site output, or `undefined` when there isn't one. */\nexport function permalinksText(siteRoot: string): string | undefined {\n try {\n return readFileSync(listFile(siteRoot), \"utf8\");\n } catch {\n return undefined;\n }\n}\n\nfunction listFile(siteRoot: string): string {\n return path.join(siteRoot, \"_ascribe\", \"permalinks.json\");\n}\n\n/** The list in a build's site output, or `undefined` when there isn't one. */\nexport function readPermalinks(siteRoot: string): PermalinkList | undefined {\n const file = listFile(siteRoot);\n const text = permalinksText(siteRoot);\n if (text === undefined) return undefined;\n const list = JSON.parse(text) as PermalinkList;\n if (list.version !== VERSION) {\n throw new Error(\n `@ascribed/astro: ${file} is version ${String(list.version)} of the permalinks list, and this version of the integration reads version ${VERSION}. Update @ascribed/astro and @ascribed/cli together.`,\n );\n }\n return list;\n}\n\n/**\n * The route pattern for the addresses, as Astro takes it: under `base`, which\n * Astro adds. Throws when the list's path isn't under `base`, since an Astro\n * site serves nothing outside it.\n */\nexport function permalinkPattern(listPath: string, base: string): string {\n const root = normalizeBase(base);\n const at = normalizeBase(listPath);\n if (!at.startsWith(root)) {\n throw new Error(\n `@ascribed/astro: permalinks are published at \"${at}\", which isn't under Astro's \\`base\\`, \"${base}\", and an Astro site serves nothing outside it. Set [consumer] permalink-path in ascribe.toml to a path under \"${root}\".`,\n );\n }\n return `/${at.slice(root.length)}[id]`;\n}\n\n/** Where a permalink leads: its page's route, and the heading's anchor. */\nfunction destination(entry: { route: string; fragment?: string }): string {\n return entry.fragment === undefined ? entry.route : `${entry.route}#${entry.fragment}`;\n}\n\n/**\n * `getStaticPaths` for the route: one page for each permalink in the site\n * output's list, and one for each retired permalink it lists as removed.\n */\nexport function forwardingPaths(siteRoot: string): { params: { id: string }; props: Forwarding }[] {\n const list = readPermalinks(siteRoot);\n if (list === undefined) return [];\n const dimensions = list.dimensions ?? {};\n const forwarding = Object.entries(list.permalinks).map(([id, entry]) => ({\n params: { id },\n props: { to: destination(entry), dimensions } as Forwarding,\n }));\n const removed = Object.entries(list.removed ?? {}).map(([id, entry]) => ({\n params: { id },\n props: { reason: entry.reason } as Forwarding,\n }));\n return [...forwarding, ...removed];\n}\n"]}
|
package/dist/project.d.ts
CHANGED
|
@@ -13,14 +13,24 @@ export interface ProjectInfo {
|
|
|
13
13
|
site: string | undefined;
|
|
14
14
|
basePath: string;
|
|
15
15
|
trailingSlash: "always" | "never";
|
|
16
|
+
/** Whether builds publish what agents read: `llms.txt` and each page's Markdown, in the plain output. */
|
|
17
|
+
agents: boolean;
|
|
16
18
|
};
|
|
17
19
|
/** The site output's root for a build: `<output-dir>/<build>/site`. */
|
|
18
20
|
siteRoot(build: string): string;
|
|
21
|
+
/** The plain output's root for a build: `<output-dir>/<build>/plain`. */
|
|
22
|
+
plainRoot(build: string): string;
|
|
19
23
|
}
|
|
20
24
|
/** Reads `ascribe.toml` in `dir`. Throws a readable error if it can't be read. */
|
|
21
25
|
export declare function readProject(dir: string): ProjectInfo;
|
|
22
26
|
/** A base path with a leading and a trailing `/`, as Ascribe's router has it. */
|
|
23
27
|
export declare function normalizeBase(base: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The route of the site's root, Astro's `base`, ending the way `trailingSlash`
|
|
30
|
+
* says the site's routes do. (`import.meta.env.BASE_URL` isn't replaced in a
|
|
31
|
+
* package's components.)
|
|
32
|
+
*/
|
|
33
|
+
export declare function homeRoute(base: string, trailingSlash: "always" | "never" | "ignore"): string;
|
|
24
34
|
/**
|
|
25
35
|
* The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,
|
|
26
36
|
* as sentences. Ascribe writes every link with its own settings, so a site
|
package/dist/project.js
CHANGED
|
@@ -29,8 +29,10 @@ export function readProject(dir) {
|
|
|
29
29
|
site: typeof consumer["site"] === "string" ? consumer["site"] : undefined,
|
|
30
30
|
basePath: normalizeBase(typeof consumer["base-path"] === "string" ? consumer["base-path"] : "/"),
|
|
31
31
|
trailingSlash,
|
|
32
|
+
agents: consumer["agents"] === true,
|
|
32
33
|
},
|
|
33
34
|
siteRoot: (build) => path.resolve(dir, outputDir, build, "site"),
|
|
35
|
+
plainRoot: (build) => path.resolve(dir, outputDir, build, "plain"),
|
|
34
36
|
};
|
|
35
37
|
}
|
|
36
38
|
function section(table, name) {
|
|
@@ -48,6 +50,15 @@ export function normalizeBase(base) {
|
|
|
48
50
|
result += "/";
|
|
49
51
|
return result;
|
|
50
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* The route of the site's root, Astro's `base`, ending the way `trailingSlash`
|
|
55
|
+
* says the site's routes do. (`import.meta.env.BASE_URL` isn't replaced in a
|
|
56
|
+
* package's components.)
|
|
57
|
+
*/
|
|
58
|
+
export function homeRoute(base, trailingSlash) {
|
|
59
|
+
const root = normalizeBase(base);
|
|
60
|
+
return trailingSlash === "never" && root !== "/" ? root.slice(0, -1) : root;
|
|
61
|
+
}
|
|
51
62
|
/**
|
|
52
63
|
* The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,
|
|
53
64
|
* as sentences. Ascribe writes every link with its own settings, so a site
|
package/dist/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,uEAAuE;AACvE,oDAAoD;AACpD,yBAAyB;AACzB,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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","sourcesContent":["// What the integration needs from `ascribe.toml`: where a build's site output\n// goes, and the consumer settings that must agree with `astro.config`.\n// The compiler validates the whole file; this reads\n// only what it compiled.\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { parse } from \"smol-toml\";\n\n/** The parts of an Ascribe project the integration reads. */\nexport interface ProjectInfo {\n /** The directory holding `ascribe.toml`. */\n dir: string;\n /** The path of `ascribe.toml`. */\n configPath: string;\n /** Source files and assets watched during development. */\n contentRoot: string;\n /** Generated output, never treated as a source change. */\n outputRoot: string;\n /** `[consumer]`, with the profile's defaults. */\n consumer: {\n site: string | undefined;\n basePath: string;\n trailingSlash: \"always\" | \"never\";\n /** Whether builds publish what agents read: `llms.txt` and each page's Markdown, in the plain output. */\n agents: boolean;\n };\n /** The site output's root for a build: `<output-dir>/<build>/site`. */\n siteRoot(build: string): string;\n /** The plain output's root for a build: `<output-dir>/<build>/plain`. */\n plainRoot(build: string): string;\n}\n\n/** Reads `ascribe.toml` in `dir`. Throws a readable error if it can't be read. */\nexport function readProject(dir: string): ProjectInfo {\n const configPath = path.join(dir, \"ascribe.toml\");\n let table: Record<string, unknown>;\n try {\n table = parse(readFileSync(configPath, \"utf8\"));\n } catch (error) {\n throw new Error(\n `@ascribed/astro can't read ${configPath}: ${error instanceof Error ? error.message : String(error)}`,\n { cause: error },\n );\n }\n const project = section(table, \"project\");\n const consumer = section(table, \"consumer\");\n const outputDir =\n typeof project[\"output-dir\"] === \"string\" ? project[\"output-dir\"] : \".ascribe/build\";\n const contentDir = typeof project[\"content-root\"] === \"string\" ? project[\"content-root\"] : \"docs\";\n const trailingSlash = consumer[\"trailing-slash\"] === \"never\" ? \"never\" : \"always\";\n return {\n dir,\n configPath,\n contentRoot: path.resolve(dir, contentDir),\n outputRoot: path.resolve(dir, outputDir),\n consumer: {\n site: typeof consumer[\"site\"] === \"string\" ? consumer[\"site\"] : undefined,\n basePath: normalizeBase(\n typeof consumer[\"base-path\"] === \"string\" ? consumer[\"base-path\"] : \"/\",\n ),\n trailingSlash,\n agents: consumer[\"agents\"] === true,\n },\n siteRoot: (build) => path.resolve(dir, outputDir, build, \"site\"),\n plainRoot: (build) => path.resolve(dir, outputDir, build, \"plain\"),\n };\n}\n\nfunction section(table: Record<string, unknown>, name: string): Record<string, unknown> {\n const value = table[name];\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** A base path with a leading and a trailing `/`, as Ascribe's router has it. */\nexport function normalizeBase(base: string): string {\n let result = base.trim();\n if (!result.startsWith(\"/\")) result = `/${result}`;\n if (!result.endsWith(\"/\")) result += \"/\";\n return result;\n}\n\n/**\n * The route of the site's root, Astro's `base`, ending the way `trailingSlash`\n * says the site's routes do. (`import.meta.env.BASE_URL` isn't replaced in a\n * package's components.)\n */\nexport function homeRoute(base: string, trailingSlash: \"always\" | \"never\" | \"ignore\"): string {\n const root = normalizeBase(base);\n return trailingSlash === \"never\" && root !== \"/\" ? root.slice(0, -1) : root;\n}\n\n/**\n * The ways `ascribe.toml`'s `[consumer]` disagrees with Astro's configuration,\n * as sentences. Ascribe writes every link with its own settings, so a site\n * whose routes differ would have broken links.\n */\nexport function consumerMismatches(\n project: ProjectInfo,\n astro: { base: string; trailingSlash: string; site: string | undefined },\n): string[] {\n const problems: string[] = [];\n const { basePath, trailingSlash, site } = project.consumer;\n if (normalizeBase(astro.base) !== basePath) {\n problems.push(\n `[consumer] base-path is \"${basePath}\", but Astro's \\`base\\` is \"${astro.base}\". Set them to the same path.`,\n );\n }\n // Astro's \"ignore\" serves both forms, so Ascribe's links work either way.\n if (astro.trailingSlash !== \"ignore\" && astro.trailingSlash !== trailingSlash) {\n problems.push(\n `[consumer] trailing-slash is \"${trailingSlash}\", but Astro's \\`trailingSlash\\` is \"${astro.trailingSlash}\".`,\n );\n }\n if (site !== undefined && astro.site !== undefined && origin(site) !== origin(astro.site)) {\n problems.push(`[consumer] site is \"${site}\", but Astro's \\`site\\` is \"${astro.site}\".`);\n }\n return problems;\n}\n\nfunction origin(url: string): string {\n try {\n return new URL(url).origin;\n } catch {\n return url;\n }\n}\n"]}
|
package/dist/review/diff.d.ts
CHANGED
|
@@ -19,3 +19,16 @@ export declare function runDiff(options: {
|
|
|
19
19
|
}): Promise<DiffResult>;
|
|
20
20
|
/** The build's part of `ascribe diff --format json`'s output. */
|
|
21
21
|
export declare function parseDiff(json: string, build: string): DiffResult;
|
|
22
|
+
/**
|
|
23
|
+
* `ascribe diff --format prompt`'s prompt about a page, or a fragment's reach,
|
|
24
|
+
* at a content path; `undefined` when it didn't change. Rejects with
|
|
25
|
+
* `ascribe diff`'s reason as the message.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runPrompt(options: {
|
|
28
|
+
binary: string;
|
|
29
|
+
configPath: string;
|
|
30
|
+
build: string;
|
|
31
|
+
cwd: string;
|
|
32
|
+
base: string | undefined;
|
|
33
|
+
path: string;
|
|
34
|
+
}): Promise<string | undefined>;
|
package/dist/review/diff.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
// Running `ascribe diff
|
|
1
|
+
// Running `ascribe diff` for the integration's build: its changes as JSON, and
|
|
2
|
+
// its agent prompts.
|
|
2
3
|
import { execFile } from "node:child_process";
|
|
3
4
|
/**
|
|
4
5
|
* Compares the build's pages with `base` (the default branch when it's
|
|
@@ -45,4 +46,36 @@ export function parseDiff(json, build) {
|
|
|
45
46
|
const errors = typeof report.working_tree_errors === "number" ? report.working_tree_errors : 0;
|
|
46
47
|
return { base: report.base, pages, errors };
|
|
47
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* `ascribe diff --format prompt`'s prompt about a page, or a fragment's reach,
|
|
51
|
+
* at a content path; `undefined` when it didn't change. Rejects with
|
|
52
|
+
* `ascribe diff`'s reason as the message.
|
|
53
|
+
*/
|
|
54
|
+
export function runPrompt(options) {
|
|
55
|
+
const args = [
|
|
56
|
+
"diff",
|
|
57
|
+
"--format",
|
|
58
|
+
"prompt",
|
|
59
|
+
"--build",
|
|
60
|
+
options.build,
|
|
61
|
+
"--config",
|
|
62
|
+
options.configPath,
|
|
63
|
+
"--color",
|
|
64
|
+
"never",
|
|
65
|
+
];
|
|
66
|
+
if (options.base !== undefined)
|
|
67
|
+
args.push("--base", options.base);
|
|
68
|
+
// After `--`: a content path that starts with `-` is still a path.
|
|
69
|
+
args.push("--", options.path);
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
execFile(options.binary, args, { cwd: options.cwd, maxBuffer: 16 * 1024 * 1024, windowsHide: true }, (error, stdout, stderr) => {
|
|
72
|
+
if (error !== null) {
|
|
73
|
+
const reason = stderr.trim();
|
|
74
|
+
reject(new Error(reason === "" ? error.message : reason, { cause: error }));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
resolve(stdout === "" ? undefined : stdout);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
48
81
|
//# sourceMappingURL=diff.js.map
|
package/dist/review/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/review/diff.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/review/diff.ts"],"names":[],"mappings":"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","sourcesContent":["// Running `ascribe diff` for the integration's build: its changes as JSON, and\n// its agent prompts.\nimport { execFile } from \"node:child_process\";\nimport type { DiffReport } from \"../shapes.js\";\nimport type { BaseInfo, PageDiff } from \"./protocol.js\";\n\n/** One build's changes against a base. */\nexport interface DiffResult {\n base: BaseInfo;\n pages: PageDiff[];\n /** How many errors `ascribe check` finds in the working tree for the build. */\n errors: number;\n}\n\n/**\n * Compares the build's pages with `base` (the default branch when it's\n * `undefined`). Rejects with `ascribe diff`'s reason as the message.\n */\nexport function runDiff(options: {\n binary: string;\n configPath: string;\n build: string;\n cwd: string;\n base: string | undefined;\n}): Promise<DiffResult> {\n const args = [\n \"diff\",\n \"--format\",\n \"json\",\n \"--build\",\n options.build,\n \"--config\",\n options.configPath,\n \"--color\",\n \"never\",\n ];\n if (options.base !== undefined) args.push(\"--base\", options.base);\n return new Promise((resolve, reject) => {\n execFile(\n options.binary,\n args,\n { cwd: options.cwd, maxBuffer: 256 * 1024 * 1024, windowsHide: true },\n (error, stdout, stderr) => {\n if (error !== null) {\n const reason = stderr.trim();\n reject(new Error(reason === \"\" ? error.message : reason, { cause: error }));\n return;\n }\n try {\n resolve(parseDiff(stdout, options.build));\n } catch (cause) {\n reject(new Error(\"`ascribe diff` wrote output that isn't its JSON.\", { cause }));\n }\n },\n );\n });\n}\n\n/** The build's part of `ascribe diff --format json`'s output. */\nexport function parseDiff(json: string, build: string): DiffResult {\n // Checked as far as telling a diff report from other JSON.\n const report = JSON.parse(json) as Partial<DiffReport>;\n if (report.base === undefined || !Array.isArray(report.builds)) {\n throw new Error(\"not a diff report\");\n }\n const pages = report.builds.find((b) => b.build === build)?.pages ?? [];\n const errors = typeof report.working_tree_errors === \"number\" ? report.working_tree_errors : 0;\n return { base: report.base, pages, errors };\n}\n\n/**\n * `ascribe diff --format prompt`'s prompt about a page, or a fragment's reach,\n * at a content path; `undefined` when it didn't change. Rejects with\n * `ascribe diff`'s reason as the message.\n */\nexport function runPrompt(options: {\n binary: string;\n configPath: string;\n build: string;\n cwd: string;\n base: string | undefined;\n path: string;\n}): Promise<string | undefined> {\n const args = [\n \"diff\",\n \"--format\",\n \"prompt\",\n \"--build\",\n options.build,\n \"--config\",\n options.configPath,\n \"--color\",\n \"never\",\n ];\n if (options.base !== undefined) args.push(\"--base\", options.base);\n // After `--`: a content path that starts with `-` is still a path.\n args.push(\"--\", options.path);\n return new Promise((resolve, reject) => {\n execFile(\n options.binary,\n args,\n { cwd: options.cwd, maxBuffer: 16 * 1024 * 1024, windowsHide: true },\n (error, stdout, stderr) => {\n if (error !== null) {\n const reason = stderr.trim();\n reject(new Error(reason === \"\" ? error.message : reason, { cause: error }));\n return;\n }\n resolve(stdout === \"\" ? undefined : stdout);\n },\n );\n });\n}\n"]}
|
package/dist/review/server.d.ts
CHANGED
|
@@ -27,6 +27,13 @@ export interface ReviewServerOptions {
|
|
|
27
27
|
channelProblem: string | undefined;
|
|
28
28
|
/** Compares the build with `base` (the default branch when `undefined`). */
|
|
29
29
|
diff(base: string | undefined): Promise<DiffResult>;
|
|
30
|
+
/**
|
|
31
|
+
* `ascribe diff`'s agent prompt about the page or fragment at a content
|
|
32
|
+
* path, against `base`; `undefined` when it didn't change.
|
|
33
|
+
*/
|
|
34
|
+
prompt(path: string, base: string | undefined): Promise<string | undefined>;
|
|
35
|
+
/** The project's folder: where its `ascribe.toml` is. */
|
|
36
|
+
projectDir: string;
|
|
30
37
|
/** Opens the pull request's review. */
|
|
31
38
|
connect(): Promise<Connection>;
|
|
32
39
|
/** Writes the build's JSON output, in turn with the dev server's builds: it has the routes. */
|
|
@@ -66,6 +73,12 @@ export declare class ReviewServer {
|
|
|
66
73
|
private receive;
|
|
67
74
|
/** Answers one request. Rejects with a sentence to show. */
|
|
68
75
|
handle(method: string, params: Record<string, unknown>, tab?: string): Promise<unknown>;
|
|
76
|
+
/**
|
|
77
|
+
* The agent prompt the page asked for: a thread's, or every open one's,
|
|
78
|
+
* from the pull request; the page's changes, or a fragment's reach, from
|
|
79
|
+
* `ascribe diff`. `undefined` when there's nothing to prompt about.
|
|
80
|
+
*/
|
|
81
|
+
private prompt;
|
|
69
82
|
private start;
|
|
70
83
|
private stop;
|
|
71
84
|
private refresh;
|
package/dist/review/server.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// build with the pull request's base (or the default branch), and compares
|
|
11
11
|
// again after each rebuild.
|
|
12
12
|
import path from "node:path";
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
13
14
|
import { messageOf } from "./github.js";
|
|
14
15
|
import { CHANGED_EVENT, REQUEST_EVENT, RESULT_EVENT, } from "./protocol.js";
|
|
15
16
|
import { normalizeRoute } from "./routes.js";
|
|
@@ -120,6 +121,8 @@ export class ReviewServer {
|
|
|
120
121
|
await this.refresh();
|
|
121
122
|
this.changed(tab);
|
|
122
123
|
return null;
|
|
124
|
+
case "prompt":
|
|
125
|
+
return (await this.prompt(params)) ?? null;
|
|
123
126
|
}
|
|
124
127
|
if (!OVERLAY.has(method))
|
|
125
128
|
throw new Error(`Unknown request: ${method}.`);
|
|
@@ -145,6 +148,47 @@ export class ReviewServer {
|
|
|
145
148
|
this.changed(tab);
|
|
146
149
|
return answer.result;
|
|
147
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* The agent prompt the page asked for: a thread's, or every open one's,
|
|
153
|
+
* from the pull request; the page's changes, or a fragment's reach, from
|
|
154
|
+
* `ascribe diff`. `undefined` when there's nothing to prompt about.
|
|
155
|
+
*/
|
|
156
|
+
async prompt(params) {
|
|
157
|
+
if (!this.on)
|
|
158
|
+
throw new Error("Review is off.");
|
|
159
|
+
const request = params["request"];
|
|
160
|
+
const pagePath = typeof params["path"] === "string" ? params["path"] : undefined;
|
|
161
|
+
const connection = await this.connected();
|
|
162
|
+
switch (request?.kind) {
|
|
163
|
+
case "thread":
|
|
164
|
+
case "open-threads": {
|
|
165
|
+
if (connection.state !== "on")
|
|
166
|
+
throw new Error("Review comments aren't on.");
|
|
167
|
+
const github = await import("@ascribed/review/github");
|
|
168
|
+
const contentRoot = this.options.contentRoot;
|
|
169
|
+
return github.buildThreadsPrompt({
|
|
170
|
+
session: connection.session,
|
|
171
|
+
project: github.promptProject(this.options.projectDir),
|
|
172
|
+
changedPages: async () => {
|
|
173
|
+
const changes = await this.changes();
|
|
174
|
+
return "result" in changes ? changes.result.pages : [];
|
|
175
|
+
},
|
|
176
|
+
// The agent reads the files on disk, as the dev server does: no editor here.
|
|
177
|
+
readSource: (file) => readFile(path.join(contentRoot, ...file.split("/")), "utf8").then((text) => ({ text, unsaved: false }), () => undefined),
|
|
178
|
+
}, request);
|
|
179
|
+
}
|
|
180
|
+
case "page-changes":
|
|
181
|
+
case "fragment-reach": {
|
|
182
|
+
const target = request.kind === "page-changes" ? pagePath : request.fragment;
|
|
183
|
+
if (target === undefined)
|
|
184
|
+
throw new Error("That isn't a page of the project.");
|
|
185
|
+
const base = connection.state === "on" ? connection.base : undefined;
|
|
186
|
+
return this.options.prompt(target, base);
|
|
187
|
+
}
|
|
188
|
+
default:
|
|
189
|
+
throw new Error("Unknown prompt.");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
148
192
|
async start() {
|
|
149
193
|
if (this.on)
|
|
150
194
|
return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/review/server.ts"],"names":[],"mappings":"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","sourcesContent":["// Review in the site preview, on the dev server's side: everything the\n// toolbar app asks for, answered over Astro's dev toolbar channel. Every\n// `git`, `ascribe`, and `gh` call happens here; the page gets the changes and\n// the threads, never a token. There are no HTTP routes: any page open in the\n// same browser could call a route on localhost, while the toolbar's channel\n// takes only clients that have the dev server's token (`channelProblem`).\n//\n// Nothing runs until the reviewer turns the app on. Then the server opens\n// the pull request's review (when the GitHub CLI finds one), compares the\n// build with the pull request's base (or the default branch), and compares\n// again after each rebuild.\nimport path from \"node:path\";\nimport type { OverlayMethod, ReviewSession } from \"@ascribed/review/github\";\nimport type { PageRef } from \"@ascribed/review/place\";\nimport type { DiffResult } from \"./diff.js\";\nimport { messageOf, type Connection } from \"./github.js\";\nimport {\n CHANGED_EVENT,\n REQUEST_EVENT,\n RESULT_EVENT,\n type ChangedPage,\n type PageDiff,\n type PageView,\n type Request,\n type Result,\n type Status,\n type ThreadsState,\n} from \"./protocol.js\";\nimport { normalizeRoute, type RoutedPage } from \"./routes.js\";\n\n/** A page's end of the channel: the server answers it alone. */\nexport interface ChannelClient {\n send(event: string, payload?: unknown): void;\n}\n\n/**\n * The toolbar's channel, as `astro:server:setup`'s `toolbar` has it. Its\n * listeners get the sending page's client too (it is Vite's `server.hot`);\n * without one, an answer goes to every page, which tell theirs by `tab`.\n */\nexport interface ToolbarChannel {\n on(event: string, callback: (data: unknown, client?: ChannelClient) => void): void;\n send(event: string, payload: unknown): void;\n}\n\nexport interface ReviewServerOptions {\n channel: ToolbarChannel;\n logger: { info(message: string): void; warn(message: string): void };\n build: string;\n /** The content root, absolute. */\n contentRoot: string;\n /** Why someone else could use the channel, if they could: then GitHub stays off. */\n channelProblem: string | undefined;\n /** Compares the build with `base` (the default branch when `undefined`). */\n diff(base: string | undefined): Promise<DiffResult>;\n /** Opens the pull request's review. */\n connect(): Promise<Connection>;\n /** Writes the build's JSON output, in turn with the dev server's builds: it has the routes. */\n writeRoutes(): Promise<void>;\n /** Every page of the build, by normalized route. */\n readRoutes(): Promise<Map<string, RoutedPage>>;\n /** How long to wait after a rebuild before comparing again. Default 150 ms. */\n debounceMs?: number;\n}\n\n/** The overlay's requests, which go to the session. */\nconst OVERLAY: ReadonlySet<string> = new Set<OverlayMethod>([\n \"load\",\n \"commentTarget\",\n \"comment\",\n \"reply\",\n \"allThreads\",\n \"resolve\",\n \"submit\",\n \"discard\",\n]);\n\nexport class ReviewServer {\n private on = false;\n private problem: string | undefined;\n private connection: Connection | undefined;\n private connecting: Promise<Connection> | undefined;\n private diffed: { result: DiffResult } | { error: string } | undefined;\n private diffing: Promise<void> | undefined;\n private stale = false;\n private timer: ReturnType<typeof setTimeout> | undefined;\n private routes: Promise<Map<string, RoutedPage>> | undefined;\n /** The page each overlay last read, by content path: comments are made on it. */\n private readonly pages = new Map<string, PageRef>();\n /** How long the last comparison took, in milliseconds. */\n lastDiffMs: number | undefined;\n\n constructor(private readonly options: ReviewServerOptions) {\n this.problem = options.channelProblem;\n options.channel.on(REQUEST_EVENT, (data, client) => {\n void this.receive(data, client);\n });\n }\n\n /**\n * The address the dev server listens on, once it does: one other machines\n * can reach turns comments off, as `server.host` does.\n */\n listening(address: string): void {\n if (this.problem !== undefined || isLoopback(address)) return;\n this.problem = NETWORK_PROBLEM;\n this.connection = undefined;\n this.connecting = undefined;\n }\n\n /** Whether review is on: then the dev server's builds write JSON output too. */\n get active(): boolean {\n return this.on;\n }\n\n /** The dev server rebuilt: compare again soon, and read the routes again when asked. */\n rebuilt(): void {\n if (!this.on) return;\n this.stale = true;\n this.routes = undefined;\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.timer = undefined;\n void this.changes();\n }, this.options.debounceMs ?? 150);\n }\n\n /** The dev server stopped. */\n dispose(): void {\n if (this.timer) clearTimeout(this.timer);\n this.on = false;\n }\n\n private async receive(data: unknown, client: ChannelClient | undefined): Promise<void> {\n const request = data as Partial<Request> | null;\n if (typeof request?.id !== \"number\" || typeof request.method !== \"string\") return;\n const params =\n typeof request.params === \"object\" && request.params !== null ? request.params : {};\n const tab = typeof request.tab === \"string\" ? request.tab : \"\";\n const answer: Result = { tab, id: request.id };\n try {\n answer.result = await this.handle(request.method, params, tab);\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n answer.error =\n typeof code === \"string\"\n ? { message: messageOf(error), code }\n : { message: messageOf(error) };\n }\n if (typeof client?.send === \"function\") client.send(RESULT_EVENT, answer);\n else this.options.channel.send(RESULT_EVENT, answer);\n }\n\n /** Answers one request. Rejects with a sentence to show. */\n async handle(method: string, params: Record<string, unknown>, tab = \"\"): Promise<unknown> {\n switch (method) {\n case \"status\":\n return { on: this.on } satisfies Status;\n case \"start\":\n await this.start();\n return { on: true } satisfies Status;\n case \"stop\":\n this.stop();\n this.changed(tab);\n return { on: false } satisfies Status;\n case \"page\":\n return this.pageView(params);\n case \"refresh\":\n await this.refresh();\n this.changed(tab);\n return null;\n }\n if (!OVERLAY.has(method)) throw new Error(`Unknown request: ${method}.`);\n const connection = this.on ? await this.connected() : undefined;\n if (connection?.state !== \"on\") throw new Error(\"Review comments aren't on.\");\n const github = await import(\"@ascribed/review/github\");\n const pagePath = typeof params[\"path\"] === \"string\" ? params[\"path\"] : undefined;\n const answer = await github.answerRequest(\n {\n session: connection.session,\n page: pagePath === undefined ? undefined : { build: this.options.build, path: pagePath },\n lastPage: pagePath === undefined ? undefined : this.pages.get(pagePath),\n changedPages: async () => {\n const [changes, routes] = await Promise.all([this.changes(), this.readRoutes()]);\n if (!(\"result\" in changes)) return [];\n return changes.result.pages.map((p) => listed(p, routes.get(normalizeRoute(p.route))));\n },\n },\n method as OverlayMethod,\n params,\n );\n if (answer.page) this.pages.set(answer.page.path, answer.page);\n if (answer.changed) this.changed(tab);\n return answer.result;\n }\n\n private async start(): Promise<void> {\n if (this.on) return;\n this.on = true;\n this.stale = true;\n this.routes = undefined;\n this.connection = undefined;\n const started = Date.now();\n // The routes come from the JSON output, which the next builds write too.\n const routes = this.options.writeRoutes().catch((error: unknown) => {\n this.options.logger.warn(`couldn't write the routes for review: ${messageOf(error)}`);\n });\n await this.connected();\n const changes = await this.changes();\n await routes;\n if (\"result\" in changes) {\n const base = changes.result.base;\n this.options.logger.info(\n `review: compared \"${this.options.build}\" with ${base.requested} in ${this.lastDiffMs ?? 0} ms (started in ${Date.now() - started} ms)`,\n );\n } else {\n this.options.logger.warn(`review: ${changes.error}`);\n }\n }\n\n private stop(): void {\n this.on = false;\n this.connection = undefined;\n this.connecting = undefined;\n this.diffed = undefined;\n this.routes = undefined;\n this.pages.clear();\n if (this.timer) clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n private async refresh(): Promise<void> {\n if (!this.on) return;\n const connection = await this.connected();\n if (connection.state === \"on\") await connection.session.refresh();\n else this.connection = undefined;\n // A pull request found now changes the base.\n await this.connected();\n this.stale = true;\n await this.changes();\n }\n\n /** The pull request's review, opened once while review is on. */\n private connected(): Promise<Connection> {\n if (this.connection) return Promise.resolve(this.connection);\n if (this.problem !== undefined) {\n this.connection = {\n state: \"unprotected\",\n message: `Showing changes only. Comments are off because ${this.problem}.`,\n };\n return Promise.resolve(this.connection);\n }\n this.connecting ??= this.options\n .connect()\n .catch((error: unknown): Connection => ({ state: \"error\", message: messageOf(error) }))\n .then((connection) => {\n this.connecting = undefined;\n // The server turned out to listen on the network meanwhile.\n if (this.problem !== undefined) return this.connected();\n if (this.on) this.connection = connection;\n return connection;\n });\n return this.connecting;\n }\n\n /** The build's changes, compared again first if a rebuild made them stale. */\n private async changes(): Promise<{ result: DiffResult } | { error: string }> {\n while (this.diffing) await this.diffing;\n if (this.stale || this.diffed === undefined) {\n this.stale = false;\n this.diffing = this.compare().finally(() => {\n this.diffing = undefined;\n });\n await this.diffing;\n }\n return this.diffed ?? { error: \"Review is off.\" };\n }\n\n private async compare(): Promise<void> {\n const connection = await this.connected();\n const base = connection.state === \"on\" ? connection.base : undefined;\n const started = Date.now();\n try {\n this.diffed = { result: await this.options.diff(base) };\n } catch (error) {\n this.diffed = { error: messageOf(error) };\n }\n this.lastDiffMs = Date.now() - started;\n }\n\n private async pageView(params: Record<string, unknown>): Promise<PageView> {\n if (!this.on) throw new Error(\"Review is off.\");\n const route = normalizeRoute(typeof params[\"route\"] === \"string\" ? params[\"route\"] : \"/\");\n const fromPage = typeof params[\"path\"] === \"string\" ? params[\"path\"] : null;\n const [changes, connection, routes] = await Promise.all([\n this.changes(),\n this.connected(),\n this.readRoutes(),\n ]);\n const pages = \"result\" in changes ? changes.result.pages : [];\n const shown = pages.filter((p) => p.status !== \"removed\");\n let page = shown.find((p) => normalizeRoute(p.route) === route) ?? null;\n const pagePath = page?.path ?? fromPage ?? routes.get(route)?.path ?? null;\n if (page === null && pagePath !== null) page = shown.find((p) => p.path === pagePath) ?? null;\n return {\n kind: pagePath === null ? \"not-page\" : \"page\",\n path: pagePath,\n page,\n base: \"result\" in changes ? changes.result.base : null,\n problem: \"error\" in changes ? changes.error : null,\n errors: \"result\" in changes ? changes.result.errors : 0,\n threads: threadsState(connection),\n changedPages: pages.map((p) => listed(p, routes.get(normalizeRoute(p.route)))),\n contentRoot: this.options.contentRoot,\n separator: path.sep,\n };\n }\n\n /** The build's pages by route, read once until the next rebuild. */\n private readRoutes(): Promise<Map<string, RoutedPage>> {\n this.routes ??= this.options.readRoutes().catch(() => new Map<string, RoutedPage>());\n return this.routes;\n }\n\n /** Tells every page the threads or the changes changed; `from` is the page that did it. */\n private changed(from: string): void {\n this.options.channel.send(CHANGED_EVENT, { from });\n }\n}\n\n/** A changed page as the list shows it: without its changes, with its title. */\nfunction listed(page: PageDiff, routed: RoutedPage | undefined): ChangedPage {\n return {\n title: routed?.title ?? null,\n formatted_title: routed?.formatted_title ?? null,\n path: page.path,\n route: page.route,\n status: page.status,\n own_file_changed: page.own_file_changed,\n because: page.because,\n page_changed: page.page_changed,\n counts: page.counts,\n };\n}\n\n/** What the page shows about the threads. */\nfunction threadsState(connection: Connection): ThreadsState {\n if (connection.state !== \"on\") return connection;\n const pr: ReviewSession[\"pullRequest\"] = connection.session.pullRequest;\n return {\n state: \"on\",\n pullRequest: { number: pr.number, url: pr.url, baseRefName: pr.baseRefName },\n local: connection.local,\n };\n}\n\n/**\n * Why someone other than the developer could use the dev server's toolbar\n * channel, or `undefined` when they can't. Vite takes a browser's connection\n * only with the token in its client script, which the page's own origin can\n * read and, by default, only localhost origins can fetch. These settings undo\n * that, and a server listening on the network gives its pages, token and all,\n * to anyone who can reach it.\n */\nexport function channelProblem(config: {\n server: { cors?: unknown; allowedHosts?: unknown; host?: unknown };\n legacy?: { skipWebSocketTokenCheck?: unknown } | undefined;\n}): string | undefined {\n const pages = \"other web pages could talk to this dev server\";\n if (config.legacy?.skipWebSocketTokenCheck === true) {\n return `${pages}: \\`legacy.skipWebSocketTokenCheck\\` is on`;\n }\n if (config.server.allowedHosts === true) return `${pages}: \\`server.allowedHosts\\` is \\`true\\``;\n const cors = config.server.cors;\n const origin =\n typeof cors === \"object\" && cors !== null ? (cors as { origin?: unknown }).origin : undefined;\n if (cors === true || origin === true || origin === \"*\") {\n return `${pages}: \\`server.cors\\` lets any origin read its pages`;\n }\n if (!isLoopback(config.server.host)) return NETWORK_PROBLEM;\n return undefined;\n}\n\nconst NETWORK_PROBLEM =\n \"the dev server is listening on the network (`--host`), so anyone who can reach it could comment as you\";\n\n/** Whether Vite's `server.host`, or the address the server listens on, keeps the server on this machine: unset, `false`, or a loopback name. */\nfunction isLoopback(host: unknown): boolean {\n if (host === undefined || host === false) return true;\n if (typeof host !== \"string\") return false;\n const name = host.toLowerCase().replace(/^\\[(.*)\\]$/, \"$1\");\n return name === \"localhost\" || name === \"::1\" || /^127(\\.\\d{1,3}){3}$/.test(name);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/review/server.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yEAAyE;AACzE,8EAA8E;AAC9E,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,EAAE;AACF,0EAA0E;AAC1E,0EAA0E;AAC1E,2EAA2E;AAC3E,4BAA4B;AAC5B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAI5C,OAAO,EAAE,SAAS,EAAmB,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,GAQb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAmB,MAAM,aAAa,CAAC;AA4C9D,uDAAuD;AACvD,MAAM,OAAO,GAAwB,IAAI,GAAG,CAAgB;IAC1D,MAAM;IACN,eAAe;IACf,SAAS;IACT,OAAO;IACP,YAAY;IACZ,SAAS;IACT,QAAQ;IACR,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,OAAO,YAAY;IAeM,OAAO;IAd5B,EAAE,GAAG,KAAK,CAAC;IACX,OAAO,CAAqB;IAC5B,UAAU,CAAyB;IACnC,UAAU,CAAkC;IAC5C,MAAM,CAAyD;IAC/D,OAAO,CAA4B;IACnC,KAAK,GAAG,KAAK,CAAC;IACd,KAAK,CAA4C;IACjD,MAAM,CAA+C;IAC7D,iFAAiF;IAChE,KAAK,GAAG,IAAI,GAAG,EAAmB,CAAC;IACpD,0DAA0D;IAC1D,UAAU,CAAqB;IAE/B,YAA6B,OAA4B;uBAA5B,OAAO;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;QACtC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACjD,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,OAAe;QACvB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO;QAC9D,IAAI,CAAC,OAAO,GAAG,eAAe,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,gFAAgF;IAChF,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,wFAAwF;IACxF,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,OAAO;QACrB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,IAAI,CAAC,KAAK;YAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC3B,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;YACvB,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,GAAG,CAAC,CAAC;IACrC,CAAC;IAED,8BAA8B;IAC9B,OAAO;QACL,IAAI,IAAI,CAAC,KAAK;YAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,IAAa,EAAE,MAAiC;QACpE,MAAM,OAAO,GAAG,IAA+B,CAAC;QAChD,IAAI,OAAO,OAAO,EAAE,EAAE,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO;QAClF,MAAM,MAAM,GACV,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,MAAM,GAAG,GAAG,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,MAAM,MAAM,GAAW,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;QAC/C,IAAI,CAAC;YACH,MAAM,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;QACjE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,GAAI,KAA4B,CAAC,IAAI,CAAC;YAChD,MAAM,CAAC,KAAK;gBACV,OAAO,IAAI,KAAK,QAAQ;oBACtB,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE;oBACrC,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,CAAC;QACD,IAAI,OAAO,MAAM,EAAE,IAAI,KAAK,UAAU;YAAE,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;;YACrE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,MAA+B,EAAE,GAAG,GAAG,EAAE;QACpE,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,QAAQ;gBACX,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAmB,CAAC;YAC1C,KAAK,OAAO;gBACV,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;gBACnB,OAAO,EAAE,EAAE,EAAE,IAAI,EAAmB,CAAC;YACvC,KAAK,MAAM;gBACT,IAAI,CAAC,IAAI,EAAE,CAAC;gBACZ,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAClB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAmB,CAAC;YACxC,KAAK,MAAM;gBACT,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC/B,KAAK,SAAS;gBACZ,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;gBACrB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAClB,OAAO,IAAI,CAAC;YACd,KAAK,QAAQ;gBACX,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,CAAC;QAC/C,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,GAAG,CAAC,CAAC;QACzE,MAAM,UAAU,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAChE,IAAI,UAAU,EAAE,KAAK,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAC9E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;QACvD,MAAM,QAAQ,GAAG,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACjF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CACvC;YACE,OAAO,EAAE,UAAU,CAAC,OAAO;YAC3B,IAAI,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE;YACxF,QAAQ,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;YACvE,YAAY,EAAE,KAAK,IAAI,EAAE;gBACvB,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;gBACjF,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC;oBAAE,OAAO,EAAE,CAAC;gBACtC,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACzF,CAAC;SACF,EACD,MAAuB,EACvB,MAAM,CACP,CAAC;QACF,IAAI,MAAM,CAAC,IAAI;YAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAC/D,IAAI,MAAM,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,MAAM,CAAC,MAA+B;QAClD,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAA8B,CAAC;QAC/D,MAAM,QAAQ,GAAG,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACjF,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1C,QAAQ,OAAO,EAAE,IAAI,EAAE,CAAC;YACtB,KAAK,QAAQ,CAAC;YACd,KAAK,cAAc,EAAE,CAAC;gBACpB,IAAI,UAAU,CAAC,KAAK,KAAK,IAAI;oBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;gBAC7E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;gBACvD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;gBAC7C,OAAO,MAAM,CAAC,kBAAkB,CAC9B;oBACE,OAAO,EAAE,UAAU,CAAC,OAAO;oBAC3B,OAAO,EAAE,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;oBACtD,YAAY,EAAE,KAAK,IAAI,EAAE;wBACvB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;wBACrC,OAAO,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;oBACzD,CAAC;oBACD,6EAA6E;oBAC7E,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CACnB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAC/D,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EACpC,GAAG,EAAE,CAAC,SAAS,CAChB;iBACJ,EACD,OAAO,CACR,CAAC;YACJ,CAAC;YACD,KAAK,cAAc,CAAC;YACpB,KAAK,gBAAgB,EAAE,CAAC;gBACtB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;gBAC7E,IAAI,MAAM,KAAK,SAAS;oBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;gBAC/E,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;gBACrE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC3C,CAAC;YACD;gBACE,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,IAAI,IAAI,CAAC,EAAE;YAAE,OAAO;QACpB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACjE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,yCAAyC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxF,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,MAAM,CAAC;QACb,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CACtB,qBAAqB,IAAI,CAAC,OAAO,CAAC,KAAK,UAAU,IAAI,CAAC,SAAS,OAAO,IAAI,CAAC,UAAU,IAAI,CAAC,mBAAmB,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,MAAM,CACxI,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAEO,IAAI;QACV,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC;QAChB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACnB,IAAI,IAAI,CAAC,KAAK;YAAE,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACzB,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,OAAO;QACrB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,UAAU,CAAC,KAAK,KAAK,IAAI;YAAE,MAAM,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;;YAC7D,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QACjC,6CAA6C;QAC7C,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;QAClB,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;IAED,iEAAiE;IACzD,SAAS;QACf,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7D,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,UAAU,GAAG;gBAChB,KAAK,EAAE,aAAa;gBACpB,OAAO,EAAE,kDAAkD,IAAI,CAAC,OAAO,GAAG;aAC3E,CAAC;YACF,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1C,CAAC;QACD,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,OAAO;aAC7B,OAAO,EAAE;aACT,KAAK,CAAC,CAAC,KAAc,EAAc,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aACtF,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;YACnB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC5B,4DAA4D;YAC5D,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;gBAAE,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;YACxD,IAAI,IAAI,CAAC,EAAE;gBAAE,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;YAC1C,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,CAAC;QACL,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,8EAA8E;IACtE,KAAK,CAAC,OAAO;QACnB,OAAO,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,CAAC,OAAO,CAAC;QACxC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC5C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;YACnB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;gBACzC,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YAC3B,CAAC,CAAC,CAAC;YACH,MAAM,IAAI,CAAC,OAAO,CAAC;QACrB,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,IAAI,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IACpD,CAAC;IAEO,KAAK,CAAC,OAAO;QACnB,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,MAA+B;QACpD,IAAI,CAAC,IAAI,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC1F,MAAM,QAAQ,GAAG,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5E,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACtD,IAAI,CAAC,OAAO,EAAE;YACd,IAAI,CAAC,SAAS,EAAE;YAChB,IAAI,CAAC,UAAU,EAAE;SAClB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC1D,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC;QACxE,MAAM,QAAQ,GAAG,IAAI,EAAE,IAAI,IAAI,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,IAAI,CAAC;QAC3E,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI;YAAE,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,CAAC;QAC9F,OAAO;YACL,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;YAC7C,IAAI,EAAE,QAAQ;YACd,IAAI;YACJ,IAAI,EAAE,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI;YACtD,OAAO,EAAE,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI;YAClD,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACvD,OAAO,EAAE,YAAY,CAAC,UAAU,CAAC;YACjC,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YAC9E,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YACrC,SAAS,EAAE,IAAI,CAAC,GAAG;SACpB,CAAC;IACJ,CAAC;IAED,oEAAoE;IAC5D,UAAU;QAChB,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,GAAG,EAAsB,CAAC,CAAC;QACrF,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,2FAA2F;IACnF,OAAO,CAAC,IAAY;QAC1B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC;CACF;AAED,gFAAgF;AAChF,SAAS,MAAM,CAAC,IAAc,EAAE,MAA8B;IAC5D,OAAO;QACL,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,IAAI;QAC5B,eAAe,EAAE,MAAM,EAAE,eAAe,IAAI,IAAI;QAChD,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC;AACJ,CAAC;AAED,6CAA6C;AAC7C,SAAS,YAAY,CAAC,UAAsB;IAC1C,IAAI,UAAU,CAAC,KAAK,KAAK,IAAI;QAAE,OAAO,UAAU,CAAC;IACjD,MAAM,EAAE,GAAiC,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC;IACxE,OAAO;QACL,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,CAAC,WAAW,EAAE;QAC5E,KAAK,EAAE,UAAU,CAAC,KAAK;KACxB,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAAC,MAG9B;IACC,MAAM,KAAK,GAAG,+CAA+C,CAAC;IAC9D,IAAI,MAAM,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,EAAE,CAAC;QACpD,OAAO,GAAG,KAAK,4CAA4C,CAAC;IAC9D,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,KAAK,IAAI;QAAE,OAAO,GAAG,KAAK,uCAAuC,CAAC;IAChG,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IAChC,MAAM,MAAM,GACV,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAE,IAA6B,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAChG,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;QACvD,OAAO,GAAG,KAAK,kDAAkD,CAAC;IACpE,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,eAAe,CAAC;IAC5D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,eAAe,GACnB,wGAAwG,CAAC;AAE3G,gJAAgJ;AAChJ,SAAS,UAAU,CAAC,IAAa;IAC/B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAC5D,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpF,CAAC","sourcesContent":["// Review in the site preview, on the dev server's side: everything the\n// toolbar app asks for, answered over Astro's dev toolbar channel. Every\n// `git`, `ascribe`, and `gh` call happens here; the page gets the changes and\n// the threads, never a token. There are no HTTP routes: any page open in the\n// same browser could call a route on localhost, while the toolbar's channel\n// takes only clients that have the dev server's token (`channelProblem`).\n//\n// Nothing runs until the reviewer turns the app on. Then the server opens\n// the pull request's review (when the GitHub CLI finds one), compares the\n// build with the pull request's base (or the default branch), and compares\n// again after each rebuild.\nimport path from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport type { OverlayMethod, PromptRequest, ReviewSession } from \"@ascribed/review/github\";\nimport type { PageRef } from \"@ascribed/review/place\";\nimport type { DiffResult } from \"./diff.js\";\nimport { messageOf, type Connection } from \"./github.js\";\nimport {\n CHANGED_EVENT,\n REQUEST_EVENT,\n RESULT_EVENT,\n type ChangedPage,\n type PageDiff,\n type PageView,\n type Request,\n type Result,\n type Status,\n type ThreadsState,\n} from \"./protocol.js\";\nimport { normalizeRoute, type RoutedPage } from \"./routes.js\";\n\n/** A page's end of the channel: the server answers it alone. */\nexport interface ChannelClient {\n send(event: string, payload?: unknown): void;\n}\n\n/**\n * The toolbar's channel, as `astro:server:setup`'s `toolbar` has it. Its\n * listeners get the sending page's client too (it is Vite's `server.hot`);\n * without one, an answer goes to every page, which tell theirs by `tab`.\n */\nexport interface ToolbarChannel {\n on(event: string, callback: (data: unknown, client?: ChannelClient) => void): void;\n send(event: string, payload: unknown): void;\n}\n\nexport interface ReviewServerOptions {\n channel: ToolbarChannel;\n logger: { info(message: string): void; warn(message: string): void };\n build: string;\n /** The content root, absolute. */\n contentRoot: string;\n /** Why someone else could use the channel, if they could: then GitHub stays off. */\n channelProblem: string | undefined;\n /** Compares the build with `base` (the default branch when `undefined`). */\n diff(base: string | undefined): Promise<DiffResult>;\n /**\n * `ascribe diff`'s agent prompt about the page or fragment at a content\n * path, against `base`; `undefined` when it didn't change.\n */\n prompt(path: string, base: string | undefined): Promise<string | undefined>;\n /** The project's folder: where its `ascribe.toml` is. */\n projectDir: string;\n /** Opens the pull request's review. */\n connect(): Promise<Connection>;\n /** Writes the build's JSON output, in turn with the dev server's builds: it has the routes. */\n writeRoutes(): Promise<void>;\n /** Every page of the build, by normalized route. */\n readRoutes(): Promise<Map<string, RoutedPage>>;\n /** How long to wait after a rebuild before comparing again. Default 150 ms. */\n debounceMs?: number;\n}\n\n/** The overlay's requests, which go to the session. */\nconst OVERLAY: ReadonlySet<string> = new Set<OverlayMethod>([\n \"load\",\n \"commentTarget\",\n \"comment\",\n \"reply\",\n \"allThreads\",\n \"resolve\",\n \"submit\",\n \"discard\",\n]);\n\nexport class ReviewServer {\n private on = false;\n private problem: string | undefined;\n private connection: Connection | undefined;\n private connecting: Promise<Connection> | undefined;\n private diffed: { result: DiffResult } | { error: string } | undefined;\n private diffing: Promise<void> | undefined;\n private stale = false;\n private timer: ReturnType<typeof setTimeout> | undefined;\n private routes: Promise<Map<string, RoutedPage>> | undefined;\n /** The page each overlay last read, by content path: comments are made on it. */\n private readonly pages = new Map<string, PageRef>();\n /** How long the last comparison took, in milliseconds. */\n lastDiffMs: number | undefined;\n\n constructor(private readonly options: ReviewServerOptions) {\n this.problem = options.channelProblem;\n options.channel.on(REQUEST_EVENT, (data, client) => {\n void this.receive(data, client);\n });\n }\n\n /**\n * The address the dev server listens on, once it does: one other machines\n * can reach turns comments off, as `server.host` does.\n */\n listening(address: string): void {\n if (this.problem !== undefined || isLoopback(address)) return;\n this.problem = NETWORK_PROBLEM;\n this.connection = undefined;\n this.connecting = undefined;\n }\n\n /** Whether review is on: then the dev server's builds write JSON output too. */\n get active(): boolean {\n return this.on;\n }\n\n /** The dev server rebuilt: compare again soon, and read the routes again when asked. */\n rebuilt(): void {\n if (!this.on) return;\n this.stale = true;\n this.routes = undefined;\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.timer = undefined;\n void this.changes();\n }, this.options.debounceMs ?? 150);\n }\n\n /** The dev server stopped. */\n dispose(): void {\n if (this.timer) clearTimeout(this.timer);\n this.on = false;\n }\n\n private async receive(data: unknown, client: ChannelClient | undefined): Promise<void> {\n const request = data as Partial<Request> | null;\n if (typeof request?.id !== \"number\" || typeof request.method !== \"string\") return;\n const params =\n typeof request.params === \"object\" && request.params !== null ? request.params : {};\n const tab = typeof request.tab === \"string\" ? request.tab : \"\";\n const answer: Result = { tab, id: request.id };\n try {\n answer.result = await this.handle(request.method, params, tab);\n } catch (error) {\n const code = (error as { code?: unknown }).code;\n answer.error =\n typeof code === \"string\"\n ? { message: messageOf(error), code }\n : { message: messageOf(error) };\n }\n if (typeof client?.send === \"function\") client.send(RESULT_EVENT, answer);\n else this.options.channel.send(RESULT_EVENT, answer);\n }\n\n /** Answers one request. Rejects with a sentence to show. */\n async handle(method: string, params: Record<string, unknown>, tab = \"\"): Promise<unknown> {\n switch (method) {\n case \"status\":\n return { on: this.on } satisfies Status;\n case \"start\":\n await this.start();\n return { on: true } satisfies Status;\n case \"stop\":\n this.stop();\n this.changed(tab);\n return { on: false } satisfies Status;\n case \"page\":\n return this.pageView(params);\n case \"refresh\":\n await this.refresh();\n this.changed(tab);\n return null;\n case \"prompt\":\n return (await this.prompt(params)) ?? null;\n }\n if (!OVERLAY.has(method)) throw new Error(`Unknown request: ${method}.`);\n const connection = this.on ? await this.connected() : undefined;\n if (connection?.state !== \"on\") throw new Error(\"Review comments aren't on.\");\n const github = await import(\"@ascribed/review/github\");\n const pagePath = typeof params[\"path\"] === \"string\" ? params[\"path\"] : undefined;\n const answer = await github.answerRequest(\n {\n session: connection.session,\n page: pagePath === undefined ? undefined : { build: this.options.build, path: pagePath },\n lastPage: pagePath === undefined ? undefined : this.pages.get(pagePath),\n changedPages: async () => {\n const [changes, routes] = await Promise.all([this.changes(), this.readRoutes()]);\n if (!(\"result\" in changes)) return [];\n return changes.result.pages.map((p) => listed(p, routes.get(normalizeRoute(p.route))));\n },\n },\n method as OverlayMethod,\n params,\n );\n if (answer.page) this.pages.set(answer.page.path, answer.page);\n if (answer.changed) this.changed(tab);\n return answer.result;\n }\n\n /**\n * The agent prompt the page asked for: a thread's, or every open one's,\n * from the pull request; the page's changes, or a fragment's reach, from\n * `ascribe diff`. `undefined` when there's nothing to prompt about.\n */\n private async prompt(params: Record<string, unknown>): Promise<string | undefined> {\n if (!this.on) throw new Error(\"Review is off.\");\n const request = params[\"request\"] as PromptRequest | undefined;\n const pagePath = typeof params[\"path\"] === \"string\" ? params[\"path\"] : undefined;\n const connection = await this.connected();\n switch (request?.kind) {\n case \"thread\":\n case \"open-threads\": {\n if (connection.state !== \"on\") throw new Error(\"Review comments aren't on.\");\n const github = await import(\"@ascribed/review/github\");\n const contentRoot = this.options.contentRoot;\n return github.buildThreadsPrompt(\n {\n session: connection.session,\n project: github.promptProject(this.options.projectDir),\n changedPages: async () => {\n const changes = await this.changes();\n return \"result\" in changes ? changes.result.pages : [];\n },\n // The agent reads the files on disk, as the dev server does: no editor here.\n readSource: (file) =>\n readFile(path.join(contentRoot, ...file.split(\"/\")), \"utf8\").then(\n (text) => ({ text, unsaved: false }),\n () => undefined,\n ),\n },\n request,\n );\n }\n case \"page-changes\":\n case \"fragment-reach\": {\n const target = request.kind === \"page-changes\" ? pagePath : request.fragment;\n if (target === undefined) throw new Error(\"That isn't a page of the project.\");\n const base = connection.state === \"on\" ? connection.base : undefined;\n return this.options.prompt(target, base);\n }\n default:\n throw new Error(\"Unknown prompt.\");\n }\n }\n\n private async start(): Promise<void> {\n if (this.on) return;\n this.on = true;\n this.stale = true;\n this.routes = undefined;\n this.connection = undefined;\n const started = Date.now();\n // The routes come from the JSON output, which the next builds write too.\n const routes = this.options.writeRoutes().catch((error: unknown) => {\n this.options.logger.warn(`couldn't write the routes for review: ${messageOf(error)}`);\n });\n await this.connected();\n const changes = await this.changes();\n await routes;\n if (\"result\" in changes) {\n const base = changes.result.base;\n this.options.logger.info(\n `review: compared \"${this.options.build}\" with ${base.requested} in ${this.lastDiffMs ?? 0} ms (started in ${Date.now() - started} ms)`,\n );\n } else {\n this.options.logger.warn(`review: ${changes.error}`);\n }\n }\n\n private stop(): void {\n this.on = false;\n this.connection = undefined;\n this.connecting = undefined;\n this.diffed = undefined;\n this.routes = undefined;\n this.pages.clear();\n if (this.timer) clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n private async refresh(): Promise<void> {\n if (!this.on) return;\n const connection = await this.connected();\n if (connection.state === \"on\") await connection.session.refresh();\n else this.connection = undefined;\n // A pull request found now changes the base.\n await this.connected();\n this.stale = true;\n await this.changes();\n }\n\n /** The pull request's review, opened once while review is on. */\n private connected(): Promise<Connection> {\n if (this.connection) return Promise.resolve(this.connection);\n if (this.problem !== undefined) {\n this.connection = {\n state: \"unprotected\",\n message: `Showing changes only. Comments are off because ${this.problem}.`,\n };\n return Promise.resolve(this.connection);\n }\n this.connecting ??= this.options\n .connect()\n .catch((error: unknown): Connection => ({ state: \"error\", message: messageOf(error) }))\n .then((connection) => {\n this.connecting = undefined;\n // The server turned out to listen on the network meanwhile.\n if (this.problem !== undefined) return this.connected();\n if (this.on) this.connection = connection;\n return connection;\n });\n return this.connecting;\n }\n\n /** The build's changes, compared again first if a rebuild made them stale. */\n private async changes(): Promise<{ result: DiffResult } | { error: string }> {\n while (this.diffing) await this.diffing;\n if (this.stale || this.diffed === undefined) {\n this.stale = false;\n this.diffing = this.compare().finally(() => {\n this.diffing = undefined;\n });\n await this.diffing;\n }\n return this.diffed ?? { error: \"Review is off.\" };\n }\n\n private async compare(): Promise<void> {\n const connection = await this.connected();\n const base = connection.state === \"on\" ? connection.base : undefined;\n const started = Date.now();\n try {\n this.diffed = { result: await this.options.diff(base) };\n } catch (error) {\n this.diffed = { error: messageOf(error) };\n }\n this.lastDiffMs = Date.now() - started;\n }\n\n private async pageView(params: Record<string, unknown>): Promise<PageView> {\n if (!this.on) throw new Error(\"Review is off.\");\n const route = normalizeRoute(typeof params[\"route\"] === \"string\" ? params[\"route\"] : \"/\");\n const fromPage = typeof params[\"path\"] === \"string\" ? params[\"path\"] : null;\n const [changes, connection, routes] = await Promise.all([\n this.changes(),\n this.connected(),\n this.readRoutes(),\n ]);\n const pages = \"result\" in changes ? changes.result.pages : [];\n const shown = pages.filter((p) => p.status !== \"removed\");\n let page = shown.find((p) => normalizeRoute(p.route) === route) ?? null;\n const pagePath = page?.path ?? fromPage ?? routes.get(route)?.path ?? null;\n if (page === null && pagePath !== null) page = shown.find((p) => p.path === pagePath) ?? null;\n return {\n kind: pagePath === null ? \"not-page\" : \"page\",\n path: pagePath,\n page,\n base: \"result\" in changes ? changes.result.base : null,\n problem: \"error\" in changes ? changes.error : null,\n errors: \"result\" in changes ? changes.result.errors : 0,\n threads: threadsState(connection),\n changedPages: pages.map((p) => listed(p, routes.get(normalizeRoute(p.route)))),\n contentRoot: this.options.contentRoot,\n separator: path.sep,\n };\n }\n\n /** The build's pages by route, read once until the next rebuild. */\n private readRoutes(): Promise<Map<string, RoutedPage>> {\n this.routes ??= this.options.readRoutes().catch(() => new Map<string, RoutedPage>());\n return this.routes;\n }\n\n /** Tells every page the threads or the changes changed; `from` is the page that did it. */\n private changed(from: string): void {\n this.options.channel.send(CHANGED_EVENT, { from });\n }\n}\n\n/** A changed page as the list shows it: without its changes, with its title. */\nfunction listed(page: PageDiff, routed: RoutedPage | undefined): ChangedPage {\n return {\n title: routed?.title ?? null,\n formatted_title: routed?.formatted_title ?? null,\n path: page.path,\n route: page.route,\n status: page.status,\n own_file_changed: page.own_file_changed,\n because: page.because,\n page_changed: page.page_changed,\n counts: page.counts,\n };\n}\n\n/** What the page shows about the threads. */\nfunction threadsState(connection: Connection): ThreadsState {\n if (connection.state !== \"on\") return connection;\n const pr: ReviewSession[\"pullRequest\"] = connection.session.pullRequest;\n return {\n state: \"on\",\n pullRequest: { number: pr.number, url: pr.url, baseRefName: pr.baseRefName },\n local: connection.local,\n };\n}\n\n/**\n * Why someone other than the developer could use the dev server's toolbar\n * channel, or `undefined` when they can't. Vite takes a browser's connection\n * only with the token in its client script, which the page's own origin can\n * read and, by default, only localhost origins can fetch. These settings undo\n * that, and a server listening on the network gives its pages, token and all,\n * to anyone who can reach it.\n */\nexport function channelProblem(config: {\n server: { cors?: unknown; allowedHosts?: unknown; host?: unknown };\n legacy?: { skipWebSocketTokenCheck?: unknown } | undefined;\n}): string | undefined {\n const pages = \"other web pages could talk to this dev server\";\n if (config.legacy?.skipWebSocketTokenCheck === true) {\n return `${pages}: \\`legacy.skipWebSocketTokenCheck\\` is on`;\n }\n if (config.server.allowedHosts === true) return `${pages}: \\`server.allowedHosts\\` is \\`true\\``;\n const cors = config.server.cors;\n const origin =\n typeof cors === \"object\" && cors !== null ? (cors as { origin?: unknown }).origin : undefined;\n if (cors === true || origin === true || origin === \"*\") {\n return `${pages}: \\`server.cors\\` lets any origin read its pages`;\n }\n if (!isLoopback(config.server.host)) return NETWORK_PROBLEM;\n return undefined;\n}\n\nconst NETWORK_PROBLEM =\n \"the dev server is listening on the network (`--host`), so anyone who can reach it could comment as you\";\n\n/** Whether Vite's `server.host`, or the address the server listens on, keeps the server on this machine: unset, `false`, or a loopback name. */\nfunction isLoopback(host: unknown): boolean {\n if (host === undefined || host === false) return true;\n if (typeof host !== \"string\") return false;\n const name = host.toLowerCase().replace(/^\\[(.*)\\]$/, \"$1\");\n return name === \"localhost\" || name === \"::1\" || /^127(\\.\\d{1,3}){3}$/.test(name);\n}\n"]}
|
package/dist/run.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/** What a successful build printed. */
|
|
2
2
|
export interface BuildResult {
|
|
3
|
-
/** The checks' report, which is more than a summary line when there
|
|
3
|
+
/** The checks' report, which is more than a summary line when there is anything to report. */
|
|
4
4
|
diagnostics: string;
|
|
5
5
|
/** The `built <build>/site: …` lines. */
|
|
6
6
|
summary: string;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* Whether a build's report is worth a warning: anything but a
|
|
10
|
-
* summary line, which is logged at info
|
|
9
|
+
* Whether a build's report is worth a warning: anything but a report whose
|
|
10
|
+
* summary line counts no errors and no warnings, which is logged at info
|
|
11
|
+
* level, advice and all.
|
|
11
12
|
*/
|
|
12
13
|
export declare function hasWarnings(diagnostics: string): boolean;
|
|
13
14
|
/**
|
|
@@ -31,5 +32,5 @@ export declare function runBuild(options: {
|
|
|
31
32
|
/** Write source anchors (`--anchors`). */
|
|
32
33
|
anchors?: boolean;
|
|
33
34
|
/** The outputs to write. Default: `["site"]`. */
|
|
34
|
-
outputs?: readonly ("site" | "json")[];
|
|
35
|
+
outputs?: readonly ("site" | "plain" | "json")[];
|
|
35
36
|
}): Promise<BuildResult>;
|
package/dist/run.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
// Running `ascribe build` for the configured build: its site output, and
|
|
2
2
|
// its JSON output while review is on.
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
|
-
/** A check summary with nothing to report: `checked 14 files: 0 errors, 0 warnings`. */
|
|
5
|
-
const CLEAN = /^checked [^:\n]*: 0 errors, 0 warnings$/;
|
|
6
4
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* A check's summary line, the report's last: `checked 14 files: 0 errors, 0 warnings`,
|
|
6
|
+
* with `, 3 advice` after when there is advice.
|
|
7
|
+
*/
|
|
8
|
+
const SUMMARY = /^checked [^:\n]*: (\d+) errors?, (\d+) warnings?(?:, \d+ advice)?$/;
|
|
9
|
+
/**
|
|
10
|
+
* Whether a build's report is worth a warning: anything but a report whose
|
|
11
|
+
* summary line counts no errors and no warnings, which is logged at info
|
|
12
|
+
* level, advice and all.
|
|
9
13
|
*/
|
|
10
14
|
export function hasWarnings(diagnostics) {
|
|
11
|
-
|
|
15
|
+
if (diagnostics === "")
|
|
16
|
+
return false;
|
|
17
|
+
const summary = SUMMARY.exec(diagnostics.trimEnd().split("\n").at(-1) ?? "");
|
|
18
|
+
return summary === null || summary[1] !== "0" || summary[2] !== "0";
|
|
12
19
|
}
|
|
13
20
|
/**
|
|
14
21
|
* Whether an Astro command builds with source anchors, given the integration's
|
package/dist/run.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,sCAAsC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAU9C
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,sCAAsC;AACtC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAU9C;;;GAGG;AACH,MAAM,OAAO,GAAG,oEAAoE,CAAC;AAErF;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,WAAmB;IAC7C,IAAI,WAAW,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACrC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,OAAO,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CACxB,OAAoC,EACpC,OAAe,EACf,MAAM,GAAG,KAAK;IAEd,OAAO,OAAO,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,OAAO,KAAK,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,0FAA0F;AAC1F,MAAM,UAAU,SAAS,CAAC,MAA2B,EAAE,OAAe;IACpE,OAAO,OAAO,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,OASxB;IACC,MAAM,IAAI,GAAG;QACX,OAAO;QACP,QAAQ;QACR,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACvC,SAAS;QACT,OAAO,CAAC,KAAK;QACb,UAAU;QACV,OAAO,CAAC,UAAU;QAClB,SAAS;QACT,OAAO;KACR,CAAC;IACF,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI;QAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACrD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CACN,OAAO,CAAC,MAAM,EACd,IAAI,EACJ,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,EACjD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;gBAC3C,MAAM,CACJ,IAAI,KAAK,CACP,2BAA2B,OAAO,CAAC,KAAK,YAAY,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,EAAE,EAC3G,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YACD,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAClE,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["// Running `ascribe build` for the configured build: its site output, and\n// its JSON output while review is on.\nimport { execFile } from \"node:child_process\";\n\n/** What a successful build printed. */\nexport interface BuildResult {\n /** The checks' report, which is more than a summary line when there is anything to report. */\n diagnostics: string;\n /** The `built <build>/site: …` lines. */\n summary: string;\n}\n\n/**\n * A check's summary line, the report's last: `checked 14 files: 0 errors, 0 warnings`,\n * with `, 3 advice` after when there is advice.\n */\nconst SUMMARY = /^checked [^:\\n]*: (\\d+) errors?, (\\d+) warnings?(?:, \\d+ advice)?$/;\n\n/**\n * Whether a build's report is worth a warning: anything but a report whose\n * summary line counts no errors and no warnings, which is logged at info\n * level, advice and all.\n */\nexport function hasWarnings(diagnostics: string): boolean {\n if (diagnostics === \"\") return false;\n const summary = SUMMARY.exec(diagnostics.trimEnd().split(\"\\n\").at(-1) ?? \"\");\n return summary === null || summary[1] !== \"0\" || summary[2] !== \"0\";\n}\n\n/**\n * Whether an Astro command builds with source anchors, given the integration's\n * `anchors` option: `\"dev\"` means in `astro dev` only, so `astro build` has\n * them only when it is `true`. Review in the site preview (`review`) needs\n * them in `astro dev`.\n */\nexport function anchorsFor(\n anchors: boolean | \"dev\" | undefined,\n command: string,\n review = false,\n): boolean {\n return anchors === true || (command === \"dev\" && (anchors === \"dev\" || review));\n}\n\n/** Whether an Astro command has review in the site preview, given the `review` option. */\nexport function reviewFor(review: boolean | undefined, command: string): boolean {\n return command === \"dev\" && review !== false;\n}\n\n/**\n * Builds one build's outputs, the site output by default. Rejects, with the compiler's report as the\n * message, if the build has errors: the Astro build fails with them.\n */\nexport function runBuild(options: {\n binary: string;\n configPath: string;\n build: string;\n cwd: string;\n /** Write source anchors (`--anchors`). */\n anchors?: boolean;\n /** The outputs to write. Default: `[\"site\"]`. */\n outputs?: readonly (\"site\" | \"plain\" | \"json\")[];\n}): Promise<BuildResult> {\n const args = [\n \"build\",\n \"--emit\",\n (options.outputs ?? [\"site\"]).join(\",\"),\n \"--build\",\n options.build,\n \"--config\",\n options.configPath,\n \"--color\",\n \"never\",\n ];\n if (options.anchors === true) args.push(\"--anchors\");\n return new Promise((resolve, reject) => {\n execFile(\n options.binary,\n args,\n { cwd: options.cwd, maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error !== null) {\n const report = `${stdout}${stderr}`.trim();\n reject(\n new Error(\n `\\`ascribe build --build ${options.build}\\` failed${report === \"\" ? `: ${error.message}` : `:\\n${report}`}`,\n { cause: error },\n ),\n );\n return;\n }\n resolve({ diagnostics: stdout.trim(), summary: stderr.trim() });\n },\n );\n });\n}\n"]}
|