@norskvideo/ctl-dev-kit 0.1.54 → 0.1.56

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.
@@ -50,6 +50,11 @@ COPY dashboards /usr/src/dashboards
50
50
  COPY components/lib /usr/src/components/lib
51
51
  # Optional product-template assets (funke's slate/black PNGs). Empty otherwise.
52
52
  COPY assets /usr/src/assets
53
+ # The docs bundle (docs/generated/bundle/ on the host), served by the product
54
+ # at /docs. Beside backend/ and frontend/ so server.ts reaches it the same way
55
+ # it reaches frontend/dist (../../docs from the bundle dir). Empty when the
56
+ # product built none.
57
+ COPY docs ./docs
53
58
  ENV PORT=${PRODUCT_PORT}
54
59
  EXPOSE ${PRODUCT_PORT}
55
60
  CMD ["/usr/local/bin/bun", "run", "backend/dist/index.js"]
@@ -23,6 +23,7 @@
23
23
  # components/lib the components workspace's build output
24
24
  # native/ native addon(s) (probe's signer) -> beside bundle
25
25
  # assets/ product-template assets (funke's PNGs)
26
+ # docs/generated/bundle/ the docs bundle (doc-guide buildBundle) -> docs/
26
27
  # scripts/runtime-images.ts -> emits the norsk-ctl.runtime-images label
27
28
  #
28
29
  # Only backend/dist is required. A product with no frontend/dashboard/components
@@ -51,7 +52,7 @@ trap 'rm -rf "${stage}"' EXIT
51
52
 
52
53
  echo "==> staging artifacts"
53
54
  mkdir -p "${stage}/backend" "${stage}/frontend/dist" "${stage}/components/lib" \
54
- "${stage}/dashboards" "${stage}/native" "${stage}/assets"
55
+ "${stage}/dashboards" "${stage}/native" "${stage}/assets" "${stage}/docs"
55
56
  # The backend bundle is the one non-negotiable payload — every bundle product has
56
57
  # one, so a missing dist is a broken build, not an absent workspace.
57
58
  cp -R "${PRODUCT_DIR}/backend/dist" "${stage}/backend/dist"
@@ -87,6 +88,12 @@ if [ -d "${PRODUCT_DIR}/assets" ]; then
87
88
  cp -R "${PRODUCT_DIR}/assets/." "${stage}/assets/"
88
89
  fi
89
90
 
91
+ # The docs bundle (optional): ONLY the built bundle, never the hand-written
92
+ # docs/ tree beside it (planning notes, customer folders).
93
+ if [ -d "${PRODUCT_DIR}/docs/generated/bundle" ]; then
94
+ cp -R "${PRODUCT_DIR}/docs/generated/bundle/." "${stage}/docs/"
95
+ fi
96
+
90
97
  # Runtime images the default templates launch, stamped on as a label so a
91
98
  # golden-image bake can warm them with `docker inspect` + `docker pull` alone —
92
99
  # no daemon, no license. Derived from the rendered compose (scripts/runtime-images.ts),
@@ -264,7 +264,11 @@ export function demoProblems(repoRoot: string, canonicalShim: string): string[]
264
264
  const problems: string[] = [];
265
265
  const hasSpec = existsSync(join(repoRoot, DEMO_SPEC));
266
266
  const shimPath = join(repoRoot, DEMO_SHIM);
267
- if (existsSync(shimPath)) {
267
+ if (existsSync(shimPath) && statSync(shimPath).isDirectory()) {
268
+ problems.push(
269
+ `${DEMO_SHIM} is a directory; the convention puts the executable shim at that path — move the directory (scripts/demo-tools, say) and re-run sync-drift to write the shim.`,
270
+ );
271
+ } else if (existsSync(shimPath)) {
268
272
  const actual = readFileSync(shimPath, "utf8");
269
273
  if (actual !== canonicalShim) {
270
274
  problems.push(
@@ -95,9 +95,22 @@ they do not copy it:
95
95
  docsRoot, … })`, the Playwright config for the fixture/frontend tiers, carrying
96
96
  the CI defaults (bind+probe `127.0.0.1`, 120s webServer timeout, piped output).
97
97
  - `@norskvideo/ctl-dev-kit/doc-guide/build-manual` — `buildManual(spec, {
98
- docsRoot })`, the generic assembler. The product supplies ONLY the page content
99
- (`ManualSpec`: brand, overview, functionality[], examples[]); layout, CSS and
100
- routing live in the assembler so every product's manual looks the same.
98
+ docsRoot, provenance })`, the generic assembler. The product supplies ONLY the
99
+ page content (`ManualSpec`: brand, overview, functionality[], examples[], and
100
+ `links` the way out: the product's page on norsk.video, a contact); layout,
101
+ CSS and routing live in the assembler so every product's manual looks the
102
+ same. Every export ends in a provenance footer (build time, source sha, CI
103
+ run id — `provenanceFromEnv()` reads them from a GitHub Actions run).
104
+ - `@norskvideo/ctl-dev-kit/doc-guide/bundle` — `buildBundle(spec, { docsRoot,
105
+ outDir, provenance })`, the docs bundle: `bundle.json` + `pages/<id>.md` +
106
+ `captures/` + the derived `manual.html`/`manual-linked.html`, in one
107
+ directory. Write it to `docs/generated/bundle/`: the image build stages that
108
+ directory as the image's `docs/` and the backend serves it at `/docs` with
109
+ the SDK's `serveDocs(app, docsBundleDir(import.meta.dir))` — one line in
110
+ `server.ts`, a no-op when there is no bundle. Declare the nav entry in the
111
+ manifest: `ui.sidebarEntries: [{ label: "Documentation", route: "/docs/" }]`;
112
+ ctl links it under `/products/<name>/`. An older ctl simply does not link it
113
+ (additive: nothing about launch changes).
101
114
  - `@norskvideo/ctl-dev-kit/doc-guide/{instance-proxy,live-browser}` — engine-tier
102
115
  support (serve the baked dashboard under its advertised instance prefix; launch
103
116
  the nix chromium for a live capture).
@@ -18,7 +18,7 @@
18
18
  // cannot mechanically converge (e.g. a workflow with no product key to preserve)
19
19
  // is left for check-drift to flag, and a convention that genuinely breaks the
20
20
  // product surfaces as a red lint/typecheck/test — not as a silent bad copy.
21
- import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
21
+ import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
22
22
  import { dirname, join } from "node:path";
23
23
  import {
24
24
  BEGIN,
@@ -203,6 +203,12 @@ function syncDemoShim(repoRoot: string, canonical: string, r: SyncReport): void
203
203
  const rel = "scripts/demo";
204
204
  const path = join(repoRoot, rel);
205
205
  if (!existsSync(join(repoRoot, "tests/demo.spec.ts")) && !existsSync(path)) return;
206
+ // A product's own tooling may already live at scripts/demo/ (playout did):
207
+ // never write over it; the gate names it and says where the shim goes.
208
+ if (existsSync(path) && statSync(path).isDirectory()) {
209
+ r.skipped.push(`${rel} (a directory; move it, the shim goes here)`);
210
+ return;
211
+ }
206
212
  mkdirSync(dirname(path), { recursive: true });
207
213
  writeIfChanged(path, canonical, rel, r.written);
208
214
  chmodSync(path, 0o755);
@@ -34,6 +34,13 @@ export interface ManualOverview {
34
34
  /** The small print under the right column heading. */
35
35
  byExampleIntro: string;
36
36
  }
37
+ /** A way out of the manual: the product's marketing page, the platform docs,
38
+ * a contact. Rendered in the footer of every export. Hash links are the only
39
+ * navigation otherwise, so a manual without these is a cul-de-sac. */
40
+ export interface ManualLink {
41
+ label: string;
42
+ href: string;
43
+ }
37
44
  export interface ManualSpec {
38
45
  /** Sidebar wordmark + document brand. */
39
46
  brand: string;
@@ -42,7 +49,27 @@ export interface ManualSpec {
42
49
  overview: ManualOverview;
43
50
  functionality: ManualPage[];
44
51
  examples: ManualPage[];
52
+ /** Outbound links for the footer. Optional, but every published manual should
53
+ * carry at least one. */
54
+ links?: ManualLink[];
55
+ }
56
+ /** Where a build came from, stamped into the footer so two copies of "the
57
+ * manual" can be told apart without hashing them. `builtAt` defaults to now;
58
+ * the rest is whatever the build knows (CI knows all of it, see
59
+ * {@link provenanceFromEnv}). */
60
+ export interface Provenance {
61
+ /** Source commit the captures and copy were built from. */
62
+ sha?: string;
63
+ /** The CI run that built it, when there was one. */
64
+ runId?: string;
65
+ /** ISO-8601. */
66
+ builtAt?: string;
67
+ /** The product version, when the build knows it. */
68
+ version?: string;
45
69
  }
70
+ /** Provenance as a GitHub Actions run sees it (GITHUB_SHA / GITHUB_RUN_ID), or
71
+ * as much of it as a local build has. */
72
+ export declare function provenanceFromEnv(env?: NodeJS.ProcessEnv): Provenance;
46
73
  export interface BuildManualResult {
47
74
  /** Artifact-ready fragment (no doctype/head/body). */
48
75
  indexHtml: string;
@@ -55,6 +82,9 @@ export interface BuildManualResult {
55
82
  standaloneAssetHtml?: string;
56
83
  /** `<slug>/<file>` of every referenced capture not found on disk. */
57
84
  missing: string[];
85
+ /** `<slug>/<file>` -> the sidecar href it was written to. Only when `assets`
86
+ * is set; what a bundle's markdown pages reference captures by. */
87
+ sidecarHrefs?: Record<string, string>;
58
88
  }
59
89
  /** Where to write de-inlined captures for the standalone manual. `dir` is the
60
90
  * filesystem directory; `href` (default "images") is the relative URL prefix
@@ -63,7 +93,9 @@ export interface ManualAssets {
63
93
  dir: string;
64
94
  href?: string;
65
95
  }
66
- export declare function buildManual(spec: ManualSpec, opts: {
96
+ export interface BuildManualOptions {
67
97
  docsRoot: string;
68
98
  assets?: ManualAssets;
69
- }): BuildManualResult;
99
+ provenance?: Provenance;
100
+ }
101
+ export declare function buildManual(spec: ManualSpec, opts: BuildManualOptions): BuildManualResult;
@@ -26,6 +26,16 @@
26
26
  import { spawnSync } from "node:child_process";
27
27
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
28
28
  import { join } from "node:path";
29
+ /** Provenance as a GitHub Actions run sees it (GITHUB_SHA / GITHUB_RUN_ID), or
30
+ * as much of it as a local build has. */
31
+ export function provenanceFromEnv(env = process.env) {
32
+ const p = { builtAt: new Date().toISOString() };
33
+ if (env.GITHUB_SHA)
34
+ p.sha = env.GITHUB_SHA;
35
+ if (env.GITHUB_RUN_ID)
36
+ p.runId = env.GITHUB_RUN_ID;
37
+ return p;
38
+ }
29
39
  const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
30
40
  /** Resize a captured PNG to <=1100px wide JPEG. A missing capture becomes a
31
41
  * labelled placeholder so the manual still builds, and is recorded in `missing`. */
@@ -63,8 +73,22 @@ function fileUri(a, file, assets) {
63
73
  writeFileSync(join(assets.dir, name), a.kind === "svg" ? Buffer.from(a.text) : a.bytes);
64
74
  return `${assets.href ?? "images"}/${name}`;
65
75
  }
76
+ /** The footer every export carries: provenance first, then the way out. */
77
+ function footerHtml(spec, provenance) {
78
+ const facts = [];
79
+ if (provenance.version)
80
+ facts.push(`version ${esc(provenance.version)}`);
81
+ facts.push(`built ${esc(provenance.builtAt ?? new Date().toISOString())}`);
82
+ if (provenance.sha)
83
+ facts.push(`source <span class="mono">${esc(provenance.sha)}</span>`);
84
+ if (provenance.runId)
85
+ facts.push(`run <span class="mono">${esc(provenance.runId)}</span>`);
86
+ const links = (spec.links ?? []).map((l) => `<a href="${esc(l.href)}">${esc(l.label)}</a>`).join("");
87
+ return `<footer class="prov"><div class="facts">${esc(spec.brand)} &middot; ${facts.join(" &middot; ")}</div>${links ? `<div class="out">${links}</div>` : ""}</footer>`;
88
+ }
66
89
  export function buildManual(spec, opts) {
67
90
  const { docsRoot, assets } = opts;
91
+ const provenance = { builtAt: new Date().toISOString(), ...opts.provenance };
68
92
  const missing = [];
69
93
  // Each shot is loaded (resized) at most once, then rendered inline for the
70
94
  // fragment and — when `assets` is set — as a sidecar file for the standalone.
@@ -79,7 +103,12 @@ export function buildManual(spec, opts) {
79
103
  return a;
80
104
  };
81
105
  const inline = (slug, file) => inlineUri(load(slug, file));
82
- const external = (slug, file) => fileUri(load(slug, file), file, assets);
106
+ const sidecarHrefs = {};
107
+ const external = (slug, file) => {
108
+ const href = fileUri(load(slug, file), file, assets);
109
+ sidecarHrefs[`${slug}/${file}`] = href;
110
+ return href;
111
+ };
83
112
  const FN = spec.functionality;
84
113
  const EX = spec.examples;
85
114
  const ALL = [...FN, ...EX];
@@ -124,8 +153,7 @@ export function buildManual(spec, opts) {
124
153
  <a href="#overview" data-nav class="ovlink">Overview</a>
125
154
  <div class="ngroup"><div class="ghead">Functionality</div>${FN.map((p) => `<a href="#${p.id}" data-nav>${esc(p.nav)}</a>`).join("")}</div>
126
155
  <div class="ngroup"><div class="ghead">Examples <span class="ct">${EX.length}</span></div>${EX.map((p) => `<a href="#${p.id}" data-nav>${esc(p.nav)}</a>`).join("")}</div>`;
127
- const html = `<title>${esc(spec.title)}</title>
128
- <style>
156
+ const html = `<style>
129
157
  :root{--bg:#0a0d12;--bg2:#0c1118;--surf:#131923;--ink:#eaeff5;--dim:#939dab;--faint:#5f6a79;
130
158
  --line:#212a36;--accent:#54d6cf;--measure:64ch}
131
159
  @media (prefers-color-scheme:light){:root{--bg:#f5f7f9;--bg2:#eef1f4;--surf:#fff;--ink:#0f1620;
@@ -185,6 +213,10 @@ main{min-width:0;padding:0 clamp(20px,5vw,72px)}
185
213
  .linklist{display:flex;flex-direction:column;gap:2px}
186
214
  .linklist a{text-decoration:none;color:var(--dim);font-size:14px;padding:5px 0}
187
215
  .linklist a:hover{color:var(--accent)}
216
+ .prov{margin:0 0 48px;padding-top:18px;border-top:1px solid var(--line);font-size:12px;color:var(--faint);display:flex;flex-wrap:wrap;gap:8px 24px;justify-content:space-between}
217
+ .prov .out{display:flex;flex-wrap:wrap;gap:14px}
218
+ .prov a{color:var(--dim);text-decoration:none}
219
+ .prov a:hover{color:var(--accent)}
188
220
  @media (max-width:880px){.wrap{grid-template-columns:1fr}
189
221
  aside{position:static;height:auto;border-right:none;border-bottom:1px solid var(--line)}
190
222
  .twocol{grid-template-columns:1fr}}
@@ -195,6 +227,7 @@ main{min-width:0;padding:0 clamp(20px,5vw,72px)}
195
227
  <main>
196
228
  ${overview}
197
229
  ${ALL.map(pageHtml).join("\n")}
230
+ ${footerHtml(spec, provenance)}
198
231
  </main>
199
232
  </div>
200
233
 
@@ -223,12 +256,17 @@ ${ALL.map(pageHtml).join("\n")}
223
256
  ${body}
224
257
  </body>
225
258
  </html>`;
259
+ // The Artifact tool supplies the head, so the fragment carries its own
260
+ // <title>; the standalone wraps the BODY alone, so the title appears once.
261
+ const fragment = (body) => `<title>${esc(spec.title)}</title>\n${body}`;
226
262
  // Fragment and standalone are both inlined (Artifact CSP; publishable on its
227
263
  // own). Sidecars are an EXTRA output, never a substitution.
228
- const indexHtml = render(inline);
229
- const standaloneHtml = document(indexHtml);
264
+ const body = render(inline);
265
+ const indexHtml = fragment(body);
266
+ const standaloneHtml = document(body);
230
267
  if (!assets)
231
268
  return { indexHtml, standaloneHtml, missing };
232
269
  mkdirSync(assets.dir, { recursive: true });
233
- return { indexHtml, standaloneHtml, standaloneAssetHtml: document(render(external)), missing };
270
+ const standaloneAssetHtml = document(render(external));
271
+ return { indexHtml, standaloneHtml, standaloneAssetHtml, missing, sidecarHrefs };
234
272
  }
@@ -0,0 +1,37 @@
1
+ import { type BuildManualResult, type ManualLink, type ManualSpec, type Provenance } from "./build-manual.js";
2
+ export declare const BUNDLE_SCHEMA_VERSION = 1;
3
+ export interface BundlePageEntry {
4
+ id: string;
5
+ nav: string;
6
+ title: string;
7
+ kind: "overview" | "fn" | "ex";
8
+ /** Relative to the bundle directory. */
9
+ file: string;
10
+ }
11
+ export interface BundleManifest {
12
+ schemaVersion: number;
13
+ brand: string;
14
+ title: string;
15
+ provenance: Provenance;
16
+ links: ManualLink[];
17
+ pages: BundlePageEntry[];
18
+ /** Derived single-page exports, relative to the bundle directory. */
19
+ exports: {
20
+ manual: string;
21
+ manualLinked: string;
22
+ };
23
+ /** `<slug>/<file>` of every referenced capture that was not on disk — a
24
+ * bundle with gaps is still a bundle, but a consumer can see them. */
25
+ missing: string[];
26
+ }
27
+ export interface BuildBundleOptions {
28
+ docsRoot: string;
29
+ /** Where the bundle is written; created if absent. */
30
+ outDir: string;
31
+ provenance?: Provenance;
32
+ }
33
+ export interface BuildBundleResult extends BuildManualResult {
34
+ manifest: BundleManifest;
35
+ outDir: string;
36
+ }
37
+ export declare function buildBundle(spec: ManualSpec, opts: BuildBundleOptions): BuildBundleResult;
@@ -0,0 +1,97 @@
1
+ // The docs bundle: what a product's docs build produces, and the unit every
2
+ // downstream consumer reads (ADR-0012). A directory holding
3
+ //
4
+ // bundle.json this manifest — identity, provenance, the page list
5
+ // pages/<id>.md one markdown page per manual page, captures by path
6
+ // captures/<file> every referenced capture, resized (or a labelled gap)
7
+ // manual.html derived: the self-contained single-page export
8
+ // manual-linked.html derived: the same page referencing captures/
9
+ //
10
+ // The product image bakes this directory in and serves it at /docs; the public
11
+ // plane ingests pages/*.md. `ManualSpec` stays the authoring input — the
12
+ // bundle is that spec promoted to a contract, with the captures alongside.
13
+ import { mkdirSync, writeFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { buildManual, } from "./build-manual.js";
16
+ export const BUNDLE_SCHEMA_VERSION = 1;
17
+ const yamlString = (s) => JSON.stringify(s);
18
+ export function buildBundle(spec, opts) {
19
+ const { docsRoot, outDir } = opts;
20
+ const provenance = { builtAt: new Date().toISOString(), ...opts.provenance };
21
+ mkdirSync(join(outDir, "pages"), { recursive: true });
22
+ const manual = buildManual(spec, {
23
+ docsRoot,
24
+ provenance,
25
+ assets: { dir: join(outDir, "captures"), href: "captures" },
26
+ });
27
+ const hrefs = manual.sidecarHrefs ?? {};
28
+ // Pages live one level down, so a capture href is reached via `../`.
29
+ const captureRef = (slug, file) => `../${hrefs[`${slug}/${file}`]}`;
30
+ const all = [...spec.functionality, ...spec.examples];
31
+ const byId = new Map(all.map((p) => [p.id, p]));
32
+ const pageLink = (id) => {
33
+ const t = byId.get(id);
34
+ return t ? `[${t.nav}](${id}.md)` : "";
35
+ };
36
+ const pageMarkdown = (p) => {
37
+ const head = [
38
+ "---",
39
+ `title: ${yamlString(p.title)}`,
40
+ `nav: ${yamlString(p.nav)}`,
41
+ `kind: ${p.kind}`,
42
+ ...(p.tag ? [`tag: ${yamlString(p.tag)}`] : []),
43
+ "---",
44
+ ];
45
+ const steps = p.steps.map((s, i) => `### ${String(i + 1).padStart(2, "0")} ${s.head}\n\n${s.desc}\n\n![${s.head}](${captureRef(s.slug, s.file)})`);
46
+ const links = p.links.map(pageLink).filter(Boolean);
47
+ const related = links.length ? [`${p.kind === "fn" ? "Seen in" : "Uses"}: ${links.join(", ")}`] : [];
48
+ return [...head, "", p.intro, "", ...steps.flatMap((s) => [s, ""]), ...related].join("\n").trimEnd().concat("\n");
49
+ };
50
+ const ov = spec.overview;
51
+ const overviewMarkdown = [
52
+ "---",
53
+ `title: ${yamlString(ov.headline)}`,
54
+ `nav: "Overview"`,
55
+ "kind: overview",
56
+ "---",
57
+ "",
58
+ ov.lead,
59
+ "",
60
+ `![${ov.hero.alt}](${captureRef(ov.hero.slug, ov.hero.file)})`,
61
+ "",
62
+ `_${ov.hero.caption}_`,
63
+ "",
64
+ `## ${ov.byFunctionHeading ?? "By function"}`,
65
+ "",
66
+ ...spec.functionality.map((p) => `- ${pageLink(p.id)}`),
67
+ "",
68
+ `## ${ov.byExampleHeading ?? "By example"}`,
69
+ "",
70
+ ov.byExampleIntro,
71
+ "",
72
+ ...spec.examples.map((p) => `- ${pageLink(p.id)}`),
73
+ "",
74
+ ].join("\n");
75
+ const pages = [
76
+ { id: "overview", nav: "Overview", title: ov.headline, kind: "overview", file: "pages/overview.md" },
77
+ ...all.map((p) => ({ id: p.id, nav: p.nav, title: p.title, kind: p.kind, file: `pages/${p.id}.md` })),
78
+ ];
79
+ writeFileSync(join(outDir, "pages/overview.md"), overviewMarkdown);
80
+ for (const p of all)
81
+ writeFileSync(join(outDir, `pages/${p.id}.md`), pageMarkdown(p));
82
+ const exports = { manual: "manual.html", manualLinked: "manual-linked.html" };
83
+ writeFileSync(join(outDir, exports.manual), manual.standaloneHtml);
84
+ writeFileSync(join(outDir, exports.manualLinked), manual.standaloneAssetHtml ?? manual.standaloneHtml);
85
+ const manifest = {
86
+ schemaVersion: BUNDLE_SCHEMA_VERSION,
87
+ brand: spec.brand,
88
+ title: spec.title,
89
+ provenance,
90
+ links: spec.links ?? [],
91
+ pages,
92
+ exports,
93
+ missing: manual.missing,
94
+ };
95
+ writeFileSync(join(outDir, "bundle.json"), `${JSON.stringify(manifest, null, 2)}\n`);
96
+ return { ...manual, manifest, outDir };
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@norskvideo/ctl-dev-kit",
3
- "version": "0.1.54",
3
+ "version": "0.1.56",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -10,6 +10,7 @@
10
10
  "./docs/path-existence": "./docs/path-existence.ts",
11
11
  "./doc-guide": "./doc-guide/doc-guide.js",
12
12
  "./doc-guide/build-manual": "./doc-guide/build-manual.js",
13
+ "./doc-guide/bundle": "./doc-guide/bundle.js",
13
14
  "./doc-guide/guides-config": "./doc-guide/guides-config.js",
14
15
  "./doc-guide/instance-proxy": "./doc-guide/instance-proxy.js",
15
16
  "./doc-guide/live-browser": "./doc-guide/live-browser.js"