forgebook 0.2.0 → 0.3.1
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 +113 -5
- package/dist/bin/forgebook/agentLoopCommands.mjs +3 -0
- package/dist/bin/forgebook/agentText.d.ts +2 -2
- package/dist/bin/forgebook/agentText.js +15 -13
- package/dist/bin/forgebook/agents.md +21 -6
- package/dist/bin/forgebook/mcpTools.js +1 -1
- package/dist/bin/forgebook/newStoriesFile.js +7 -2
- package/dist/bin/forgebook-docs/collectDocs.js +1 -1
- package/dist/src/argsProps.d.ts +32 -0
- package/dist/src/argsProps.js +69 -0
- package/dist/src/catalogue.d.ts +48 -5
- package/dist/src/catalogue.js +47 -8
- package/dist/src/frame/mountFrame.js +26 -7
- package/dist/src/frame/touchCaret.d.ts +43 -0
- package/dist/src/frame/touchCaret.js +195 -0
- package/dist/src/frame/touchEvents.d.ts +3 -2
- package/dist/src/frame/touchEvents.js +17 -18
- package/dist/src/frame/touchLayer.js +48 -7
- package/dist/src/propGroups.d.ts +66 -3
- package/dist/src/propGroups.js +67 -13
- package/dist/src/shell/ApiReference.d.ts +9 -4
- package/dist/src/shell/ApiReference.js +47 -39
- package/dist/src/shell/AudienceSections.js +1 -1
- package/dist/src/shell/ComponentPage.d.ts +5 -2
- package/dist/src/shell/ComponentPage.js +4 -18
- package/dist/src/shell/DocBlocks.js +1 -1
- package/dist/src/shell/PropRows.d.ts +42 -0
- package/dist/src/shell/PropRows.js +60 -0
- package/dist/src/shell/PropsTab.d.ts +14 -10
- package/dist/src/shell/PropsTab.js +18 -58
- package/dist/src/shell/RequiredFirstSwitch.d.ts +16 -0
- package/dist/src/shell/RequiredFirstSwitch.js +8 -0
- package/dist/src/shell/Stage.js +11 -23
- package/dist/src/shell/StoryCoverage.js +4 -1
- package/dist/src/shell/docPage.css +6 -0
- package/dist/src/shell/dockTabs.d.ts +7 -5
- package/dist/src/shell/dockTabs.js +4 -4
- package/dist/src/shell/reportedPanel.d.ts +12 -0
- package/dist/src/shell/reportedPanel.js +19 -0
- package/dist/src/shell/shell.css +56 -1
- package/dist/src/shell/strings.d.ts +2 -0
- package/dist/src/shell/strings.js +2 -0
- package/dist/src/shell/usePanelDocs.d.ts +11 -0
- package/dist/src/shell/usePanelDocs.js +29 -0
- package/dist/src/shell/useRequiredFirst.d.ts +27 -0
- package/dist/src/shell/useRequiredFirst.js +67 -0
- package/dist/src/storyCheck.d.ts +2 -0
- package/dist/src/storyCheck.js +2 -1
- package/dist/src/storyGlobals.js +2 -1
- package/dist/src/storyTree.d.ts +11 -1
- package/dist/src/storyTree.js +3 -1
- package/dist/src/viewState.d.ts +7 -0
- package/dist/src/viewState.js +13 -0
- package/dist/src/vite/agent.types.d.ts +12 -0
- package/dist/src/vite/agentApi.js +6 -1
- package/dist/src/vite/agentRoutes.js +18 -4
- package/dist/src/vite/builtTexts.d.ts +2 -2
- package/dist/src/vite/builtTexts.js +7 -6
- package/dist/src/vite/configFile.d.ts +3 -2
- package/dist/src/vite/configFile.js +63 -14
- package/dist/src/vite/docPagesModule.d.ts +10 -7
- package/dist/src/vite/docPagesModule.js +14 -11
- package/dist/src/vite/docsExtractor.d.ts +6 -0
- package/dist/src/vite/docsExtractor.js +13 -0
- package/dist/src/vite/fileAccess.d.ts +75 -0
- package/dist/src/vite/fileAccess.js +199 -0
- package/dist/src/vite/forgebookPlugin.d.ts +1 -1
- package/dist/src/vite/forgebookPlugin.js +67 -98
- package/dist/src/vite/framesServer.js +21 -5
- package/dist/src/vite/generatedModules.d.ts +23 -0
- package/dist/src/vite/generatedModules.js +58 -0
- package/dist/src/vite/hotSnippets.js +7 -3
- package/dist/src/vite/indexStoryFile.d.ts +2 -2
- package/dist/src/vite/indexStoryFile.js +2 -2
- package/dist/src/vite/indexedParameters.d.ts +1 -1
- package/dist/src/vite/indexedParameters.js +21 -1
- package/dist/src/vite/originGuard.d.ts +14 -4
- package/dist/src/vite/originGuard.js +15 -6
- package/dist/src/vite/pages.d.ts +3 -2
- package/dist/src/vite/pages.js +23 -16
- package/dist/src/vite/pluginIds.d.ts +24 -2
- package/dist/src/vite/pluginIds.js +44 -6
- package/dist/src/vite/pluginModules.d.ts +39 -10
- package/dist/src/vite/pluginModules.js +67 -22
- package/dist/src/vite/prebundled.js +2 -1
- package/dist/src/vite/searchModule.d.ts +6 -4
- package/dist/src/vite/searchModule.js +8 -7
- package/dist/src/vite/storyFilesModule.d.ts +59 -27
- package/dist/src/vite/storyFilesModule.js +88 -58
- package/dist/src/vite/storyPatterns.d.ts +39 -0
- package/dist/src/vite/storyPatterns.js +100 -0
- package/dist/src/vite/textQueries.d.ts +7 -1
- package/dist/src/vite/textQueries.js +7 -1
- package/package.json +4 -4
|
@@ -13,38 +13,40 @@
|
|
|
13
13
|
* come the same way, with `?forgebook-docs-pages` (`docPagesModule.ts`), and the written home with `?forgebook-home`. It also sets Lightning CSS as the CSS transformer with the forced-states visitor (`forcedStates.ts`) and lets Sass resolve
|
|
14
14
|
* `pkg:` URLs, which the library's style sheets use.
|
|
15
15
|
*
|
|
16
|
-
* A
|
|
16
|
+
* A group's module watches its story files, so an edit renews it. Vite watches only its root for new files, and the
|
|
17
17
|
* sources' story files live in other packages: `watch` names their folders, and a story file added or removed there
|
|
18
|
-
* renews
|
|
18
|
+
* renews the modules of the groups whose list it changes and reloads the page, so the sidebar lists it at once; one no
|
|
19
|
+
* group lists (a `!` pattern leaves it out) reloads nothing.
|
|
19
20
|
*
|
|
20
21
|
* @module
|
|
21
22
|
*/
|
|
22
|
-
import { existsSync } from "node:fs";
|
|
23
23
|
import { extname, join, resolve } from "node:path";
|
|
24
|
-
import {
|
|
24
|
+
import { checksA11y, offersUsedIn, readsComponentDocs } from "../catalogue.js";
|
|
25
25
|
import { SAME_ORIGIN } from "../frameOrigins.js";
|
|
26
26
|
import { createLiveErrors, isLiveErrorReport, LIVE_ERROR_EVENT } from "../liveErrors.js";
|
|
27
27
|
import { agentMiddleware, FRAMED_AGENT_PAGES } from "./agentRoutes.js";
|
|
28
28
|
import { STORY_FILE } from "./autoTitle.js";
|
|
29
|
-
import {
|
|
29
|
+
import { axeBuildAsset } from "./axeScript.js";
|
|
30
30
|
import { groupTexts } from "./builtTexts.js";
|
|
31
31
|
import { catalogueConfig } from "./catalogueConfig.js";
|
|
32
32
|
import { currentCloseWatch } from "./closeWatch.js";
|
|
33
33
|
import { findConfigFile, loadConfigFile, packageResolver } from "./configFile.js";
|
|
34
34
|
import { codeStamp, diskCache } from "./diskCache.js";
|
|
35
|
-
import { createDocsExtractor } from "./docsExtractor.js";
|
|
35
|
+
import { createDocsExtractor, NO_DOCS_EXTRACTOR } from "./docsExtractor.js";
|
|
36
|
+
import { devServerAccess, VITEST_HOOKS } from "./fileAccess.js";
|
|
36
37
|
import { FORCED_STATES_VISITOR } from "./forcedStates.js";
|
|
37
38
|
import { builtPlacement } from "./framesConfig.js";
|
|
38
39
|
import { serveFrames } from "./framesServer.js";
|
|
40
|
+
import { generatedId, generatedModule } from "./generatedModules.js";
|
|
39
41
|
import { missingHeightsMiddleware } from "./heightsRoute.js";
|
|
40
42
|
import { FILES_MODULE_HOT, renewedModule, servedPatterns, storyFileHotHooks } from "./hotSnippets.js";
|
|
41
43
|
import { inlineJson, packedJsonOf } from "./packedModule.js";
|
|
42
|
-
import { AGENT_SOURCES_FILE,
|
|
44
|
+
import { AGENT_SOURCES_FILE, configModule, PAGE_FILES, pageHtml, pageModulePath } from "./pages.js";
|
|
43
45
|
import { CONFIG_ID, DOCS_PAGES_PREFIX, patternId, TOKENS_ID, virtualId } from "./pluginIds.js";
|
|
44
|
-
import { docsModule, docsValue, FILES_PREFIX, patternModule, REACH_ID_PREFIX, reachModule, searchIndexes, USAGE_ID_PREFIX, usageModule } from "./pluginModules.js";
|
|
46
|
+
import { docsModule, docsValue, FILES_PREFIX, listingRenewals, patternModule, REACH_ID_PREFIX, reachModule, searchIndexes, USAGE_ID_PREFIX, usageModule } from "./pluginModules.js";
|
|
45
47
|
import { isKeptIndex, storyFilesGroupOf } from "./storyFilesModule.js";
|
|
46
48
|
import { tokensModule } from "./storylessRoutes.js";
|
|
47
|
-
import {
|
|
49
|
+
import { DOCS_PAGE_QUERY, DOCS_QUERY, HOME_QUERY, RENEWED_QUERIES, TEXT_QUERIES } from "./textQueries.js";
|
|
48
50
|
import { VITE_PATHS } from "./vitePaths.js";
|
|
49
51
|
/** This module's folder, in the core's package: the core's own dependencies resolve from here, not the project's. */
|
|
50
52
|
const PACKAGE_DIR = import.meta.dirname;
|
|
@@ -62,31 +64,6 @@ const sassOptions = async () => {
|
|
|
62
64
|
const DOCS_PAGE_FILE = /\.md$/;
|
|
63
65
|
/** A TypeScript source file, whose edit may change a component's docs. */
|
|
64
66
|
const SOURCE_FILE = /\.tsx?$/;
|
|
65
|
-
/** The query that asks for a story file's component docs. */
|
|
66
|
-
const DOCS_QUERY = "?forgebook-docs";
|
|
67
|
-
/** The queries `textModules.ts` answers. */
|
|
68
|
-
const TEXT_QUERIES = [CODE_QUERY, PAGE_QUERY, MARKDOWN_QUERY];
|
|
69
|
-
/** The queries of a story file's modules the shell reads, which hand an edit to it in place on a dev server. */
|
|
70
|
-
const RENEWED_QUERIES = [DOCS_QUERY, CODE_QUERY, PAGE_QUERY];
|
|
71
|
-
/** The queries the plugin answers with a file's text, so each read keeps to what the dev server may serve. */
|
|
72
|
-
const FILE_QUERIES = [
|
|
73
|
-
DOCS_QUERY,
|
|
74
|
-
CODE_QUERY,
|
|
75
|
-
PAGE_QUERY,
|
|
76
|
-
MARKDOWN_QUERY,
|
|
77
|
-
DOCS_PAGE_QUERY,
|
|
78
|
-
HOME_QUERY
|
|
79
|
-
];
|
|
80
|
-
/**
|
|
81
|
-
* Whether a dev server may read the file a query names: a plugin's answer never passes Vite's own check of
|
|
82
|
-
* `server.fs`, so without this any file could be asked for by adding a query to its address.
|
|
83
|
-
*
|
|
84
|
-
* @param id - the module's id.
|
|
85
|
-
* @param server - the dev server; none in a build, which reads only what the sources import.
|
|
86
|
-
*/
|
|
87
|
-
const isReadable = (id, server) => server === undefined ||
|
|
88
|
-
!FILE_QUERIES.some((query) => id.endsWith(query)) ||
|
|
89
|
-
isFileLoadingAllowed(server.config, id.slice(0, id.lastIndexOf("?")));
|
|
90
67
|
/**
|
|
91
68
|
* Renews modules in every environment that loaded them: the agents' routes load them on the server too.
|
|
92
69
|
*
|
|
@@ -120,8 +97,9 @@ const requestedPage = (url) => {
|
|
|
120
97
|
* @param watch - adds a file whose edit renews the module.
|
|
121
98
|
* @param isServing - whether a dev server runs.
|
|
122
99
|
* @param plugins - the catalogue's plugins, whose fences a page may draw.
|
|
100
|
+
* @param isKept - whether a page its group lists may be read.
|
|
123
101
|
*/
|
|
124
|
-
const docsModuleOf = async (id, watch, isServing, plugins) => {
|
|
102
|
+
const docsModuleOf = async (id, watch, isServing, plugins, isKept) => {
|
|
125
103
|
const isHome = id.endsWith(HOME_QUERY);
|
|
126
104
|
const isPage = id.endsWith(DOCS_PAGE_QUERY);
|
|
127
105
|
if (!isHome && !isPage && !id.startsWith(DOCS_PAGES_PREFIX))
|
|
@@ -133,47 +111,8 @@ const docsModuleOf = async (id, watch, isServing, plugins) => {
|
|
|
133
111
|
const file = id.slice(0, -DOCS_PAGE_QUERY.length);
|
|
134
112
|
return { code: docPageModule(file, watch, isServing, plugins), moduleType: "js" };
|
|
135
113
|
}
|
|
136
|
-
const { root,
|
|
137
|
-
return { code: docPagesModule(root,
|
|
138
|
-
};
|
|
139
|
-
/**
|
|
140
|
-
* A module the plugin generates for a catalogue's pages, when the id names one: an entry, the project's own stories'
|
|
141
|
-
* source, or a page's HTML (unless the folder has one of its own). The configuration's data waits for the frames'
|
|
142
|
-
* placement, so the plugin's `load` makes it.
|
|
143
|
-
*
|
|
144
|
-
* @param id - the module's id.
|
|
145
|
-
* @param config - the catalogue's configuration.
|
|
146
|
-
* @param command - whether a dev server runs, or a build: where the frame finds axe's script.
|
|
147
|
-
*/
|
|
148
|
-
const generatedModule = (id, config, command) => {
|
|
149
|
-
const named = pageModuleOf(id, config.dir);
|
|
150
|
-
if (named === undefined)
|
|
151
|
-
return undefined;
|
|
152
|
-
if (named.kind === "entry") {
|
|
153
|
-
const code = named.page === "index.html" ? shellEntry(config) : frameEntry(config, axeAddress(command, PACKAGE_DIR));
|
|
154
|
-
return { code, moduleType: "js" };
|
|
155
|
-
}
|
|
156
|
-
if (named.kind === "stories-source")
|
|
157
|
-
return { code: storiesSourceModule(config), moduleType: "js" };
|
|
158
|
-
if (named.kind === "agent-sources")
|
|
159
|
-
return { code: agentSourcesModule(config), moduleType: "js" };
|
|
160
|
-
return existsSync(id) ? undefined : pageHtml(named.page, config, builtPlacement(config.frames));
|
|
161
|
-
};
|
|
162
|
-
/**
|
|
163
|
-
* The id of a module the plugin generates for a catalogue's pages, when an import names one: a page or an entry by
|
|
164
|
-
* its address (`/index.html`, `/.forgebook-shell.ts`) or by its path, a source by its path.
|
|
165
|
-
*
|
|
166
|
-
* @param source - the import's specifier.
|
|
167
|
-
* @param dir - the configuration's folder.
|
|
168
|
-
*/
|
|
169
|
-
const generatedId = (source, dir) => {
|
|
170
|
-
const byAddress = PAGE_FILES.find((page) => source === `/${PAGE_ENTRIES[page]}`);
|
|
171
|
-
if (byAddress !== undefined)
|
|
172
|
-
return pageModulePath(dir, PAGE_ENTRIES[byAddress]);
|
|
173
|
-
const pageByAddress = PAGE_FILES.find((page) => source === `/${page}`);
|
|
174
|
-
if (pageByAddress !== undefined)
|
|
175
|
-
return pageModulePath(dir, pageByAddress);
|
|
176
|
-
return pageModuleOf(source, dir) === undefined ? undefined : VITE_PATHS.normalize(source);
|
|
114
|
+
const { root, patterns } = storyFilesGroupOf(id.slice(DOCS_PAGES_PREFIX.length));
|
|
115
|
+
return { code: docPagesModule(root, patterns, watch, isServing, plugins, isKept), moduleType: "js" };
|
|
177
116
|
};
|
|
178
117
|
/**
|
|
179
118
|
* The catalogue's Vite plugin: the story index, the docs, the code and the Markdown of the stories, the forced-states
|
|
@@ -184,8 +123,11 @@ const generatedId = (source, dir) => {
|
|
|
184
123
|
* @returns the plugin, for the app's `plugins`.
|
|
185
124
|
*/
|
|
186
125
|
export const forgebookPlugin = (options = {}) => {
|
|
187
|
-
/**
|
|
188
|
-
|
|
126
|
+
/**
|
|
127
|
+
* The modules of the groups of story files, with the files each listed when served (none while it could not be
|
|
128
|
+
* made), renewed when a story file is added to or removed from what one lists.
|
|
129
|
+
*/
|
|
130
|
+
const filesModules = new Map();
|
|
189
131
|
// what each pattern's module was served with: an edit of a story file that changes its list reloads the pages
|
|
190
132
|
const served = servedPatterns();
|
|
191
133
|
/** The groups' "Used in" modules, renewed when a source file changes: any edit may change what reaches what. */
|
|
@@ -194,6 +136,8 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
194
136
|
const docsModules = new Set();
|
|
195
137
|
/** The groups' search indexes: any edit may change what a component's docs say. */
|
|
196
138
|
const searchModules = new Set();
|
|
139
|
+
/** What a client may read of a dev server. */
|
|
140
|
+
const access = devServerAccess();
|
|
197
141
|
/** The errors the open frames report, for `forgebook errors` and the agents. */
|
|
198
142
|
const live = createLiveErrors();
|
|
199
143
|
/** Where the frames run: the dev server's second port once it listens, the configuration's choice in a build. */
|
|
@@ -212,8 +156,6 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
212
156
|
return { code: configModule(config, await framesPlacement()), moduleType: "js" };
|
|
213
157
|
return generatedModule(id, config, command);
|
|
214
158
|
};
|
|
215
|
-
/** The dev server; none in a build. */
|
|
216
|
-
let devServer;
|
|
217
159
|
/** The files followed one by one, so an edit reaches the dev server at once. */
|
|
218
160
|
const watching = currentCloseWatch();
|
|
219
161
|
/** Whether a dev server runs, or a build: tools ask only the former. */
|
|
@@ -223,7 +165,8 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
223
165
|
const search = searchIndexes({
|
|
224
166
|
extract: (storyFile) => extractor().extract(storyFile),
|
|
225
167
|
isServing,
|
|
226
|
-
track: (id, isPages) => (isPages ? docsModules : searchModules).add(id)
|
|
168
|
+
track: (id, isPages) => (isPages ? docsModules : searchModules).add(id),
|
|
169
|
+
isKept: access.isKept
|
|
227
170
|
});
|
|
228
171
|
// a build's docs, code and pages of each group's story files, read while the build loads the stories
|
|
229
172
|
const texts = groupTexts();
|
|
@@ -258,6 +201,9 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
258
201
|
// build and would hold every other answer of the dev server meanwhile
|
|
259
202
|
let docs;
|
|
260
203
|
const extractor = () => {
|
|
204
|
+
// a catalogue that reads no component docs never starts the worker: its programs are most of the server's memory
|
|
205
|
+
if (config !== undefined && !readsComponentDocs(config.lists))
|
|
206
|
+
return NO_DOCS_EXTRACTOR;
|
|
261
207
|
const framework = config?.framework ?? options.framework;
|
|
262
208
|
if (framework === undefined)
|
|
263
209
|
throw new Error("Forgebook has no framework: add forgebook.config.ts, or give the plugin `framework`");
|
|
@@ -294,21 +240,23 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
294
240
|
const storiesModule = async (id, watch, pack, startTexts) => {
|
|
295
241
|
if (id.startsWith(USAGE_ID_PREFIX)) {
|
|
296
242
|
usageModules.add(id);
|
|
297
|
-
return usageModule(id, watch, extractor().usage, indexCache(), pack);
|
|
243
|
+
return usageModule(id, watch, extractor().usage, indexCache(), pack, access.isKept);
|
|
298
244
|
}
|
|
299
245
|
// what each story reaches is for the dev server's tools only: a build ships none of it
|
|
300
246
|
if (id.startsWith(REACH_ID_PREFIX)) {
|
|
301
247
|
usageModules.add(id);
|
|
302
248
|
return isServing()
|
|
303
|
-
? reachModule(id, watch, extractor().usage, indexCache())
|
|
249
|
+
? reachModule(id, watch, extractor().usage, indexCache(), access.isKept)
|
|
304
250
|
: { code: "export default [];", moduleType: "js" };
|
|
305
251
|
}
|
|
306
252
|
if (!id.startsWith(FILES_PREFIX))
|
|
307
253
|
return undefined;
|
|
308
|
-
filesModules.
|
|
309
|
-
const
|
|
254
|
+
filesModules.set(id, undefined);
|
|
255
|
+
const lists = config?.lists ?? {};
|
|
256
|
+
const hasUsage = offersUsedIn(lists);
|
|
310
257
|
// the worker starts while Vite loads the pages, unless the disk keeps the docs; a test run asks for no page
|
|
311
|
-
const { files, code } = patternModule(id, watch, { hasUsage, hasReach: hasUsage && isServing() }, indexCache(), isTestRun ? undefined : extractor().warm, startTexts);
|
|
258
|
+
const { files, code } = patternModule(id, watch, { hasDocs: readsComponentDocs(lists), hasUsage, hasReach: hasUsage && isServing() }, indexCache(), isTestRun ? undefined : extractor().warm, startTexts, access.isKept);
|
|
259
|
+
filesModules.set(id, new Set(files.map((file) => file.file)));
|
|
312
260
|
if (!isRedrawing())
|
|
313
261
|
return { code, moduleType: "js" };
|
|
314
262
|
served.keep(id, files);
|
|
@@ -333,9 +281,9 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
333
281
|
* @param file - the file.
|
|
334
282
|
*/
|
|
335
283
|
const modulesListing = (file) => {
|
|
336
|
-
// the
|
|
284
|
+
// the groups' modules list the story files, their "Used in" and search indexes read them
|
|
337
285
|
if (STORY_FILE.test(file))
|
|
338
|
-
return
|
|
286
|
+
return listingRenewals(filesModules, file, [...usageModules, ...searchModules], access.isKept);
|
|
339
287
|
return DOCS_PAGE_FILE.test(file) && docsModules.size > 0 ? [...docsModules] : undefined;
|
|
340
288
|
};
|
|
341
289
|
/**
|
|
@@ -356,7 +304,7 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
356
304
|
}
|
|
357
305
|
if (id.startsWith(DOCS_PAGES_PREFIX))
|
|
358
306
|
docsModules.add(id);
|
|
359
|
-
const docsPages = await docsModuleOf(id, watch, isServing(), config?.plugins ?? []);
|
|
307
|
+
const docsPages = await docsModuleOf(id, watch, isServing(), config?.plugins ?? [], access.isKept);
|
|
360
308
|
if (docsPages !== undefined)
|
|
361
309
|
return docsPages;
|
|
362
310
|
if (!TEXT_QUERIES.some((query) => id.endsWith(query)))
|
|
@@ -380,7 +328,13 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
380
328
|
const pack = emit === undefined ? inlineJson : packedJsonOf(emit);
|
|
381
329
|
const componentFiles = (config?.framework ?? options.framework)?.componentFiles;
|
|
382
330
|
const docsOf = async (storyFile) => docsValue(await search.docsOf(storyFile), watch);
|
|
383
|
-
const
|
|
331
|
+
const hasDocs = config === undefined || readsComponentDocs(config.lists);
|
|
332
|
+
const sources = {
|
|
333
|
+
docsOf: hasDocs ? docsOf : undefined,
|
|
334
|
+
watch,
|
|
335
|
+
resolveImport,
|
|
336
|
+
componentFiles
|
|
337
|
+
};
|
|
384
338
|
// a build's browser files only: a dev server answers each text from its query, and no texts module
|
|
385
339
|
const startTexts = emit === undefined ? undefined : texts.starter(id, sources);
|
|
386
340
|
return ((emit === undefined ? undefined : await texts.moduleOf(id, sources, emit)) ??
|
|
@@ -410,13 +364,23 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
410
364
|
};
|
|
411
365
|
},
|
|
412
366
|
resolveId(source, importer) {
|
|
367
|
+
const isModule = access.isModule(importer);
|
|
413
368
|
const virtual = virtualId(source);
|
|
414
|
-
if (virtual !== undefined)
|
|
369
|
+
if (virtual !== undefined) {
|
|
370
|
+
if (isModule)
|
|
371
|
+
access.noteImported(virtual);
|
|
415
372
|
return virtual;
|
|
373
|
+
}
|
|
416
374
|
const generated = config === undefined ? undefined : generatedId(source, config.dir);
|
|
417
375
|
if (generated !== undefined)
|
|
418
376
|
return generated;
|
|
419
|
-
|
|
377
|
+
if (!access.isModule(importer))
|
|
378
|
+
return null;
|
|
379
|
+
// the importer's file: its id may carry whatever query an address gave it
|
|
380
|
+
const pattern = patternId(source, VITE_PATHS.dirname(importer.split("?")[0] ?? importer));
|
|
381
|
+
if (pattern !== null)
|
|
382
|
+
access.noteImported(pattern);
|
|
383
|
+
return pattern;
|
|
420
384
|
},
|
|
421
385
|
// a warning and not a failure: a theme a reader can still use must not stop a catalogue from building
|
|
422
386
|
configResolved: (resolved) => {
|
|
@@ -428,7 +392,10 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
428
392
|
resolved.logger.warn(`Forgebook's shell theme: ${problem}`);
|
|
429
393
|
},
|
|
430
394
|
configureServer: (server) => {
|
|
431
|
-
|
|
395
|
+
access.serve(server, config === undefined ? undefined : pageModulePath(config.dir, AGENT_SOURCES_FILE));
|
|
396
|
+
server.middlewares.use((request, _response, next) => {
|
|
397
|
+
access.settle(request.url ?? "/").then(() => next(), next);
|
|
398
|
+
});
|
|
432
399
|
// no person waits for a page in a test run
|
|
433
400
|
watching.serve(isTestRun ? undefined : server);
|
|
434
401
|
server.httpServer?.on("close", () => void docs?.close());
|
|
@@ -469,7 +436,7 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
469
436
|
.catch(next);
|
|
470
437
|
});
|
|
471
438
|
// folders outside the configuration's are watched only when named; a story file added or removed anywhere the
|
|
472
|
-
// server watches (its own folder, a story file it loaded) renews the lists either way
|
|
439
|
+
// server watches (its own folder, a story file it loaded) renews the lists that change either way
|
|
473
440
|
const watched = [...(config?.watch ?? []), ...(options.watch ?? [])];
|
|
474
441
|
if (watched.length > 0)
|
|
475
442
|
server.watcher.add(watched);
|
|
@@ -488,7 +455,8 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
488
455
|
// axe's script, for the frames' Accessibility card: loaded by the frame, never imported; the browser's build only
|
|
489
456
|
if (this.environment.name !== "client")
|
|
490
457
|
return;
|
|
491
|
-
|
|
458
|
+
// a catalogue that checks no accessibility ships none of it
|
|
459
|
+
const axe = config === undefined || !checksA11y(config.lists) ? undefined : axeBuildAsset(PACKAGE_DIR);
|
|
492
460
|
if (axe !== undefined)
|
|
493
461
|
this.emitFile({ type: "asset", fileName: axe.fileName, source: axe.source });
|
|
494
462
|
},
|
|
@@ -501,13 +469,14 @@ export const forgebookPlugin = (options = {}) => {
|
|
|
501
469
|
await docs?.close();
|
|
502
470
|
},
|
|
503
471
|
...storyFileHotHooks(isRedrawing, served.changedBy, watching),
|
|
472
|
+
...VITEST_HOOKS,
|
|
504
473
|
async load(id) {
|
|
505
474
|
const generated = await pagesModule(id);
|
|
506
475
|
if (generated !== undefined)
|
|
507
476
|
return generated;
|
|
508
|
-
|
|
509
|
-
if (
|
|
510
|
-
return
|
|
477
|
+
const refusal = access.refusal(id);
|
|
478
|
+
if (refusal !== undefined)
|
|
479
|
+
return refusal;
|
|
511
480
|
// in Vite's form, the text Vite's module graph knows a file by: a Node path on Windows (`C:\app\x.md`) is another
|
|
512
481
|
const watch = (file) => this.addWatchFile(VITE_PATHS.normalize(file));
|
|
513
482
|
const resolveImport = async (specifier, importer) => {
|
|
@@ -45,6 +45,20 @@ export const hostOf = (request) => headerOf(request, "host") ?? headerOf(request
|
|
|
45
45
|
* @param url - the request's address.
|
|
46
46
|
*/
|
|
47
47
|
const isProxied = (proxy, url) => Object.keys(proxy ?? {}).some((key) => key.startsWith("^") ? new RegExp(key).test(url) : url.startsWith(key));
|
|
48
|
+
/**
|
|
49
|
+
* A request's path as an address parser reads it, which is how the routes behind the guards read it: a guard that read
|
|
50
|
+
* `//x/__forgebook/api/stories` as written would pass what the route answers as `/__forgebook/api/stories`.
|
|
51
|
+
*
|
|
52
|
+
* @param url - the request's address, as it was sent.
|
|
53
|
+
*/
|
|
54
|
+
const parsedPath = (url) => {
|
|
55
|
+
try {
|
|
56
|
+
return new URL(url, "http://parsed.invalid").pathname;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return (url.split("?")[0] ?? url).split("#")[0] ?? "/";
|
|
60
|
+
}
|
|
61
|
+
};
|
|
48
62
|
/**
|
|
49
63
|
* What the browser says of a request.
|
|
50
64
|
*
|
|
@@ -54,7 +68,7 @@ const factsOf = (request) => {
|
|
|
54
68
|
const url = request.url ?? "/";
|
|
55
69
|
const queryAt = url.indexOf("?");
|
|
56
70
|
return {
|
|
57
|
-
path: (
|
|
71
|
+
path: parsedPath(url),
|
|
58
72
|
query: queryAt === -1 ? "" : (url.slice(queryAt).split("#")[0] ?? ""),
|
|
59
73
|
site: headerOf(request, "sec-fetch-site"),
|
|
60
74
|
mode: headerOf(request, "sec-fetch-mode"),
|
|
@@ -83,14 +97,16 @@ const isPublicFile = (publicDir, path) => {
|
|
|
83
97
|
return file.startsWith(`${publicDir}${sep}`) && existsSync(file) && statSync(file).isFile();
|
|
84
98
|
};
|
|
85
99
|
/**
|
|
86
|
-
* Whether a module
|
|
100
|
+
* Whether a module the catalogue imports names an address in its code, as a whole string: how a worker's file
|
|
101
|
+
* (`?worker_file`) is started. Only an imported module counts, and only a whole string: an address asked for directly
|
|
102
|
+
* writes its own query into its module's code (its HMR context), and a story's script could plant any address there.
|
|
87
103
|
*
|
|
88
104
|
* @param modules - the graph's modules.
|
|
89
105
|
* @param url - the address, as the graph reads it; its `t=` left out.
|
|
90
106
|
*/
|
|
91
107
|
const isNamedInCode = (modules, url) => {
|
|
92
|
-
const named = url.replace(/[?&]t=\d+/, "");
|
|
93
|
-
return modules.some((module) => module.transformResult?.code.includes(named) === true);
|
|
108
|
+
const named = JSON.stringify(url.replace(/[?&]t=\d+/, ""));
|
|
109
|
+
return modules.some((module) => module.importers.size > 0 && module.transformResult?.code.includes(named) === true);
|
|
94
110
|
};
|
|
95
111
|
/**
|
|
96
112
|
* Ends a request the guards turned down, with its status and nothing a page could use.
|
|
@@ -204,7 +220,7 @@ export const framesServing = (server, frames, frameHtml, framedPages) => {
|
|
|
204
220
|
next();
|
|
205
221
|
return;
|
|
206
222
|
}
|
|
207
|
-
const answer = catalogueAnswer(factsOf(request), framedPages);
|
|
223
|
+
const answer = catalogueAnswer(factsOf(request), framedPages, (path) => isPublicFile(server.config.publicDir, path));
|
|
208
224
|
if (answer === "refuse") {
|
|
209
225
|
turnDown(response, 403);
|
|
210
226
|
return;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ConfigEnv } from "vite";
|
|
2
|
+
import type { ResolvedConfig } from "./configFile.ts";
|
|
3
|
+
/**
|
|
4
|
+
* A module the plugin generates for a catalogue's pages, when the id names one: an entry, the project's own stories'
|
|
5
|
+
* source, or a page's HTML (unless the folder has one of its own). The configuration's data waits for the frames'
|
|
6
|
+
* placement, so the plugin's `load` makes it.
|
|
7
|
+
*
|
|
8
|
+
* @param id - the module's id.
|
|
9
|
+
* @param config - the catalogue's configuration.
|
|
10
|
+
* @param command - whether a dev server runs, or a build: where the frame finds axe's script.
|
|
11
|
+
*/
|
|
12
|
+
export declare const generatedModule: (id: string, config: ResolvedConfig, command: ConfigEnv["command"]) => {
|
|
13
|
+
code: string;
|
|
14
|
+
moduleType: "js";
|
|
15
|
+
} | string | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* The id of a module the plugin generates for a catalogue's pages, when an import names one: a page or an entry by
|
|
18
|
+
* its address (`/index.html`, `/.forgebook-shell.ts`) or by its path, a source by its path.
|
|
19
|
+
*
|
|
20
|
+
* @param source - the import's specifier.
|
|
21
|
+
* @param dir - the configuration's folder.
|
|
22
|
+
*/
|
|
23
|
+
export declare const generatedId: (source: string, dir: string) => string | undefined;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The modules the plugin generates for a catalogue's pages: the shell's and the frame's entries, the project's own
|
|
3
|
+
* stories' source, the agents' sources and the pages' HTML, and the ids imports name them by.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* The configuration's data is not among them: it waits for the frames' placement, which only the plugin knows.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
11
|
+
import { checksA11y } from "../catalogue.js";
|
|
12
|
+
import { axeAddress } from "./axeScript.js";
|
|
13
|
+
import { builtPlacement } from "./framesConfig.js";
|
|
14
|
+
import { agentSourcesModule, frameEntry, PAGE_ENTRIES, PAGE_FILES, pageHtml, pageModuleOf, pageModulePath, shellEntry, storiesSourceModule } from "./pages.js";
|
|
15
|
+
import { VITE_PATHS } from "./vitePaths.js";
|
|
16
|
+
/** The core's folder, where axe's script is found from. */
|
|
17
|
+
const PACKAGE_DIR = import.meta.dirname;
|
|
18
|
+
/**
|
|
19
|
+
* A module the plugin generates for a catalogue's pages, when the id names one: an entry, the project's own stories'
|
|
20
|
+
* source, or a page's HTML (unless the folder has one of its own). The configuration's data waits for the frames'
|
|
21
|
+
* placement, so the plugin's `load` makes it.
|
|
22
|
+
*
|
|
23
|
+
* @param id - the module's id.
|
|
24
|
+
* @param config - the catalogue's configuration.
|
|
25
|
+
* @param command - whether a dev server runs, or a build: where the frame finds axe's script.
|
|
26
|
+
*/
|
|
27
|
+
export const generatedModule = (id, config, command) => {
|
|
28
|
+
const named = pageModuleOf(id, config.dir);
|
|
29
|
+
if (named === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
if (named.kind === "entry") {
|
|
32
|
+
const code = named.page === "index.html"
|
|
33
|
+
? shellEntry(config)
|
|
34
|
+
: frameEntry(config, checksA11y(config.lists) ? axeAddress(command, PACKAGE_DIR) : undefined);
|
|
35
|
+
return { code, moduleType: "js" };
|
|
36
|
+
}
|
|
37
|
+
if (named.kind === "stories-source")
|
|
38
|
+
return { code: storiesSourceModule(config), moduleType: "js" };
|
|
39
|
+
if (named.kind === "agent-sources")
|
|
40
|
+
return { code: agentSourcesModule(config), moduleType: "js" };
|
|
41
|
+
return existsSync(id) ? undefined : pageHtml(named.page, config, builtPlacement(config.frames));
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The id of a module the plugin generates for a catalogue's pages, when an import names one: a page or an entry by
|
|
45
|
+
* its address (`/index.html`, `/.forgebook-shell.ts`) or by its path, a source by its path.
|
|
46
|
+
*
|
|
47
|
+
* @param source - the import's specifier.
|
|
48
|
+
* @param dir - the configuration's folder.
|
|
49
|
+
*/
|
|
50
|
+
export const generatedId = (source, dir) => {
|
|
51
|
+
const byAddress = PAGE_FILES.find((page) => source === `/${PAGE_ENTRIES[page]}`);
|
|
52
|
+
if (byAddress !== undefined)
|
|
53
|
+
return pageModulePath(dir, PAGE_ENTRIES[byAddress]);
|
|
54
|
+
const pageByAddress = PAGE_FILES.find((page) => source === `/${page}`);
|
|
55
|
+
if (pageByAddress !== undefined)
|
|
56
|
+
return pageModulePath(dir, pageByAddress);
|
|
57
|
+
return pageModuleOf(source, dir) === undefined ? undefined : VITE_PATHS.normalize(source);
|
|
58
|
+
};
|
|
@@ -105,10 +105,14 @@ const readIndex = (root, pattern, key) => {
|
|
|
105
105
|
};
|
|
106
106
|
/** The patterns' modules a dev server serves, kept to tell an edit that changes a list. */
|
|
107
107
|
export const servedPatterns = () => {
|
|
108
|
+
// by group, each story file as served: its key, the pattern that lists it, its index
|
|
108
109
|
const served = new Map();
|
|
109
110
|
return {
|
|
110
111
|
keep: (id, files) => {
|
|
111
|
-
served.set(id, new Map(files.map((file) => [
|
|
112
|
+
served.set(id, new Map(files.map((file) => [
|
|
113
|
+
file.file,
|
|
114
|
+
{ key: file.key, pattern: file.pattern, index: JSON.stringify(file.index) }
|
|
115
|
+
])));
|
|
112
116
|
},
|
|
113
117
|
changedBy: (file) => [...served]
|
|
114
118
|
.filter(([id, files]) => {
|
|
@@ -116,8 +120,8 @@ export const servedPatterns = () => {
|
|
|
116
120
|
const kept = files.get(VITE_PATHS.normalize(file));
|
|
117
121
|
if (kept === undefined)
|
|
118
122
|
return false;
|
|
119
|
-
const { root
|
|
120
|
-
const index = readIndex(root, pattern, kept.key);
|
|
123
|
+
const { root } = storyFilesGroupOf(id.slice(FILES_PREFIX.length));
|
|
124
|
+
const index = readIndex(root, kept.pattern, kept.key);
|
|
121
125
|
return typeof index !== "string" && JSON.stringify(index) !== kept.index;
|
|
122
126
|
})
|
|
123
127
|
.map(([id]) => id)
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
*
|
|
18
18
|
* A value the index needs (the title, a name, `parameters.controls`, `group`, `height`, `main`, the meta's `alsoIn`,
|
|
19
19
|
* `related`, `keywords`) is read when written plainly; written by code, it is left to the frame, which reads it when
|
|
20
|
-
* the story loads. `touch`, `logic`, `device`, `design.figma` and the meta's `propGroups` are read by
|
|
21
|
-
* (`indexedParameters.ts`, `indexFields.ts`), so written by code they are a problem.
|
|
20
|
+
* the story loads. `touch`, `logic`, `device`, `requiredFirst`, `design.figma` and the meta's `propGroups` are read by
|
|
21
|
+
* the index only (`indexedParameters.ts`, `indexFields.ts`), so written by code they are a problem.
|
|
22
22
|
* A `design.figma` is checked here too, so the pages never carry the checker: one that is not a link to a Figma file is
|
|
23
23
|
* noted and left out, and the file keeps its stories. A title written by code, or none, is the file's path
|
|
24
24
|
* (`autoTitle`, as Storybook's auto-title); one written by code is also noted, since the author meant another. A value written plainly but wrongly
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
*
|
|
18
18
|
* A value the index needs (the title, a name, `parameters.controls`, `group`, `height`, `main`, the meta's `alsoIn`,
|
|
19
19
|
* `related`, `keywords`) is read when written plainly; written by code, it is left to the frame, which reads it when
|
|
20
|
-
* the story loads. `touch`, `logic`, `device`, `design.figma` and the meta's `propGroups` are read by
|
|
21
|
-
* (`indexedParameters.ts`, `indexFields.ts`), so written by code they are a problem.
|
|
20
|
+
* the story loads. `touch`, `logic`, `device`, `requiredFirst`, `design.figma` and the meta's `propGroups` are read by
|
|
21
|
+
* the index only (`indexedParameters.ts`, `indexFields.ts`), so written by code they are a problem.
|
|
22
22
|
* A `design.figma` is checked here too, so the pages never carry the checker: one that is not a link to a Figma file is
|
|
23
23
|
* noted and left out, and the file keeps its stories. A title written by code, or none, is the file's path
|
|
24
24
|
* (`autoTitle`, as Storybook's auto-title); one written by code is also noted, since the author meant another. A value written plainly but wrongly
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { StoryIndexEntry } from "@forgebook/story-format/source";
|
|
2
2
|
import { type Fields } from "./indexFields.ts";
|
|
3
3
|
/** What the index keeps of these parameters: each only when written. */
|
|
4
|
-
export type IndexedParameters = Pick<StoryIndexEntry, "isTouch" | "isLogic" | "device">;
|
|
4
|
+
export type IndexedParameters = Pick<StoryIndexEntry, "isTouch" | "isLogic" | "device" | "requiredFirst">;
|
|
5
5
|
/**
|
|
6
6
|
* The parameters only the index reads, each only when written, or the first problem.
|
|
7
7
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deviceSettingProblem, isDeviceOrientation } from "../deviceView.js";
|
|
2
|
+
import { REQUIRED_FIRST_LAYOUTS } from "../propGroups.js";
|
|
2
3
|
import { keyName, parameter, unwrapped } from "./indexFields.js";
|
|
3
4
|
/** Why `parameters.device` cannot be read as it is written. */
|
|
4
5
|
const DEVICE_PROBLEM = '`parameters.device` must be written as a plain object (`{ frame: true, model: "iphone-16" }`)';
|
|
@@ -87,6 +88,21 @@ const deviceOf = (value) => {
|
|
|
87
88
|
const problem = deviceSettingProblem(setting);
|
|
88
89
|
return problem === undefined ? setting : new Error(`\`parameters.device\`: ${problem}`);
|
|
89
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* `parameters.requiredFirst`: `undefined` when none, `"in-groups"`, `"section"` or `false` written plainly, or a
|
|
93
|
+
* problem otherwise.
|
|
94
|
+
*
|
|
95
|
+
* @param value - the parameter's value.
|
|
96
|
+
*/
|
|
97
|
+
const requiredFirstOf = (value) => {
|
|
98
|
+
if (value === undefined)
|
|
99
|
+
return undefined;
|
|
100
|
+
const written = value.type === "Literal" ? value.value : undefined;
|
|
101
|
+
const layout = REQUIRED_FIRST_LAYOUTS.find((each) => each === written);
|
|
102
|
+
if (layout !== undefined)
|
|
103
|
+
return layout;
|
|
104
|
+
return new Error('`parameters.requiredFirst` must be written as a plain `"in-groups"`, `"section"` or `false`');
|
|
105
|
+
};
|
|
90
106
|
/**
|
|
91
107
|
* The parameters only the index reads, each only when written, or the first problem.
|
|
92
108
|
*
|
|
@@ -102,9 +118,13 @@ export const indexedParameters = (fields) => {
|
|
|
102
118
|
const device = deviceOf(parameter(fields, "device"));
|
|
103
119
|
if (device instanceof Error)
|
|
104
120
|
return device;
|
|
121
|
+
const requiredFirst = requiredFirstOf(parameter(fields, "requiredFirst"));
|
|
122
|
+
if (requiredFirst instanceof Error)
|
|
123
|
+
return requiredFirst;
|
|
105
124
|
return {
|
|
106
125
|
...(touch === undefined ? {} : { isTouch: touch }),
|
|
107
126
|
...(logic === undefined ? {} : { isLogic: logic }),
|
|
108
|
-
...(device === undefined ? {} : { device })
|
|
127
|
+
...(device === undefined ? {} : { device }),
|
|
128
|
+
...(requiredFirst === undefined ? {} : { requiredFirst })
|
|
109
129
|
};
|
|
110
130
|
};
|
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
* @remarks
|
|
7
7
|
* Vite's CORS lets any page on `localhost` or `127.0.0.1` read the dev server, whatever its port: a frame on a second
|
|
8
8
|
* port would read the catalogue's agent API and files as before. So the catalogue refuses a request another origin
|
|
9
|
-
* sends, unless it
|
|
9
|
+
* sends, unless it opens one of its pages (a link, an embed) or a public file, which shows it but lets nobody read it.
|
|
10
|
+
* No other address is opened for another origin's page: a server's route acts when opened (`/__open-in-editor` opens a
|
|
11
|
+
* file), and a module or a source file has no use as a page.
|
|
12
|
+
*
|
|
13
|
+
* Vite checks `Host` first, on both ports, against `server.allowedHosts`: a page that reaches `127.0.0.1` through its
|
|
14
|
+
* own domain (DNS rebinding) is not told apart here, since it is same-origin for its browser, and over plain HTTP on
|
|
15
|
+
* that domain its browser sends no `Sec-Fetch-*`.
|
|
10
16
|
*
|
|
11
17
|
* The frames' origin would otherwise serve everything the catalogue's does, and a story's script would read the
|
|
12
18
|
* project from there. It serves a script or a `fetch` (whose body a script reads) only for a module that another
|
|
@@ -23,7 +29,10 @@
|
|
|
23
29
|
*/
|
|
24
30
|
/** What a browser says of a request, from its headers. */
|
|
25
31
|
export type RequestFacts = {
|
|
26
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Its path as an address parser reads it, without the query (`/frame.html`): the routes behind the guards read the
|
|
34
|
+
* same, so `//x/__forgebook/api/stories` or `/./__forgebook/api/stories` is the route it would reach.
|
|
35
|
+
*/
|
|
27
36
|
readonly path: string;
|
|
28
37
|
/** Its query, with its `?` (`?raw`); empty without one. */
|
|
29
38
|
readonly query: string;
|
|
@@ -44,13 +53,14 @@ export declare const FRAME_PAGE_PATH = "/frame.html";
|
|
|
44
53
|
export type CatalogueAnswer = "serve" | "refuse" | "to-frames";
|
|
45
54
|
/**
|
|
46
55
|
* What the catalogue's origin does with a request: the frame page goes to the frames' origin, another origin's
|
|
47
|
-
* request is refused, the rest is served.
|
|
56
|
+
* request is refused unless it opens a page or a public file, the rest is served.
|
|
48
57
|
*
|
|
49
58
|
* @param request - what the browser says of it.
|
|
50
59
|
* @param framedPages - the server's routes that are pages made to be framed by another origin's page (a client's
|
|
51
60
|
* inline preview), which only show a story and act on nothing.
|
|
61
|
+
* @param isPublic - whether a path names a file of the public folder; asked only of another origin's navigation.
|
|
52
62
|
*/
|
|
53
|
-
export declare const catalogueAnswer: (request: RequestFacts, framedPages: ReadonlySet<string
|
|
63
|
+
export declare const catalogueAnswer: (request: RequestFacts, framedPages: ReadonlySet<string>, isPublic?: (path: string) => boolean) => CatalogueAnswer;
|
|
54
64
|
/** What the frames' origin does with a request: `ask-graph` serves it when another module imports it. */
|
|
55
65
|
export type FramesAnswer = "serve" | "refuse" | "not-found" | "ask-graph";
|
|
56
66
|
/** What the frames' origin knows of a request's path. */
|