@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.
Files changed (124) hide show
  1. package/dist/config/index.d.ts +2 -0
  2. package/dist/config/index.d.ts.map +1 -0
  3. package/dist/config/index.js +1 -0
  4. package/dist/config/project-config.d.ts +10 -0
  5. package/dist/config/project-config.d.ts.map +1 -0
  6. package/dist/config/project-config.js +52 -0
  7. package/dist/errors/domain-error.d.ts +12 -0
  8. package/dist/errors/domain-error.d.ts.map +1 -0
  9. package/dist/errors/domain-error.js +8 -0
  10. package/dist/errors/index.d.ts +3 -0
  11. package/dist/errors/index.d.ts.map +1 -0
  12. package/dist/errors/index.js +2 -0
  13. package/dist/errors/validation-error.d.ts +5 -0
  14. package/dist/errors/validation-error.d.ts.map +1 -0
  15. package/dist/errors/validation-error.js +7 -0
  16. package/dist/fs/api-source-repository.d.ts +16 -0
  17. package/dist/fs/api-source-repository.d.ts.map +1 -0
  18. package/dist/fs/api-source-repository.js +89 -0
  19. package/dist/fs/content-repository.d.ts +13 -0
  20. package/dist/fs/content-repository.d.ts.map +1 -0
  21. package/dist/fs/content-repository.js +171 -0
  22. package/dist/fs/docs-repository.d.ts +26 -0
  23. package/dist/fs/docs-repository.d.ts.map +1 -0
  24. package/dist/fs/docs-repository.js +270 -0
  25. package/dist/fs/index.d.ts +5 -0
  26. package/dist/fs/index.d.ts.map +1 -0
  27. package/dist/fs/index.js +4 -0
  28. package/dist/fs/project-paths.d.ts +4 -0
  29. package/dist/fs/project-paths.d.ts.map +1 -0
  30. package/dist/fs/project-paths.js +55 -0
  31. package/dist/index.d.ts +9 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +8 -0
  34. package/dist/publishing/build-artifacts.d.ts +4 -0
  35. package/dist/publishing/build-artifacts.d.ts.map +1 -0
  36. package/dist/publishing/build-artifacts.js +453 -0
  37. package/dist/publishing/build-openapi-artifacts.d.ts +3 -0
  38. package/dist/publishing/build-openapi-artifacts.d.ts.map +1 -0
  39. package/dist/publishing/build-openapi-artifacts.js +253 -0
  40. package/dist/publishing/index.d.ts +4 -0
  41. package/dist/publishing/index.d.ts.map +1 -0
  42. package/dist/publishing/index.js +3 -0
  43. package/dist/publishing/publication-filter.d.ts +22 -0
  44. package/dist/publishing/publication-filter.d.ts.map +1 -0
  45. package/dist/publishing/publication-filter.js +98 -0
  46. package/dist/schemas/api-source-schema.d.ts +3 -0
  47. package/dist/schemas/api-source-schema.d.ts.map +1 -0
  48. package/dist/schemas/api-source-schema.js +110 -0
  49. package/dist/schemas/docs-schema.d.ts +7 -0
  50. package/dist/schemas/docs-schema.d.ts.map +1 -0
  51. package/dist/schemas/docs-schema.js +212 -0
  52. package/dist/schemas/index.d.ts +4 -0
  53. package/dist/schemas/index.d.ts.map +1 -0
  54. package/dist/schemas/index.js +3 -0
  55. package/dist/schemas/project-schema.d.ts +3 -0
  56. package/dist/schemas/project-schema.d.ts.map +1 -0
  57. package/dist/schemas/project-schema.js +268 -0
  58. package/dist/services/authoring-service.d.ts +137 -0
  59. package/dist/services/authoring-service.d.ts.map +1 -0
  60. package/dist/services/authoring-service.js +583 -0
  61. package/dist/services/build-service.d.ts +35 -0
  62. package/dist/services/build-service.d.ts.map +1 -0
  63. package/dist/services/build-service.js +84 -0
  64. package/dist/services/index.d.ts +11 -0
  65. package/dist/services/index.d.ts.map +1 -0
  66. package/dist/services/index.js +10 -0
  67. package/dist/services/init-service.d.ts +15 -0
  68. package/dist/services/init-service.d.ts.map +1 -0
  69. package/dist/services/init-service.js +127 -0
  70. package/dist/services/legacy-conversion-service.d.ts +8 -0
  71. package/dist/services/legacy-conversion-service.d.ts.map +1 -0
  72. package/dist/services/legacy-conversion-service.js +601 -0
  73. package/dist/services/legacy-import-service.d.ts +10 -0
  74. package/dist/services/legacy-import-service.d.ts.map +1 -0
  75. package/dist/services/legacy-import-service.js +239 -0
  76. package/dist/services/page-template-service.d.ts +81 -0
  77. package/dist/services/page-template-service.d.ts.map +1 -0
  78. package/dist/services/page-template-service.js +342 -0
  79. package/dist/services/preview-service.d.ts +29 -0
  80. package/dist/services/preview-service.d.ts.map +1 -0
  81. package/dist/services/preview-service.js +45 -0
  82. package/dist/services/watch-service.d.ts +24 -0
  83. package/dist/services/watch-service.d.ts.map +1 -0
  84. package/dist/services/watch-service.js +216 -0
  85. package/dist/services/web-runtime-bridge.d.ts +33 -0
  86. package/dist/services/web-runtime-bridge.d.ts.map +1 -0
  87. package/dist/services/web-runtime-bridge.js +330 -0
  88. package/dist/services/workflow-compatibility-service.d.ts +3 -0
  89. package/dist/services/workflow-compatibility-service.d.ts.map +1 -0
  90. package/dist/services/workflow-compatibility-service.js +53 -0
  91. package/dist/services/workflow-standard-service.d.ts +9 -0
  92. package/dist/services/workflow-standard-service.d.ts.map +1 -0
  93. package/dist/services/workflow-standard-service.js +372 -0
  94. package/dist/types/api-source.d.ts +34 -0
  95. package/dist/types/api-source.d.ts.map +1 -0
  96. package/dist/types/api-source.js +8 -0
  97. package/dist/types/docs.d.ts +65 -0
  98. package/dist/types/docs.d.ts.map +1 -0
  99. package/dist/types/docs.js +8 -0
  100. package/dist/types/index.d.ts +6 -0
  101. package/dist/types/index.d.ts.map +1 -0
  102. package/dist/types/index.js +5 -0
  103. package/dist/types/legacy-import.d.ts +72 -0
  104. package/dist/types/legacy-import.d.ts.map +1 -0
  105. package/dist/types/legacy-import.js +1 -0
  106. package/dist/types/project.d.ts +85 -0
  107. package/dist/types/project.d.ts.map +1 -0
  108. package/dist/types/project.js +5 -0
  109. package/dist/types/workflow-standard.d.ts +51 -0
  110. package/dist/types/workflow-standard.d.ts.map +1 -0
  111. package/dist/types/workflow-standard.js +1 -0
  112. package/dist/utils/index.d.ts +4 -0
  113. package/dist/utils/index.d.ts.map +1 -0
  114. package/dist/utils/index.js +3 -0
  115. package/dist/utils/slug.d.ts +3 -0
  116. package/dist/utils/slug.d.ts.map +1 -0
  117. package/dist/utils/slug.js +21 -0
  118. package/dist/utils/yoopta-content.d.ts +13 -0
  119. package/dist/utils/yoopta-content.d.ts.map +1 -0
  120. package/dist/utils/yoopta-content.js +73 -0
  121. package/dist/utils/yoopta-render.d.ts +7 -0
  122. package/dist/utils/yoopta-render.d.ts.map +1 -0
  123. package/dist/utils/yoopta-render.js +155 -0
  124. package/package.json +30 -0
@@ -0,0 +1,253 @@
1
+ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createApiSourceRepository, listApiSources } from "../fs/api-source-repository.js";
4
+ function isRecord(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ async function writeJson(filePath, value) {
8
+ await mkdir(path.dirname(filePath), { recursive: true });
9
+ await writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
10
+ }
11
+ async function writeText(filePath, value) {
12
+ await mkdir(path.dirname(filePath), { recursive: true });
13
+ await writeFile(filePath, `${value}\n`, 'utf8');
14
+ }
15
+ async function cleanupOpenApiArtifacts(root) {
16
+ await rm(root, { recursive: true, force: true });
17
+ await mkdir(root, { recursive: true });
18
+ }
19
+ async function resolveSourcePayload(contract, source) {
20
+ if (source.source.kind === 'url') {
21
+ const response = await fetch(source.source.url);
22
+ if (!response.ok) {
23
+ throw new Error(`Failed to fetch api source "${source.id}" from "${source.source.url}": ${response.status} ${response.statusText}`);
24
+ }
25
+ return response.json();
26
+ }
27
+ const targetPath = path.isAbsolute(source.source.path)
28
+ ? source.source.path
29
+ : path.join(contract.paths.projectRoot, source.source.path);
30
+ return JSON.parse(await readFile(targetPath, 'utf8'));
31
+ }
32
+ function normalizeRouteBase(source) {
33
+ if (source.runtime?.routeBase) {
34
+ return source.runtime.routeBase.startsWith('/') ? source.runtime.routeBase : `/${source.runtime.routeBase}`;
35
+ }
36
+ return `/${source.lang}/reference/${source.id}`;
37
+ }
38
+ function schemaToText(schema) {
39
+ if (!isRecord(schema)) {
40
+ return 'unknown';
41
+ }
42
+ if (typeof schema.$ref === 'string') {
43
+ return schema.$ref.split('/').at(-1) ?? schema.$ref;
44
+ }
45
+ if (schema.type === 'array') {
46
+ return `array<${schemaToText(schema.items)}>`;
47
+ }
48
+ if (Array.isArray(schema.oneOf)) {
49
+ return schema.oneOf.map((item) => schemaToText(item)).join(' | ');
50
+ }
51
+ if (Array.isArray(schema.allOf)) {
52
+ return schema.allOf.map((item) => schemaToText(item)).join(' & ');
53
+ }
54
+ if (Array.isArray(schema.anyOf)) {
55
+ return schema.anyOf.map((item) => schemaToText(item)).join(' | ');
56
+ }
57
+ if (typeof schema.type === 'string') {
58
+ return typeof schema.format === 'string' ? `${schema.type}(${schema.format})` : schema.type;
59
+ }
60
+ return 'object';
61
+ }
62
+ function collectSchemaRefs(node, refs = new Set()) {
63
+ if (Array.isArray(node)) {
64
+ for (const item of node) {
65
+ collectSchemaRefs(item, refs);
66
+ }
67
+ return refs;
68
+ }
69
+ if (!isRecord(node)) {
70
+ return refs;
71
+ }
72
+ if (typeof node.$ref === 'string') {
73
+ refs.add(node.$ref.split('/').at(-1) ?? node.$ref);
74
+ }
75
+ for (const value of Object.values(node)) {
76
+ collectSchemaRefs(value, refs);
77
+ }
78
+ return refs;
79
+ }
80
+ function toOperationId(method, endpointPath, operation) {
81
+ if (typeof operation.operationId === 'string' && operation.operationId.trim().length > 0) {
82
+ return operation.operationId.trim();
83
+ }
84
+ return `${method.toLowerCase()}-${endpointPath.replace(/[^a-zA-Z0-9]+/g, '-').replace(/^-|-$/g, '')}`;
85
+ }
86
+ function toOperationDocs(source, spec) {
87
+ const routeBase = normalizeRouteBase(source);
88
+ const rawPaths = isRecord(spec.paths) ? spec.paths : {};
89
+ const rawSchemas = isRecord(spec.components) && isRecord(spec.components.schemas) ? spec.components.schemas : {};
90
+ const operations = [];
91
+ const operationIdsBySchema = new Map();
92
+ for (const [endpointPath, rawMethods] of Object.entries(rawPaths)) {
93
+ if (!isRecord(rawMethods)) {
94
+ continue;
95
+ }
96
+ for (const [method, rawOperation] of Object.entries(rawMethods)) {
97
+ if (!isRecord(rawOperation)) {
98
+ continue;
99
+ }
100
+ const operationId = toOperationId(method, endpointPath, rawOperation);
101
+ const summary = typeof rawOperation.summary === 'string' && rawOperation.summary.trim().length > 0
102
+ ? rawOperation.summary.trim()
103
+ : `${method.toUpperCase()} ${endpointPath}`;
104
+ const description = typeof rawOperation.description === 'string' && rawOperation.description.trim().length > 0
105
+ ? rawOperation.description.trim()
106
+ : summary;
107
+ const tag = Array.isArray(rawOperation.tags) && typeof rawOperation.tags[0] === 'string'
108
+ ? rawOperation.tags[0]
109
+ : 'Untagged';
110
+ const schemaRefs = [...collectSchemaRefs(rawOperation)].sort((left, right) => left.localeCompare(right));
111
+ for (const ref of schemaRefs) {
112
+ if (!operationIdsBySchema.has(ref)) {
113
+ operationIdsBySchema.set(ref, new Set());
114
+ }
115
+ operationIdsBySchema.get(ref)?.add(operationId);
116
+ }
117
+ const plainText = [
118
+ `${method.toUpperCase()} ${endpointPath}`,
119
+ summary,
120
+ description,
121
+ `tag ${tag}`,
122
+ ...(schemaRefs.length > 0 ? [`schemas ${schemaRefs.join(', ')}`] : []),
123
+ ].join('. ');
124
+ operations.push({
125
+ id: operationId,
126
+ method: method.toUpperCase(),
127
+ path: endpointPath,
128
+ summary,
129
+ description,
130
+ tag,
131
+ schemaRefs,
132
+ href: routeBase,
133
+ plainText,
134
+ });
135
+ }
136
+ }
137
+ const schemas = Object.entries(rawSchemas)
138
+ .filter(([, value]) => isRecord(value))
139
+ .map(([name, rawSchema]) => {
140
+ const schemaRecord = rawSchema;
141
+ const properties = isRecord(schemaRecord.properties) ? schemaRecord.properties : {};
142
+ const propertyDocs = Object.entries(properties).map(([propertyName, propertySchema]) => ({
143
+ name: propertyName,
144
+ type: schemaToText(propertySchema),
145
+ description: isRecord(propertySchema) && typeof propertySchema.description === 'string'
146
+ ? propertySchema.description
147
+ : '',
148
+ }));
149
+ const required = Array.isArray(schemaRecord.required)
150
+ ? schemaRecord.required.filter((entry) => typeof entry === 'string')
151
+ : [];
152
+ const usedByOperations = [...(operationIdsBySchema.get(name) ?? new Set())].sort((left, right) => left.localeCompare(right));
153
+ const plainText = [
154
+ name,
155
+ `type ${typeof schemaRecord.type === 'string' ? schemaRecord.type : 'object'}`,
156
+ ...(required.length > 0 ? [`required ${required.join(', ')}`] : []),
157
+ ...(propertyDocs.length > 0
158
+ ? [`properties ${propertyDocs.map((property) => `${property.name}:${property.type}`).join(', ')}`]
159
+ : []),
160
+ ].join('. ');
161
+ return {
162
+ name,
163
+ type: typeof schemaRecord.type === 'string' ? schemaRecord.type : 'object',
164
+ required,
165
+ properties: propertyDocs,
166
+ usedByOperations,
167
+ plainText,
168
+ };
169
+ })
170
+ .sort((left, right) => left.name.localeCompare(right.name));
171
+ const chunks = [
172
+ ...operations.map((operation) => ({
173
+ id: `${source.id}:${operation.id}:summary`,
174
+ entityType: 'operation',
175
+ entityId: operation.id,
176
+ title: operation.summary,
177
+ text: operation.plainText,
178
+ href: routeBase,
179
+ })),
180
+ ...schemas.map((schema) => ({
181
+ id: `${source.id}:${schema.name}:schema`,
182
+ entityType: 'schema',
183
+ entityId: schema.name,
184
+ title: schema.name,
185
+ text: schema.plainText,
186
+ href: routeBase,
187
+ })),
188
+ ];
189
+ const sourceIndex = {
190
+ id: source.id,
191
+ title: source.display.title,
192
+ lang: source.lang,
193
+ href: routeBase,
194
+ operationCount: operations.length,
195
+ schemaCount: schemas.length,
196
+ };
197
+ return { operations, schemas, chunks, sourceIndex };
198
+ }
199
+ function buildLlmsOpenApiTxt(indexesByLanguage) {
200
+ const lines = ['# OpenAPI Sources', ''];
201
+ for (const [lang, sources] of Object.entries(indexesByLanguage).sort(([left], [right]) => left.localeCompare(right))) {
202
+ lines.push(`## ${lang}`);
203
+ lines.push('');
204
+ for (const source of sources) {
205
+ lines.push(`- ${source.title} — ${source.href} — operations: ${source.operationCount}, schemas: ${source.schemaCount}`);
206
+ }
207
+ lines.push('');
208
+ }
209
+ return lines.join('\n').trimEnd();
210
+ }
211
+ export async function writePublishedOpenApiArtifacts(contract) {
212
+ const repository = createApiSourceRepository(contract.paths.projectRoot);
213
+ const publishedSources = await listApiSources(repository, { status: 'published' });
214
+ const openApiRoot = path.join(contract.paths.machineReadableRoot, 'openapi');
215
+ await cleanupOpenApiArtifacts(openApiRoot);
216
+ const indexesByLanguage = Object.fromEntries(contract.config.languages.map((lang) => [lang, []]));
217
+ for (const source of publishedSources) {
218
+ const rawSpec = await resolveSourcePayload(contract, source);
219
+ if (!isRecord(rawSpec)) {
220
+ continue;
221
+ }
222
+ const { operations, schemas, chunks, sourceIndex } = toOperationDocs(source, rawSpec);
223
+ indexesByLanguage[source.lang]?.push(sourceIndex);
224
+ await writeJson(path.join(openApiRoot, `source.${source.id}.json`), rawSpec);
225
+ await writeJson(path.join(openApiRoot, `operations.${source.id}.${source.lang}.json`), {
226
+ version: 1,
227
+ sourceId: source.id,
228
+ items: operations,
229
+ });
230
+ await writeJson(path.join(openApiRoot, `schemas.${source.id}.${source.lang}.json`), {
231
+ version: 1,
232
+ sourceId: source.id,
233
+ items: schemas,
234
+ });
235
+ await writeJson(path.join(openApiRoot, `chunks.${source.id}.${source.lang}.json`), {
236
+ version: 1,
237
+ sourceId: source.id,
238
+ items: chunks,
239
+ });
240
+ }
241
+ for (const lang of contract.config.languages) {
242
+ await writeJson(path.join(openApiRoot, `index.${lang}.json`), {
243
+ version: 1,
244
+ sources: indexesByLanguage[lang] ?? [],
245
+ });
246
+ }
247
+ const hasPublishedSources = Object.values(indexesByLanguage).some((sources) => sources.length > 0);
248
+ if (!hasPublishedSources) {
249
+ await rm(path.join(contract.paths.artifactRoot, 'llms-openapi.txt'), { force: true });
250
+ return;
251
+ }
252
+ await writeText(path.join(contract.paths.artifactRoot, 'llms-openapi.txt'), buildLlmsOpenApiTxt(indexesByLanguage));
253
+ }
@@ -0,0 +1,4 @@
1
+ export * from './build-artifacts.ts';
2
+ export * from './build-openapi-artifacts.ts';
3
+ export * from './publication-filter.ts';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/publishing/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./build-artifacts.js";
2
+ export * from "./build-openapi-artifacts.js";
3
+ export * from "./publication-filter.js";
@@ -0,0 +1,22 @@
1
+ import type { DocsLang, NavigationDoc, NavItem, PageDoc } from '../types/docs.ts';
2
+ export type PublishedLanguageContent<TContent = unknown> = {
3
+ navigation: NavigationDoc;
4
+ pages: PageDoc<TContent>[];
5
+ };
6
+ export type PublishedRoute = {
7
+ pageId: string;
8
+ slug: string;
9
+ segments: string[];
10
+ href: string;
11
+ title: string;
12
+ };
13
+ export type PublishedSiteLanguageContent<TContent = unknown> = PublishedLanguageContent<TContent> & {
14
+ routes: PublishedRoute[];
15
+ };
16
+ export declare function isPageApprovedForPublication<TContent>(page: PageDoc<TContent>): boolean;
17
+ export declare function filterPublishedPages<TContent>(pages: PageDoc<TContent>[]): PageDoc<TContent>[];
18
+ export declare function filterNavigationToPublished(items: NavItem[], publishedPageIds: Set<string>): NavItem[];
19
+ export declare function buildPublishedLanguageContent<TContent>(navigation: NavigationDoc, pages: PageDoc<TContent>[]): PublishedLanguageContent<TContent>;
20
+ export declare function orderPublishedPagesByNavigation<TContent>(navigation: NavigationDoc, pages: PageDoc<TContent>[]): PageDoc<TContent>[];
21
+ export declare function buildPublishedSiteLanguageContent<TContent>(lang: DocsLang, navigation: NavigationDoc, pages: PageDoc<TContent>[]): PublishedSiteLanguageContent<TContent>;
22
+ //# sourceMappingURL=publication-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publication-filter.d.ts","sourceRoot":"","sources":["../../src/publishing/publication-filter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElF,MAAM,MAAM,wBAAwB,CAAC,QAAQ,GAAG,OAAO,IAAI;IACzD,UAAU,EAAE,aAAa,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,4BAA4B,CAAC,QAAQ,GAAG,OAAO,IAAI,wBAAwB,CAAC,QAAQ,CAAC,GAAG;IAClG,MAAM,EAAE,cAAc,EAAE,CAAC;CAC1B,CAAC;AAEF,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAUvF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC,EAAE,CAI9F;AAED,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,OAAO,EAAE,EAChB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC5B,OAAO,EAAE,CAyBX;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EACpD,UAAU,EAAE,aAAa,EACzB,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,GACzB,wBAAwB,CAAC,QAAQ,CAAC,CAWpC;AAqBD,wBAAgB,+BAA+B,CAAC,QAAQ,EACtD,UAAU,EAAE,aAAa,EACzB,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,GACzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CA2BrB;AAED,wBAAgB,iCAAiC,CAAC,QAAQ,EACxD,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,aAAa,EACzB,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,GACzB,4BAA4B,CAAC,QAAQ,CAAC,CAexC"}
@@ -0,0 +1,98 @@
1
+ export function isPageApprovedForPublication(page) {
2
+ if (page.status !== 'published') {
3
+ return false;
4
+ }
5
+ if (!page.review?.required) {
6
+ return true;
7
+ }
8
+ return typeof page.review.approvedAt === 'string' && page.review.approvedAt.trim().length > 0;
9
+ }
10
+ export function filterPublishedPages(pages) {
11
+ return pages
12
+ .filter((page) => isPageApprovedForPublication(page))
13
+ .sort((left, right) => left.slug.localeCompare(right.slug));
14
+ }
15
+ export function filterNavigationToPublished(items, publishedPageIds) {
16
+ const filtered = [];
17
+ for (const item of items) {
18
+ if (item.type === 'page') {
19
+ if (publishedPageIds.has(item.pageId) && !item.hidden) {
20
+ filtered.push(item);
21
+ }
22
+ continue;
23
+ }
24
+ if (item.type === 'link') {
25
+ filtered.push(item);
26
+ continue;
27
+ }
28
+ const children = filterNavigationToPublished(item.children, publishedPageIds);
29
+ if (children.length === 0) {
30
+ continue;
31
+ }
32
+ filtered.push({ ...item, children });
33
+ }
34
+ return filtered;
35
+ }
36
+ export function buildPublishedLanguageContent(navigation, pages) {
37
+ const publishedPages = filterPublishedPages(pages);
38
+ const publishedPageIds = new Set(publishedPages.map((page) => page.id));
39
+ return {
40
+ navigation: {
41
+ version: navigation.version,
42
+ items: filterNavigationToPublished(navigation.items, publishedPageIds),
43
+ },
44
+ pages: publishedPages,
45
+ };
46
+ }
47
+ function collectNavigationPageIds(items) {
48
+ const orderedPageIds = [];
49
+ for (const item of items) {
50
+ if (item.type === 'page') {
51
+ orderedPageIds.push(item.pageId);
52
+ continue;
53
+ }
54
+ if (item.type === 'link') {
55
+ continue;
56
+ }
57
+ orderedPageIds.push(...collectNavigationPageIds(item.children));
58
+ }
59
+ return orderedPageIds;
60
+ }
61
+ export function orderPublishedPagesByNavigation(navigation, pages) {
62
+ const publishedContent = buildPublishedLanguageContent(navigation, pages);
63
+ const orderedPageIds = collectNavigationPageIds(publishedContent.navigation.items);
64
+ const pagesById = new Map(publishedContent.pages.map((page) => [page.id, page]));
65
+ const orderedPages = [];
66
+ const seen = new Set();
67
+ for (const pageId of orderedPageIds) {
68
+ const page = pagesById.get(pageId);
69
+ if (!page || seen.has(pageId)) {
70
+ continue;
71
+ }
72
+ seen.add(pageId);
73
+ orderedPages.push(page);
74
+ }
75
+ for (const page of publishedContent.pages) {
76
+ if (seen.has(page.id)) {
77
+ continue;
78
+ }
79
+ seen.add(page.id);
80
+ orderedPages.push(page);
81
+ }
82
+ return orderedPages;
83
+ }
84
+ export function buildPublishedSiteLanguageContent(lang, navigation, pages) {
85
+ const publishedContent = buildPublishedLanguageContent(navigation, pages);
86
+ const orderedPages = orderPublishedPagesByNavigation(navigation, pages);
87
+ return {
88
+ navigation: publishedContent.navigation,
89
+ pages: orderedPages,
90
+ routes: orderedPages.map((page) => ({
91
+ pageId: page.id,
92
+ slug: page.slug,
93
+ segments: page.slug.split('/').filter(Boolean),
94
+ href: `/${lang}/${page.slug}`,
95
+ title: page.title,
96
+ })),
97
+ };
98
+ }
@@ -0,0 +1,3 @@
1
+ import type { ApiSourceDoc } from '../types/api-source.ts';
2
+ export declare function validateApiSourceDoc(input: unknown): ApiSourceDoc;
3
+ //# sourceMappingURL=api-source-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-source-schema.d.ts","sourceRoot":"","sources":["../../src/schemas/api-source-schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAgK3D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CA8CjE"}
@@ -0,0 +1,110 @@
1
+ import { ValidationError } from "../errors/validation-error.js";
2
+ import { isApiSourceStatus, isApiSourceType } from "../types/api-source.js";
3
+ import { isDocsLang } from "../types/docs.js";
4
+ function isRecord(value) {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+ function createApiSourceValidationError(rule, remediation, metadata) {
8
+ return new ValidationError(`API source validation failed for rule "${rule}".`, {
9
+ entity: 'api-source',
10
+ rule,
11
+ remediation,
12
+ metadata,
13
+ });
14
+ }
15
+ function assertNonEmptyString(value, rule, remediation, metadata) {
16
+ if (typeof value !== 'string' || value.trim().length === 0) {
17
+ throw createApiSourceValidationError(rule, remediation, {
18
+ ...metadata,
19
+ received: value,
20
+ });
21
+ }
22
+ }
23
+ function validateId(value) {
24
+ assertNonEmptyString(value, 'api-source-id-required', 'Provide a non-empty string for api source "id".');
25
+ const trimmed = value.trim();
26
+ if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(trimmed)) {
27
+ throw createApiSourceValidationError('api-source-id-format', 'Use lowercase letters, numbers, and hyphens only for api source ids.', { received: value });
28
+ }
29
+ return trimmed;
30
+ }
31
+ function validateSource(input) {
32
+ if (!isRecord(input) || typeof input.kind !== 'string') {
33
+ throw createApiSourceValidationError('api-source-source-object', 'Use an object with "kind" for api source "source".', { received: input });
34
+ }
35
+ if (input.kind === 'url') {
36
+ assertNonEmptyString(input.url, 'api-source-source-url-required', 'Provide a non-empty "url" when api source kind is "url".');
37
+ return {
38
+ kind: 'url',
39
+ url: input.url.trim(),
40
+ };
41
+ }
42
+ if (input.kind === 'file') {
43
+ assertNonEmptyString(input.path, 'api-source-source-path-required', 'Provide a non-empty "path" when api source kind is "file".');
44
+ return {
45
+ kind: 'file',
46
+ path: input.path.trim(),
47
+ };
48
+ }
49
+ throw createApiSourceValidationError('api-source-source-kind-supported', 'Use "url" or "file" for api source "source.kind".', { received: input.kind });
50
+ }
51
+ function validateDisplay(input) {
52
+ if (!isRecord(input)) {
53
+ throw createApiSourceValidationError('api-source-display-object', 'Use an object for api source "display".', { received: input });
54
+ }
55
+ assertNonEmptyString(input.title, 'api-source-display-title-required', 'Provide a non-empty title for api source display settings.');
56
+ if (input.groupId != null) {
57
+ assertNonEmptyString(input.groupId, 'api-source-display-group-id-string', 'Use a non-empty string for display.groupId when present.');
58
+ }
59
+ return {
60
+ title: input.title.trim(),
61
+ ...(typeof input.groupId === 'string' ? { groupId: input.groupId.trim() } : {}),
62
+ };
63
+ }
64
+ function validateRuntime(input) {
65
+ if (input == null) {
66
+ return undefined;
67
+ }
68
+ if (!isRecord(input)) {
69
+ throw createApiSourceValidationError('api-source-runtime-object', 'Use an object for api source "runtime" when present.', { received: input });
70
+ }
71
+ if (input.routeBase != null) {
72
+ assertNonEmptyString(input.routeBase, 'api-source-runtime-route-base-string', 'Use a non-empty string for runtime.routeBase when present.');
73
+ }
74
+ if (input.tryIt != null) {
75
+ if (!isRecord(input.tryIt) || typeof input.tryIt.enabled !== 'boolean') {
76
+ throw createApiSourceValidationError('api-source-runtime-try-it-object', 'Use an object with a boolean "enabled" for runtime.tryIt when present.', { received: input.tryIt });
77
+ }
78
+ }
79
+ return {
80
+ ...(typeof input.routeBase === 'string' ? { routeBase: input.routeBase.trim() } : {}),
81
+ ...(isRecord(input.tryIt) && typeof input.tryIt.enabled === 'boolean'
82
+ ? { tryIt: { enabled: input.tryIt.enabled } }
83
+ : {}),
84
+ };
85
+ }
86
+ export function validateApiSourceDoc(input) {
87
+ if (!isRecord(input)) {
88
+ throw createApiSourceValidationError('api-source-object', 'Ensure the api source document is a JSON object.', { received: input });
89
+ }
90
+ const id = validateId(input.id);
91
+ if (!isApiSourceType(input.type)) {
92
+ throw createApiSourceValidationError('api-source-type-supported', 'Use one of the supported api source types.', { received: input.type });
93
+ }
94
+ if (!isDocsLang(input.lang)) {
95
+ throw createApiSourceValidationError('api-source-lang-supported', 'Use a supported docs language for api sources.', { received: input.lang });
96
+ }
97
+ if (!isApiSourceStatus(input.status)) {
98
+ throw createApiSourceValidationError('api-source-status-supported', 'Use a supported api source status.', { received: input.status });
99
+ }
100
+ const runtime = validateRuntime(input.runtime);
101
+ return {
102
+ id,
103
+ type: input.type,
104
+ lang: input.lang,
105
+ status: input.status,
106
+ source: validateSource(input.source),
107
+ display: validateDisplay(input.display),
108
+ ...(runtime ? { runtime } : {}),
109
+ };
110
+ }
@@ -0,0 +1,7 @@
1
+ import { type NavigationDoc, type PageDoc } from '../types/docs.ts';
2
+ export declare function validateNavigationDoc(input: unknown): NavigationDoc;
3
+ export type PageDocValidationOptions = {
4
+ validateContent?: (content: unknown) => void;
5
+ };
6
+ export declare function validatePageDoc<TContent = unknown>(input: unknown, options?: PageDocValidationOptions): PageDoc<TContent>;
7
+ //# sourceMappingURL=docs-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs-schema.d.ts","sourceRoot":"","sources":["../../src/schemas/docs-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAClB,KAAK,OAAO,EAGb,MAAM,kBAAkB,CAAC;AA8T1B,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CA+BnE;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,wBAAgB,eAAe,CAAC,QAAQ,GAAG,OAAO,EAChD,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,QAAQ,CAAC,CAwHnB"}