@anydocs/core 1.0.0
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/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +1 -0
- package/dist/config/project-config.d.ts +10 -0
- package/dist/config/project-config.d.ts.map +1 -0
- package/dist/config/project-config.js +52 -0
- package/dist/errors/domain-error.d.ts +12 -0
- package/dist/errors/domain-error.d.ts.map +1 -0
- package/dist/errors/domain-error.js +8 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/validation-error.d.ts +5 -0
- package/dist/errors/validation-error.d.ts.map +1 -0
- package/dist/errors/validation-error.js +7 -0
- package/dist/fs/api-source-repository.d.ts +16 -0
- package/dist/fs/api-source-repository.d.ts.map +1 -0
- package/dist/fs/api-source-repository.js +89 -0
- package/dist/fs/content-repository.d.ts +13 -0
- package/dist/fs/content-repository.d.ts.map +1 -0
- package/dist/fs/content-repository.js +171 -0
- package/dist/fs/docs-repository.d.ts +26 -0
- package/dist/fs/docs-repository.d.ts.map +1 -0
- package/dist/fs/docs-repository.js +270 -0
- package/dist/fs/index.d.ts +5 -0
- package/dist/fs/index.d.ts.map +1 -0
- package/dist/fs/index.js +4 -0
- package/dist/fs/project-paths.d.ts +4 -0
- package/dist/fs/project-paths.d.ts.map +1 -0
- package/dist/fs/project-paths.js +55 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/publishing/build-artifacts.d.ts +4 -0
- package/dist/publishing/build-artifacts.d.ts.map +1 -0
- package/dist/publishing/build-artifacts.js +453 -0
- package/dist/publishing/build-openapi-artifacts.d.ts +3 -0
- package/dist/publishing/build-openapi-artifacts.d.ts.map +1 -0
- package/dist/publishing/build-openapi-artifacts.js +253 -0
- package/dist/publishing/index.d.ts +4 -0
- package/dist/publishing/index.d.ts.map +1 -0
- package/dist/publishing/index.js +3 -0
- package/dist/publishing/publication-filter.d.ts +22 -0
- package/dist/publishing/publication-filter.d.ts.map +1 -0
- package/dist/publishing/publication-filter.js +98 -0
- package/dist/schemas/api-source-schema.d.ts +3 -0
- package/dist/schemas/api-source-schema.d.ts.map +1 -0
- package/dist/schemas/api-source-schema.js +110 -0
- package/dist/schemas/docs-schema.d.ts +7 -0
- package/dist/schemas/docs-schema.d.ts.map +1 -0
- package/dist/schemas/docs-schema.js +212 -0
- package/dist/schemas/index.d.ts +4 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +3 -0
- package/dist/schemas/project-schema.d.ts +3 -0
- package/dist/schemas/project-schema.d.ts.map +1 -0
- package/dist/schemas/project-schema.js +268 -0
- package/dist/services/authoring-service.d.ts +137 -0
- package/dist/services/authoring-service.d.ts.map +1 -0
- package/dist/services/authoring-service.js +583 -0
- package/dist/services/build-service.d.ts +35 -0
- package/dist/services/build-service.d.ts.map +1 -0
- package/dist/services/build-service.js +84 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +10 -0
- package/dist/services/init-service.d.ts +15 -0
- package/dist/services/init-service.d.ts.map +1 -0
- package/dist/services/init-service.js +127 -0
- package/dist/services/legacy-conversion-service.d.ts +8 -0
- package/dist/services/legacy-conversion-service.d.ts.map +1 -0
- package/dist/services/legacy-conversion-service.js +601 -0
- package/dist/services/legacy-import-service.d.ts +10 -0
- package/dist/services/legacy-import-service.d.ts.map +1 -0
- package/dist/services/legacy-import-service.js +239 -0
- package/dist/services/page-template-service.d.ts +81 -0
- package/dist/services/page-template-service.d.ts.map +1 -0
- package/dist/services/page-template-service.js +342 -0
- package/dist/services/preview-service.d.ts +29 -0
- package/dist/services/preview-service.d.ts.map +1 -0
- package/dist/services/preview-service.js +45 -0
- package/dist/services/watch-service.d.ts +24 -0
- package/dist/services/watch-service.d.ts.map +1 -0
- package/dist/services/watch-service.js +216 -0
- package/dist/services/web-runtime-bridge.d.ts +33 -0
- package/dist/services/web-runtime-bridge.d.ts.map +1 -0
- package/dist/services/web-runtime-bridge.js +330 -0
- package/dist/services/workflow-compatibility-service.d.ts +3 -0
- package/dist/services/workflow-compatibility-service.d.ts.map +1 -0
- package/dist/services/workflow-compatibility-service.js +53 -0
- package/dist/services/workflow-standard-service.d.ts +9 -0
- package/dist/services/workflow-standard-service.d.ts.map +1 -0
- package/dist/services/workflow-standard-service.js +372 -0
- package/dist/types/api-source.d.ts +34 -0
- package/dist/types/api-source.d.ts.map +1 -0
- package/dist/types/api-source.js +8 -0
- package/dist/types/docs.d.ts +65 -0
- package/dist/types/docs.d.ts.map +1 -0
- package/dist/types/docs.js +8 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/legacy-import.d.ts +72 -0
- package/dist/types/legacy-import.d.ts.map +1 -0
- package/dist/types/legacy-import.js +1 -0
- package/dist/types/project.d.ts +85 -0
- package/dist/types/project.d.ts.map +1 -0
- package/dist/types/project.js +5 -0
- package/dist/types/workflow-standard.d.ts +51 -0
- package/dist/types/workflow-standard.d.ts.map +1 -0
- package/dist/types/workflow-standard.js +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/slug.d.ts +3 -0
- package/dist/utils/slug.d.ts.map +1 -0
- package/dist/utils/slug.js +21 -0
- package/dist/utils/yoopta-content.d.ts +13 -0
- package/dist/utils/yoopta-content.d.ts.map +1 -0
- package/dist/utils/yoopta-content.js +73 -0
- package/dist/utils/yoopta-render.d.ts +7 -0
- package/dist/utils/yoopta-render.d.ts.map +1 -0
- package/dist/utils/yoopta-render.js +155 -0
- package/package.json +30 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./project-config.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ProjectConfig } from '../types/project.ts';
|
|
2
|
+
export declare const ANYDOCS_CONFIG_FILE = "anydocs.config.json";
|
|
3
|
+
export declare const ANYDOCS_WORKFLOW_FILE = "anydocs.workflow.json";
|
|
4
|
+
export declare const DEFAULT_PROJECT_ID = "default";
|
|
5
|
+
export declare const DEFAULT_PROJECT_NAME = "Anydocs Project";
|
|
6
|
+
export declare const DEFAULT_PROJECT_LANGUAGES: readonly ["en"];
|
|
7
|
+
export declare function createDefaultProjectConfig(overrides?: Partial<ProjectConfig>): ProjectConfig;
|
|
8
|
+
export declare function resolveProjectRoot(repoRoot: string, _projectId?: string): string;
|
|
9
|
+
export declare function assertValidProjectId(projectId: string): void;
|
|
10
|
+
//# sourceMappingURL=project-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-config.d.ts","sourceRoot":"","sources":["../../src/config/project-config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkD,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzG,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AACzD,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAC7D,eAAO,MAAM,kBAAkB,YAAY,CAAC;AAC5C,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AACtD,eAAO,MAAM,yBAAyB,iBAAkB,CAAC;AAIzD,wBAAgB,0BAA0B,CACxC,SAAS,GAAE,OAAO,CAAC,aAAa,CAAM,GACrC,aAAa,CAsBf;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAkB5D"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { ValidationError } from "../errors/validation-error.js";
|
|
3
|
+
import { DEFAULT_DOCS_CODE_THEME, DEFAULT_DOCS_THEME_ID } from "../types/project.js";
|
|
4
|
+
export const ANYDOCS_CONFIG_FILE = 'anydocs.config.json';
|
|
5
|
+
export const ANYDOCS_WORKFLOW_FILE = 'anydocs.workflow.json';
|
|
6
|
+
export const DEFAULT_PROJECT_ID = 'default';
|
|
7
|
+
export const DEFAULT_PROJECT_NAME = 'Anydocs Project';
|
|
8
|
+
export const DEFAULT_PROJECT_LANGUAGES = ['en'];
|
|
9
|
+
const PROJECT_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
10
|
+
export function createDefaultProjectConfig(overrides = {}) {
|
|
11
|
+
const defaultLanguage = overrides.defaultLanguage ?? 'en';
|
|
12
|
+
const languages = overrides.languages ?? [defaultLanguage];
|
|
13
|
+
return {
|
|
14
|
+
version: 1,
|
|
15
|
+
projectId: overrides.projectId ?? DEFAULT_PROJECT_ID,
|
|
16
|
+
name: overrides.name ?? DEFAULT_PROJECT_NAME,
|
|
17
|
+
defaultLanguage,
|
|
18
|
+
languages,
|
|
19
|
+
site: {
|
|
20
|
+
theme: {
|
|
21
|
+
id: overrides.site?.theme?.id ?? DEFAULT_DOCS_THEME_ID,
|
|
22
|
+
...(overrides.site?.theme?.branding ? { branding: overrides.site.theme.branding } : {}),
|
|
23
|
+
...(overrides.site?.theme?.chrome ? { chrome: overrides.site.theme.chrome } : {}),
|
|
24
|
+
...(overrides.site?.theme?.colors ? { colors: overrides.site.theme.colors } : {}),
|
|
25
|
+
codeTheme: overrides.site?.theme?.codeTheme ?? DEFAULT_DOCS_CODE_THEME,
|
|
26
|
+
},
|
|
27
|
+
...(overrides.site?.navigation ? { navigation: overrides.site.navigation } : {}),
|
|
28
|
+
},
|
|
29
|
+
...(overrides.build ? { build: overrides.build } : {}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function resolveProjectRoot(repoRoot, _projectId) {
|
|
33
|
+
return path.resolve(repoRoot);
|
|
34
|
+
}
|
|
35
|
+
export function assertValidProjectId(projectId) {
|
|
36
|
+
if (typeof projectId !== 'string' || projectId.trim().length === 0) {
|
|
37
|
+
throw new ValidationError('Project id must be a non-empty string.', {
|
|
38
|
+
entity: 'project-id',
|
|
39
|
+
rule: 'project-id-required',
|
|
40
|
+
remediation: 'Provide a non-empty projectId using lowercase letters, numbers, and hyphens only.',
|
|
41
|
+
metadata: { received: projectId },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
45
|
+
throw new ValidationError(`Invalid project id "${projectId}".`, {
|
|
46
|
+
entity: 'project-id',
|
|
47
|
+
rule: 'project-id-format',
|
|
48
|
+
remediation: 'Use lowercase letters, numbers, and hyphens only for projectId values passed into core workflows.',
|
|
49
|
+
metadata: { received: projectId },
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type DomainErrorDetails = {
|
|
2
|
+
entity: string;
|
|
3
|
+
rule: string;
|
|
4
|
+
remediation?: string;
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
export declare class DomainError extends Error {
|
|
8
|
+
readonly code: string;
|
|
9
|
+
readonly details: DomainErrorDetails;
|
|
10
|
+
constructor(code: string, message: string, details: DomainErrorDetails);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=domain-error.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain-error.d.ts","sourceRoot":"","sources":["../../src/errors/domain-error.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC,CAAC;AAEF,qBAAa,WAAY,SAAQ,KAAK;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;gBAEzB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB;CAMvE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-error.d.ts","sourceRoot":"","sources":["../../src/errors/validation-error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,qBAAa,eAAgB,SAAQ,WAAW;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB;CAIzD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DocsLang } from '../types/docs.ts';
|
|
2
|
+
import type { ApiSourceDoc } from '../types/api-source.ts';
|
|
3
|
+
export type ApiSourceRepository = {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
apiSourcesRoot: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function createApiSourceRepository(projectRoot: string): ApiSourceRepository;
|
|
8
|
+
export declare function initializeApiSourceRepository(repository: ApiSourceRepository): Promise<void>;
|
|
9
|
+
export declare function loadApiSource(repository: ApiSourceRepository, sourceId: string): Promise<ApiSourceDoc | null>;
|
|
10
|
+
export declare function listApiSources(repository: ApiSourceRepository, options?: {
|
|
11
|
+
lang?: DocsLang;
|
|
12
|
+
status?: ApiSourceDoc['status'];
|
|
13
|
+
}): Promise<ApiSourceDoc[]>;
|
|
14
|
+
export declare function saveApiSource(repository: ApiSourceRepository, source: ApiSourceDoc): Promise<ApiSourceDoc>;
|
|
15
|
+
export declare function deleteApiSource(repository: ApiSourceRepository, sourceId: string): Promise<void>;
|
|
16
|
+
//# sourceMappingURL=api-source-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-source-repository.d.ts","sourceRoot":"","sources":["../../src/fs/api-source-repository.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,MAAM,MAAM,mBAAmB,GAAG;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAuBF,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,mBAAmB,CAKlF;AAED,wBAAsB,6BAA6B,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAElG;AAED,wBAAsB,aAAa,CAAC,UAAU,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CASnH;AAED,wBAAsB,cAAc,CAClC,UAAU,EAAE,mBAAmB,EAC/B,OAAO,GAAE;IACP,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;CAC5B,GACL,OAAO,CAAC,YAAY,EAAE,CAAC,CAkCzB;AAED,wBAAsB,aAAa,CAAC,UAAU,EAAE,mBAAmB,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAIhH;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAStG"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { validateApiSourceDoc } from "../schemas/api-source-schema.js";
|
|
4
|
+
function isMissingFileError(error) {
|
|
5
|
+
return !!error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT';
|
|
6
|
+
}
|
|
7
|
+
async function readJson(filePath) {
|
|
8
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
9
|
+
return JSON.parse(raw);
|
|
10
|
+
}
|
|
11
|
+
async function writeJsonAtomic(filePath, value) {
|
|
12
|
+
const dir = path.dirname(filePath);
|
|
13
|
+
const tempFilePath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`);
|
|
14
|
+
await fs.mkdir(dir, { recursive: true });
|
|
15
|
+
await fs.writeFile(tempFilePath, JSON.stringify(value, null, 2) + '\n', 'utf8');
|
|
16
|
+
await fs.rename(tempFilePath, filePath);
|
|
17
|
+
}
|
|
18
|
+
function sourceFile(repository, sourceId) {
|
|
19
|
+
return path.join(repository.apiSourcesRoot, `${sourceId}.json`);
|
|
20
|
+
}
|
|
21
|
+
export function createApiSourceRepository(projectRoot) {
|
|
22
|
+
return {
|
|
23
|
+
projectRoot,
|
|
24
|
+
apiSourcesRoot: path.join(projectRoot, 'api-sources'),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export async function initializeApiSourceRepository(repository) {
|
|
28
|
+
await fs.mkdir(repository.apiSourcesRoot, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
export async function loadApiSource(repository, sourceId) {
|
|
31
|
+
try {
|
|
32
|
+
return validateApiSourceDoc(await readJson(sourceFile(repository, sourceId)));
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (isMissingFileError(error)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function listApiSources(repository, options = {}) {
|
|
42
|
+
let entries = [];
|
|
43
|
+
try {
|
|
44
|
+
entries = await fs.readdir(repository.apiSourcesRoot);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
const items = [];
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (!entry.endsWith('.json')) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const source = validateApiSourceDoc(await readJson(path.join(repository.apiSourcesRoot, entry)));
|
|
56
|
+
if (options.lang && source.lang !== options.lang) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (options.status && source.status !== options.status) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
items.push(source);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (isMissingFileError(error)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
items.sort((left, right) => left.id.localeCompare(right.id));
|
|
72
|
+
return items;
|
|
73
|
+
}
|
|
74
|
+
export async function saveApiSource(repository, source) {
|
|
75
|
+
const validated = validateApiSourceDoc(source);
|
|
76
|
+
await writeJsonAtomic(sourceFile(repository, validated.id), validated);
|
|
77
|
+
return validated;
|
|
78
|
+
}
|
|
79
|
+
export async function deleteApiSource(repository, sourceId) {
|
|
80
|
+
try {
|
|
81
|
+
await fs.unlink(sourceFile(repository, sourceId));
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (isMissingFileError(error)) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ValidationError } from '../errors/validation-error.ts';
|
|
2
|
+
import type { ProjectConfig, ProjectContract } from '../types/project.ts';
|
|
3
|
+
export type Result<T, E extends Error = Error> = {
|
|
4
|
+
ok: true;
|
|
5
|
+
value: T;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: E;
|
|
9
|
+
};
|
|
10
|
+
export declare function loadProjectContract(repoRoot: string, projectId?: string, outputDir?: string): Promise<Result<ProjectContract, ValidationError>>;
|
|
11
|
+
export declare function validateProjectContract(repoRoot: string, projectId?: string): Promise<Result<ProjectConfig, ValidationError>>;
|
|
12
|
+
export declare function updateProjectConfig(repoRoot: string, patch: Partial<ProjectConfig>, projectId?: string): Promise<Result<ProjectConfig, ValidationError>>;
|
|
13
|
+
//# sourceMappingURL=content-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-repository.d.ts","sourceRoot":"","sources":["../../src/fs/content-repository.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAOhE,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAkE1E,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,KAAK,GAAG,KAAK,IACzC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAmD5B,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC,CAuEnD;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,CAUjD;AAED,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,CAAC,aAAa,CAAC,EAC7B,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,CA4BjD"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { assertValidProjectId, ANYDOCS_CONFIG_FILE, resolveProjectRoot } from "../config/project-config.js";
|
|
4
|
+
import { ValidationError } from "../errors/validation-error.js";
|
|
5
|
+
import { validateProjectConfig } from "../schemas/project-schema.js";
|
|
6
|
+
import { assertWorkflowStandardMatchesContract, createWorkflowStandardDefinition, readWorkflowStandardDefinition, } from "../services/workflow-standard-service.js";
|
|
7
|
+
import { assertProjectContractStructure, createProjectPathContract, } from "./project-paths.js";
|
|
8
|
+
import { createDocsRepository, initializeDocsRepository, loadNavigation } from "./docs-repository.js";
|
|
9
|
+
function collectReferencedTopNavGroupIds(config) {
|
|
10
|
+
return (config.site.navigation?.topNav ?? [])
|
|
11
|
+
.filter((item) => item.type === 'nav-group')
|
|
12
|
+
.map((item) => item.groupId);
|
|
13
|
+
}
|
|
14
|
+
function collectTopLevelGroupIds(items) {
|
|
15
|
+
return items.flatMap((item) => item.type === 'section' || item.type === 'folder'
|
|
16
|
+
? item.id
|
|
17
|
+
? [item.id]
|
|
18
|
+
: []
|
|
19
|
+
: []);
|
|
20
|
+
}
|
|
21
|
+
async function assertTopNavGroupsExistForLanguages(config, paths) {
|
|
22
|
+
const requiredGroupIds = [...new Set(collectReferencedTopNavGroupIds(config))];
|
|
23
|
+
if (requiredGroupIds.length === 0) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const repository = createDocsRepository(paths.projectRoot);
|
|
27
|
+
for (const language of config.languages) {
|
|
28
|
+
const navigation = await loadNavigation(repository, language);
|
|
29
|
+
const topLevelGroupIds = collectTopLevelGroupIds(navigation.items);
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
for (const groupId of topLevelGroupIds) {
|
|
32
|
+
if (!seen.has(groupId)) {
|
|
33
|
+
seen.add(groupId);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
throw new ValidationError(`Navigation for "${language}" contains duplicate top-level group id "${groupId}".`, {
|
|
37
|
+
entity: 'navigation-doc',
|
|
38
|
+
rule: 'navigation-top-level-group-id-unique',
|
|
39
|
+
remediation: 'Use unique ids for top-level section and folder items in each language navigation file.',
|
|
40
|
+
metadata: { lang: language, groupId },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
for (const groupId of requiredGroupIds) {
|
|
44
|
+
if (seen.has(groupId)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
throw new ValidationError(`Top navigation references missing group "${groupId}" in language "${language}".`, {
|
|
48
|
+
entity: 'project-config',
|
|
49
|
+
rule: 'site-navigation-top-nav-group-exists',
|
|
50
|
+
remediation: 'Create the matching top-level section or folder id in every enabled language navigation file, or update site.navigation.topNav to reference an existing group.',
|
|
51
|
+
metadata: { lang: language, groupId },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function toValidationResult(work) {
|
|
57
|
+
return work()
|
|
58
|
+
.then((value) => ({ ok: true, value }))
|
|
59
|
+
.catch((error) => {
|
|
60
|
+
if (error instanceof ValidationError) {
|
|
61
|
+
return { ok: false, error };
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async function ensureExists(targetPath, entity, remediation) {
|
|
67
|
+
try {
|
|
68
|
+
await fs.access(targetPath);
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
throw new ValidationError(`Missing required ${entity} at "${targetPath}".`, {
|
|
72
|
+
entity,
|
|
73
|
+
rule: 'required-path-exists',
|
|
74
|
+
remediation,
|
|
75
|
+
metadata: { targetPath },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function readProjectConfigFile(configPath) {
|
|
80
|
+
const rawConfig = await fs.readFile(configPath, 'utf8');
|
|
81
|
+
try {
|
|
82
|
+
return JSON.parse(rawConfig);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw new ValidationError(`Project configuration at "${configPath}" is not valid JSON.`, {
|
|
86
|
+
entity: 'project-config-file',
|
|
87
|
+
rule: 'project-config-json-valid',
|
|
88
|
+
remediation: 'Fix anydocs.config.json so it contains valid JSON before loading the project contract.',
|
|
89
|
+
metadata: {
|
|
90
|
+
configPath,
|
|
91
|
+
cause: error instanceof Error ? error.message : String(error),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async function writeJsonAtomic(targetPath, value) {
|
|
97
|
+
const tempPath = `${targetPath}.${process.pid}.${Date.now()}.tmp`;
|
|
98
|
+
await fs.writeFile(tempPath, JSON.stringify(value, null, 2) + '\n', 'utf8');
|
|
99
|
+
await fs.rename(tempPath, targetPath);
|
|
100
|
+
}
|
|
101
|
+
export async function loadProjectContract(repoRoot, projectId, outputDir) {
|
|
102
|
+
return toValidationResult(async () => {
|
|
103
|
+
if (projectId) {
|
|
104
|
+
assertValidProjectId(projectId);
|
|
105
|
+
}
|
|
106
|
+
const projectRoot = resolveProjectRoot(repoRoot, projectId);
|
|
107
|
+
const configPath = path.join(projectRoot, ANYDOCS_CONFIG_FILE);
|
|
108
|
+
await ensureExists(configPath, 'project-config-file', 'Create anydocs.config.json in the canonical project root before loading the contract.');
|
|
109
|
+
const rawConfig = await readProjectConfigFile(configPath);
|
|
110
|
+
const config = validateProjectConfig(rawConfig);
|
|
111
|
+
if (projectId && config.projectId !== projectId) {
|
|
112
|
+
throw new ValidationError(`Project configuration at "${configPath}" declares projectId "${config.projectId}" but the requested project is "${projectId}".`, {
|
|
113
|
+
entity: 'project-config',
|
|
114
|
+
rule: 'project-id-matches-requested-project-root',
|
|
115
|
+
remediation: 'Update anydocs.config.json so projectId matches the canonical project directory, or load the project using the matching projectId.',
|
|
116
|
+
metadata: {
|
|
117
|
+
configPath,
|
|
118
|
+
expectedProjectId: projectId,
|
|
119
|
+
receivedProjectId: config.projectId,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const paths = createProjectPathContract(repoRoot, config, outputDir);
|
|
124
|
+
assertProjectContractStructure(config, paths);
|
|
125
|
+
await ensureExists(paths.workflowFile, 'workflow-standard-file', 'Create anydocs.workflow.json in the canonical project root before loading the workflow standard.');
|
|
126
|
+
const workflow = await readWorkflowStandardDefinition(paths.workflowFile);
|
|
127
|
+
assertWorkflowStandardMatchesContract(workflow, { config, paths });
|
|
128
|
+
await ensureExists(paths.pagesRoot, 'pages-root', 'Create the pages directory for the canonical project structure.');
|
|
129
|
+
await ensureExists(paths.navigationRoot, 'navigation-root', 'Create the navigation directory for the canonical project structure.');
|
|
130
|
+
for (const language of config.languages) {
|
|
131
|
+
await ensureExists(paths.languageRoots[language].pagesDir, 'pages-language-root', `Create the pages/${language} directory for the canonical project structure.`);
|
|
132
|
+
await ensureExists(paths.languageRoots[language].navigationFile, 'navigation-language-file', `Create navigation/${language}.json for every enabled language.`);
|
|
133
|
+
}
|
|
134
|
+
await assertTopNavGroupsExistForLanguages(config, paths);
|
|
135
|
+
return { config, paths };
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
export async function validateProjectContract(repoRoot, projectId) {
|
|
139
|
+
const result = await loadProjectContract(repoRoot, projectId);
|
|
140
|
+
if (!result.ok) {
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
ok: true,
|
|
145
|
+
value: result.value.config,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export async function updateProjectConfig(repoRoot, patch, projectId) {
|
|
149
|
+
const contractResult = await loadProjectContract(repoRoot, projectId);
|
|
150
|
+
if (!contractResult.ok) {
|
|
151
|
+
return contractResult;
|
|
152
|
+
}
|
|
153
|
+
return toValidationResult(async () => {
|
|
154
|
+
const currentConfig = contractResult.value.config;
|
|
155
|
+
const nextConfig = validateProjectConfig({
|
|
156
|
+
...currentConfig,
|
|
157
|
+
...patch,
|
|
158
|
+
projectId: currentConfig.projectId,
|
|
159
|
+
version: currentConfig.version,
|
|
160
|
+
});
|
|
161
|
+
const nextPaths = createProjectPathContract(repoRoot, nextConfig);
|
|
162
|
+
await assertTopNavGroupsExistForLanguages(nextConfig, nextPaths);
|
|
163
|
+
await writeJsonAtomic(contractResult.value.paths.configFile, nextConfig);
|
|
164
|
+
await writeJsonAtomic(contractResult.value.paths.workflowFile, createWorkflowStandardDefinition({
|
|
165
|
+
config: nextConfig,
|
|
166
|
+
paths: nextPaths,
|
|
167
|
+
}));
|
|
168
|
+
await initializeDocsRepository(createDocsRepository(nextPaths.projectRoot), nextConfig.languages);
|
|
169
|
+
return nextConfig;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type PageDocValidationOptions } from '../schemas/docs-schema.ts';
|
|
2
|
+
import type { DocsLang, NavigationDoc, PageDoc } from '../types/docs.ts';
|
|
3
|
+
export type DocsRepository = {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
pagesRoot: string;
|
|
6
|
+
navigationRoot: string;
|
|
7
|
+
};
|
|
8
|
+
export type NavigationValidationOptions = {
|
|
9
|
+
existingPageIds?: Iterable<string>;
|
|
10
|
+
requiredTopLevelGroupIds?: Iterable<string>;
|
|
11
|
+
};
|
|
12
|
+
export type DeletePageResult = {
|
|
13
|
+
pageId: string;
|
|
14
|
+
lang: DocsLang;
|
|
15
|
+
removedNavigationRefs: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function createDocsRepository(projectRoot: string): DocsRepository;
|
|
18
|
+
export declare function initializeDocsRepository(repository: DocsRepository, languages?: DocsLang[]): Promise<void>;
|
|
19
|
+
export declare function loadNavigation(repository: DocsRepository, lang: DocsLang): Promise<NavigationDoc>;
|
|
20
|
+
export declare function saveNavigation(repository: DocsRepository, lang: DocsLang, navigation: NavigationDoc, options?: NavigationValidationOptions): Promise<NavigationDoc>;
|
|
21
|
+
export declare function listPages<TContent = unknown>(repository: DocsRepository, lang: DocsLang, options?: PageDocValidationOptions): Promise<PageDoc<TContent>[]>;
|
|
22
|
+
export declare function loadPage<TContent = unknown>(repository: DocsRepository, lang: DocsLang, pageId: string, options?: PageDocValidationOptions): Promise<PageDoc<TContent> | null>;
|
|
23
|
+
export declare function savePage<TContent = unknown>(repository: DocsRepository, lang: DocsLang, page: PageDoc<TContent>, options?: PageDocValidationOptions): Promise<PageDoc<TContent>>;
|
|
24
|
+
export declare function findPageBySlug<TContent = unknown>(repository: DocsRepository, lang: DocsLang, slug: string, options?: PageDocValidationOptions): Promise<PageDoc<TContent> | null>;
|
|
25
|
+
export declare function deletePage(repository: DocsRepository, lang: DocsLang, pageId: string): Promise<DeletePageResult>;
|
|
26
|
+
//# sourceMappingURL=docs-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs-repository.d.ts","sourceRoot":"","sources":["../../src/fs/docs-repository.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAGzE,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,eAAe,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,wBAAwB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,QAAQ,CAAC;IACf,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC;AAeF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAcxE;AA8FD,wBAAsB,wBAAwB,CAC5C,UAAU,EAAE,cAAc,EAC1B,SAAS,GAAE,QAAQ,EAAiB,GACnC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,cAAc,CAClC,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,wBAAsB,cAAc,CAClC,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,aAAa,EACzB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,aAAa,CAAC,CAkCxB;AAED,wBAAsB,SAAS,CAAC,QAAQ,GAAG,OAAO,EAChD,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,QAAQ,EACd,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CA+B9B;AAED,wBAAsB,QAAQ,CAAC,QAAQ,GAAG,OAAO,EAC/C,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAgBnC;AAED,wBAAsB,QAAQ,CAAC,QAAQ,GAAG,OAAO,EAC/C,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,EACvB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAiE5B;AAED,wBAAsB,cAAc,CAAC,QAAQ,GAAG,OAAO,EACrD,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,CAInC;AAED,wBAAsB,UAAU,CAC9B,UAAU,EAAE,cAAc,EAC1B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,gBAAgB,CAAC,CAyB3B"}
|