@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,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow-creation handlers (longform, shortform). Pulled out of
|
|
3
|
+
* `handlers.ts` to keep that file under the 500-line guideline.
|
|
4
|
+
*
|
|
5
|
+
* Both handlers follow the same shape:
|
|
6
|
+
* 1. Validate input (site/slug/platform — same SLUG_RE the rest of
|
|
7
|
+
* deskwork uses).
|
|
8
|
+
* 2. Resolve the calendar entry (idem-key for the workflow tuple).
|
|
9
|
+
* 3. Resolve the on-disk markdown file path. Longform requires the file
|
|
10
|
+
* to already exist (scaffolded by /deskwork:outline). Shortform
|
|
11
|
+
* scaffolds the file in place when missing — frontmatter carries
|
|
12
|
+
* the deskwork-namespaced binding (`deskwork: { id, platform,
|
|
13
|
+
* channel? }`), body starts as `initialMarkdown ?? ''`.
|
|
14
|
+
* 4. Call `createWorkflow` (idempotent on the tuple).
|
|
15
|
+
*
|
|
16
|
+
* Phase 21a: shortform IS the same shape as longform — file on disk is
|
|
17
|
+
* SSOT, journal stores snapshots, the studio's review surface renders
|
|
18
|
+
* both kinds identically.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
21
|
+
import { dirname } from 'node:path';
|
|
22
|
+
import { isPlatform } from "../types.js";
|
|
23
|
+
import { writeFrontmatter } from "../frontmatter.js";
|
|
24
|
+
import { createWorkflow, readWorkflows } from "./pipeline.js";
|
|
25
|
+
import { lookupEntry, resolveLongformFilePath, resolveShortformWorkflowFilePath, } from "./workflow-paths.js";
|
|
26
|
+
import { err, ok } from "./result.js";
|
|
27
|
+
const SLUG_RE = /^[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)*$/;
|
|
28
|
+
/**
|
|
29
|
+
* Enqueue a longform draft review from the studio dashboard. Reads the
|
|
30
|
+
* blog post markdown from disk (honoring blogFilenameTemplate) and calls
|
|
31
|
+
* createWorkflow. Idempotent on (entryId | (site, slug), 'longform').
|
|
32
|
+
*/
|
|
33
|
+
export function handleStartLongform(projectRoot, config, body) {
|
|
34
|
+
if (!body || typeof body !== 'object')
|
|
35
|
+
return err(400, 'expected JSON object body');
|
|
36
|
+
const b = body;
|
|
37
|
+
if (!b.site)
|
|
38
|
+
return err(400, 'site is required');
|
|
39
|
+
if (!(b.site in config.sites)) {
|
|
40
|
+
const known = Object.keys(config.sites).join(', ');
|
|
41
|
+
return err(400, `unknown site: ${b.site}. Configured: ${known}`);
|
|
42
|
+
}
|
|
43
|
+
if (!b.slug || typeof b.slug !== 'string')
|
|
44
|
+
return err(400, 'slug is required');
|
|
45
|
+
if (!SLUG_RE.test(b.slug)) {
|
|
46
|
+
return err(400, `invalid slug: ${b.slug}. Must match ${SLUG_RE}`);
|
|
47
|
+
}
|
|
48
|
+
const callerEntryId = b.entryId !== undefined && b.entryId !== '' ? b.entryId : undefined;
|
|
49
|
+
const entry = lookupEntry(projectRoot, config, b.site, {
|
|
50
|
+
...(callerEntryId !== undefined ? { entryId: callerEntryId } : {}),
|
|
51
|
+
slug: b.slug,
|
|
52
|
+
});
|
|
53
|
+
const entryId = callerEntryId ?? entry?.id;
|
|
54
|
+
const path = resolveLongformFilePath(projectRoot, config, b.site, b.slug, {
|
|
55
|
+
...(entryId !== undefined ? { entryId } : {}),
|
|
56
|
+
...(entry !== undefined ? { entry } : {}),
|
|
57
|
+
});
|
|
58
|
+
if (!existsSync(path)) {
|
|
59
|
+
return err(404, `blog draft not found at ${path}`);
|
|
60
|
+
}
|
|
61
|
+
const markdown = readFileSync(path, 'utf-8');
|
|
62
|
+
const before = readWorkflows(projectRoot, config).find((w) => {
|
|
63
|
+
const identityMatch = entryId && w.entryId
|
|
64
|
+
? w.entryId === entryId
|
|
65
|
+
: w.site === b.site && w.slug === b.slug;
|
|
66
|
+
return (identityMatch &&
|
|
67
|
+
w.contentKind === 'longform' &&
|
|
68
|
+
w.state !== 'applied' &&
|
|
69
|
+
w.state !== 'cancelled');
|
|
70
|
+
});
|
|
71
|
+
const workflow = createWorkflow(projectRoot, config, {
|
|
72
|
+
site: b.site,
|
|
73
|
+
slug: b.slug,
|
|
74
|
+
...(entryId !== undefined && entryId !== '' ? { entryId } : {}),
|
|
75
|
+
contentKind: 'longform',
|
|
76
|
+
initialMarkdown: markdown,
|
|
77
|
+
initialOriginatedBy: 'agent',
|
|
78
|
+
});
|
|
79
|
+
return ok({ workflow, existing: !!before && before.id === workflow.id });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Enqueue a shortform draft review. Mirrors `handleStartLongform`'s shape:
|
|
83
|
+
* resolve the calendar entry by slug, compute the shortform file path,
|
|
84
|
+
* scaffold the file (frontmatter `deskwork: { id, platform, channel? }` +
|
|
85
|
+
* body) when missing, then `createWorkflow` with `contentKind: 'shortform'`
|
|
86
|
+
* and v1 mirroring the file body.
|
|
87
|
+
*
|
|
88
|
+
* Idempotent: if a workflow already exists for the
|
|
89
|
+
* (entryId|site+slug, contentKind, platform, channel) tuple,
|
|
90
|
+
* `createWorkflow` returns it unchanged. If the file already exists on
|
|
91
|
+
* disk (operator resuming), creation is skipped and the body is read.
|
|
92
|
+
*
|
|
93
|
+
* Lifecycle decoupling: shortform is independent of the longform
|
|
94
|
+
* outline → drafting → published path. If the entry has no body file
|
|
95
|
+
* yet (Ideas / Planned / Outlining without scaffold), this handler
|
|
96
|
+
* still works — it creates the entry directory + scrapbook subdirs
|
|
97
|
+
* before writing the shortform file.
|
|
98
|
+
*/
|
|
99
|
+
export function handleStartShortform(projectRoot, config, body) {
|
|
100
|
+
if (!body || typeof body !== 'object')
|
|
101
|
+
return err(400, 'expected JSON object body');
|
|
102
|
+
const b = body;
|
|
103
|
+
if (!b.site)
|
|
104
|
+
return err(400, 'site is required');
|
|
105
|
+
if (!(b.site in config.sites)) {
|
|
106
|
+
const known = Object.keys(config.sites).join(', ');
|
|
107
|
+
return err(400, `unknown site: ${b.site}. Configured: ${known}`);
|
|
108
|
+
}
|
|
109
|
+
if (!b.slug || typeof b.slug !== 'string')
|
|
110
|
+
return err(400, 'slug is required');
|
|
111
|
+
if (!SLUG_RE.test(b.slug)) {
|
|
112
|
+
return err(400, `invalid slug: ${b.slug}. Must match ${SLUG_RE}`);
|
|
113
|
+
}
|
|
114
|
+
if (!b.platform)
|
|
115
|
+
return err(400, 'platform is required');
|
|
116
|
+
if (!isPlatform(b.platform)) {
|
|
117
|
+
return err(400, `invalid platform: ${String(b.platform)}`);
|
|
118
|
+
}
|
|
119
|
+
const channel = b.channel !== undefined && b.channel !== '' ? b.channel : undefined;
|
|
120
|
+
const callerEntryId = b.entryId !== undefined && b.entryId !== '' ? b.entryId : undefined;
|
|
121
|
+
const entry = lookupEntry(projectRoot, config, b.site, {
|
|
122
|
+
...(callerEntryId !== undefined ? { entryId: callerEntryId } : {}),
|
|
123
|
+
slug: b.slug,
|
|
124
|
+
});
|
|
125
|
+
if (!entry) {
|
|
126
|
+
return err(404, `no calendar entry for site=${b.site} slug=${b.slug}`);
|
|
127
|
+
}
|
|
128
|
+
const entryId = callerEntryId ?? entry.id;
|
|
129
|
+
let filePath;
|
|
130
|
+
try {
|
|
131
|
+
filePath = resolveShortformWorkflowFilePath(projectRoot, config, b.site, b.slug, b.platform, channel, {
|
|
132
|
+
...(entryId !== undefined ? { entryId } : {}),
|
|
133
|
+
entry,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
return err(400, e instanceof Error ? e.message : String(e));
|
|
138
|
+
}
|
|
139
|
+
// The shortform path resolver returns undefined when the entry has no
|
|
140
|
+
// body file scaffolded yet (no entry-dir to anchor against). Shortform
|
|
141
|
+
// is decoupled from the lifecycle, so we materialize the directory
|
|
142
|
+
// tree from the slug-template fallback and proceed.
|
|
143
|
+
if (filePath === undefined) {
|
|
144
|
+
const fallback = resolveLongformFilePath(projectRoot, config, b.site, b.slug, { ...(entryId !== undefined ? { entryId } : {}), entry });
|
|
145
|
+
const entryDir = dirname(fallback);
|
|
146
|
+
const filename = channel !== undefined ? `${b.platform}-${channel}.md` : `${b.platform}.md`;
|
|
147
|
+
filePath = `${entryDir}/scrapbook/shortform/${filename}`;
|
|
148
|
+
}
|
|
149
|
+
// Scaffold the file when missing. Frontmatter carries the
|
|
150
|
+
// deskwork-namespaced binding so the studio can render the workflow
|
|
151
|
+
// off the file content directly.
|
|
152
|
+
let markdown;
|
|
153
|
+
if (existsSync(filePath)) {
|
|
154
|
+
markdown = readFileSync(filePath, 'utf-8');
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
158
|
+
const initialBody = b.initialMarkdown ?? '';
|
|
159
|
+
const deskworkMeta = {
|
|
160
|
+
platform: b.platform,
|
|
161
|
+
};
|
|
162
|
+
if (channel !== undefined)
|
|
163
|
+
deskworkMeta.channel = channel;
|
|
164
|
+
if (entryId !== undefined && entryId !== '')
|
|
165
|
+
deskworkMeta.id = entryId;
|
|
166
|
+
const fmData = {
|
|
167
|
+
title: entry.title,
|
|
168
|
+
deskwork: deskworkMeta,
|
|
169
|
+
};
|
|
170
|
+
writeFrontmatter(filePath, fmData, initialBody);
|
|
171
|
+
markdown = initialBody;
|
|
172
|
+
}
|
|
173
|
+
// Idempotent on (entryId|site+slug, contentKind, platform, channel).
|
|
174
|
+
const before = readWorkflows(projectRoot, config).find((w) => {
|
|
175
|
+
const identityMatch = entryId && w.entryId
|
|
176
|
+
? w.entryId === entryId
|
|
177
|
+
: w.site === b.site && w.slug === b.slug;
|
|
178
|
+
return (identityMatch &&
|
|
179
|
+
w.contentKind === 'shortform' &&
|
|
180
|
+
(w.platform ?? null) === (b.platform ?? null) &&
|
|
181
|
+
(w.channel ?? null) === (channel ?? null) &&
|
|
182
|
+
w.state !== 'applied' &&
|
|
183
|
+
w.state !== 'cancelled');
|
|
184
|
+
});
|
|
185
|
+
const workflow = createWorkflow(projectRoot, config, {
|
|
186
|
+
site: b.site,
|
|
187
|
+
slug: b.slug,
|
|
188
|
+
...(entryId !== undefined && entryId !== '' ? { entryId } : {}),
|
|
189
|
+
contentKind: 'shortform',
|
|
190
|
+
platform: b.platform,
|
|
191
|
+
...(channel !== undefined ? { channel } : {}),
|
|
192
|
+
initialMarkdown: markdown,
|
|
193
|
+
initialOriginatedBy: 'agent',
|
|
194
|
+
});
|
|
195
|
+
return ok({
|
|
196
|
+
workflow,
|
|
197
|
+
existing: !!before && before.id === workflow.id,
|
|
198
|
+
filePath,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
// ---------------------------------------------------------------------------
|
|
202
|
+
// Workflow → file dispatch (used by handleCreateVersion)
|
|
203
|
+
// ---------------------------------------------------------------------------
|
|
204
|
+
/**
|
|
205
|
+
* Resolve the on-disk markdown file path for any kind of workflow.
|
|
206
|
+
* Dispatches on `contentKind`. Returns `undefined` only for shortform when
|
|
207
|
+
* the entry directory cannot be derived (no entry id and no body file
|
|
208
|
+
* scaffolded yet).
|
|
209
|
+
*/
|
|
210
|
+
export function workflowFilePath(projectRoot, config, workflow) {
|
|
211
|
+
if (workflow.contentKind === 'shortform') {
|
|
212
|
+
if (workflow.platform === undefined) {
|
|
213
|
+
throw new Error(`shortform workflow ${JSON.stringify(workflow)} has no platform`);
|
|
214
|
+
}
|
|
215
|
+
return resolveShortformWorkflowFilePath(projectRoot, config, workflow.site, workflow.slug, workflow.platform, workflow.channel, {
|
|
216
|
+
...(workflow.entryId !== undefined ? { entryId: workflow.entryId } : {}),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return resolveLongformFilePath(projectRoot, config, workflow.site, workflow.slug, {
|
|
220
|
+
...(workflow.entryId !== undefined ? { entryId: workflow.entryId } : {}),
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=start-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start-handlers.js","sourceRoot":"","sources":["../../src/review/start-handlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editorial review pipeline — data model.
|
|
3
|
+
*
|
|
4
|
+
* Ported from audiocontrol.org's scripts/lib/editorial-review/types.ts.
|
|
5
|
+
* The `Site` type was replaced with `string` since deskwork makes sites
|
|
6
|
+
* config-driven rather than a hardcoded enum. Platform stays typed —
|
|
7
|
+
* it's a bounded set in deskwork's editorial types.
|
|
8
|
+
*
|
|
9
|
+
* The review pipeline is a journal-backed workflow system: each post
|
|
10
|
+
* draft gets a workflow, each iteration produces a new version, and
|
|
11
|
+
* every event (version, annotation, state transition) is an append-only
|
|
12
|
+
* history entry. Callers reconstruct state by reading the journal.
|
|
13
|
+
*/
|
|
14
|
+
import type { Platform } from '../types.ts';
|
|
15
|
+
export type DraftWorkflowState = 'open' | 'in-review' | 'iterating' | 'approved' | 'applied' | 'cancelled';
|
|
16
|
+
export type ContentKind = 'longform' | 'shortform' | 'outline';
|
|
17
|
+
export type OriginatedBy = 'agent' | 'operator';
|
|
18
|
+
/**
|
|
19
|
+
* Annotation category for comments — used by the voice-drift report to
|
|
20
|
+
* aggregate which skill principles produce the most corrections. `other`
|
|
21
|
+
* is the catch-all.
|
|
22
|
+
*/
|
|
23
|
+
export type AnnotationCategory = 'voice-drift' | 'missing-receipt' | 'tutorial-framing' | 'saas-vocabulary' | 'fake-authority' | 'structural' | 'other';
|
|
24
|
+
/** Character-offset range against the raw markdown source. */
|
|
25
|
+
export interface DraftRange {
|
|
26
|
+
start: number;
|
|
27
|
+
end: number;
|
|
28
|
+
}
|
|
29
|
+
interface AnnotationBase {
|
|
30
|
+
/** ISO-8601 timestamp when the annotation was recorded. */
|
|
31
|
+
createdAt: string;
|
|
32
|
+
/** Workflow the annotation belongs to. */
|
|
33
|
+
workflowId: string;
|
|
34
|
+
/** Monotonic id within the workflow (server-assigned). */
|
|
35
|
+
id: string;
|
|
36
|
+
}
|
|
37
|
+
export interface CommentAnnotation extends AnnotationBase {
|
|
38
|
+
type: 'comment';
|
|
39
|
+
/** Draft version the comment was attached to. */
|
|
40
|
+
version: number;
|
|
41
|
+
/** Character range against the raw markdown of that version. */
|
|
42
|
+
range: DraftRange;
|
|
43
|
+
/** Free-text operator comment. */
|
|
44
|
+
text: string;
|
|
45
|
+
/** Optional category for review-report aggregation. */
|
|
46
|
+
category?: AnnotationCategory;
|
|
47
|
+
/**
|
|
48
|
+
* The displayed text the operator selected when making the comment.
|
|
49
|
+
* Used on later versions to re-locate the anchor via `indexOf`: if the
|
|
50
|
+
* quote appears exactly once in the new version the comment rebases,
|
|
51
|
+
* otherwise it renders as unresolved-from-v{N}.
|
|
52
|
+
*/
|
|
53
|
+
anchor?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface EditAnnotation extends AnnotationBase {
|
|
56
|
+
type: 'edit';
|
|
57
|
+
/** Version that was edited (the source of the edit). */
|
|
58
|
+
beforeVersion: number;
|
|
59
|
+
/** Full markdown of the new version the edit produced. */
|
|
60
|
+
afterMarkdown: string;
|
|
61
|
+
/** Unified diff of afterMarkdown against the beforeVersion's markdown. */
|
|
62
|
+
diff: string;
|
|
63
|
+
}
|
|
64
|
+
export interface ApproveAnnotation extends AnnotationBase {
|
|
65
|
+
type: 'approve';
|
|
66
|
+
/** Version the operator approved. */
|
|
67
|
+
version: number;
|
|
68
|
+
}
|
|
69
|
+
export interface RejectAnnotation extends AnnotationBase {
|
|
70
|
+
type: 'reject';
|
|
71
|
+
/** Version the operator rejected. */
|
|
72
|
+
version: number;
|
|
73
|
+
/** Optional reason free-text. */
|
|
74
|
+
reason?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Marks a comment annotation as resolved. Emitted separately rather than
|
|
78
|
+
* mutating the comment because the journal is append-only — readers
|
|
79
|
+
* reconstruct resolved state by scanning for the most recent resolve
|
|
80
|
+
* annotation per commentId. `resolved: false` re-opens.
|
|
81
|
+
*/
|
|
82
|
+
export interface ResolveAnnotation extends AnnotationBase {
|
|
83
|
+
type: 'resolve';
|
|
84
|
+
commentId: string;
|
|
85
|
+
resolved: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Per-iteration agent disposition for a comment. Written when a new
|
|
89
|
+
* version addresses (or defers) an operator comment. Latest-wins when
|
|
90
|
+
* rendering the sidebar badge.
|
|
91
|
+
*/
|
|
92
|
+
export interface AddressAnnotation extends AnnotationBase {
|
|
93
|
+
type: 'address';
|
|
94
|
+
commentId: string;
|
|
95
|
+
/** Version the disposition was recorded against (the just-produced version). */
|
|
96
|
+
version: number;
|
|
97
|
+
disposition: 'addressed' | 'deferred' | 'wontfix';
|
|
98
|
+
reason?: string;
|
|
99
|
+
}
|
|
100
|
+
export type DraftAnnotation = CommentAnnotation | EditAnnotation | ApproveAnnotation | RejectAnnotation | ResolveAnnotation | AddressAnnotation;
|
|
101
|
+
export interface DraftVersion {
|
|
102
|
+
/** 1-based version number; v1 is the initial draft. */
|
|
103
|
+
version: number;
|
|
104
|
+
/** Full raw markdown of this version. */
|
|
105
|
+
markdown: string;
|
|
106
|
+
/** ISO-8601 when this version was recorded. */
|
|
107
|
+
createdAt: string;
|
|
108
|
+
/** Who produced this version — the agent or the operator (edit mode). */
|
|
109
|
+
originatedBy: OriginatedBy;
|
|
110
|
+
}
|
|
111
|
+
export interface DraftWorkflowItem {
|
|
112
|
+
/** Stable UUID for this workflow. */
|
|
113
|
+
id: string;
|
|
114
|
+
/**
|
|
115
|
+
* Stable UUID of the target calendar entry. Preferred join key for
|
|
116
|
+
* `matchesKey` / `findOpenByKey` — survives slug renames. Optional
|
|
117
|
+
* for legacy workflows created before entry UUIDs landed; those keep
|
|
118
|
+
* joining via the slug fallback.
|
|
119
|
+
*/
|
|
120
|
+
entryId?: string;
|
|
121
|
+
/** Which site this draft belongs to (deskwork config site slug). */
|
|
122
|
+
site: string;
|
|
123
|
+
/** Post slug (blog entry slug for longform; calendar entry slug for shortform). */
|
|
124
|
+
slug: string;
|
|
125
|
+
/** longform / shortform / outline. */
|
|
126
|
+
contentKind: ContentKind;
|
|
127
|
+
/** For shortform only: which distribution platform. */
|
|
128
|
+
platform?: Platform;
|
|
129
|
+
/** For shortform only: channel (e.g. `r/synthdiy`). */
|
|
130
|
+
channel?: string;
|
|
131
|
+
/** Current state in the review pipeline. */
|
|
132
|
+
state: DraftWorkflowState;
|
|
133
|
+
/** Version number of the most recent DraftVersion for this workflow. */
|
|
134
|
+
currentVersion: number;
|
|
135
|
+
/** ISO-8601 when the workflow was first created. */
|
|
136
|
+
createdAt: string;
|
|
137
|
+
/** ISO-8601 when last modified (state transition or new version). */
|
|
138
|
+
updatedAt: string;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* A single entry in the editorial-review history journal. Discriminated by
|
|
142
|
+
* `kind` so versions, annotations, and workflow transitions interleave in
|
|
143
|
+
* one stream.
|
|
144
|
+
*/
|
|
145
|
+
export type DraftHistoryEntry = {
|
|
146
|
+
kind: 'workflow-created';
|
|
147
|
+
at: string;
|
|
148
|
+
workflow: DraftWorkflowItem;
|
|
149
|
+
} | {
|
|
150
|
+
kind: 'workflow-state';
|
|
151
|
+
at: string;
|
|
152
|
+
workflowId: string;
|
|
153
|
+
from: DraftWorkflowState;
|
|
154
|
+
to: DraftWorkflowState;
|
|
155
|
+
} | {
|
|
156
|
+
kind: 'version';
|
|
157
|
+
at: string;
|
|
158
|
+
workflowId: string;
|
|
159
|
+
version: DraftVersion;
|
|
160
|
+
} | {
|
|
161
|
+
kind: 'annotation';
|
|
162
|
+
at: string;
|
|
163
|
+
annotation: DraftAnnotation;
|
|
164
|
+
};
|
|
165
|
+
/** Allowed state transitions. All others are invalid. */
|
|
166
|
+
export declare const VALID_TRANSITIONS: Readonly<Record<DraftWorkflowState, readonly DraftWorkflowState[]>>;
|
|
167
|
+
export declare function isValidTransition(from: DraftWorkflowState, to: DraftWorkflowState): boolean;
|
|
168
|
+
export {};
|
|
169
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/review/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editorial review pipeline — data model.
|
|
3
|
+
*
|
|
4
|
+
* Ported from audiocontrol.org's scripts/lib/editorial-review/types.ts.
|
|
5
|
+
* The `Site` type was replaced with `string` since deskwork makes sites
|
|
6
|
+
* config-driven rather than a hardcoded enum. Platform stays typed —
|
|
7
|
+
* it's a bounded set in deskwork's editorial types.
|
|
8
|
+
*
|
|
9
|
+
* The review pipeline is a journal-backed workflow system: each post
|
|
10
|
+
* draft gets a workflow, each iteration produces a new version, and
|
|
11
|
+
* every event (version, annotation, state transition) is an append-only
|
|
12
|
+
* history entry. Callers reconstruct state by reading the journal.
|
|
13
|
+
*/
|
|
14
|
+
/** Allowed state transitions. All others are invalid. */
|
|
15
|
+
export const VALID_TRANSITIONS = {
|
|
16
|
+
open: ['in-review', 'cancelled'],
|
|
17
|
+
'in-review': ['iterating', 'approved', 'cancelled'],
|
|
18
|
+
iterating: ['in-review', 'cancelled'],
|
|
19
|
+
approved: ['applied', 'cancelled'],
|
|
20
|
+
applied: [],
|
|
21
|
+
cancelled: [],
|
|
22
|
+
};
|
|
23
|
+
export function isValidTransition(from, to) {
|
|
24
|
+
return VALID_TRANSITIONS[from].includes(to);
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/review/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAoLH,yDAAyD;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAE1B;IACF,IAAI,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;IAChC,WAAW,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,WAAW,CAAC;IACnD,SAAS,EAAE,CAAC,WAAW,EAAE,WAAW,CAAC;IACrC,QAAQ,EAAE,CAAC,SAAS,EAAE,WAAW,CAAC;IAClC,OAAO,EAAE,EAAE;IACX,SAAS,EAAE,EAAE;CACd,CAAC;AAEF,MAAM,UAAU,iBAAiB,CAC/B,IAAwB,EACxB,EAAsB;IAEtB,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow → on-disk markdown file resolution.
|
|
3
|
+
*
|
|
4
|
+
* Both `handleStartLongform` and `handleCreateVersion` need to translate a
|
|
5
|
+
* (site, slug, contentKind, entryId?, platform?, channel?) tuple into an
|
|
6
|
+
* absolute file path. Pulled out of `handlers.ts` to keep that file under
|
|
7
|
+
* the 500-line guideline and to give the shortform path resolver a clean
|
|
8
|
+
* home alongside its longform sibling.
|
|
9
|
+
*
|
|
10
|
+
* Resolution rules:
|
|
11
|
+
*
|
|
12
|
+
* - `longform` / `outline`: prefer the content index (so writingcontrol-
|
|
13
|
+
* shaped non-template paths work); fall back to the site's
|
|
14
|
+
* `blogFilenameTemplate` for legacy / pre-doctor entries.
|
|
15
|
+
* - `shortform`: defers to `resolveShortformFilePath` in `paths.ts`,
|
|
16
|
+
* which composes the entry directory (via `findEntryFile`) with
|
|
17
|
+
* `scrapbook/shortform/<platform>[-<channel>].md`.
|
|
18
|
+
*
|
|
19
|
+
* The shortform variant returns `undefined` when the entry has no body file
|
|
20
|
+
* scaffolded yet — the caller decides whether to create the entry directory
|
|
21
|
+
* or surface a 404. The longform variant always returns a path because the
|
|
22
|
+
* slug-template fallback is unconditional for it.
|
|
23
|
+
*/
|
|
24
|
+
import type { DeskworkConfig } from '../config.ts';
|
|
25
|
+
import type { ContentIndex } from '../content-index.ts';
|
|
26
|
+
import type { CalendarEntry, Platform } from '../types.ts';
|
|
27
|
+
/**
|
|
28
|
+
* Read a calendar entry by id or slug for a given site, returning
|
|
29
|
+
* `undefined` when the calendar is missing or the entry can't be found.
|
|
30
|
+
*/
|
|
31
|
+
export declare function lookupEntry(projectRoot: string, config: DeskworkConfig, site: string, match: {
|
|
32
|
+
entryId?: string;
|
|
33
|
+
slug?: string;
|
|
34
|
+
}): CalendarEntry | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Hint bundle accepted by the longform/outline path resolver. Any one of
|
|
37
|
+
* `entryId`, `entry`, or `index` may be supplied; the resolver fills the
|
|
38
|
+
* rest in.
|
|
39
|
+
*/
|
|
40
|
+
export interface LongformPathHint {
|
|
41
|
+
entryId?: string;
|
|
42
|
+
entry?: CalendarEntry;
|
|
43
|
+
index?: ContentIndex;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the absolute path of the markdown file backing a longform or
|
|
47
|
+
* outline workflow.
|
|
48
|
+
*
|
|
49
|
+
* Precedence:
|
|
50
|
+
* 1. Content index — when an entry id is known (passed in or derived
|
|
51
|
+
* from the workflow's site+slug via the calendar), look up the file
|
|
52
|
+
* whose frontmatter `deskwork.id:` matches. Refactor-proof.
|
|
53
|
+
* 2. Slug-template fallback — when no entry id is available (legacy
|
|
54
|
+
* workflow, pre-doctor entry, ad-hoc draft with no calendar record),
|
|
55
|
+
* fall back to the site's `blogFilenameTemplate`.
|
|
56
|
+
*
|
|
57
|
+
* Always returns a path (the slug-template fallback is unconditional);
|
|
58
|
+
* callers should `existsSync` if they need existence guarantees.
|
|
59
|
+
*/
|
|
60
|
+
export declare function resolveLongformFilePath(projectRoot: string, config: DeskworkConfig, site: string, slug: string, hint: LongformPathHint): string;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the absolute path of the markdown file backing a shortform
|
|
63
|
+
* workflow. Returns `undefined` when the entry has no body file
|
|
64
|
+
* scaffolded yet — the entry directory cannot be derived in that case
|
|
65
|
+
* and the caller decides what to do (create the directory tree, or 404).
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveShortformWorkflowFilePath(projectRoot: string, config: DeskworkConfig, site: string, slug: string, platform: Platform, channel: string | undefined, hint: LongformPathHint): string | undefined;
|
|
68
|
+
//# sourceMappingURL=workflow-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-paths.d.ts","sourceRoot":"","sources":["../../src/review/workflow-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AASxD,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,aAAa,GAAG,SAAS,CAgB3B;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,gBAAgB,GACrB,MAAM,CAyBR;AAED;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAC9C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,IAAI,EAAE,gBAAgB,GACrB,MAAM,GAAG,SAAS,CAwBpB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow → on-disk markdown file resolution.
|
|
3
|
+
*
|
|
4
|
+
* Both `handleStartLongform` and `handleCreateVersion` need to translate a
|
|
5
|
+
* (site, slug, contentKind, entryId?, platform?, channel?) tuple into an
|
|
6
|
+
* absolute file path. Pulled out of `handlers.ts` to keep that file under
|
|
7
|
+
* the 500-line guideline and to give the shortform path resolver a clean
|
|
8
|
+
* home alongside its longform sibling.
|
|
9
|
+
*
|
|
10
|
+
* Resolution rules:
|
|
11
|
+
*
|
|
12
|
+
* - `longform` / `outline`: prefer the content index (so writingcontrol-
|
|
13
|
+
* shaped non-template paths work); fall back to the site's
|
|
14
|
+
* `blogFilenameTemplate` for legacy / pre-doctor entries.
|
|
15
|
+
* - `shortform`: defers to `resolveShortformFilePath` in `paths.ts`,
|
|
16
|
+
* which composes the entry directory (via `findEntryFile`) with
|
|
17
|
+
* `scrapbook/shortform/<platform>[-<channel>].md`.
|
|
18
|
+
*
|
|
19
|
+
* The shortform variant returns `undefined` when the entry has no body file
|
|
20
|
+
* scaffolded yet — the caller decides whether to create the entry directory
|
|
21
|
+
* or surface a 404. The longform variant always returns a path because the
|
|
22
|
+
* slug-template fallback is unconditional for it.
|
|
23
|
+
*/
|
|
24
|
+
import { existsSync } from 'node:fs';
|
|
25
|
+
import { buildContentIndex } from "../content-index.js";
|
|
26
|
+
import { resolveCalendarPath, resolveEntryFilePath, resolveShortformFilePath, } from "../paths.js";
|
|
27
|
+
import { readCalendar } from "../calendar.js";
|
|
28
|
+
import { findEntry, findEntryById } from "../calendar-mutations.js";
|
|
29
|
+
/**
|
|
30
|
+
* Read a calendar entry by id or slug for a given site, returning
|
|
31
|
+
* `undefined` when the calendar is missing or the entry can't be found.
|
|
32
|
+
*/
|
|
33
|
+
export function lookupEntry(projectRoot, config, site, match) {
|
|
34
|
+
try {
|
|
35
|
+
const calendarPath = resolveCalendarPath(projectRoot, config, site);
|
|
36
|
+
if (!existsSync(calendarPath))
|
|
37
|
+
return undefined;
|
|
38
|
+
const cal = readCalendar(calendarPath);
|
|
39
|
+
if (match.entryId !== undefined && match.entryId !== '') {
|
|
40
|
+
const byId = findEntryById(cal, match.entryId);
|
|
41
|
+
if (byId !== undefined)
|
|
42
|
+
return byId;
|
|
43
|
+
}
|
|
44
|
+
if (match.slug !== undefined && match.slug !== '') {
|
|
45
|
+
return findEntry(cal, match.slug);
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the absolute path of the markdown file backing a longform or
|
|
55
|
+
* outline workflow.
|
|
56
|
+
*
|
|
57
|
+
* Precedence:
|
|
58
|
+
* 1. Content index — when an entry id is known (passed in or derived
|
|
59
|
+
* from the workflow's site+slug via the calendar), look up the file
|
|
60
|
+
* whose frontmatter `deskwork.id:` matches. Refactor-proof.
|
|
61
|
+
* 2. Slug-template fallback — when no entry id is available (legacy
|
|
62
|
+
* workflow, pre-doctor entry, ad-hoc draft with no calendar record),
|
|
63
|
+
* fall back to the site's `blogFilenameTemplate`.
|
|
64
|
+
*
|
|
65
|
+
* Always returns a path (the slug-template fallback is unconditional);
|
|
66
|
+
* callers should `existsSync` if they need existence guarantees.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveLongformFilePath(projectRoot, config, site, slug, hint) {
|
|
69
|
+
let entry = hint.entry;
|
|
70
|
+
let entryId = hint.entryId;
|
|
71
|
+
if (entry === undefined && (entryId === undefined || entryId === '')) {
|
|
72
|
+
entry = lookupEntry(projectRoot, config, site, { slug });
|
|
73
|
+
entryId = entry?.id;
|
|
74
|
+
}
|
|
75
|
+
else if (entry === undefined && entryId !== undefined) {
|
|
76
|
+
entry = lookupEntry(projectRoot, config, site, { entryId });
|
|
77
|
+
}
|
|
78
|
+
else if (entryId === undefined || entryId === '') {
|
|
79
|
+
entryId = entry?.id;
|
|
80
|
+
}
|
|
81
|
+
// Delegate to the top-level helper — same precedence (id-bound path
|
|
82
|
+
// first, slug-template fallback). The studio passes a per-request
|
|
83
|
+
// memoized index; the CLI can let it build per call.
|
|
84
|
+
const idx = hint.index ?? buildContentIndex(projectRoot, config, site);
|
|
85
|
+
const slugForFallback = entry?.slug ?? slug;
|
|
86
|
+
return resolveEntryFilePath(projectRoot, config, site, slugForFallback, entryId, idx);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the absolute path of the markdown file backing a shortform
|
|
90
|
+
* workflow. Returns `undefined` when the entry has no body file
|
|
91
|
+
* scaffolded yet — the entry directory cannot be derived in that case
|
|
92
|
+
* and the caller decides what to do (create the directory tree, or 404).
|
|
93
|
+
*/
|
|
94
|
+
export function resolveShortformWorkflowFilePath(projectRoot, config, site, slug, platform, channel, hint) {
|
|
95
|
+
let entry = hint.entry;
|
|
96
|
+
let entryId = hint.entryId;
|
|
97
|
+
if (entry === undefined && (entryId === undefined || entryId === '')) {
|
|
98
|
+
entry = lookupEntry(projectRoot, config, site, { slug });
|
|
99
|
+
entryId = entry?.id;
|
|
100
|
+
}
|
|
101
|
+
else if (entry === undefined && entryId !== undefined) {
|
|
102
|
+
entry = lookupEntry(projectRoot, config, site, { entryId });
|
|
103
|
+
}
|
|
104
|
+
else if (entryId === undefined || entryId === '') {
|
|
105
|
+
entryId = entry?.id;
|
|
106
|
+
}
|
|
107
|
+
return resolveShortformFilePath(projectRoot, config, site, {
|
|
108
|
+
...(entryId !== undefined && entryId !== '' ? { id: entryId } : {}),
|
|
109
|
+
slug: entry?.slug ?? slug,
|
|
110
|
+
}, platform, channel, hint.index);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=workflow-paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-paths.js","sourceRoot":"","sources":["../../src/review/workflow-paths.ts"],"names":[],"mappings":"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"}
|