@ctcstack/ctcdocs-core 0.22.0 → 0.22.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/dist/document-format.d.ts +42 -0
- package/dist/document-format.js +87 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/site-configuration.d.ts +10 -1
- package/dist/site-configuration.js +7 -0
- package/package.json +5 -1
- package/src/document-format.ts +116 -0
- package/src/index.ts +15 -0
- package/src/site-configuration.ts +18 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a published page is made from, said once for the sync, the site and
|
|
3
|
+
* the Worker: a Google Doc, a PDF file (ADR-027), a spreadsheet (ADR-046), or
|
|
4
|
+
* a video or audio file (ADR-047).
|
|
5
|
+
*
|
|
6
|
+
* The same fact travels under three names. The page's frontmatter records its
|
|
7
|
+
* `sourceType`, the generated index and a run's report its format, and the
|
|
8
|
+
* MCP server its own `format`, which calls a Google Doc `doc` (ADR-044). Each
|
|
9
|
+
* is derived here from the format, so a new kind of page is added in one
|
|
10
|
+
* place and every list that names the kinds follows.
|
|
11
|
+
*/
|
|
12
|
+
/** The kinds of page a Drive file is published as. */
|
|
13
|
+
export declare const DOCUMENT_FORMATS: readonly ["google-doc", "pdf", "sheet", "video", "audio"];
|
|
14
|
+
export type DocumentFormat = (typeof DOCUMENT_FORMATS)[number];
|
|
15
|
+
/**
|
|
16
|
+
* The frontmatter `sourceType` of a page published from a Drive file. A video
|
|
17
|
+
* and an audio file share one; the page's `media.kind` tells them apart.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DOCUMENT_SOURCE_TYPES: readonly ["google-doc", "drive-pdf", "drive-sheet", "drive-media"];
|
|
20
|
+
export type DocumentSourceType = (typeof DOCUMENT_SOURCE_TYPES)[number];
|
|
21
|
+
/** The `sourceType` a page of `format` records. */
|
|
22
|
+
export declare function documentSourceType(format: DocumentFormat): DocumentSourceType;
|
|
23
|
+
/** Whether a page's `sourceType` says it was published from a Drive file. */
|
|
24
|
+
export declare function isDocumentSourceType(value: string | undefined): value is DocumentSourceType;
|
|
25
|
+
/**
|
|
26
|
+
* The format of a page, from its `sourceType` and, for a recording, the kind
|
|
27
|
+
* its frontmatter records; `undefined` for a page no Drive file made.
|
|
28
|
+
*/
|
|
29
|
+
export declare function documentFormatOf(sourceType: string | undefined, mediaKind?: 'video' | 'audio'): DocumentFormat | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The mark beside a page that is not a Google Doc, in the sidebar and in a
|
|
32
|
+
* run's list of pages.
|
|
33
|
+
*/
|
|
34
|
+
export declare const DOCUMENT_FORMAT_BADGES: Readonly<Record<DocumentFormat, string | undefined>>;
|
|
35
|
+
/**
|
|
36
|
+
* How `llms.txt` names a page that is not a Google Doc after its title, as in
|
|
37
|
+
* `Price list (spreadsheet)`.
|
|
38
|
+
*/
|
|
39
|
+
export declare const DOCUMENT_FORMAT_NOUNS: Readonly<Record<DocumentFormat, string | undefined>>;
|
|
40
|
+
/** The format the MCP server reports: a Google Doc is `doc` (ADR-044). */
|
|
41
|
+
export type AgentDocumentFormat = Exclude<DocumentFormat, 'google-doc'> | 'doc';
|
|
42
|
+
export declare function agentDocumentFormat(format: DocumentFormat): AgentDocumentFormat;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a published page is made from, said once for the sync, the site and
|
|
3
|
+
* the Worker: a Google Doc, a PDF file (ADR-027), a spreadsheet (ADR-046), or
|
|
4
|
+
* a video or audio file (ADR-047).
|
|
5
|
+
*
|
|
6
|
+
* The same fact travels under three names. The page's frontmatter records its
|
|
7
|
+
* `sourceType`, the generated index and a run's report its format, and the
|
|
8
|
+
* MCP server its own `format`, which calls a Google Doc `doc` (ADR-044). Each
|
|
9
|
+
* is derived here from the format, so a new kind of page is added in one
|
|
10
|
+
* place and every list that names the kinds follows.
|
|
11
|
+
*/
|
|
12
|
+
/** The kinds of page a Drive file is published as. */
|
|
13
|
+
export const DOCUMENT_FORMATS = [
|
|
14
|
+
'google-doc',
|
|
15
|
+
'pdf',
|
|
16
|
+
'sheet',
|
|
17
|
+
'video',
|
|
18
|
+
'audio',
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* The frontmatter `sourceType` of a page published from a Drive file. A video
|
|
22
|
+
* and an audio file share one; the page's `media.kind` tells them apart.
|
|
23
|
+
*/
|
|
24
|
+
export const DOCUMENT_SOURCE_TYPES = [
|
|
25
|
+
'google-doc',
|
|
26
|
+
'drive-pdf',
|
|
27
|
+
'drive-sheet',
|
|
28
|
+
'drive-media',
|
|
29
|
+
];
|
|
30
|
+
const SOURCE_TYPES = {
|
|
31
|
+
'google-doc': 'google-doc',
|
|
32
|
+
pdf: 'drive-pdf',
|
|
33
|
+
sheet: 'drive-sheet',
|
|
34
|
+
video: 'drive-media',
|
|
35
|
+
audio: 'drive-media',
|
|
36
|
+
};
|
|
37
|
+
/** The `sourceType` a page of `format` records. */
|
|
38
|
+
export function documentSourceType(format) {
|
|
39
|
+
return SOURCE_TYPES[format];
|
|
40
|
+
}
|
|
41
|
+
/** Whether a page's `sourceType` says it was published from a Drive file. */
|
|
42
|
+
export function isDocumentSourceType(value) {
|
|
43
|
+
return DOCUMENT_SOURCE_TYPES.includes(value);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The format of a page, from its `sourceType` and, for a recording, the kind
|
|
47
|
+
* its frontmatter records; `undefined` for a page no Drive file made.
|
|
48
|
+
*/
|
|
49
|
+
export function documentFormatOf(sourceType, mediaKind) {
|
|
50
|
+
switch (sourceType) {
|
|
51
|
+
case 'google-doc':
|
|
52
|
+
return 'google-doc';
|
|
53
|
+
case 'drive-pdf':
|
|
54
|
+
return 'pdf';
|
|
55
|
+
case 'drive-sheet':
|
|
56
|
+
return 'sheet';
|
|
57
|
+
case 'drive-media':
|
|
58
|
+
return mediaKind;
|
|
59
|
+
default:
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The mark beside a page that is not a Google Doc, in the sidebar and in a
|
|
65
|
+
* run's list of pages.
|
|
66
|
+
*/
|
|
67
|
+
export const DOCUMENT_FORMAT_BADGES = {
|
|
68
|
+
'google-doc': undefined,
|
|
69
|
+
pdf: 'PDF',
|
|
70
|
+
sheet: 'Sheet',
|
|
71
|
+
video: 'Video',
|
|
72
|
+
audio: 'Audio',
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* How `llms.txt` names a page that is not a Google Doc after its title, as in
|
|
76
|
+
* `Price list (spreadsheet)`.
|
|
77
|
+
*/
|
|
78
|
+
export const DOCUMENT_FORMAT_NOUNS = {
|
|
79
|
+
'google-doc': undefined,
|
|
80
|
+
pdf: 'PDF',
|
|
81
|
+
sheet: 'spreadsheet',
|
|
82
|
+
video: 'video',
|
|
83
|
+
audio: 'audio',
|
|
84
|
+
};
|
|
85
|
+
export function agentDocumentFormat(format) {
|
|
86
|
+
return format === 'google-doc' ? 'doc' : format;
|
|
87
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,3 +12,5 @@ export { accessFindings } from './access-findings.js';
|
|
|
12
12
|
export type { AccessFinding } from './access-findings.js';
|
|
13
13
|
export { fetchCharacterLimit, loadSiteConfiguration, MCP_DEFAULTS, parseSiteConfiguration, SiteConfigurationError, } from './site-configuration.js';
|
|
14
14
|
export type { AddressPolicy, BrandConfiguration, DeploymentConfiguration, DeploymentEnvironmentConfiguration, DeploymentEnvironmentConfigurations, DeploymentVisibility, HomeConfiguration, McpConfiguration, McpRecentConfiguration, McpSearchConfiguration, NavigationConfiguration, SignInConfiguration, SiteConfiguration, SyncConfigurationDefaults, } from './site-configuration.js';
|
|
15
|
+
export { agentDocumentFormat, DOCUMENT_FORMAT_BADGES, DOCUMENT_FORMAT_NOUNS, DOCUMENT_FORMATS, DOCUMENT_SOURCE_TYPES, documentFormatOf, documentSourceType, isDocumentSourceType, } from './document-format.js';
|
|
16
|
+
export type { AgentDocumentFormat, DocumentFormat, DocumentSourceType, } from './document-format.js';
|
package/dist/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { CorpusStructureError, EMPTY_CORPUS, parseCorpusStructure, readCorpusStr
|
|
|
7
7
|
export { ADMINS_CLASS, chainReaders, chainRules, classIdentifiers, computeAccessModel, documentClass, folderChain, folderClass, intersectReaders, MEMBERS_CLASS, nextPublishedReaders, widensReaders, } from './access-classes.js';
|
|
8
8
|
export { accessFindings } from './access-findings.js';
|
|
9
9
|
export { fetchCharacterLimit, loadSiteConfiguration, MCP_DEFAULTS, parseSiteConfiguration, SiteConfigurationError, } from './site-configuration.js';
|
|
10
|
+
export { agentDocumentFormat, DOCUMENT_FORMAT_BADGES, DOCUMENT_FORMAT_NOUNS, DOCUMENT_FORMATS, DOCUMENT_SOURCE_TYPES, documentFormatOf, documentSourceType, isDocumentSourceType, } from './document-format.js';
|
|
@@ -6,8 +6,17 @@ export interface BrandConfiguration {
|
|
|
6
6
|
readonly siteTitle: string;
|
|
7
7
|
/** One sentence, used as the site-wide meta description. */
|
|
8
8
|
readonly siteDescription: string;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Site-root-relative path of the favicon served from `public/`, which is
|
|
11
|
+
* also the mark in the header.
|
|
12
|
+
*/
|
|
10
13
|
readonly faviconPath: string;
|
|
14
|
+
/**
|
|
15
|
+
* The same mark drawn for a dark ground, when the light one would not show
|
|
16
|
+
* on it: the header shows it under the dark theme, and a browser whose
|
|
17
|
+
* interface is dark shows it in the tab.
|
|
18
|
+
*/
|
|
19
|
+
readonly faviconDarkPath?: string;
|
|
11
20
|
}
|
|
12
21
|
export interface HomeConfiguration {
|
|
13
22
|
/**
|
|
@@ -333,8 +333,15 @@ export function parseSiteConfiguration(input) {
|
|
|
333
333
|
if (!faviconPath.startsWith('/')) {
|
|
334
334
|
fail('brand.faviconPath', 'must be a site-root-relative path');
|
|
335
335
|
}
|
|
336
|
+
const faviconDarkPath = brandSource.faviconDarkPath === undefined
|
|
337
|
+
? undefined
|
|
338
|
+
: text(brandSource, 'faviconDarkPath', 'brand.faviconDarkPath');
|
|
339
|
+
if (faviconDarkPath !== undefined && !faviconDarkPath.startsWith('/')) {
|
|
340
|
+
fail('brand.faviconDarkPath', 'must be a site-root-relative path');
|
|
341
|
+
}
|
|
336
342
|
const brand = {
|
|
337
343
|
faviconPath,
|
|
344
|
+
...(faviconDarkPath === undefined ? {} : { faviconDarkPath }),
|
|
338
345
|
name: text(brandSource, 'name', 'brand.name'),
|
|
339
346
|
siteDescription: text(brandSource, 'siteDescription', 'brand.siteDescription'),
|
|
340
347
|
siteTitle: text(brandSource, 'siteTitle', 'brand.siteTitle'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ctcstack/ctcdocs-core",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.1",
|
|
4
4
|
"description": "Configuration schema, project layout, generated-path allowlist and the published Markdown form shared by the CTCDocs platform packages.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ctcdocs",
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"./published-markdown": {
|
|
32
32
|
"types": "./dist/published-markdown.d.ts",
|
|
33
33
|
"default": "./dist/published-markdown.js"
|
|
34
|
+
},
|
|
35
|
+
"./document-format": {
|
|
36
|
+
"types": "./dist/document-format.d.ts",
|
|
37
|
+
"default": "./dist/document-format.js"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"files": [
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a published page is made from, said once for the sync, the site and
|
|
3
|
+
* the Worker: a Google Doc, a PDF file (ADR-027), a spreadsheet (ADR-046), or
|
|
4
|
+
* a video or audio file (ADR-047).
|
|
5
|
+
*
|
|
6
|
+
* The same fact travels under three names. The page's frontmatter records its
|
|
7
|
+
* `sourceType`, the generated index and a run's report its format, and the
|
|
8
|
+
* MCP server its own `format`, which calls a Google Doc `doc` (ADR-044). Each
|
|
9
|
+
* is derived here from the format, so a new kind of page is added in one
|
|
10
|
+
* place and every list that names the kinds follows.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** The kinds of page a Drive file is published as. */
|
|
14
|
+
export const DOCUMENT_FORMATS = [
|
|
15
|
+
'google-doc',
|
|
16
|
+
'pdf',
|
|
17
|
+
'sheet',
|
|
18
|
+
'video',
|
|
19
|
+
'audio',
|
|
20
|
+
] as const;
|
|
21
|
+
|
|
22
|
+
export type DocumentFormat = (typeof DOCUMENT_FORMATS)[number];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The frontmatter `sourceType` of a page published from a Drive file. A video
|
|
26
|
+
* and an audio file share one; the page's `media.kind` tells them apart.
|
|
27
|
+
*/
|
|
28
|
+
export const DOCUMENT_SOURCE_TYPES = [
|
|
29
|
+
'google-doc',
|
|
30
|
+
'drive-pdf',
|
|
31
|
+
'drive-sheet',
|
|
32
|
+
'drive-media',
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
export type DocumentSourceType = (typeof DOCUMENT_SOURCE_TYPES)[number];
|
|
36
|
+
|
|
37
|
+
const SOURCE_TYPES: Readonly<Record<DocumentFormat, DocumentSourceType>> = {
|
|
38
|
+
'google-doc': 'google-doc',
|
|
39
|
+
pdf: 'drive-pdf',
|
|
40
|
+
sheet: 'drive-sheet',
|
|
41
|
+
video: 'drive-media',
|
|
42
|
+
audio: 'drive-media',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** The `sourceType` a page of `format` records. */
|
|
46
|
+
export function documentSourceType(format: DocumentFormat): DocumentSourceType {
|
|
47
|
+
return SOURCE_TYPES[format];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether a page's `sourceType` says it was published from a Drive file. */
|
|
51
|
+
export function isDocumentSourceType(
|
|
52
|
+
value: string | undefined,
|
|
53
|
+
): value is DocumentSourceType {
|
|
54
|
+
return (DOCUMENT_SOURCE_TYPES as readonly (string | undefined)[]).includes(
|
|
55
|
+
value,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The format of a page, from its `sourceType` and, for a recording, the kind
|
|
61
|
+
* its frontmatter records; `undefined` for a page no Drive file made.
|
|
62
|
+
*/
|
|
63
|
+
export function documentFormatOf(
|
|
64
|
+
sourceType: string | undefined,
|
|
65
|
+
mediaKind?: 'video' | 'audio',
|
|
66
|
+
): DocumentFormat | undefined {
|
|
67
|
+
switch (sourceType) {
|
|
68
|
+
case 'google-doc':
|
|
69
|
+
return 'google-doc';
|
|
70
|
+
case 'drive-pdf':
|
|
71
|
+
return 'pdf';
|
|
72
|
+
case 'drive-sheet':
|
|
73
|
+
return 'sheet';
|
|
74
|
+
case 'drive-media':
|
|
75
|
+
return mediaKind;
|
|
76
|
+
default:
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The mark beside a page that is not a Google Doc, in the sidebar and in a
|
|
83
|
+
* run's list of pages.
|
|
84
|
+
*/
|
|
85
|
+
export const DOCUMENT_FORMAT_BADGES: Readonly<
|
|
86
|
+
Record<DocumentFormat, string | undefined>
|
|
87
|
+
> = {
|
|
88
|
+
'google-doc': undefined,
|
|
89
|
+
pdf: 'PDF',
|
|
90
|
+
sheet: 'Sheet',
|
|
91
|
+
video: 'Video',
|
|
92
|
+
audio: 'Audio',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* How `llms.txt` names a page that is not a Google Doc after its title, as in
|
|
97
|
+
* `Price list (spreadsheet)`.
|
|
98
|
+
*/
|
|
99
|
+
export const DOCUMENT_FORMAT_NOUNS: Readonly<
|
|
100
|
+
Record<DocumentFormat, string | undefined>
|
|
101
|
+
> = {
|
|
102
|
+
'google-doc': undefined,
|
|
103
|
+
pdf: 'PDF',
|
|
104
|
+
sheet: 'spreadsheet',
|
|
105
|
+
video: 'video',
|
|
106
|
+
audio: 'audio',
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** The format the MCP server reports: a Google Doc is `doc` (ADR-044). */
|
|
110
|
+
export type AgentDocumentFormat = Exclude<DocumentFormat, 'google-doc'> | 'doc';
|
|
111
|
+
|
|
112
|
+
export function agentDocumentFormat(
|
|
113
|
+
format: DocumentFormat,
|
|
114
|
+
): AgentDocumentFormat {
|
|
115
|
+
return format === 'google-doc' ? 'doc' : format;
|
|
116
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -87,3 +87,18 @@ export type {
|
|
|
87
87
|
SiteConfiguration,
|
|
88
88
|
SyncConfigurationDefaults,
|
|
89
89
|
} from './site-configuration.js';
|
|
90
|
+
export {
|
|
91
|
+
agentDocumentFormat,
|
|
92
|
+
DOCUMENT_FORMAT_BADGES,
|
|
93
|
+
DOCUMENT_FORMAT_NOUNS,
|
|
94
|
+
DOCUMENT_FORMATS,
|
|
95
|
+
DOCUMENT_SOURCE_TYPES,
|
|
96
|
+
documentFormatOf,
|
|
97
|
+
documentSourceType,
|
|
98
|
+
isDocumentSourceType,
|
|
99
|
+
} from './document-format.js';
|
|
100
|
+
export type {
|
|
101
|
+
AgentDocumentFormat,
|
|
102
|
+
DocumentFormat,
|
|
103
|
+
DocumentSourceType,
|
|
104
|
+
} from './document-format.js';
|
|
@@ -27,8 +27,17 @@ export interface BrandConfiguration {
|
|
|
27
27
|
readonly siteTitle: string;
|
|
28
28
|
/** One sentence, used as the site-wide meta description. */
|
|
29
29
|
readonly siteDescription: string;
|
|
30
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* Site-root-relative path of the favicon served from `public/`, which is
|
|
32
|
+
* also the mark in the header.
|
|
33
|
+
*/
|
|
31
34
|
readonly faviconPath: string;
|
|
35
|
+
/**
|
|
36
|
+
* The same mark drawn for a dark ground, when the light one would not show
|
|
37
|
+
* on it: the header shows it under the dark theme, and a browser whose
|
|
38
|
+
* interface is dark shows it in the tab.
|
|
39
|
+
*/
|
|
40
|
+
readonly faviconDarkPath?: string;
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
export interface HomeConfiguration {
|
|
@@ -737,8 +746,16 @@ export function parseSiteConfiguration(input: unknown): SiteConfiguration {
|
|
|
737
746
|
if (!faviconPath.startsWith('/')) {
|
|
738
747
|
fail('brand.faviconPath', 'must be a site-root-relative path');
|
|
739
748
|
}
|
|
749
|
+
const faviconDarkPath =
|
|
750
|
+
brandSource.faviconDarkPath === undefined
|
|
751
|
+
? undefined
|
|
752
|
+
: text(brandSource, 'faviconDarkPath', 'brand.faviconDarkPath');
|
|
753
|
+
if (faviconDarkPath !== undefined && !faviconDarkPath.startsWith('/')) {
|
|
754
|
+
fail('brand.faviconDarkPath', 'must be a site-root-relative path');
|
|
755
|
+
}
|
|
740
756
|
const brand: BrandConfiguration = {
|
|
741
757
|
faviconPath,
|
|
758
|
+
...(faviconDarkPath === undefined ? {} : { faviconDarkPath }),
|
|
742
759
|
name: text(brandSource, 'name', 'brand.name'),
|
|
743
760
|
siteDescription: text(
|
|
744
761
|
brandSource,
|