canopycms 0.0.4 → 0.0.6
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/LICENSE +21 -0
- package/dist/api/entries.d.ts.map +1 -1
- package/dist/api/entries.js +2 -64
- package/dist/api/entries.js.map +1 -1
- package/dist/auth/plugin.d.ts +9 -0
- package/dist/auth/plugin.d.ts.map +1 -1
- package/dist/branch-workspace.d.ts +11 -0
- package/dist/branch-workspace.d.ts.map +1 -1
- package/dist/branch-workspace.js +21 -1
- package/dist/branch-workspace.js.map +1 -1
- package/dist/cli/generate-ai-content.d.ts +1 -0
- package/dist/cli/generate-ai-content.d.ts.map +1 -1
- package/dist/cli/generate-ai-content.js +3 -3
- package/dist/cli/generate-ai-content.js.map +1 -1
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +30 -19
- package/dist/cli/init.js.map +1 -1
- package/dist/content-listing.d.ts +52 -0
- package/dist/content-listing.d.ts.map +1 -0
- package/dist/content-listing.js +166 -0
- package/dist/content-listing.js.map +1 -0
- package/dist/content-reader.d.ts +0 -2
- package/dist/content-reader.d.ts.map +1 -1
- package/dist/content-reader.js +17 -13
- package/dist/content-reader.js.map +1 -1
- package/dist/content-tree.d.ts +77 -0
- package/dist/content-tree.d.ts.map +1 -0
- package/dist/content-tree.js +153 -0
- package/dist/content-tree.js.map +1 -0
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +19 -1
- package/dist/context.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/paths/branch.js +1 -1
- package/dist/paths/branch.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -1
- package/package.json +71 -78
- package/src/cli/init.ts +0 -462
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared content-listing utilities used by both the entries API and the content tree builder.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from api/entries.ts to avoid duplication.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import matter from 'gray-matter';
|
|
9
|
+
import { getFormatExtension } from './utils/format';
|
|
10
|
+
import { resolveCollectionPath } from './content-id-index';
|
|
11
|
+
import { validateAndNormalizePath } from './paths';
|
|
12
|
+
import { isNotFoundError } from './utils/error';
|
|
13
|
+
import { isValidId } from './id';
|
|
14
|
+
import { ContentStoreError } from './content-store';
|
|
15
|
+
/**
|
|
16
|
+
* Validate and normalize a path relative to root.
|
|
17
|
+
* Throws ContentStoreError on traversal attempt.
|
|
18
|
+
*/
|
|
19
|
+
const normalizePath = (root, target) => {
|
|
20
|
+
const result = validateAndNormalizePath(root, target);
|
|
21
|
+
if (!result.valid) {
|
|
22
|
+
throw new ContentStoreError(result.error || 'Path traversal detected');
|
|
23
|
+
}
|
|
24
|
+
return result.normalizedPath;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Read frontmatter (md/mdx) or full JSON data from an entry file.
|
|
28
|
+
* Returns an empty object on read/parse failure.
|
|
29
|
+
*/
|
|
30
|
+
export const readEntryData = async (filePath, format) => {
|
|
31
|
+
try {
|
|
32
|
+
const raw = await fs.readFile(filePath, 'utf8');
|
|
33
|
+
if (format === 'json') {
|
|
34
|
+
const parsed = JSON.parse(raw);
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
const parsed = matter(raw);
|
|
38
|
+
return parsed.data ?? {};
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Parse a filename: {type}.{slug}.{id}.{ext}
|
|
46
|
+
* Returns { type, slug, id } or null if the filename doesn't match the pattern.
|
|
47
|
+
*/
|
|
48
|
+
export const parseTypedFilename = (filename, entryTypes) => {
|
|
49
|
+
// Remove extension
|
|
50
|
+
const lastDot = filename.lastIndexOf('.');
|
|
51
|
+
if (lastDot === -1)
|
|
52
|
+
return null;
|
|
53
|
+
const nameWithoutExt = filename.slice(0, lastDot);
|
|
54
|
+
// Parse: {type}.{slug}.{id}
|
|
55
|
+
const parts = nameWithoutExt.split('.');
|
|
56
|
+
if (parts.length >= 3) {
|
|
57
|
+
// Check if first part matches a known entry type
|
|
58
|
+
const potentialType = parts[0];
|
|
59
|
+
const matchingType = entryTypes.find((e) => e.name === potentialType);
|
|
60
|
+
if (matchingType) {
|
|
61
|
+
const id = parts[parts.length - 1];
|
|
62
|
+
if (!isValidId(id))
|
|
63
|
+
return null;
|
|
64
|
+
const slug = parts.slice(1, -1).join('.');
|
|
65
|
+
return {
|
|
66
|
+
type: potentialType,
|
|
67
|
+
slug: slug,
|
|
68
|
+
id: id,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Sort items by a content ID order array.
|
|
76
|
+
* Items in the order array come first (in order), items not in the array come at the end
|
|
77
|
+
* sorted by the provided fallback key.
|
|
78
|
+
*/
|
|
79
|
+
export const sortByOrder = (items, order, fallbackKey) => {
|
|
80
|
+
if (!order || order.length === 0) {
|
|
81
|
+
return items.sort((a, b) => fallbackKey(a).localeCompare(fallbackKey(b)));
|
|
82
|
+
}
|
|
83
|
+
const orderMap = new Map();
|
|
84
|
+
order.forEach((id, index) => orderMap.set(id, index));
|
|
85
|
+
return items.sort((a, b) => {
|
|
86
|
+
const aIndex = a.contentId ? orderMap.get(a.contentId) : undefined;
|
|
87
|
+
const bIndex = b.contentId ? orderMap.get(b.contentId) : undefined;
|
|
88
|
+
if (aIndex !== undefined && bIndex !== undefined)
|
|
89
|
+
return aIndex - bIndex;
|
|
90
|
+
if (aIndex !== undefined)
|
|
91
|
+
return -1;
|
|
92
|
+
if (bIndex !== undefined)
|
|
93
|
+
return 1;
|
|
94
|
+
return fallbackKey(a).localeCompare(fallbackKey(b));
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* List all entries in a collection directory.
|
|
99
|
+
* Reads each entry's data (frontmatter or JSON).
|
|
100
|
+
*/
|
|
101
|
+
export const listCollectionEntries = async (root, collection) => {
|
|
102
|
+
if (collection.type !== 'collection' || !collection.entries) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
const entryTypes = collection.entries;
|
|
106
|
+
// Build a map of extension to entry types for efficient lookup
|
|
107
|
+
const extToTypes = new Map();
|
|
108
|
+
for (const entryType of entryTypes) {
|
|
109
|
+
const ext = getFormatExtension(entryType.format);
|
|
110
|
+
const existing = extToTypes.get(ext) || [];
|
|
111
|
+
existing.push(entryType);
|
|
112
|
+
extToTypes.set(ext, existing);
|
|
113
|
+
}
|
|
114
|
+
const validExts = Array.from(extToTypes.keys());
|
|
115
|
+
// Resolve the full collection path with embedded IDs
|
|
116
|
+
const collectionRoot = await resolveCollectionPath(root, collection.logicalPath);
|
|
117
|
+
if (!collectionRoot) {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
normalizePath(root, collectionRoot);
|
|
121
|
+
let dirents;
|
|
122
|
+
try {
|
|
123
|
+
dirents = await fs.readdir(collectionRoot, { withFileTypes: true });
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
if (isNotFoundError(err))
|
|
127
|
+
return [];
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
const files = dirents
|
|
131
|
+
.filter((d) => d.isFile() &&
|
|
132
|
+
validExts.some((ext) => d.name.endsWith(ext)) &&
|
|
133
|
+
d.name !== '.collection.json')
|
|
134
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
135
|
+
const entries = await Promise.all(files.map(async (file) => {
|
|
136
|
+
const absolutePath = path.join(collectionRoot, file.name);
|
|
137
|
+
const relativePath = normalizePath(root, absolutePath);
|
|
138
|
+
const parsed = parseTypedFilename(file.name, entryTypes);
|
|
139
|
+
if (!parsed) {
|
|
140
|
+
console.warn(`Skipping file with unrecognized filename format: ${file.name} (expected {type}.{slug}.{id}.{ext} with a known entry type and valid 12-char Base58 ID)`);
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const { type: entryTypeName, slug, id: contentId } = parsed;
|
|
144
|
+
const entryType = entryTypes.find((e) => e.name === entryTypeName);
|
|
145
|
+
const format = entryType?.format || 'json';
|
|
146
|
+
const [stats, data] = await Promise.all([
|
|
147
|
+
fs.stat(absolutePath),
|
|
148
|
+
readEntryData(absolutePath, format),
|
|
149
|
+
]);
|
|
150
|
+
const item = {
|
|
151
|
+
logicalPath: `${collection.logicalPath}/${slug}`,
|
|
152
|
+
contentId,
|
|
153
|
+
slug,
|
|
154
|
+
collectionPath: collection.logicalPath,
|
|
155
|
+
collectionName: collection.name,
|
|
156
|
+
format,
|
|
157
|
+
entryType: entryTypeName || 'default',
|
|
158
|
+
physicalPath: relativePath,
|
|
159
|
+
data,
|
|
160
|
+
updatedAt: stats.mtime.toISOString(),
|
|
161
|
+
};
|
|
162
|
+
return item;
|
|
163
|
+
}));
|
|
164
|
+
return entries.filter((e) => e !== null);
|
|
165
|
+
};
|
|
166
|
+
//# sourceMappingURL=content-listing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-listing.js","sourceRoot":"","sources":["../src/content-listing.ts"],"names":[],"mappings":"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"}
|
package/dist/content-reader.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { BranchWorkspaceManager } from './branch-workspace';
|
|
2
1
|
import { type LogicalPath, type EntrySlug } from './paths';
|
|
3
2
|
import type { CanopyServices } from './services';
|
|
4
3
|
import type { BranchContext } from './types';
|
|
5
4
|
import type { CanopyUser } from './user';
|
|
6
5
|
export interface ContentReaderOptions {
|
|
7
6
|
services: CanopyServices;
|
|
8
|
-
workspaceManager?: BranchWorkspaceManager;
|
|
9
7
|
basePathOverride?: string;
|
|
10
8
|
defaultBranch?: string;
|
|
11
9
|
createdBy?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-reader.d.ts","sourceRoot":"","sources":["../src/content-reader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"content-reader.d.ts","sourceRoot":"","sources":["../src/content-reader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,WAAW,EAAqB,KAAK,SAAS,EAAE,MAAM,SAAS,CAAA;AAEjG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAIxC,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAA;CACrE;AAED,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,SAAS,EAAE,WAAW,CAAA;IACtB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,IAAI,EAAE,UAAU,CAAA;IAChB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,EAChB,KAAK,EAAE,gBAAgB,EACvB,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACxC;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,oBAAoB,KAAG,aA+KnE,CAAA"}
|
package/dist/content-reader.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { loadBranchContext, loadOrCreateBranchContext } from './branch-workspace';
|
|
2
2
|
import { ContentStore, ContentStoreError } from './content-store';
|
|
3
3
|
import { resolveBranchPaths } from './paths';
|
|
4
4
|
import { isDeployedStatic, isBuildMode } from './build-mode';
|
|
@@ -11,31 +11,35 @@ export const createContentReader = (options) => {
|
|
|
11
11
|
const services = options.services;
|
|
12
12
|
const operatingMode = services.config.mode;
|
|
13
13
|
const basePathOverride = options.basePathOverride;
|
|
14
|
-
const workspaceManager = options.workspaceManager ?? new BranchWorkspaceManager(services.config);
|
|
15
14
|
const defaultBranch = options.defaultBranch ?? services.config.defaultBaseBranch ?? 'main';
|
|
16
15
|
const allowCreateBranch = options.allowCreateBranch ?? true;
|
|
17
16
|
const createdBy = options.createdBy ?? 'canopycms-content-reader';
|
|
18
17
|
const resolveBranchContext = async (branchName) => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
// Check custom resolver first (e.g., from HTTP handler)
|
|
19
|
+
if (options.getBranchContext) {
|
|
20
|
+
const existing = await options.getBranchContext(branchName);
|
|
21
|
+
if (existing)
|
|
22
|
+
return existing;
|
|
23
|
+
}
|
|
24
|
+
if (allowCreateBranch) {
|
|
25
|
+
return loadOrCreateBranchContext({
|
|
26
|
+
config: services.config,
|
|
22
27
|
branchName,
|
|
23
28
|
mode: operatingMode,
|
|
24
29
|
basePathOverride,
|
|
30
|
+
createdBy,
|
|
31
|
+
remoteUrl: services.config.defaultRemoteUrl,
|
|
25
32
|
});
|
|
26
|
-
if (existing) {
|
|
27
|
-
return existing;
|
|
28
|
-
}
|
|
29
|
-
if (!allowCreateBranch) {
|
|
30
|
-
throw new ContentStoreError(`Branch not found: ${branchName}`);
|
|
31
33
|
}
|
|
32
|
-
|
|
34
|
+
// Not allowed to create — must exist
|
|
35
|
+
const existing = await loadBranchContext({
|
|
33
36
|
branchName,
|
|
34
37
|
mode: operatingMode,
|
|
35
38
|
basePathOverride,
|
|
36
|
-
createdBy,
|
|
37
|
-
remoteUrl: services.config.defaultRemoteUrl,
|
|
38
39
|
});
|
|
40
|
+
if (!existing)
|
|
41
|
+
throw new ContentStoreError(`Branch not found: ${branchName}`);
|
|
42
|
+
return existing;
|
|
39
43
|
};
|
|
40
44
|
const resolveStore = async (branchName) => {
|
|
41
45
|
const context = await resolveBranchContext(branchName);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-reader.js","sourceRoot":"","sources":["../src/content-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"content-reader.js","sourceRoot":"","sources":["../src/content-reader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content tree builder for adopters.
|
|
3
|
+
*
|
|
4
|
+
* Walks the schema + filesystem and returns a tree of content nodes that
|
|
5
|
+
* adopters can use for navigation, sitemaps, search indexes, breadcrumbs, etc.
|
|
6
|
+
*
|
|
7
|
+
* Nodes carry Canopy's structural facts (logicalPath, contentId, collection
|
|
8
|
+
* metadata, entry metadata). Display concerns like labels are left to the
|
|
9
|
+
* adopter via the `extract` callback.
|
|
10
|
+
*/
|
|
11
|
+
import type { FlatSchemaItem, ContentFormat } from './config';
|
|
12
|
+
import type { LogicalPath, ContentId, EntrySlug } from './paths/types';
|
|
13
|
+
export interface ContentTreeNode<T = unknown> {
|
|
14
|
+
/** URL path, e.g. "/docs/getting-started". Computed by buildPath option. */
|
|
15
|
+
path: string;
|
|
16
|
+
/** Logical CMS path */
|
|
17
|
+
logicalPath: LogicalPath;
|
|
18
|
+
/** 'collection' or 'entry' */
|
|
19
|
+
kind: 'collection' | 'entry';
|
|
20
|
+
/** Content ID (collections from schema, entries from filename) */
|
|
21
|
+
contentId?: ContentId;
|
|
22
|
+
/** Collection metadata — present when kind === 'collection' */
|
|
23
|
+
collection?: {
|
|
24
|
+
name: string;
|
|
25
|
+
label?: string;
|
|
26
|
+
};
|
|
27
|
+
/** Entry metadata — present when kind === 'entry' */
|
|
28
|
+
entry?: {
|
|
29
|
+
slug: EntrySlug;
|
|
30
|
+
entryType: string;
|
|
31
|
+
format: ContentFormat;
|
|
32
|
+
/** Raw entry data (frontmatter for md/mdx, parsed JSON for json). */
|
|
33
|
+
data: Record<string, unknown>;
|
|
34
|
+
};
|
|
35
|
+
/** Adopter-extracted custom fields from the extract callback */
|
|
36
|
+
fields?: T;
|
|
37
|
+
/** Children (entries + subcollections interleaved by order array) */
|
|
38
|
+
children?: ContentTreeNode<T>[];
|
|
39
|
+
}
|
|
40
|
+
export interface BuildContentTreeOptions<T = unknown> {
|
|
41
|
+
/** Starting collection path. Defaults to content root. */
|
|
42
|
+
rootPath?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Extract typed custom fields from each node's raw data.
|
|
45
|
+
* For entries: data is frontmatter (md/mdx) or parsed JSON.
|
|
46
|
+
* For collections: data is `{ name, label }` from the schema.
|
|
47
|
+
*/
|
|
48
|
+
extract?: (data: Record<string, unknown>, node: {
|
|
49
|
+
kind: 'collection' | 'entry';
|
|
50
|
+
logicalPath: LogicalPath;
|
|
51
|
+
}) => T;
|
|
52
|
+
/**
|
|
53
|
+
* Filter: return false to exclude a node and its descendants.
|
|
54
|
+
* Runs after extract, so `fields` is available.
|
|
55
|
+
*/
|
|
56
|
+
filter?: (node: ContentTreeNode<T>) => boolean;
|
|
57
|
+
/** Custom URL path builder. Default: strips content root prefix, joins with /. */
|
|
58
|
+
buildPath?: (logicalPath: LogicalPath, kind: 'collection' | 'entry') => string;
|
|
59
|
+
/**
|
|
60
|
+
* Custom sort for children at each level.
|
|
61
|
+
* When provided, replaces the default sort (order array → alphabetical).
|
|
62
|
+
* Runs after extract + filter, so `fields` is available.
|
|
63
|
+
*/
|
|
64
|
+
sort?: (a: ContentTreeNode<T>, b: ContentTreeNode<T>) => number;
|
|
65
|
+
/** Max depth to traverse. Default: unlimited. */
|
|
66
|
+
maxDepth?: number;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build a content tree from a flattened schema and the filesystem.
|
|
70
|
+
*
|
|
71
|
+
* @param branchRoot - Absolute path to the branch workspace root
|
|
72
|
+
* @param flatSchema - Flattened schema items (from flattenSchema)
|
|
73
|
+
* @param contentRootName - The content root name (e.g. "content")
|
|
74
|
+
* @param options - Tree-building options
|
|
75
|
+
*/
|
|
76
|
+
export declare function buildContentTree<T = unknown>(branchRoot: string, flatSchema: FlatSchemaItem[], contentRootName: string, options?: BuildContentTreeOptions<T>): Promise<ContentTreeNode<T>[]>;
|
|
77
|
+
//# sourceMappingURL=content-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-tree.d.ts","sourceRoot":"","sources":["../src/content-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAOtE,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAA;IACZ,uBAAuB;IACvB,WAAW,EAAE,WAAW,CAAA;IACxB,8BAA8B;IAC9B,IAAI,EAAE,YAAY,GAAG,OAAO,CAAA;IAC5B,kEAAkE;IAClE,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,+DAA+D;IAC/D,UAAU,CAAC,EAAE;QACX,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,qDAAqD;IACrD,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,SAAS,CAAA;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,MAAM,EAAE,aAAa,CAAA;QACrB,qEAAqE;QACrE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAC9B,CAAA;IACD,gEAAgE;IAChE,MAAM,CAAC,EAAE,CAAC,CAAA;IACV,qEAAqE;IACrE,QAAQ,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAA;CAChC;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,OAAO;IAClD,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CACR,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,EAAE;QAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE,KAC7D,CAAC,CAAA;IACN;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,OAAO,CAAA;IAC9C,kFAAkF;IAClF,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,KAAK,MAAM,CAAA;IAC9E;;;;OAIG;IACH,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,MAAM,CAAA;IAC/D,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAgCD;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAChD,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,cAAc,EAAE,EAC5B,eAAe,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE,uBAAuB,CAAC,CAAC,CAAC,GACnC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAmG/B"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content tree builder for adopters.
|
|
3
|
+
*
|
|
4
|
+
* Walks the schema + filesystem and returns a tree of content nodes that
|
|
5
|
+
* adopters can use for navigation, sitemaps, search indexes, breadcrumbs, etc.
|
|
6
|
+
*
|
|
7
|
+
* Nodes carry Canopy's structural facts (logicalPath, contentId, collection
|
|
8
|
+
* metadata, entry metadata). Display concerns like labels are left to the
|
|
9
|
+
* adopter via the `extract` callback.
|
|
10
|
+
*/
|
|
11
|
+
import { listCollectionEntries, sortByOrder } from './content-listing';
|
|
12
|
+
/** Group flat schema items by parentPath for O(1) child lookup. */
|
|
13
|
+
const groupByParent = (flat) => {
|
|
14
|
+
const map = new Map();
|
|
15
|
+
for (const item of flat) {
|
|
16
|
+
if (item.type !== 'collection')
|
|
17
|
+
continue;
|
|
18
|
+
const key = item.parentPath;
|
|
19
|
+
if (!map.has(key))
|
|
20
|
+
map.set(key, []);
|
|
21
|
+
map.get(key).push(item);
|
|
22
|
+
}
|
|
23
|
+
return map;
|
|
24
|
+
};
|
|
25
|
+
/** Default path builder: strips the content root prefix and prepends /. */
|
|
26
|
+
const defaultBuildPath = (logicalPath, contentRootName) => {
|
|
27
|
+
const prefix = contentRootName ? `${contentRootName}/` : '';
|
|
28
|
+
const stripped = prefix && logicalPath.startsWith(prefix) ? logicalPath.slice(prefix.length) : logicalPath;
|
|
29
|
+
return stripped ? `/${stripped}` : '/';
|
|
30
|
+
};
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Core builder
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* Build a content tree from a flattened schema and the filesystem.
|
|
36
|
+
*
|
|
37
|
+
* @param branchRoot - Absolute path to the branch workspace root
|
|
38
|
+
* @param flatSchema - Flattened schema items (from flattenSchema)
|
|
39
|
+
* @param contentRootName - The content root name (e.g. "content")
|
|
40
|
+
* @param options - Tree-building options
|
|
41
|
+
*/
|
|
42
|
+
export async function buildContentTree(branchRoot, flatSchema, contentRootName, options) {
|
|
43
|
+
const childrenByParent = groupByParent(flatSchema);
|
|
44
|
+
const extract = options?.extract;
|
|
45
|
+
const filter = options?.filter;
|
|
46
|
+
const buildPath = options?.buildPath ?? ((lp) => defaultBuildPath(lp, contentRootName));
|
|
47
|
+
const customSort = options?.sort;
|
|
48
|
+
const maxDepth = options?.maxDepth;
|
|
49
|
+
// Find the starting collection(s)
|
|
50
|
+
const rootPath = options?.rootPath ?? contentRootName;
|
|
51
|
+
const rootCollection = flatSchema.find((item) => item.type === 'collection' && item.logicalPath === rootPath);
|
|
52
|
+
if (!rootCollection)
|
|
53
|
+
return [];
|
|
54
|
+
const buildNode = async (collection, depth) => {
|
|
55
|
+
// Build collection node (before children, so filter can reject early)
|
|
56
|
+
const collectionData = {
|
|
57
|
+
name: collection.name,
|
|
58
|
+
label: collection.label,
|
|
59
|
+
};
|
|
60
|
+
const node = {
|
|
61
|
+
path: buildPath(collection.logicalPath, 'collection'),
|
|
62
|
+
logicalPath: collection.logicalPath,
|
|
63
|
+
kind: 'collection',
|
|
64
|
+
contentId: collection.contentId,
|
|
65
|
+
collection: {
|
|
66
|
+
name: collection.name,
|
|
67
|
+
label: collection.label,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
if (extract) {
|
|
71
|
+
node.fields = extract(collectionData, {
|
|
72
|
+
kind: 'collection',
|
|
73
|
+
logicalPath: collection.logicalPath,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (filter && !filter(node))
|
|
77
|
+
return null;
|
|
78
|
+
// If at max depth, return collection without children
|
|
79
|
+
if (maxDepth !== undefined && depth >= maxDepth)
|
|
80
|
+
return node;
|
|
81
|
+
// Gather child collections and entries in parallel
|
|
82
|
+
const childCollections = childrenByParent.get(collection.logicalPath) ?? [];
|
|
83
|
+
const [childCollectionNodes, entries] = await Promise.all([
|
|
84
|
+
Promise.all(childCollections.map((child) => buildNode(child, depth + 1))),
|
|
85
|
+
listCollectionEntries(branchRoot, collection),
|
|
86
|
+
]);
|
|
87
|
+
// Build entry nodes
|
|
88
|
+
const entryNodes = [];
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const entryNode = buildEntryNode(entry, buildPath, extract);
|
|
91
|
+
if (filter && !filter(entryNode))
|
|
92
|
+
continue;
|
|
93
|
+
entryNodes.push(entryNode);
|
|
94
|
+
}
|
|
95
|
+
// Combine and interleave by order array (or custom sort)
|
|
96
|
+
const allChildren = interleaveChildren(childCollectionNodes.filter((n) => n !== null), entryNodes, collection.order, customSort);
|
|
97
|
+
// Prune empty collections (no children after filtering)
|
|
98
|
+
if (allChildren.length === 0)
|
|
99
|
+
return null;
|
|
100
|
+
node.children = allChildren;
|
|
101
|
+
return node;
|
|
102
|
+
};
|
|
103
|
+
// Start from root's children (don't include the root collection itself)
|
|
104
|
+
const topLevelCollections = childrenByParent.get(rootCollection.logicalPath) ?? [];
|
|
105
|
+
// Also get entries directly in the root collection
|
|
106
|
+
const [collectionNodes, rootEntries] = await Promise.all([
|
|
107
|
+
Promise.all(topLevelCollections.map((child) => buildNode(child, 1))),
|
|
108
|
+
listCollectionEntries(branchRoot, rootCollection),
|
|
109
|
+
]);
|
|
110
|
+
const rootEntryNodes = [];
|
|
111
|
+
for (const entry of rootEntries) {
|
|
112
|
+
const entryNode = buildEntryNode(entry, buildPath, extract);
|
|
113
|
+
if (filter && !filter(entryNode))
|
|
114
|
+
continue;
|
|
115
|
+
rootEntryNodes.push(entryNode);
|
|
116
|
+
}
|
|
117
|
+
return interleaveChildren(collectionNodes.filter((n) => n !== null), rootEntryNodes, rootCollection.order, customSort);
|
|
118
|
+
}
|
|
119
|
+
/** Build a ContentTreeNode for an entry. */
|
|
120
|
+
function buildEntryNode(entry, buildPath, extract) {
|
|
121
|
+
const node = {
|
|
122
|
+
path: buildPath(entry.logicalPath, 'entry'),
|
|
123
|
+
logicalPath: entry.logicalPath,
|
|
124
|
+
kind: 'entry',
|
|
125
|
+
contentId: entry.contentId,
|
|
126
|
+
entry: {
|
|
127
|
+
slug: entry.slug,
|
|
128
|
+
entryType: entry.entryType,
|
|
129
|
+
format: entry.format,
|
|
130
|
+
data: entry.data,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
if (extract) {
|
|
134
|
+
node.fields = extract(entry.data, {
|
|
135
|
+
kind: 'entry',
|
|
136
|
+
logicalPath: entry.logicalPath,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return node;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Interleave collection nodes and entry nodes.
|
|
143
|
+
* When a custom sort is provided, it replaces the default order-array sort entirely.
|
|
144
|
+
* Otherwise: items in the order array come first (by position), then the rest alphabetically.
|
|
145
|
+
*/
|
|
146
|
+
function interleaveChildren(collectionNodes, entryNodes, order, customSort) {
|
|
147
|
+
const all = [...collectionNodes, ...entryNodes];
|
|
148
|
+
if (customSort) {
|
|
149
|
+
return all.sort(customSort);
|
|
150
|
+
}
|
|
151
|
+
return sortByOrder(all, order, (item) => item.kind === 'collection' ? (item.collection?.name ?? '') : (item.entry?.slug ?? ''));
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=content-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"content-tree.js","sourceRoot":"","sources":["../src/content-tree.ts"],"names":[],"mappings":"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"}
|
package/dist/context.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CanopyUser } from './user';
|
|
2
2
|
import type { CanopyServices } from './services';
|
|
3
|
+
import { type BuildContentTreeOptions, type ContentTreeNode } from './content-tree';
|
|
3
4
|
export interface CanopyContextOptions {
|
|
4
5
|
services: CanopyServices;
|
|
5
6
|
/**
|
|
@@ -21,6 +22,8 @@ export interface CanopyContext {
|
|
|
21
22
|
data: T;
|
|
22
23
|
path: string;
|
|
23
24
|
}>;
|
|
25
|
+
/** Build a content tree from the schema and filesystem entries. */
|
|
26
|
+
buildContentTree: <T = unknown>(options?: BuildContentTreeOptions<T>) => Promise<ContentTreeNode<T>[]>;
|
|
24
27
|
/** Underlying services */
|
|
25
28
|
services: CanopyServices;
|
|
26
29
|
/** Current authenticated user */
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAMhD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAA;IACxB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE;QACzB,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAC5B,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAExC,0BAA0B;IAC1B,QAAQ,EAAE,cAAc,CAAA;IAExB,iCAAiC;IACjC,IAAI,EAAE,UAAU,CAAA;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB;sBAsBlC,OAAO,CAAC,aAAa,CAAC;;
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAMhD,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAA;AAEvB,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,cAAc,CAAA;IACxB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE;QACzB,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,iBAAiB,CAAC,EAAE,OAAO,CAAA;KAC5B,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAExC,mEAAmE;IACnE,gBAAgB,EAAE,CAAC,CAAC,GAAG,OAAO,EAC5B,OAAO,CAAC,EAAE,uBAAuB,CAAC,CAAC,CAAC,KACjC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAElC,0BAA0B;IAC1B,QAAQ,EAAE,cAAc,CAAA;IAExB,iCAAiC;IACjC,IAAI,EAAE,UAAU,CAAA;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB;sBAsBlC,OAAO,CAAC,aAAa,CAAC;;EAkEpD"}
|
package/dist/context.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isDeployedStatic, isBuildMode, STATIC_DEPLOY_USER } from './build-mode';
|
|
2
2
|
import { createContentReader } from './content-reader';
|
|
3
|
-
import { createLogicalPath, parseSlug } from './paths';
|
|
3
|
+
import { createLogicalPath, parseSlug, resolveBranchPaths } from './paths';
|
|
4
|
+
import { loadOrCreateBranchContext } from './branch-workspace';
|
|
5
|
+
import { buildContentTree as buildContentTreeImpl, } from './content-tree';
|
|
4
6
|
/**
|
|
5
7
|
* Create a Canopy context that manages auth + content reading.
|
|
6
8
|
* Framework-agnostic - the adapter provides the extractUser function.
|
|
@@ -55,8 +57,24 @@ export function createCanopyContext(options) {
|
|
|
55
57
|
};
|
|
56
58
|
return baseReader.read(readInput);
|
|
57
59
|
};
|
|
60
|
+
const buildContentTree = async (options) => {
|
|
61
|
+
const operatingMode = services.config.mode;
|
|
62
|
+
const defaultBranch = services.config.defaultBaseBranch ?? 'main';
|
|
63
|
+
const branchContext = await loadOrCreateBranchContext({
|
|
64
|
+
config: services.config,
|
|
65
|
+
branchName: defaultBranch,
|
|
66
|
+
mode: operatingMode,
|
|
67
|
+
createdBy: 'canopycms-context',
|
|
68
|
+
remoteUrl: services.config.defaultRemoteUrl,
|
|
69
|
+
});
|
|
70
|
+
const { branchRoot } = resolveBranchPaths(branchContext, operatingMode);
|
|
71
|
+
const contentRootName = services.config.contentRoot || 'content';
|
|
72
|
+
const { flatSchema } = await services.branchSchemaCache.getSchema(branchRoot, services.entrySchemaRegistry, contentRootName);
|
|
73
|
+
return buildContentTreeImpl(branchRoot, flatSchema, contentRootName, options);
|
|
74
|
+
};
|
|
58
75
|
return {
|
|
59
76
|
read,
|
|
77
|
+
buildContentTree,
|
|
60
78
|
services,
|
|
61
79
|
user,
|
|
62
80
|
};
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAkB,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ export type { AuthPlugin, AuthPluginFactory } from './auth/plugin';
|
|
|
8
8
|
export type { UserSearchResult, GroupMetadata, AuthenticationResult } from './auth/types';
|
|
9
9
|
export { isCanopyRequest, isHeadersLike, extractHeaders, validateAuthContext, } from './auth/context-helpers';
|
|
10
10
|
export type { HeadersLike } from './auth/context-helpers';
|
|
11
|
+
export type { ContentTreeNode, BuildContentTreeOptions } from './content-tree';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AAGtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAClD,YAAY,EACV,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAA;AAGnB,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACzF,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,EACd,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,QAAQ,CAAA;AAGtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAClD,YAAY,EACV,eAAe,EACf,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,eAAe,GAChB,MAAM,YAAY,CAAA;AAGnB,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAClE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACzF,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,EACd,mBAAmB,GACpB,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzD,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/paths/branch.js
CHANGED
|
@@ -17,7 +17,7 @@ export class BranchPathError extends Error {
|
|
|
17
17
|
export function sanitizeBranchName(branchName) {
|
|
18
18
|
const replaced = branchName.replace(/[^a-zA-Z0-9._-]/g, '-');
|
|
19
19
|
const squashed = replaced.replace(/-+/g, '-');
|
|
20
|
-
const trimmedDots = squashed.replace(/^\.+/, '').replace(
|
|
20
|
+
const trimmedDots = squashed.replace(/^\.+/, '').replace(/(?<!\.)\.+$/, '');
|
|
21
21
|
return (trimmedDots || 'branch');
|
|
22
22
|
}
|
|
23
23
|
const resolveContentBranchesRoot = (mode, override) => {
|