@deskwork/core 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/body-state.d.ts +27 -0
- package/dist/body-state.d.ts.map +1 -0
- package/dist/body-state.js +62 -0
- package/dist/body-state.js.map +1 -0
- package/dist/calendar-mutations.d.ts +124 -0
- package/dist/calendar-mutations.d.ts.map +1 -0
- package/dist/calendar-mutations.js +305 -0
- package/dist/calendar-mutations.js.map +1 -0
- package/dist/calendar.d.ts +54 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +430 -0
- package/dist/calendar.js.map +1 -0
- package/dist/cli.d.ts +38 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +72 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +91 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +216 -0
- package/dist/config.js.map +1 -0
- package/dist/content-index.d.ts +74 -0
- package/dist/content-index.d.ts.map +1 -0
- package/dist/content-index.js +205 -0
- package/dist/content-index.js.map +1 -0
- package/dist/content-tree-fs-walk.d.ts +54 -0
- package/dist/content-tree-fs-walk.d.ts.map +1 -0
- package/dist/content-tree-fs-walk.js +112 -0
- package/dist/content-tree-fs-walk.js.map +1 -0
- package/dist/content-tree-helpers.d.ts +52 -0
- package/dist/content-tree-helpers.d.ts.map +1 -0
- package/dist/content-tree-helpers.js +116 -0
- package/dist/content-tree-helpers.js.map +1 -0
- package/dist/content-tree-types.d.ts +175 -0
- package/dist/content-tree-types.d.ts.map +1 -0
- package/dist/content-tree-types.js +10 -0
- package/dist/content-tree-types.js.map +1 -0
- package/dist/content-tree.d.ts +93 -0
- package/dist/content-tree.d.ts.map +1 -0
- package/dist/content-tree.js +385 -0
- package/dist/content-tree.js.map +1 -0
- package/dist/doctor/index.d.ts +11 -0
- package/dist/doctor/index.d.ts.map +1 -0
- package/dist/doctor/index.js +10 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/doctor/project-rules.d.ts +59 -0
- package/dist/doctor/project-rules.d.ts.map +1 -0
- package/dist/doctor/project-rules.js +143 -0
- package/dist/doctor/project-rules.js.map +1 -0
- package/dist/doctor/rules/calendar-uuid-missing.d.ts +19 -0
- package/dist/doctor/rules/calendar-uuid-missing.d.ts.map +1 -0
- package/dist/doctor/rules/calendar-uuid-missing.js +176 -0
- package/dist/doctor/rules/calendar-uuid-missing.js.map +1 -0
- package/dist/doctor/rules/duplicate-id.d.ts +27 -0
- package/dist/doctor/rules/duplicate-id.d.ts.map +1 -0
- package/dist/doctor/rules/duplicate-id.js +157 -0
- package/dist/doctor/rules/duplicate-id.js.map +1 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.d.ts +40 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.d.ts.map +1 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.js +232 -0
- package/dist/doctor/rules/legacy-top-level-id-migration.js.map +1 -0
- package/dist/doctor/rules/missing-frontmatter-id.d.ts +45 -0
- package/dist/doctor/rules/missing-frontmatter-id.d.ts.map +1 -0
- package/dist/doctor/rules/missing-frontmatter-id.js +283 -0
- package/dist/doctor/rules/missing-frontmatter-id.js.map +1 -0
- package/dist/doctor/rules/orphan-frontmatter-id.d.ts +18 -0
- package/dist/doctor/rules/orphan-frontmatter-id.d.ts.map +1 -0
- package/dist/doctor/rules/orphan-frontmatter-id.js +154 -0
- package/dist/doctor/rules/orphan-frontmatter-id.js.map +1 -0
- package/dist/doctor/rules/schema-rejected.d.ts +20 -0
- package/dist/doctor/rules/schema-rejected.d.ts.map +1 -0
- package/dist/doctor/rules/schema-rejected.js +44 -0
- package/dist/doctor/rules/schema-rejected.js.map +1 -0
- package/dist/doctor/rules/slug-collision.d.ts +18 -0
- package/dist/doctor/rules/slug-collision.d.ts.map +1 -0
- package/dist/doctor/rules/slug-collision.js +65 -0
- package/dist/doctor/rules/slug-collision.js.map +1 -0
- package/dist/doctor/rules/workflow-stale.d.ts +20 -0
- package/dist/doctor/rules/workflow-stale.d.ts.map +1 -0
- package/dist/doctor/rules/workflow-stale.js +136 -0
- package/dist/doctor/rules/workflow-stale.js.map +1 -0
- package/dist/doctor/runner.d.ts +75 -0
- package/dist/doctor/runner.d.ts.map +1 -0
- package/dist/doctor/runner.js +289 -0
- package/dist/doctor/runner.js.map +1 -0
- package/dist/doctor/schema-patch.d.ts +21 -0
- package/dist/doctor/schema-patch.d.ts.map +1 -0
- package/dist/doctor/schema-patch.js +92 -0
- package/dist/doctor/schema-patch.js.map +1 -0
- package/dist/doctor/types.d.ts +185 -0
- package/dist/doctor/types.d.ts.map +1 -0
- package/dist/doctor/types.js +13 -0
- package/dist/doctor/types.js.map +1 -0
- package/dist/frontmatter.d.ts +103 -0
- package/dist/frontmatter.d.ts.map +1 -0
- package/dist/frontmatter.js +306 -0
- package/dist/frontmatter.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest-derive.d.ts +79 -0
- package/dist/ingest-derive.d.ts.map +1 -0
- package/dist/ingest-derive.js +299 -0
- package/dist/ingest-derive.js.map +1 -0
- package/dist/ingest-paths.d.ts +37 -0
- package/dist/ingest-paths.d.ts.map +1 -0
- package/dist/ingest-paths.js +176 -0
- package/dist/ingest-paths.js.map +1 -0
- package/dist/ingest.d.ts +162 -0
- package/dist/ingest.d.ts.map +1 -0
- package/dist/ingest.js +269 -0
- package/dist/ingest.js.map +1 -0
- package/dist/journal.d.ts +49 -0
- package/dist/journal.d.ts.map +1 -0
- package/dist/journal.js +113 -0
- package/dist/journal.js.map +1 -0
- package/dist/outline-split.d.ts +38 -0
- package/dist/outline-split.d.ts.map +1 -0
- package/dist/outline-split.js +84 -0
- package/dist/outline-split.js.map +1 -0
- package/dist/overrides.d.ts +83 -0
- package/dist/overrides.d.ts.map +1 -0
- package/dist/overrides.js +88 -0
- package/dist/overrides.js.map +1 -0
- package/dist/paths.d.ts +183 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +266 -0
- package/dist/paths.js.map +1 -0
- package/dist/remark-image-figure.mjs +77 -0
- package/dist/remark-strip-first-h1.mjs +26 -0
- package/dist/remark-strip-outline.mjs +44 -0
- package/dist/rename-slug.d.ts +49 -0
- package/dist/rename-slug.d.ts.map +1 -0
- package/dist/rename-slug.js +161 -0
- package/dist/rename-slug.js.map +1 -0
- package/dist/review/handlers.d.ts +55 -0
- package/dist/review/handlers.d.ts.map +1 -0
- package/dist/review/handlers.js +307 -0
- package/dist/review/handlers.js.map +1 -0
- package/dist/review/index.d.ts +14 -0
- package/dist/review/index.d.ts.map +1 -0
- package/dist/review/index.js +13 -0
- package/dist/review/index.js.map +1 -0
- package/dist/review/journal-mappers.d.ts +35 -0
- package/dist/review/journal-mappers.d.ts.map +1 -0
- package/dist/review/journal-mappers.js +48 -0
- package/dist/review/journal-mappers.js.map +1 -0
- package/dist/review/pipeline.d.ts +79 -0
- package/dist/review/pipeline.d.ts.map +1 -0
- package/dist/review/pipeline.js +234 -0
- package/dist/review/pipeline.js.map +1 -0
- package/dist/review/render.d.ts +27 -0
- package/dist/review/render.d.ts.map +1 -0
- package/dist/review/render.js +42 -0
- package/dist/review/render.js.map +1 -0
- package/dist/review/report.d.ts +50 -0
- package/dist/review/report.d.ts.map +1 -0
- package/dist/review/report.js +164 -0
- package/dist/review/report.js.map +1 -0
- package/dist/review/result.d.ts +12 -0
- package/dist/review/result.d.ts.map +1 -0
- package/dist/review/result.js +12 -0
- package/dist/review/result.js.map +1 -0
- package/dist/review/start-handlers.d.ts +62 -0
- package/dist/review/start-handlers.d.ts.map +1 -0
- package/dist/review/start-handlers.js +223 -0
- package/dist/review/start-handlers.js.map +1 -0
- package/dist/review/types.d.ts +169 -0
- package/dist/review/types.d.ts.map +1 -0
- package/dist/review/types.js +26 -0
- package/dist/review/types.js.map +1 -0
- package/dist/review/workflow-paths.d.ts +68 -0
- package/dist/review/workflow-paths.d.ts.map +1 -0
- package/dist/review/workflow-paths.js +112 -0
- package/dist/review/workflow-paths.js.map +1 -0
- package/dist/scaffold.d.ts +67 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +122 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/scrapbook.d.ts +229 -0
- package/dist/scrapbook.d.ts.map +1 -0
- package/dist/scrapbook.js +500 -0
- package/dist/scrapbook.js.map +1 -0
- package/dist/types.d.ts +197 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +120 -0
- package/dist/types.js.map +1 -0
- package/package.json +160 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ingest-derive.ts — slug / state / date / title derivation for ingest.
|
|
3
|
+
*
|
|
4
|
+
* Each `derive*` function takes the file's absolute path, its discovery
|
|
5
|
+
* root (for path-based slug derivation), the parsed frontmatter, the
|
|
6
|
+
* effective field-name table, and the operator's options. It returns a
|
|
7
|
+
* `<thing>Derivation` record that records both the derived value AND
|
|
8
|
+
* where it came from — the dry-run plan surfaces these sources so the
|
|
9
|
+
* operator can sanity-check before committing.
|
|
10
|
+
*/
|
|
11
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
12
|
+
import { join, relative, sep } from 'node:path';
|
|
13
|
+
import { isStage } from "./types.js";
|
|
14
|
+
import { MARKDOWN_EXTENSIONS } from "./ingest-paths.js";
|
|
15
|
+
const JEKYLL_RE = /^(\d{4})-(\d{2})-(\d{2})-(.+)$/;
|
|
16
|
+
const ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
17
|
+
/**
|
|
18
|
+
* Canonical state-string normalization. Maps frontmatter values that
|
|
19
|
+
* editorial projects commonly use onto our six lanes. Anything outside
|
|
20
|
+
* this table comes back ambiguous — the operator must pass `--state`.
|
|
21
|
+
*/
|
|
22
|
+
export const STATE_ALIASES = {
|
|
23
|
+
ideas: 'Ideas',
|
|
24
|
+
idea: 'Ideas',
|
|
25
|
+
ideas_lane: 'Ideas',
|
|
26
|
+
planned: 'Planned',
|
|
27
|
+
outlining: 'Outlining',
|
|
28
|
+
outline: 'Outlining',
|
|
29
|
+
drafting: 'Drafting',
|
|
30
|
+
draft: 'Drafting',
|
|
31
|
+
review: 'Review',
|
|
32
|
+
reviewing: 'Review',
|
|
33
|
+
'in-review': 'Review',
|
|
34
|
+
in_review: 'Review',
|
|
35
|
+
published: 'Published',
|
|
36
|
+
publish: 'Published',
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Derive a slug for a discovered file. Order:
|
|
40
|
+
*
|
|
41
|
+
* 1. `explicitSlug` (operator's `--slug`) — wins, marked 'explicit'.
|
|
42
|
+
* 2. When `slugFrom === 'frontmatter'`, the named frontmatter field
|
|
43
|
+
* (default `slug:`) when set; otherwise falls through to path.
|
|
44
|
+
* 3. Path-based derivation — see `slugFromPath`.
|
|
45
|
+
*/
|
|
46
|
+
export function deriveSlug(input) {
|
|
47
|
+
if (input.explicitSlug !== undefined) {
|
|
48
|
+
return { value: input.explicitSlug, source: 'explicit' };
|
|
49
|
+
}
|
|
50
|
+
if (input.slugFrom === 'frontmatter') {
|
|
51
|
+
const fmSlug = readStringField(input.frontmatter, input.fieldName);
|
|
52
|
+
if (fmSlug !== undefined) {
|
|
53
|
+
return { value: fmSlug, source: 'frontmatter' };
|
|
54
|
+
}
|
|
55
|
+
// Fall through to path when frontmatter slug is missing.
|
|
56
|
+
}
|
|
57
|
+
return slugFromPath(input.filePath, input.root);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Path-based slug derivation. Computes the slug from the file's path
|
|
61
|
+
* relative to its discovery root, with hierarchical-bundle detection.
|
|
62
|
+
*
|
|
63
|
+
* Filename rules (first match wins):
|
|
64
|
+
* 1. `<dir>/index.md` or `<dir>/README.md` (case-insensitive) →
|
|
65
|
+
* drop the suffix; the dir name becomes the slug leaf.
|
|
66
|
+
* 2. Filename matches `YYYY-MM-DD-<slug>.<ext>` → strip date prefix
|
|
67
|
+
* and extension. Jekyll posts.
|
|
68
|
+
* 3. Otherwise → filename minus extension.
|
|
69
|
+
*
|
|
70
|
+
* Hierarchy: a directory between root and the file prefixes the slug
|
|
71
|
+
* if and only if it has its own `index.md` or `README.md`. Such a
|
|
72
|
+
* directory is itself a content node with a slug, and its children
|
|
73
|
+
* nest under it. Plain directories (no own leaf bundle) are
|
|
74
|
+
* "collection containers" — their names do NOT prefix child slugs.
|
|
75
|
+
*/
|
|
76
|
+
function slugFromPath(filePath, root) {
|
|
77
|
+
const rel = relative(root, filePath);
|
|
78
|
+
const segments = rel.split(sep).filter((s) => s.length > 0);
|
|
79
|
+
if (segments.length === 0) {
|
|
80
|
+
return { value: '', source: 'path', reason: 'file path equals root' };
|
|
81
|
+
}
|
|
82
|
+
const filename = segments[segments.length - 1];
|
|
83
|
+
const dot = filename.lastIndexOf('.');
|
|
84
|
+
const base = dot > 0 ? filename.slice(0, dot) : filename;
|
|
85
|
+
const baseLower = base.toLowerCase();
|
|
86
|
+
let leafSegments;
|
|
87
|
+
let dirSegments;
|
|
88
|
+
if (baseLower === 'index' || baseLower === 'readme') {
|
|
89
|
+
if (segments.length < 2) {
|
|
90
|
+
// Leaf-bundle file at the discovery root — root's basename
|
|
91
|
+
// becomes the slug (the case for a single-file argument like
|
|
92
|
+
// `<...>/strivers/index.md` where root = `<...>/strivers`).
|
|
93
|
+
const rootSegments = root.split(sep).filter((s) => s.length > 0);
|
|
94
|
+
const rootLeaf = rootSegments[rootSegments.length - 1];
|
|
95
|
+
if (!rootLeaf) {
|
|
96
|
+
return {
|
|
97
|
+
value: '',
|
|
98
|
+
source: 'path',
|
|
99
|
+
reason: `${filename} at the filesystem root has no directory name to derive a slug from`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
leafSegments = [rootLeaf];
|
|
103
|
+
dirSegments = [];
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
leafSegments = [segments[segments.length - 2]];
|
|
107
|
+
dirSegments = segments.slice(0, -2);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
const jekyll = base.match(JEKYLL_RE);
|
|
112
|
+
leafSegments = [jekyll ? jekyll[4] : base];
|
|
113
|
+
dirSegments = segments.slice(0, -1);
|
|
114
|
+
}
|
|
115
|
+
// Walk directory ancestors from root → leaf. Each ancestor
|
|
116
|
+
// contributes to the slug only if it has its own index.md or
|
|
117
|
+
// README.md (i.e. it's itself a content node, not just a folder).
|
|
118
|
+
const prefix = [];
|
|
119
|
+
// Special case: when the discovery root itself is a content node
|
|
120
|
+
// (has its own index.md/README.md), its basename prefixes child
|
|
121
|
+
// slugs. Skip when leafSegments already equals the root's basename
|
|
122
|
+
// (the case when the discovered file IS the root's own index.md —
|
|
123
|
+
// handled above by setting leafSegments to the root's name).
|
|
124
|
+
if (directoryIsHierarchicalNode(root)) {
|
|
125
|
+
const rootSegments = root.split(sep).filter((s) => s.length > 0);
|
|
126
|
+
const rootLeaf = rootSegments[rootSegments.length - 1];
|
|
127
|
+
if (rootLeaf && leafSegments[0] !== rootLeaf) {
|
|
128
|
+
prefix.push(rootLeaf);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
let cursor = root;
|
|
132
|
+
for (const dir of dirSegments) {
|
|
133
|
+
cursor = join(cursor, dir);
|
|
134
|
+
if (directoryIsHierarchicalNode(cursor)) {
|
|
135
|
+
prefix.push(dir);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Once an ancestor isn't itself a content node, the chain
|
|
139
|
+
// ends — the operator put a non-tracked folder between
|
|
140
|
+
// content nodes, so that folder's name shouldn't bleed in.
|
|
141
|
+
prefix.length = 0;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { value: [...prefix, ...leafSegments].join('/'), source: 'path' };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* True if `dir` itself has a leaf bundle (index.md / README.md /
|
|
148
|
+
* index.mdx / README.mdx / etc.) — i.e. it's a content node whose
|
|
149
|
+
* name should prefix its children's slugs.
|
|
150
|
+
*/
|
|
151
|
+
function directoryIsHierarchicalNode(dir) {
|
|
152
|
+
let entries;
|
|
153
|
+
try {
|
|
154
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
for (const entry of entries) {
|
|
160
|
+
if (!entry.isFile())
|
|
161
|
+
continue;
|
|
162
|
+
const lower = entry.name.toLowerCase();
|
|
163
|
+
for (const ext of MARKDOWN_EXTENSIONS) {
|
|
164
|
+
if (lower === `index${ext}` || lower === `readme${ext}`) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
export function deriveState(input) {
|
|
172
|
+
if (input.explicitState !== undefined) {
|
|
173
|
+
return { value: input.explicitState, source: 'explicit' };
|
|
174
|
+
}
|
|
175
|
+
if (input.stateFrom === 'frontmatter') {
|
|
176
|
+
const raw = readStringField(input.frontmatter, input.stateField);
|
|
177
|
+
if (raw === undefined) {
|
|
178
|
+
// No state field — default to Ideas as the safest lane. The
|
|
179
|
+
// value did NOT come from the file; mark the source as
|
|
180
|
+
// `'default'` so the audit trail doesn't lie (#23).
|
|
181
|
+
return { value: 'Ideas', source: 'default' };
|
|
182
|
+
}
|
|
183
|
+
const normalized = normalizeStateString(raw);
|
|
184
|
+
if (normalized === null) {
|
|
185
|
+
return { value: null, source: 'frontmatter', rawValue: raw };
|
|
186
|
+
}
|
|
187
|
+
return { value: normalized, source: 'frontmatter' };
|
|
188
|
+
}
|
|
189
|
+
// stateFrom === 'datePublished'
|
|
190
|
+
const dateRaw = readDateField(input.frontmatter, input.dateField);
|
|
191
|
+
if (dateRaw === undefined) {
|
|
192
|
+
// No datePublished — same Ideas fallback as above; same provenance
|
|
193
|
+
// honesty applies (#23).
|
|
194
|
+
return { value: 'Ideas', source: 'default' };
|
|
195
|
+
}
|
|
196
|
+
const today = (input.now ?? new Date()).toISOString().slice(0, 10);
|
|
197
|
+
if (dateRaw <= today) {
|
|
198
|
+
return { value: 'Published', source: 'frontmatter' };
|
|
199
|
+
}
|
|
200
|
+
return { value: 'Drafting', source: 'frontmatter' };
|
|
201
|
+
}
|
|
202
|
+
function normalizeStateString(raw) {
|
|
203
|
+
const key = raw.trim().toLowerCase();
|
|
204
|
+
if (key.length === 0)
|
|
205
|
+
return null;
|
|
206
|
+
const direct = STATE_ALIASES[key];
|
|
207
|
+
if (direct)
|
|
208
|
+
return direct;
|
|
209
|
+
// Stage names verbatim (Title-cased) — accept them too.
|
|
210
|
+
const titled = key.charAt(0).toUpperCase() + key.slice(1);
|
|
211
|
+
if (isStage(titled))
|
|
212
|
+
return titled;
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
export function deriveDate(input) {
|
|
216
|
+
if (input.explicitDate !== undefined) {
|
|
217
|
+
return { value: input.explicitDate, source: 'explicit' };
|
|
218
|
+
}
|
|
219
|
+
const fmDate = readDateField(input.frontmatter, input.dateField);
|
|
220
|
+
if (fmDate !== undefined) {
|
|
221
|
+
return { value: fmDate, source: 'frontmatter' };
|
|
222
|
+
}
|
|
223
|
+
// Try the secondary `date` field too, when the primary one was
|
|
224
|
+
// overridden — `--date-field datePublished` should still find a
|
|
225
|
+
// generic `date:` as a backstop.
|
|
226
|
+
if (input.dateField !== 'date') {
|
|
227
|
+
const generic = readDateField(input.frontmatter, 'date');
|
|
228
|
+
if (generic !== undefined) {
|
|
229
|
+
return { value: generic, source: 'frontmatter' };
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// mtime fallback — accurate enough for "approximately when the
|
|
233
|
+
// operator wrote this", but the operator can override with --date.
|
|
234
|
+
try {
|
|
235
|
+
const stat = statSync(input.filePath);
|
|
236
|
+
return { value: stat.mtime.toISOString().slice(0, 10), source: 'mtime' };
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// Should never happen — we just read the file. Fall through.
|
|
240
|
+
}
|
|
241
|
+
const today = (input.now ?? new Date()).toISOString().slice(0, 10);
|
|
242
|
+
return { value: today, source: 'today' };
|
|
243
|
+
}
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// Title / description
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
export function deriveTitle(frontmatter, fieldName, slug) {
|
|
248
|
+
const raw = readStringField(frontmatter, fieldName);
|
|
249
|
+
if (raw !== undefined && raw.length > 0)
|
|
250
|
+
return raw;
|
|
251
|
+
// Humanize the slug as a last resort. Strip leading hierarchy
|
|
252
|
+
// segments — title for `the-outbound/characters/strivers` should
|
|
253
|
+
// be "Strivers", not "The Outbound Characters Strivers".
|
|
254
|
+
const leaf = slug.split('/').pop() ?? slug;
|
|
255
|
+
return leaf
|
|
256
|
+
.split('-')
|
|
257
|
+
.map((w) => (w.length > 0 ? w[0].toUpperCase() + w.slice(1) : w))
|
|
258
|
+
.join(' ');
|
|
259
|
+
}
|
|
260
|
+
export function deriveDescription(frontmatter, fieldName) {
|
|
261
|
+
return readStringField(frontmatter, fieldName) ?? '';
|
|
262
|
+
}
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// Field readers
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
function readStringField(frontmatter, field) {
|
|
267
|
+
const value = frontmatter[field];
|
|
268
|
+
if (typeof value !== 'string')
|
|
269
|
+
return undefined;
|
|
270
|
+
const trimmed = value.trim();
|
|
271
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Read a date-like field. YAML may parse `2024-01-15` as a Date or as
|
|
275
|
+
* a string depending on quoting. Both shapes resolve to YYYY-MM-DD.
|
|
276
|
+
* Anything else returns undefined.
|
|
277
|
+
*/
|
|
278
|
+
function readDateField(frontmatter, field) {
|
|
279
|
+
const value = frontmatter[field];
|
|
280
|
+
if (value === undefined || value === null)
|
|
281
|
+
return undefined;
|
|
282
|
+
if (value instanceof Date) {
|
|
283
|
+
return value.toISOString().slice(0, 10);
|
|
284
|
+
}
|
|
285
|
+
if (typeof value === 'string') {
|
|
286
|
+
const trimmed = value.trim();
|
|
287
|
+
if (ISO_DATE_RE.test(trimmed))
|
|
288
|
+
return trimmed;
|
|
289
|
+
// Try Date.parse for non-ISO formats — the operator's frontmatter
|
|
290
|
+
// might be `date: 2024/01/15` or `date: January 15, 2024`. Parse
|
|
291
|
+
// leniently and serialize as ISO.
|
|
292
|
+
const parsed = Date.parse(trimmed);
|
|
293
|
+
if (!Number.isNaN(parsed)) {
|
|
294
|
+
return new Date(parsed).toISOString().slice(0, 10);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=ingest-derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-derive.js","sourceRoot":"","sources":["../src/ingest-derive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ingest-paths.ts — markdown file collection for ingest discovery.
|
|
3
|
+
*
|
|
4
|
+
* Walks a list of operator-supplied paths (file / directory / glob)
|
|
5
|
+
* and produces a deduplicated, deterministically-ordered list of
|
|
6
|
+
* `CollectedFile` records — each one a markdown file paired with the
|
|
7
|
+
* "discovery root" it was found under. The root is what slug derivation
|
|
8
|
+
* uses to compute the file's path-relative slug.
|
|
9
|
+
*
|
|
10
|
+
* Glob expansion is hand-rolled to avoid pulling a dep onto the
|
|
11
|
+
* discovery hot path. The supported pattern surface — `*`, `**`, `?`,
|
|
12
|
+
* `[...]` — covers every operator pattern surfaced in the issue (e.g.
|
|
13
|
+
* `src/content/essays/**/*.md`).
|
|
14
|
+
*/
|
|
15
|
+
declare const MARKDOWN_EXTENSIONS: readonly [".md", ".mdx", ".markdown"];
|
|
16
|
+
/**
|
|
17
|
+
* A discovered markdown file paired with its discovery root — the
|
|
18
|
+
* directory the operator's path argument resolved to (or the deepest
|
|
19
|
+
* static prefix for a glob). Slug derivation computes the slug as
|
|
20
|
+
* the file's path relative to this root, so siblings of a flat
|
|
21
|
+
* collection ("essays/foo/index.md", "essays/bar/index.md") get
|
|
22
|
+
* unprefixed slugs while deeper nesting produces hierarchical slugs.
|
|
23
|
+
*/
|
|
24
|
+
export interface CollectedFile {
|
|
25
|
+
filePath: string;
|
|
26
|
+
root: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Walk every supplied path and return a deduplicated, sorted list of
|
|
30
|
+
* `CollectedFile` records. First-seen wins for root attribution: if
|
|
31
|
+
* the same file is reachable from two paths the operator passed, the
|
|
32
|
+
* first path's discovery root is canonical.
|
|
33
|
+
*/
|
|
34
|
+
export declare function collectMarkdownFiles(paths: string[]): CollectedFile[];
|
|
35
|
+
export declare function hasMarkdownExtension(filename: string): boolean;
|
|
36
|
+
export { MARKDOWN_EXTENSIONS };
|
|
37
|
+
//# sourceMappingURL=ingest-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-paths.d.ts","sourceRoot":"","sources":["../src/ingest-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AASH,QAAA,MAAM,mBAAmB,uCAAwC,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAYrE;AAqJD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ingest-paths.ts — markdown file collection for ingest discovery.
|
|
3
|
+
*
|
|
4
|
+
* Walks a list of operator-supplied paths (file / directory / glob)
|
|
5
|
+
* and produces a deduplicated, deterministically-ordered list of
|
|
6
|
+
* `CollectedFile` records — each one a markdown file paired with the
|
|
7
|
+
* "discovery root" it was found under. The root is what slug derivation
|
|
8
|
+
* uses to compute the file's path-relative slug.
|
|
9
|
+
*
|
|
10
|
+
* Glob expansion is hand-rolled to avoid pulling a dep onto the
|
|
11
|
+
* discovery hot path. The supported pattern surface — `*`, `**`, `?`,
|
|
12
|
+
* `[...]` — covers every operator pattern surfaced in the issue (e.g.
|
|
13
|
+
* `src/content/essays/**/*.md`).
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync, readdirSync, statSync, } from 'node:fs';
|
|
16
|
+
import { isAbsolute, join, resolve, sep } from 'node:path';
|
|
17
|
+
const MARKDOWN_EXTENSIONS = ['.md', '.mdx', '.markdown'];
|
|
18
|
+
/**
|
|
19
|
+
* Walk every supplied path and return a deduplicated, sorted list of
|
|
20
|
+
* `CollectedFile` records. First-seen wins for root attribution: if
|
|
21
|
+
* the same file is reachable from two paths the operator passed, the
|
|
22
|
+
* first path's discovery root is canonical.
|
|
23
|
+
*/
|
|
24
|
+
export function collectMarkdownFiles(paths) {
|
|
25
|
+
const seen = new Map();
|
|
26
|
+
for (const p of paths) {
|
|
27
|
+
for (const file of expandPath(p)) {
|
|
28
|
+
if (!seen.has(file.filePath)) {
|
|
29
|
+
seen.set(file.filePath, file);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return [...seen.values()].sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
34
|
+
}
|
|
35
|
+
function expandPath(input) {
|
|
36
|
+
const absolute = isAbsolute(input) ? input : resolve(process.cwd(), input);
|
|
37
|
+
if (containsGlob(input)) {
|
|
38
|
+
return expandGlob(absolute);
|
|
39
|
+
}
|
|
40
|
+
if (!existsSync(absolute)) {
|
|
41
|
+
throw new Error(`Path does not exist: ${input}`);
|
|
42
|
+
}
|
|
43
|
+
const stat = statSync(absolute);
|
|
44
|
+
if (stat.isFile()) {
|
|
45
|
+
if (!hasMarkdownExtension(absolute)) {
|
|
46
|
+
throw new Error(`Path is not a markdown file: ${input} (expected one of ${MARKDOWN_EXTENSIONS.join(', ')})`);
|
|
47
|
+
}
|
|
48
|
+
// For a single-file argument the discovery root is the file's
|
|
49
|
+
// parent — no hierarchical prefix.
|
|
50
|
+
return [{ filePath: absolute, root: dirnameOf(absolute) }];
|
|
51
|
+
}
|
|
52
|
+
if (stat.isDirectory()) {
|
|
53
|
+
return walkDirectory(absolute, absolute);
|
|
54
|
+
}
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
function dirnameOf(filePath) {
|
|
58
|
+
const idx = filePath.lastIndexOf(sep);
|
|
59
|
+
if (idx <= 0)
|
|
60
|
+
return sep;
|
|
61
|
+
return filePath.slice(0, idx);
|
|
62
|
+
}
|
|
63
|
+
function walkDirectory(dir, root) {
|
|
64
|
+
const out = [];
|
|
65
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const child = join(dir, entry.name);
|
|
68
|
+
if (entry.isDirectory()) {
|
|
69
|
+
out.push(...walkDirectory(child, root));
|
|
70
|
+
}
|
|
71
|
+
else if (entry.isFile() && hasMarkdownExtension(entry.name)) {
|
|
72
|
+
out.push({ filePath: child, root });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function containsGlob(input) {
|
|
78
|
+
return /[*?[]/.test(input);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Minimal glob expansion supporting `*`, `**`, `?`, and `[...]`.
|
|
82
|
+
*
|
|
83
|
+
* The deepest static prefix becomes the discovery root so slugs are
|
|
84
|
+
* computed relative to it (e.g. `src/posts/**/*.md` uses `src/posts`
|
|
85
|
+
* as the slug-derivation root).
|
|
86
|
+
*/
|
|
87
|
+
function expandGlob(absolutePattern) {
|
|
88
|
+
const segments = absolutePattern.split(sep);
|
|
89
|
+
let rootEnd = 0;
|
|
90
|
+
for (let i = 0; i < segments.length; i++) {
|
|
91
|
+
if (containsGlob(segments[i]))
|
|
92
|
+
break;
|
|
93
|
+
rootEnd = i;
|
|
94
|
+
}
|
|
95
|
+
const root = segments.slice(0, rootEnd + 1).join(sep) || sep;
|
|
96
|
+
const remainder = segments.slice(rootEnd + 1);
|
|
97
|
+
if (!existsSync(root)) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
return matchPattern(root, remainder, root);
|
|
101
|
+
}
|
|
102
|
+
function matchPattern(currentDir, remaining, root) {
|
|
103
|
+
if (remaining.length === 0) {
|
|
104
|
+
if (statSync(currentDir).isFile() && hasMarkdownExtension(currentDir)) {
|
|
105
|
+
return [{ filePath: currentDir, root }];
|
|
106
|
+
}
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
const [head, ...rest] = remaining;
|
|
110
|
+
const out = [];
|
|
111
|
+
let entries;
|
|
112
|
+
try {
|
|
113
|
+
entries = readdirSync(currentDir, { withFileTypes: true });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
if (head === '**') {
|
|
119
|
+
// Match zero or more directories, then continue with `rest`.
|
|
120
|
+
out.push(...matchPattern(currentDir, rest, root));
|
|
121
|
+
for (const entry of entries) {
|
|
122
|
+
if (entry.isDirectory()) {
|
|
123
|
+
out.push(...matchPattern(join(currentDir, entry.name), remaining, root));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
const matcher = globSegmentMatcher(head);
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
if (!matcher(entry.name))
|
|
131
|
+
continue;
|
|
132
|
+
const child = join(currentDir, entry.name);
|
|
133
|
+
if (rest.length === 0) {
|
|
134
|
+
if (entry.isFile() && hasMarkdownExtension(entry.name)) {
|
|
135
|
+
out.push({ filePath: child, root });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else if (entry.isDirectory()) {
|
|
139
|
+
out.push(...matchPattern(child, rest, root));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
function globSegmentMatcher(pattern) {
|
|
145
|
+
let re = '^';
|
|
146
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
147
|
+
const ch = pattern[i];
|
|
148
|
+
if (ch === '*')
|
|
149
|
+
re += '[^/]*';
|
|
150
|
+
else if (ch === '?')
|
|
151
|
+
re += '[^/]';
|
|
152
|
+
else if (ch === '[') {
|
|
153
|
+
const close = pattern.indexOf(']', i);
|
|
154
|
+
if (close === -1) {
|
|
155
|
+
re += '\\[';
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
re += pattern.slice(i, close + 1);
|
|
159
|
+
i = close;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else if (/[\\^$+().{}|]/.test(ch))
|
|
163
|
+
re += `\\${ch}`;
|
|
164
|
+
else
|
|
165
|
+
re += ch;
|
|
166
|
+
}
|
|
167
|
+
re += '$';
|
|
168
|
+
const compiled = new RegExp(re);
|
|
169
|
+
return (name) => compiled.test(name);
|
|
170
|
+
}
|
|
171
|
+
export function hasMarkdownExtension(filename) {
|
|
172
|
+
const lower = filename.toLowerCase();
|
|
173
|
+
return MARKDOWN_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
174
|
+
}
|
|
175
|
+
export { MARKDOWN_EXTENSIONS };
|
|
176
|
+
//# sourceMappingURL=ingest-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-paths.js","sourceRoot":"","sources":["../src/ingest-paths.ts"],"names":[],"mappings":"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"}
|