canopycms 0.0.62 → 0.0.63

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 (73) hide show
  1. package/dist/ai/index.d.ts +9 -0
  2. package/dist/ai/index.d.ts.map +1 -1
  3. package/dist/ai/index.js +9 -0
  4. package/dist/ai/index.js.map +1 -1
  5. package/dist/ai/to-plain-text.d.ts +57 -0
  6. package/dist/ai/to-plain-text.d.ts.map +1 -0
  7. package/dist/ai/to-plain-text.js +255 -0
  8. package/dist/ai/to-plain-text.js.map +1 -0
  9. package/dist/ai/transform-components.d.ts.map +1 -1
  10. package/dist/ai/transform-components.js +70 -5
  11. package/dist/ai/transform-components.js.map +1 -1
  12. package/dist/api/comments.d.ts +2 -2
  13. package/dist/authorization/branch.d.ts +55 -3
  14. package/dist/authorization/branch.d.ts.map +1 -1
  15. package/dist/authorization/branch.js +55 -9
  16. package/dist/authorization/branch.js.map +1 -1
  17. package/dist/authorization/types.d.ts +1 -1
  18. package/dist/authorization/types.d.ts.map +1 -1
  19. package/dist/build-canopy.d.ts +49 -0
  20. package/dist/build-canopy.d.ts.map +1 -0
  21. package/dist/build-canopy.js +51 -0
  22. package/dist/build-canopy.js.map +1 -0
  23. package/dist/cli/cli.js +78 -26
  24. package/dist/cli/generate-ai-content.js +77 -25
  25. package/dist/cli/template-files/canopycms.config.ts.template +18 -1
  26. package/dist/content-listing.d.ts +110 -5
  27. package/dist/content-listing.d.ts.map +1 -1
  28. package/dist/content-listing.js +164 -23
  29. package/dist/content-listing.js.map +1 -1
  30. package/dist/content-reader.d.ts +41 -4
  31. package/dist/content-reader.d.ts.map +1 -1
  32. package/dist/content-reader.js +17 -4
  33. package/dist/content-reader.js.map +1 -1
  34. package/dist/content-store.d.ts +10 -1
  35. package/dist/content-store.d.ts.map +1 -1
  36. package/dist/content-store.js.map +1 -1
  37. package/dist/content-tree.d.ts +31 -2
  38. package/dist/content-tree.d.ts.map +1 -1
  39. package/dist/content-tree.js +22 -5
  40. package/dist/content-tree.js.map +1 -1
  41. package/dist/context.d.ts +30 -10
  42. package/dist/context.d.ts.map +1 -1
  43. package/dist/context.js +55 -4
  44. package/dist/context.js.map +1 -1
  45. package/dist/entry-schema.d.ts +228 -8
  46. package/dist/entry-schema.d.ts.map +1 -1
  47. package/dist/entry-schema.js +124 -0
  48. package/dist/entry-schema.js.map +1 -1
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +7 -0
  52. package/dist/index.js.map +1 -1
  53. package/dist/server.d.ts +51 -0
  54. package/dist/server.d.ts.map +1 -1
  55. package/dist/server.js +49 -0
  56. package/dist/server.js.map +1 -1
  57. package/dist/services.d.ts +1 -2
  58. package/dist/services.d.ts.map +1 -1
  59. package/dist/services.js +2 -6
  60. package/dist/services.js.map +1 -1
  61. package/dist/static/index.d.ts +65 -5
  62. package/dist/static/index.d.ts.map +1 -1
  63. package/dist/static/index.js +65 -12
  64. package/dist/static/index.js.map +1 -1
  65. package/dist/static/seo.d.ts +150 -0
  66. package/dist/static/seo.d.ts.map +1 -0
  67. package/dist/static/seo.js +189 -0
  68. package/dist/static/seo.js.map +1 -0
  69. package/dist/utils/sanitize-href.d.ts +46 -0
  70. package/dist/utils/sanitize-href.d.ts.map +1 -1
  71. package/dist/utils/sanitize-href.js +63 -2
  72. package/dist/utils/sanitize-href.js.map +1 -1
  73. package/package.json +1 -1
@@ -31,10 +31,55 @@ export interface CollectionListItem {
31
31
  */
32
32
  export declare const readEntryData: (filePath: string, format: ContentFormat, bodyFieldName?: string) => Promise<Record<string, unknown>>;
33
33
  /**
34
- * Parse a filename: {type}.{slug}.{id}.{ext}
35
- * Returns { type, slug, id } or null if the filename doesn't match the pattern.
34
+ * Parse a Canopy content filename into its `{type}.{slug}.{id}.{ext}` parts.
35
+ *
36
+ * ## Filename grammar
37
+ *
38
+ * Every entry file on disk is named `{type}.{slug}.{id}.{ext}`:
39
+ * - `type` — the entry type name (a key in the collection's `entries` config).
40
+ * - `slug` — the entry's URL slug. May itself contain dots (e.g. a slug of
41
+ * `getting.started.guide`), so the type and ID anchor the split: the ID is
42
+ * always the second-to-last dot-separated segment, and the slug is
43
+ * everything between the type and the ID. The returned `slug` is
44
+ * lowercased.
45
+ * - `id` — a 12-character Base58 content ID (`generateId()`/`isValidId()`).
46
+ * Base58 excludes the ambiguous characters `0`, `O`, `I`, `l` so IDs are
47
+ * unambiguous when read aloud or hand-transcribed. A filename whose
48
+ * would-be ID segment doesn't pass `isValidId` is rejected — the whole
49
+ * parse returns `null`, even if the rest of the shape looks right.
50
+ * - `ext` — the format extension (`.md`, `.mdx`, `.json`, `.yaml`), stripped
51
+ * before parsing and not part of the returned result.
52
+ *
53
+ * @param filename - The bare filename (no directory component). **This precondition is
54
+ * not enforced.** The parser splits purely on `.`, so a `/` or `\` you pass in is not
55
+ * rejected and is not treated as special — it becomes part of whichever segment it
56
+ * falls in, most often the `type` segment (e.g. `'foo/bar.slug.<validId>.md'` parses
57
+ * to `type: 'foo/bar'`). Strip any directory component yourself (e.g.
58
+ * `path.basename(filePath)`) before calling this — every internal caller already does.
59
+ * @param entryTypes - When provided, the parsed `type` segment must match one
60
+ * of these entry types by name, or the parse is rejected (this is how
61
+ * `listCollectionEntries` filters out files that don't belong to the
62
+ * collection's configured entry types). Omit this argument to parse
63
+ * structurally without validating the type against a known list — useful
64
+ * for adopter code that needs to recover `{type, slug, id}` from a
65
+ * filename without having a schema/entry-types list on hand (e.g. a
66
+ * filesystem walk over content for tooling or diagnostics). Even without
67
+ * `entryTypes`, a leading-dot filename (dotfile, editor swap/backup file)
68
+ * is always rejected — an empty string is never a legal type, matching the
69
+ * `filename.startsWith('.')` guard `extractEntryTypeFromFilename` in
70
+ * `content-id-index.ts` already applies.
71
+ * @returns `{ type, slug, id }`, or `null` if `filename` doesn't match the
72
+ * `{type}.{slug}.{id}.{ext}` shape (too few segments, no extension, a
73
+ * leading dot, an invalid ID, or — when `entryTypes` is given — an
74
+ * unrecognized type). `id` is validated (`isValidId`) and safe to trust. **`slug` is
75
+ * not** — it is the raw dot-joined middle segment(s), lowercased, cast to the branded
76
+ * `Slug` type without running `parseSlug`'s validation. A filename with an
77
+ * unconventional slug segment (e.g. containing a space) still parses and still
78
+ * receives the `Slug` brand. Callers that need a validated slug must run the result
79
+ * through `parseSlug` themselves; this function's contract is "split the filename
80
+ * grammar apart," not "validate every part."
36
81
  */
37
- export declare const parseTypedFilename: (filename: string, entryTypes: readonly EntryTypeConfig[]) => {
82
+ export declare const parseTypedFilename: (filename: string, entryTypes?: readonly EntryTypeConfig[]) => {
38
83
  type: string;
39
84
  slug: Slug;
40
85
  id: ContentId;
@@ -76,6 +121,15 @@ export interface ListEntriesItem<T = Record<string, unknown>> {
76
121
  data: T;
77
122
  /** Field definitions for this entry's type, when resolvable. */
78
123
  schema?: EntrySchema;
124
+ /**
125
+ * Filesystem mtime (ISO 8601) of the entry file, from an unconditional `fs.stat`
126
+ * done while listing. Caveat: this is a checkout-time timestamp, not an editorial
127
+ * one — a fresh CI clone resets every file's mtime to checkout time, so treat this
128
+ * as "changed since last build" at best, not an authoritative last-edited date for
129
+ * a public-facing `<lastmod>`. Sourcing mtime from git commit history is a
130
+ * separate, not-yet-built improvement.
131
+ */
132
+ updatedAt?: string;
79
133
  }
80
134
  export interface ListEntriesOptions<T = Record<string, unknown>> {
81
135
  /**
@@ -99,18 +153,53 @@ export interface ListEntriesOptions<T = Record<string, unknown>> {
99
153
  /** Custom sort. */
100
154
  sort?: (a: ListEntriesItem<T>, b: ListEntriesItem<T>) => number;
101
155
  }
156
+ /**
157
+ * Server-only path-ACL predicate, applied to raw entries before any of their data
158
+ * reaches a caller.
159
+ *
160
+ * Deliberately NOT part of `ListEntriesOptions`/`BuildContentTreeOptions`: those are
161
+ * adopter-facing, and adopter code must not be able to supply, widen, or override the
162
+ * access check. `context.ts` is the only intended producer — it builds the predicate
163
+ * from `services.createContentAccessChecker` for the request-scoped user, and omits it
164
+ * entirely at build time / on static deployments (synthetic admin, no ACLs).
165
+ *
166
+ * Omitting it preserves the pre-existing unfiltered behavior exactly, which is what
167
+ * build-time callers want.
168
+ */
169
+ export interface ContentVisibilityOptions {
170
+ /** Return false to drop an entry. Receives the entry's branch-root-relative physical path. */
171
+ shouldInclude?: (physicalPath: PhysicalPath) => boolean;
172
+ }
102
173
  /**
103
174
  * List all content entries as a flat array.
104
175
  *
105
176
  * Walks the schema to discover collections, reads entries from each,
106
177
  * and returns a flat list suitable for generateStaticParams, search indexing, sitemaps, etc.
107
178
  *
179
+ * Build-time-only failure: a file with a recognized content extension (`.md`/`.mdx`/`.json`/
180
+ * `.yaml`) sitting in a collection directory that *looks like an attempted entry* (see
181
+ * `looksLikeMalformedEntry` below) but doesn't match `{type}.{slug}.{id}.{ext}` is, by default,
182
+ * silently dropped (see `listCollectionEntries`'s debug-gated warning). That is exactly the
183
+ * silent-page-loss failure mode static generation must not have — a schema rename without a
184
+ * matching file rename, or an entry type declared in one collection but not another, would
185
+ * otherwise vanish a page with zero build output. So when `isBuildMode()` is true, any such file
186
+ * turns the listing into a thrown error instead (see `findInvalidEntries`/`assertBuildEntriesValid`
187
+ * in `static/index.ts` for the sibling schema-validity guard this mirrors). Outside build mode
188
+ * (admin UI, content tree, `next dev`) the file is still just skipped, since a fresh scaffold or
189
+ * mid-rename file legitimately exists there.
190
+ *
191
+ * A file that was never entry-shaped to begin with — a `README.md`, or a colocated sibling
192
+ * artifact named `{contentId}.suffix.ext` per the `entryTransforms`/`readSibling` convention
193
+ * documented in the README — is not this guard's failure mode and never throws, in or out of
194
+ * build mode. See `looksLikeMalformedEntry` for the exact shape test.
195
+ *
108
196
  * @param branchRoot - Absolute path to the branch workspace root
109
197
  * @param flatSchema - Flattened schema items (from flattenSchema)
110
198
  * @param contentRootName - The content root name (e.g. "content")
111
199
  * @param options - Listing options (extract, filter, rootPath, sort)
200
+ * @param visibility - Internal path-ACL predicate; see `ContentVisibilityOptions`
112
201
  */
113
- export declare function listEntries<T = Record<string, unknown>>(branchRoot: string, flatSchema: FlatSchemaItem[], contentRootName: string, options?: ListEntriesOptions<T>): Promise<ListEntriesItem<T>[]>;
202
+ export declare function listEntries<T = Record<string, unknown>>(branchRoot: string, flatSchema: FlatSchemaItem[], contentRootName: string, options?: ListEntriesOptions<T>, visibility?: ContentVisibilityOptions): Promise<ListEntriesItem<T>[]>;
114
203
  /**
115
204
  * Sort items by a content ID order array.
116
205
  * Items in the order array come first (in order), items not in the array come at the end
@@ -121,9 +210,25 @@ export declare function listEntries<T = Record<string, unknown>>(branchRoot: str
121
210
  export declare const sortByOrder: <T extends {
122
211
  contentId?: ContentId;
123
212
  }>(items: T[], order: readonly string[] | undefined, fallbackKey: (item: T) => string) => T[];
213
+ /** A content-extension file inside a collection directory whose filename didn't parse. */
214
+ export interface SkippedListingFile {
215
+ /** The bare filename, e.g. `article.lost-page.4fBqT78gcaLd.md`. */
216
+ filename: string;
217
+ /** The collection's logical path. */
218
+ collectionPath: LogicalPath;
219
+ }
124
220
  /**
125
221
  * List all entries in a collection directory.
126
222
  * Reads each entry's data (frontmatter or JSON).
223
+ *
224
+ * @param onSkip - Called for every file that has a recognized content extension, doesn't match
225
+ * the `{type}.{slug}.{id}.{ext}` grammar (see `parseTypedFilename`), AND structurally looks
226
+ * like an attempted entry (see `looksLikeMalformedEntry`). A file that was never entry-shaped
227
+ * (too few dot-separated segments to ever be a valid entry — a `README.md`, a colocated
228
+ * sibling artifact) is always silently dropped with a debug-gated `log.warn`, never passed to
229
+ * `onSkip`. Optional and purely a diagnostic hook — existing callers that omit it keep the
230
+ * exact prior behavior. `listEntries` below uses this to turn the skip into a hard build-time
231
+ * failure instead of a silent one.
127
232
  */
128
- export declare const listCollectionEntries: (root: string, collection: FlatSchemaItem) => Promise<CollectionListItem[]>;
233
+ export declare const listCollectionEntries: (root: string, collection: FlatSchemaItem, onSkip?: (file: SkippedListingFile) => void) => Promise<CollectionListItem[]>;
129
234
  //# sourceMappingURL=content-listing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-listing.d.ts","sourceRoot":"","sources":["../src/content-listing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content-listing.d.ts","sourceRoot":"","sources":["../src/content-listing.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ import { isNotFoundError, getErrorMessage } from './utils/error.js';
15
15
  import { createDebugLogger } from './utils/debug.js';
16
16
  import { isValidId } from './id.js';
17
17
  import { ContentStoreError } from './content-store.js';
18
+ import { isBuildMode } from './build-mode.js';
18
19
  const log = createDebugLogger({ prefix: 'ContentListing' });
19
20
  /**
20
21
  * Validate and normalize a path relative to root.
@@ -58,10 +59,61 @@ export const readEntryData = async (filePath, format, bodyFieldName = 'body') =>
58
59
  }
59
60
  };
60
61
  /**
61
- * Parse a filename: {type}.{slug}.{id}.{ext}
62
- * Returns { type, slug, id } or null if the filename doesn't match the pattern.
62
+ * Parse a Canopy content filename into its `{type}.{slug}.{id}.{ext}` parts.
63
+ *
64
+ * ## Filename grammar
65
+ *
66
+ * Every entry file on disk is named `{type}.{slug}.{id}.{ext}`:
67
+ * - `type` — the entry type name (a key in the collection's `entries` config).
68
+ * - `slug` — the entry's URL slug. May itself contain dots (e.g. a slug of
69
+ * `getting.started.guide`), so the type and ID anchor the split: the ID is
70
+ * always the second-to-last dot-separated segment, and the slug is
71
+ * everything between the type and the ID. The returned `slug` is
72
+ * lowercased.
73
+ * - `id` — a 12-character Base58 content ID (`generateId()`/`isValidId()`).
74
+ * Base58 excludes the ambiguous characters `0`, `O`, `I`, `l` so IDs are
75
+ * unambiguous when read aloud or hand-transcribed. A filename whose
76
+ * would-be ID segment doesn't pass `isValidId` is rejected — the whole
77
+ * parse returns `null`, even if the rest of the shape looks right.
78
+ * - `ext` — the format extension (`.md`, `.mdx`, `.json`, `.yaml`), stripped
79
+ * before parsing and not part of the returned result.
80
+ *
81
+ * @param filename - The bare filename (no directory component). **This precondition is
82
+ * not enforced.** The parser splits purely on `.`, so a `/` or `\` you pass in is not
83
+ * rejected and is not treated as special — it becomes part of whichever segment it
84
+ * falls in, most often the `type` segment (e.g. `'foo/bar.slug.<validId>.md'` parses
85
+ * to `type: 'foo/bar'`). Strip any directory component yourself (e.g.
86
+ * `path.basename(filePath)`) before calling this — every internal caller already does.
87
+ * @param entryTypes - When provided, the parsed `type` segment must match one
88
+ * of these entry types by name, or the parse is rejected (this is how
89
+ * `listCollectionEntries` filters out files that don't belong to the
90
+ * collection's configured entry types). Omit this argument to parse
91
+ * structurally without validating the type against a known list — useful
92
+ * for adopter code that needs to recover `{type, slug, id}` from a
93
+ * filename without having a schema/entry-types list on hand (e.g. a
94
+ * filesystem walk over content for tooling or diagnostics). Even without
95
+ * `entryTypes`, a leading-dot filename (dotfile, editor swap/backup file)
96
+ * is always rejected — an empty string is never a legal type, matching the
97
+ * `filename.startsWith('.')` guard `extractEntryTypeFromFilename` in
98
+ * `content-id-index.ts` already applies.
99
+ * @returns `{ type, slug, id }`, or `null` if `filename` doesn't match the
100
+ * `{type}.{slug}.{id}.{ext}` shape (too few segments, no extension, a
101
+ * leading dot, an invalid ID, or — when `entryTypes` is given — an
102
+ * unrecognized type). `id` is validated (`isValidId`) and safe to trust. **`slug` is
103
+ * not** — it is the raw dot-joined middle segment(s), lowercased, cast to the branded
104
+ * `Slug` type without running `parseSlug`'s validation. A filename with an
105
+ * unconventional slug segment (e.g. containing a space) still parses and still
106
+ * receives the `Slug` brand. Callers that need a validated slug must run the result
107
+ * through `parseSlug` themselves; this function's contract is "split the filename
108
+ * grammar apart," not "validate every part."
63
109
  */
64
110
  export const parseTypedFilename = (filename, entryTypes) => {
111
+ // Reject dotfiles outright (matching extractEntryTypeFromFilename's guard in
112
+ // content-id-index.ts): a leading dot can never be a legal entry type, and this
113
+ // is exactly the shape of the files a structural (no-entryTypes) parse would
114
+ // otherwise misparse -- e.g. '.hidden.file.aB3cD4eF5gH6.md' -> potentialType ''.
115
+ if (filename.startsWith('.'))
116
+ return null;
65
117
  // Remove extension
66
118
  const lastDot = filename.lastIndexOf('.');
67
119
  if (lastDot === -1)
@@ -69,23 +121,23 @@ export const parseTypedFilename = (filename, entryTypes) => {
69
121
  const nameWithoutExt = filename.slice(0, lastDot);
70
122
  // Parse: {type}.{slug}.{id}
71
123
  const parts = nameWithoutExt.split('.');
72
- if (parts.length >= 3) {
73
- // Check if first part matches a known entry type
74
- const potentialType = parts[0];
75
- const matchingType = entryTypes.find((e) => e.name === potentialType);
76
- if (matchingType) {
77
- const id = parts[parts.length - 1];
78
- if (!isValidId(id))
79
- return null;
80
- const slug = parts.slice(1, -1).join('.').toLowerCase();
81
- return {
82
- type: potentialType,
83
- slug: slug,
84
- id: id,
85
- };
86
- }
124
+ if (parts.length < 3)
125
+ return null;
126
+ const potentialType = parts[0];
127
+ // When a known-types list is supplied, the first segment must match one of
128
+ // them. Without it, any non-empty first segment is accepted as the type.
129
+ if (entryTypes && !entryTypes.some((e) => e.name === potentialType)) {
130
+ return null;
87
131
  }
88
- return null;
132
+ const id = parts[parts.length - 1];
133
+ if (!isValidId(id))
134
+ return null;
135
+ const slug = parts.slice(1, -1).join('.').toLowerCase();
136
+ return {
137
+ type: potentialType,
138
+ slug: slug,
139
+ id: id,
140
+ };
89
141
  };
90
142
  /**
91
143
  * List all content entries as a flat array.
@@ -93,12 +145,30 @@ export const parseTypedFilename = (filename, entryTypes) => {
93
145
  * Walks the schema to discover collections, reads entries from each,
94
146
  * and returns a flat list suitable for generateStaticParams, search indexing, sitemaps, etc.
95
147
  *
148
+ * Build-time-only failure: a file with a recognized content extension (`.md`/`.mdx`/`.json`/
149
+ * `.yaml`) sitting in a collection directory that *looks like an attempted entry* (see
150
+ * `looksLikeMalformedEntry` below) but doesn't match `{type}.{slug}.{id}.{ext}` is, by default,
151
+ * silently dropped (see `listCollectionEntries`'s debug-gated warning). That is exactly the
152
+ * silent-page-loss failure mode static generation must not have — a schema rename without a
153
+ * matching file rename, or an entry type declared in one collection but not another, would
154
+ * otherwise vanish a page with zero build output. So when `isBuildMode()` is true, any such file
155
+ * turns the listing into a thrown error instead (see `findInvalidEntries`/`assertBuildEntriesValid`
156
+ * in `static/index.ts` for the sibling schema-validity guard this mirrors). Outside build mode
157
+ * (admin UI, content tree, `next dev`) the file is still just skipped, since a fresh scaffold or
158
+ * mid-rename file legitimately exists there.
159
+ *
160
+ * A file that was never entry-shaped to begin with — a `README.md`, or a colocated sibling
161
+ * artifact named `{contentId}.suffix.ext` per the `entryTransforms`/`readSibling` convention
162
+ * documented in the README — is not this guard's failure mode and never throws, in or out of
163
+ * build mode. See `looksLikeMalformedEntry` for the exact shape test.
164
+ *
96
165
  * @param branchRoot - Absolute path to the branch workspace root
97
166
  * @param flatSchema - Flattened schema items (from flattenSchema)
98
167
  * @param contentRootName - The content root name (e.g. "content")
99
168
  * @param options - Listing options (extract, filter, rootPath, sort)
169
+ * @param visibility - Internal path-ACL predicate; see `ContentVisibilityOptions`
100
170
  */
101
- export async function listEntries(branchRoot, flatSchema, contentRootName, options) {
171
+ export async function listEntries(branchRoot, flatSchema, contentRootName, options, visibility) {
102
172
  const rootPath = options?.rootPath ?? contentRootName;
103
173
  const extract = options?.extract;
104
174
  const filter = options?.filter;
@@ -107,11 +177,33 @@ export async function listEntries(branchRoot, flatSchema, contentRootName, optio
107
177
  const collections = flatSchema.filter((item) => item.type === 'collection' &&
108
178
  item.entries !== undefined &&
109
179
  (item.logicalPath === rootPath || item.logicalPath.startsWith(`${rootPath}/`)));
110
- // List entries from all collections in parallel
180
+ // List entries from all collections in parallel.
181
+ // The visibility predicate is applied here, before the map below, so a denied entry's
182
+ // data never reaches `extract` (let alone the returned items).
183
+ const shouldInclude = visibility?.shouldInclude;
184
+ const skippedFiles = [];
111
185
  const collectionResults = await Promise.all(collections.map(async (collection) => {
112
- const entries = await listCollectionEntries(branchRoot, collection);
113
- return entries.map((entry) => ({ entry, collection }));
186
+ const entries = await listCollectionEntries(branchRoot, collection, (file) => skippedFiles.push(file));
187
+ const visible = shouldInclude ? entries.filter((e) => shouldInclude(e.physicalPath)) : entries;
188
+ return visible.map((entry) => ({ entry, collection }));
114
189
  }));
190
+ // Build-time only: fail loudly rather than silently shipping a build with a page missing. See
191
+ // the build-time-only-failure note in this function's own doc comment above. Only files that
192
+ // structurally look like a malformed entry reach `skippedFiles` at all -- see
193
+ // `looksLikeMalformedEntry` and `listCollectionEntries`.
194
+ if (isBuildMode() && skippedFiles.length > 0) {
195
+ const lines = skippedFiles.map(({ filename, collectionPath }) => ` - ${collectionPath}/${filename}`);
196
+ throw new Error(`CanopyCMS static build: found ${skippedFiles.length} file(s) that look like malformed ` +
197
+ `content entries inside a content collection:\n${lines.join('\n')}\n` +
198
+ 'Expected {type}.{slug}.{id}.{ext} with a known entry type (a name declared in that ' +
199
+ "collection's entries config) and a valid 12-char Base58 ID. This usually means a schema " +
200
+ 'rename without a matching file rename, an entry type declared in one collection but not ' +
201
+ 'another, or a stray file placed directly in a collection directory. If this is meant to ' +
202
+ "be a colocated sibling artifact (e.g. an entry's contentId-prefixed data file read via " +
203
+ 'an entryTransforms readSibling call), keep its name to three or fewer dot-separated ' +
204
+ 'segments (id.suffix.ext) so it is never mistaken for an entry attempt. Otherwise rename ' +
205
+ 'or move the file, or fix the schema, then rebuild.');
206
+ }
115
207
  // Flatten and map to ListEntriesItem
116
208
  const contentPrefix = contentRootName ? `${contentRootName}/` : '';
117
209
  const items = [];
@@ -144,6 +236,7 @@ export async function listEntries(branchRoot, flatSchema, contentRootName, optio
144
236
  format: entry.format,
145
237
  data,
146
238
  schema: collection.entries?.find((e) => e.name === entry.entryType)?.schema,
239
+ updatedAt: entry.updatedAt,
147
240
  };
148
241
  if (filter && !filter(item))
149
242
  continue;
@@ -187,11 +280,56 @@ export const sortByOrder = (items, order, fallbackKey) => {
187
280
  return fallbackKey(a).localeCompare(fallbackKey(b));
188
281
  });
189
282
  };
283
+ /**
284
+ * True when a content-extension filename structurally resembles an attempted entry — as opposed
285
+ * to a file that was never entry-shaped to begin with.
286
+ *
287
+ * A successfully-parsed entry always has at least 4 dot-separated segments: `type` (1+),
288
+ * `slug` (1+, since `parseTypedFilename` always takes at least the one segment between type and
289
+ * id), `id` (1), `ext` (1). So a file with 4+ segments that still failed to parse is malformed
290
+ * on its face — a wrong-length or invalid-Base58 ID, most often (`post.hello-world.BADID.md`).
291
+ *
292
+ * That segment-count test alone missed the MORE common accident: losing the ID segment
293
+ * entirely. `post.hello-world.md` (3 segments) fails `parseTypedFilename` exactly like the
294
+ * 4-segment case does, but a bare 3-or-fewer-segment cutoff would treat it as never having been
295
+ * entry-shaped and drop it silently — the page just vanishes with a green build. So a 3-segment
296
+ * file whose FIRST segment matches a real entry type name in this collection is ALSO treated as
297
+ * malformed: `parseTypedFilename` requires type+slug+id (3 segments) after stripping the
298
+ * extension, so a 3-total-segment filename already has only 2 left over — it could only have
299
+ * been attempting `type.slug` with no id, or `type.id` with no slug, either way a lost segment,
300
+ * not a coincidence. This still leaves the two motivating "was never an entry" cases alone: a
301
+ * bare `README.md` (2 segments, and "README" is essentially never a configured entry type name)
302
+ * and an entry's colocated sibling artifact named `{contentId}.suffix.ext` per the
303
+ * `entryTransforms`/`readSibling` convention documented in the README (3 segments, e.g.
304
+ * `5NVkkrB1MJUv.profile.json` — a content ID is never itself an entry type name, so the same
305
+ * "first segment matches a known type" test correctly leaves it unflagged).
306
+ *
307
+ * Dot-prefixed (hidden files, editor swap/backup files) and underscore-prefixed (a common
308
+ * adopter convention for "not an entry") names are excluded outright regardless of segment
309
+ * count, matching `parseTypedFilename`'s own dotfile rejection.
310
+ */
311
+ const looksLikeMalformedEntry = (filename, entryTypes) => {
312
+ if (filename.startsWith('.') || filename.startsWith('_'))
313
+ return false;
314
+ const segments = filename.split('.');
315
+ if (segments.length >= 4)
316
+ return true;
317
+ return segments.length === 3 && entryTypes.some((e) => e.name === segments[0]);
318
+ };
190
319
  /**
191
320
  * List all entries in a collection directory.
192
321
  * Reads each entry's data (frontmatter or JSON).
322
+ *
323
+ * @param onSkip - Called for every file that has a recognized content extension, doesn't match
324
+ * the `{type}.{slug}.{id}.{ext}` grammar (see `parseTypedFilename`), AND structurally looks
325
+ * like an attempted entry (see `looksLikeMalformedEntry`). A file that was never entry-shaped
326
+ * (too few dot-separated segments to ever be a valid entry — a `README.md`, a colocated
327
+ * sibling artifact) is always silently dropped with a debug-gated `log.warn`, never passed to
328
+ * `onSkip`. Optional and purely a diagnostic hook — existing callers that omit it keep the
329
+ * exact prior behavior. `listEntries` below uses this to turn the skip into a hard build-time
330
+ * failure instead of a silent one.
193
331
  */
194
- export const listCollectionEntries = async (root, collection) => {
332
+ export const listCollectionEntries = async (root, collection, onSkip) => {
195
333
  if (collection.type !== 'collection' || !collection.entries) {
196
334
  return [];
197
335
  }
@@ -231,6 +369,9 @@ export const listCollectionEntries = async (root, collection) => {
231
369
  const parsed = parseTypedFilename(file.name, entryTypes);
232
370
  if (!parsed) {
233
371
  log.warn('listCollectionEntries', `Skipping file with unrecognized filename format: ${file.name} (expected {type}.{slug}.{id}.{ext} with a known entry type and valid 12-char Base58 ID)`);
372
+ if (looksLikeMalformedEntry(file.name, entryTypes)) {
373
+ onSkip?.({ filename: file.name, collectionPath: collection.logicalPath });
374
+ }
234
375
  return null;
235
376
  }
236
377
  const { type: entryTypeName, slug, id: contentId } = parsed;
@@ -1 +1 @@
1
- {"version":3,"file":"content-listing.js","sourceRoot":"","sources":["../src/content-listing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"content-listing.js","sourceRoot":"","sources":["../src/content-listing.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { type LogicalPath, type PhysicalPath, type Slug } from './paths';
1
+ import { type ContentId, type LogicalPath, type PhysicalPath, type Slug } from './paths';
2
2
  import type { CanopyServices } from './services';
3
3
  import type { BranchContext } from './types';
4
4
  import type { CanopyUser } from './user';
@@ -22,13 +22,50 @@ export interface ReadContentInput {
22
22
  /** Whether to resolve entry:ID links in body/markdown fields. Defaults to true. */
23
23
  resolveEntryLinks?: boolean;
24
24
  }
25
+ /**
26
+ * Structural metadata surfaced alongside a resolved content read. Shared by
27
+ * `ContentReader['read']` here and by `CanopyBuildContext`'s `read`/`readByUrlPath`
28
+ * (context.ts), which both wrap this same reader.
29
+ */
30
+ export interface ContentReadMeta {
31
+ /**
32
+ * Absolute filesystem path to the resolved entry file. It is **server-only**
33
+ * -- do not serialize it to the client or embed it in public output, as it
34
+ * reveals the deployment's filesystem layout (home dir / EFS mount / branch
35
+ * name).
36
+ */
37
+ physicalPath: PhysicalPath;
38
+ /**
39
+ * The resolved entry type name, read from the entry's own filename
40
+ * (`{type}.{slug}.{id}.{ext}`) -- immutable once the file is created, and
41
+ * NOT re-validated against the collection's current `entries` config on
42
+ * every read. It is therefore usually, but not guaranteed to be, a key in
43
+ * the collection's `entries` config: it can diverge if an entry type was
44
+ * renamed or removed from the schema after files using the old name were
45
+ * created, or if the file was hand-authored with an unrecognized type
46
+ * token.
47
+ *
48
+ * For a legacy entry file predating embedded-type filenames (`{slug}.{ext}`),
49
+ * there is no type recorded on disk at all -- `entryType` silently falls
50
+ * back to the collection's DEFAULT entry type, which may or may not be what
51
+ * the file actually is. `entryId` being `undefined` (below) is the signal
52
+ * that this happened: when `entryId` is `undefined`, `entryType` is
53
+ * inferred rather than read.
54
+ */
55
+ entryType: string;
56
+ /**
57
+ * The entry's 12-char Base58 content ID, when the resolved file carries one.
58
+ * Undefined only for legacy entry files predating embedded-ID filenames
59
+ * (`{slug}.{ext}` rather than `{type}.{slug}.{id}.{ext}`) -- see the
60
+ * `entryType` caveat above for what that implies about it.
61
+ */
62
+ entryId?: ContentId;
63
+ }
25
64
  export interface ContentReader {
26
65
  read: <T = unknown>(input: ReadContentInput, message?: string) => Promise<{
27
66
  data: T;
28
67
  path: string;
29
- meta: {
30
- physicalPath: PhysicalPath;
31
- };
68
+ meta: ContentReadMeta;
32
69
  }>;
33
70
  }
34
71
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"content-reader.d.ts","sourceRoot":"","sources":["../src/content-reader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,IAAI,EAAE,MAAM,SAAS,CAAA;AAG5F,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;AAKxC,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,IAAI,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,IAAI,EAAE,UAAU,CAAA;IAChB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,mFAAmF;IACnF,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,CAAC;QAAC,IAAI,EAAE;YAAE,YAAY,EAAE,YAAY,CAAA;SAAE,CAAA;KAAE,CAAC,CAAA;CAC9E;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,oBAAoB,KAAG,aAmNnE,CAAA"}
1
+ {"version":3,"file":"content-reader.d.ts","sourceRoot":"","sources":["../src/content-reader.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,IAAI,EACV,MAAM,SAAS,CAAA;AAGhB,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;AAKxC,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,IAAI,CAAA;IACX,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,IAAI,EAAE,UAAU,CAAA;IAChB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,YAAY,EAAE,YAAY,CAAA;IAC1B;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,CAAC,GAAG,OAAO,EAChB,KAAK,EAAE,gBAAgB,EACvB,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;QACX,IAAI,EAAE,CAAC,CAAA;QACP,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,eAAe,CAAA;KACtB,CAAC,CAAA;CACH;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,oBAAoB,KAAG,aAgOnE,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { loadBranchContext, loadOrCreateBranchContext } from './branch-workspace.js';
2
2
  import { ContentStore, ContentStoreError } from './content-store.js';
3
- import { resolveBranchPaths } from './paths/index.js';
3
+ import { resolveBranchPaths, } from './paths/index.js';
4
4
  import { trimSlashes } from './paths/normalize.js';
5
5
  import { isDeployedStatic, isBuildMode } from './build-mode.js';
6
6
  import { isNotFoundError } from './utils/error.js';
@@ -108,10 +108,23 @@ export const createContentReader = (options) => {
108
108
  // for server-side colocated-artifact reads (e.g. a sibling profile.json). Already
109
109
  // computed here for permission checks; just carried through.
110
110
  let physicalPath;
111
+ // Entry type + content ID are already resolved as part of path resolution
112
+ // (buildPaths() derives both from the schema item / filename) — surfaced on
113
+ // read() / readByUrlPath() so callers can route or key by them without a
114
+ // separate listEntries lookup or filename parse.
115
+ let entryType;
116
+ let entryId;
111
117
  try {
112
118
  const resolved = await store.resolveDocumentPath(entryPath, slug ?? '');
113
119
  relativePath = resolved.relativePath;
114
120
  physicalPath = resolved.absolutePath;
121
+ // resolveDocumentPath() always sets entryTypeName for a valid schema item
122
+ // (collection entries fall back to 'entry'; entry-type items delegate to
123
+ // their parent collection with their own name), so no further fallback
124
+ // is needed here — id is the one field that can be legitimately absent,
125
+ // for legacy entry files without an embedded ID (see ContentReadMeta).
126
+ entryType = resolved.entryTypeName;
127
+ entryId = resolved.id;
115
128
  }
116
129
  catch (err) {
117
130
  const message = err instanceof ContentStoreError ? err.message : 'Invalid content request';
@@ -145,7 +158,7 @@ export const createContentReader = (options) => {
145
158
  const doc = await store.read(entryPath, slug ?? '', {
146
159
  resolveReferences: input.resolveReferences ?? true,
147
160
  });
148
- return { doc, store, physicalPath };
161
+ return { doc, store, physicalPath, entryType, entryId };
149
162
  }
150
163
  catch (err) {
151
164
  if (isNotFoundError(err))
@@ -160,7 +173,7 @@ export const createContentReader = (options) => {
160
173
  const defaultMessage = `Content not found for ${entryPath}${slug ? `/${slug}` : ''} on branch ${branchName}`;
161
174
  throw new ContentStoreError(message ?? defaultMessage, 'NOT_FOUND');
162
175
  }
163
- const { doc, store, physicalPath } = result;
176
+ const { doc, store, physicalPath, entryType, entryId } = result;
164
177
  // For md/mdx format, merge the body into the data so callers get a complete object.
165
178
  // The field name comes from the schema's isBody flag (defaults to 'body').
166
179
  const docRecord = doc;
@@ -179,7 +192,7 @@ export const createContentReader = (options) => {
179
192
  slug,
180
193
  branch: branchName,
181
194
  });
182
- return { data, path, meta: { physicalPath } };
195
+ return { data, path, meta: { physicalPath, entryType, entryId } };
183
196
  };
184
197
  return { read };
185
198
  };