@deskwork/core 0.45.2 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +14 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -12
- package/dist/config.js.map +1 -1
- package/dist/content-index.d.ts +25 -0
- package/dist/content-index.d.ts.map +1 -1
- package/dist/content-index.js +142 -15
- package/dist/content-index.js.map +1 -1
- package/dist/doctor/index.d.ts +3 -0
- package/dist/doctor/index.d.ts.map +1 -1
- package/dist/doctor/index.js +3 -0
- package/dist/doctor/index.js.map +1 -1
- package/dist/doctor/legacy-config.d.ts +98 -0
- package/dist/doctor/legacy-config.d.ts.map +1 -0
- package/dist/doctor/legacy-config.js +174 -0
- package/dist/doctor/legacy-config.js.map +1 -0
- package/dist/doctor/project-scope-gate.d.ts +12 -21
- package/dist/doctor/project-scope-gate.d.ts.map +1 -1
- package/dist/doctor/project-scope-gate.js +13 -25
- package/dist/doctor/project-scope-gate.js.map +1 -1
- package/dist/doctor/repair.d.ts +11 -0
- package/dist/doctor/repair.d.ts.map +1 -1
- package/dist/doctor/repair.js +10 -89
- package/dist/doctor/repair.js.map +1 -1
- package/dist/doctor/rules/duplicate-id.d.ts +7 -2
- package/dist/doctor/rules/duplicate-id.d.ts.map +1 -1
- package/dist/doctor/rules/duplicate-id.js +8 -5
- package/dist/doctor/rules/duplicate-id.js.map +1 -1
- package/dist/doctor/rules/duplicate-id.ts +8 -5
- package/dist/doctor/rules/legacy-top-level-id-migration.d.ts.map +1 -1
- package/dist/doctor/rules/legacy-top-level-id-migration.js +11 -8
- package/dist/doctor/rules/legacy-top-level-id-migration.js.map +1 -1
- package/dist/doctor/rules/legacy-top-level-id-migration.ts +10 -11
- package/dist/doctor/rules/sites-to-lanes-migration.d.ts +39 -0
- package/dist/doctor/rules/sites-to-lanes-migration.d.ts.map +1 -0
- package/dist/doctor/rules/sites-to-lanes-migration.js +395 -0
- package/dist/doctor/rules/sites-to-lanes-migration.js.map +1 -0
- package/dist/doctor/rules/sites-to-lanes-migration.ts +449 -0
- package/dist/doctor/rules/workflow-stale.d.ts.map +1 -1
- package/dist/doctor/rules/workflow-stale.js +5 -2
- package/dist/doctor/rules/workflow-stale.js.map +1 -1
- package/dist/doctor/rules/workflow-stale.ts +5 -1
- package/dist/doctor/runner.d.ts +15 -1
- package/dist/doctor/runner.d.ts.map +1 -1
- package/dist/doctor/runner.js +48 -41
- package/dist/doctor/runner.js.map +1 -1
- package/dist/doctor/sites-migration-backfill.d.ts +71 -0
- package/dist/doctor/sites-migration-backfill.d.ts.map +1 -0
- package/dist/doctor/sites-migration-backfill.js +164 -0
- package/dist/doctor/sites-migration-backfill.js.map +1 -0
- package/dist/doctor/validate.d.ts.map +1 -1
- package/dist/doctor/validate.js +34 -73
- package/dist/doctor/validate.js.map +1 -1
- package/dist/entry/resolve-artifact.d.ts +80 -0
- package/dist/entry/resolve-artifact.d.ts.map +1 -0
- package/dist/entry/resolve-artifact.js +102 -0
- package/dist/entry/resolve-artifact.js.map +1 -0
- package/dist/entry/shortform-path.d.ts +34 -0
- package/dist/entry/shortform-path.d.ts.map +1 -0
- package/dist/entry/shortform-path.js +50 -0
- package/dist/entry/shortform-path.js.map +1 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/iterate/iterate.d.ts.map +1 -1
- package/dist/iterate/iterate.js +21 -26
- package/dist/iterate/iterate.js.map +1 -1
- package/dist/lanes/bootstrap.d.ts +11 -7
- package/dist/lanes/bootstrap.d.ts.map +1 -1
- package/dist/lanes/bootstrap.js +42 -17
- package/dist/lanes/bootstrap.js.map +1 -1
- package/dist/lanes/index.d.ts +1 -0
- package/dist/lanes/index.d.ts.map +1 -1
- package/dist/lanes/index.js +6 -0
- package/dist/lanes/index.js.map +1 -1
- package/dist/lanes/loader.d.ts +33 -6
- package/dist/lanes/loader.d.ts.map +1 -1
- package/dist/lanes/loader.js +44 -11
- package/dist/lanes/loader.js.map +1 -1
- package/dist/lanes/operations/create.d.ts +11 -2
- package/dist/lanes/operations/create.d.ts.map +1 -1
- package/dist/lanes/operations/create.js +15 -4
- package/dist/lanes/operations/create.js.map +1 -1
- package/dist/lanes/operations/list.d.ts +3 -2
- package/dist/lanes/operations/list.d.ts.map +1 -1
- package/dist/lanes/operations/list.js +3 -2
- package/dist/lanes/operations/list.js.map +1 -1
- package/dist/lanes/operations/move.d.ts +29 -25
- package/dist/lanes/operations/move.d.ts.map +1 -1
- package/dist/lanes/operations/move.js +45 -242
- package/dist/lanes/operations/move.js.map +1 -1
- package/dist/lanes/operations/update.d.ts +15 -5
- package/dist/lanes/operations/update.d.ts.map +1 -1
- package/dist/lanes/operations/update.js +32 -12
- package/dist/lanes/operations/update.js.map +1 -1
- package/dist/lanes/scaffold-path.d.ts +88 -0
- package/dist/lanes/scaffold-path.d.ts.map +1 -0
- package/dist/lanes/scaffold-path.js +122 -0
- package/dist/lanes/scaffold-path.js.map +1 -0
- package/dist/lanes/types.d.ts +54 -31
- package/dist/lanes/types.d.ts.map +1 -1
- package/dist/lanes/types.js +60 -21
- package/dist/lanes/types.js.map +1 -1
- package/dist/outline-split.d.ts +2 -3
- package/dist/outline-split.d.ts.map +1 -1
- package/dist/outline-split.js +2 -3
- package/dist/outline-split.js.map +1 -1
- package/dist/paths.d.ts +14 -87
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +21 -131
- package/dist/paths.js.map +1 -1
- package/dist/remark-strip-outline.mjs +2 -3
- package/dist/rename-slug.d.ts +3 -3
- package/dist/rename-slug.d.ts.map +1 -1
- package/dist/rename-slug.js +137 -44
- package/dist/rename-slug.js.map +1 -1
- package/dist/review/handlers.d.ts +3 -3
- package/dist/review/handlers.d.ts.map +1 -1
- package/dist/review/handlers.js +18 -14
- package/dist/review/handlers.js.map +1 -1
- package/dist/review/report.d.ts +13 -2
- package/dist/review/report.d.ts.map +1 -1
- package/dist/review/report.js +48 -18
- package/dist/review/report.js.map +1 -1
- package/dist/review/start-handlers.d.ts +4 -4
- package/dist/review/start-handlers.d.ts.map +1 -1
- package/dist/review/start-handlers.js +25 -25
- package/dist/review/start-handlers.js.map +1 -1
- package/dist/review/workflow-paths.d.ts +24 -26
- package/dist/review/workflow-paths.d.ts.map +1 -1
- package/dist/review/workflow-paths.js +70 -60
- package/dist/review/workflow-paths.js.map +1 -1
- package/dist/schema/draft-annotation.d.ts +8 -8
- package/dist/schema/entry.d.ts +6 -6
- package/dist/schema/journal-events.d.ts +56 -42
- package/dist/schema/journal-events.d.ts.map +1 -1
- package/dist/schema/journal-events.js +23 -9
- package/dist/schema/journal-events.js.map +1 -1
- package/dist/sidecar/read.d.ts +8 -0
- package/dist/sidecar/read.d.ts.map +1 -1
- package/dist/sidecar/read.js +36 -9
- package/dist/sidecar/read.js.map +1 -1
- package/dist/sidecar/write.d.ts +14 -0
- package/dist/sidecar/write.d.ts.map +1 -1
- package/dist/sidecar/write.js +25 -0
- package/dist/sidecar/write.js.map +1 -1
- package/package.json +9 -9
- package/dist/body-state.d.ts +0 -27
- package/dist/body-state.d.ts.map +0 -1
- package/dist/body-state.js +0 -62
- package/dist/body-state.js.map +0 -1
- package/dist/scaffold.d.ts +0 -67
- package/dist/scaffold.d.ts.map +0 -1
- package/dist/scaffold.js +0 -122
- package/dist/scaffold.js.map +0 -1
package/dist/paths.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* candidate-search by template, the legacy publish path). New code with
|
|
25
25
|
* access to a calendar entry should prefer `findEntryFile`.
|
|
26
26
|
*/
|
|
27
|
-
import {
|
|
27
|
+
import { join } from 'node:path';
|
|
28
28
|
import { buildContentIndex } from "./content-index.js";
|
|
29
29
|
/**
|
|
30
30
|
* Resolve a user-supplied site argument to a configured site slug.
|
|
@@ -48,45 +48,31 @@ function siteConfig(config, site) {
|
|
|
48
48
|
const slug = resolveSite(config, site);
|
|
49
49
|
return config.sites[slug];
|
|
50
50
|
}
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Absolute path to the project's editorial calendar file.
|
|
53
|
+
*
|
|
54
|
+
* Phase 39c (sites→lanes retirement) — the calendar is a single,
|
|
55
|
+
* project-level derived projection from sidecars (spec §"Calendar"). The
|
|
56
|
+
* legacy per-site `calendarPath` is retired; there is one calendar at
|
|
57
|
+
* `.deskwork/calendar.md`. The `config`/`site` parameters are retained so
|
|
58
|
+
* call sites (and the CLI-verb resolution path, scoped to 39c-2b) need
|
|
59
|
+
* not change in lockstep — the de-parameterization is internal. Both are
|
|
60
|
+
* intentionally ignored. Closes #234 (divergence), #357 (read-side
|
|
61
|
+
* validator), #223 (regen flip-flop) per spec §"Inherited calendar-surface
|
|
62
|
+
* cluster".
|
|
63
|
+
*/
|
|
64
|
+
export function resolveCalendarPath(projectRoot, _config, _site) {
|
|
65
|
+
return join(projectRoot, '.deskwork', 'calendar.md');
|
|
61
66
|
}
|
|
62
67
|
/** Absolute path to the site's blog content directory. */
|
|
63
68
|
export function resolveContentDir(projectRoot, config, site) {
|
|
64
69
|
return join(projectRoot, siteConfig(config, site).contentDir);
|
|
65
70
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
*/
|
|
72
|
-
export function resolveSiteHost(config, site) {
|
|
73
|
-
return siteConfig(config, site).host;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Canonical public base URL for the site, with trailing slash. Throws when
|
|
77
|
-
* the collection has no `host` configured (i.e. is not published as a
|
|
78
|
-
* website) — callers that need a URL must guarantee the collection is a
|
|
79
|
-
* website-rendered one before calling.
|
|
80
|
-
*/
|
|
81
|
-
export function resolveSiteBaseUrl(config, site) {
|
|
82
|
-
const host = resolveSiteHost(config, site);
|
|
83
|
-
if (host === undefined) {
|
|
84
|
-
const slug = site ?? config.defaultSite;
|
|
85
|
-
throw new Error(`Cannot resolve a base URL for collection "${slug}": no "host" is configured. ` +
|
|
86
|
-
`Add a "host" field to .deskwork/config.json if this collection is published as a website.`);
|
|
87
|
-
}
|
|
88
|
-
return `https://${host}/`;
|
|
89
|
-
}
|
|
71
|
+
// Phase 39c (sites→lanes retirement): `resolveSiteHost` and
|
|
72
|
+
// `resolveSiteBaseUrl` are removed. They had ZERO production call sites
|
|
73
|
+
// (the only `host` reads were two studio files reading `lane.host`
|
|
74
|
+
// directly). Per spec §18 they were effectively dead. Host now lives on
|
|
75
|
+
// the lane (`lane.host`); studio reads it from lane configs.
|
|
90
76
|
const DEFAULT_BLOG_FILENAME_TEMPLATE = '{slug}/index.md';
|
|
91
77
|
/**
|
|
92
78
|
* Absolute path to the blog post markdown for a given slug.
|
|
@@ -167,100 +153,4 @@ export function findEntryFile(projectRoot, config, site, entryId, index, legacyE
|
|
|
167
153
|
}
|
|
168
154
|
return undefined;
|
|
169
155
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Resolve the markdown file backing a calendar entry, preferring the
|
|
172
|
-
* UUID frontmatter binding (refactor-proof) and falling back to the
|
|
173
|
-
* site's slug-template only when no binding exists.
|
|
174
|
-
*
|
|
175
|
-
* Equivalent to the studio's `resolveLongformFilePath` but exposed as a
|
|
176
|
-
* top-level helper from `paths.ts` so CLI commands can use it without
|
|
177
|
-
* pulling in `review/` infrastructure. Always returns an absolute path
|
|
178
|
-
* (the slug-template fallback is unconditional); callers should
|
|
179
|
-
* `existsSync` if they need an existence guarantee.
|
|
180
|
-
*
|
|
181
|
-
* Precedence:
|
|
182
|
-
* 1. Content index — when `entryId` is supplied (and non-empty), look
|
|
183
|
-
* up the file whose frontmatter `deskwork.id:` matches. Refactor-
|
|
184
|
-
* proof: the binding follows the file regardless of slug rename or
|
|
185
|
-
* directory relocation.
|
|
186
|
-
* 2. Slug-template fallback — when the index has no record (entry's
|
|
187
|
-
* file isn't bound to frontmatter yet, e.g. pre-doctor / pre-ingest
|
|
188
|
-
* state) or no `entryId` was supplied, fall back to
|
|
189
|
-
* `resolveBlogFilePath(slug)`.
|
|
190
|
-
*
|
|
191
|
-
* @param projectRoot Absolute path to the deskwork project root.
|
|
192
|
-
* @param config Loaded deskwork config.
|
|
193
|
-
* @param site Site slug (or null/undefined for the default site).
|
|
194
|
-
* @param slug Calendar entry slug — used both as the legacy fallback
|
|
195
|
-
* template input and as a hint for the slug-template fallback.
|
|
196
|
-
* @param entryId Calendar entry's stable UUID. When omitted or empty,
|
|
197
|
-
* resolution falls straight through to the slug template.
|
|
198
|
-
* @param index Pre-built content index. When omitted, this function
|
|
199
|
-
* builds one. Pass the per-request memoized index when
|
|
200
|
-
* calling from the studio; let the CLI build per call.
|
|
201
|
-
*/
|
|
202
|
-
export function resolveEntryFilePath(projectRoot, config, site, slug, entryId, index) {
|
|
203
|
-
if (entryId !== undefined && entryId !== '') {
|
|
204
|
-
const idx = index ?? buildContentIndex(projectRoot, config, resolveSite(config, site));
|
|
205
|
-
const hit = idx.byId.get(entryId);
|
|
206
|
-
if (hit !== undefined)
|
|
207
|
-
return hit;
|
|
208
|
-
}
|
|
209
|
-
return resolveBlogFilePath(projectRoot, config, site, slug);
|
|
210
|
-
}
|
|
211
|
-
// ---------------------------------------------------------------------------
|
|
212
|
-
// Phase 21a — shortform file resolution
|
|
213
|
-
// ---------------------------------------------------------------------------
|
|
214
|
-
/**
|
|
215
|
-
* Channel must be a kebab-case token. Same shape as a slug segment so the
|
|
216
|
-
* filename remains URL-safe and matches the rest of deskwork's vocabulary.
|
|
217
|
-
*/
|
|
218
|
-
const CHANNEL_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
219
|
-
/**
|
|
220
|
-
* Resolve the markdown file path for a shortform draft.
|
|
221
|
-
*
|
|
222
|
-
* <contentDir>/<entry-dir>/scrapbook/shortform/<platform>[-<channel>].md
|
|
223
|
-
*
|
|
224
|
-
* Platform is the lowercase Platform value. Channel (if present) is appended
|
|
225
|
-
* as `-<channel>`. Channel must validate against the kebab-case regex —
|
|
226
|
-
* deskwork stores channels as kebab-case strings throughout.
|
|
227
|
-
*
|
|
228
|
-
* The entry directory is resolved through `findEntryFile` (id-driven,
|
|
229
|
-
* refactor-proof) with slug-template fallback for legacy entries created
|
|
230
|
-
* pre-doctor. The slug-template fallback is intentional migration logic so
|
|
231
|
-
* pre-bind entries keep working.
|
|
232
|
-
*
|
|
233
|
-
* Forward-compatibility: every reference to the shortform file location
|
|
234
|
-
* goes through this function. Phase 20 (sandbox migration) redirects this
|
|
235
|
-
* single function; everything downstream (handlers, CLI, studio) works
|
|
236
|
-
* unchanged.
|
|
237
|
-
*
|
|
238
|
-
* @param projectRoot Absolute path to the deskwork project root.
|
|
239
|
-
* @param config Loaded deskwork config.
|
|
240
|
-
* @param site Site slug (or null/undefined for the default site).
|
|
241
|
-
* @param entry Calendar entry — `id` preferred, `slug` used both as the
|
|
242
|
-
* legacy fallback and to identify the entry directory.
|
|
243
|
-
* @param platform Which distribution platform.
|
|
244
|
-
* @param channel Optional sub-channel (e.g. `synthdiy` for r/synthdiy).
|
|
245
|
-
* Must be kebab-case.
|
|
246
|
-
* @param index Optional pre-built content index (per-request memoization).
|
|
247
|
-
* @returns absolute file path, or undefined when neither the index nor the
|
|
248
|
-
* slug-template fallback resolves the entry's directory.
|
|
249
|
-
*/
|
|
250
|
-
export function resolveShortformFilePath(projectRoot, config, site, entry, platform, channel, index) {
|
|
251
|
-
if (channel !== undefined && channel !== '') {
|
|
252
|
-
if (!CHANNEL_RE.test(channel)) {
|
|
253
|
-
throw new Error(`Invalid shortform channel "${channel}": must match ${CHANNEL_RE} ` +
|
|
254
|
-
`(kebab-case, same shape as a slug segment).`);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
const entryFile = findEntryFile(projectRoot, config, site, entry.id ?? '', index, { slug: entry.slug });
|
|
258
|
-
if (entryFile === undefined)
|
|
259
|
-
return undefined;
|
|
260
|
-
const entryDir = dirname(entryFile);
|
|
261
|
-
const filename = channel !== undefined && channel !== ''
|
|
262
|
-
? `${platform}-${channel}.md`
|
|
263
|
-
: `${platform}.md`;
|
|
264
|
-
return join(entryDir, 'scrapbook', 'shortform', filename);
|
|
265
|
-
}
|
|
266
156
|
//# sourceMappingURL=paths.js.map
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Stripping shape: find the first H2 whose text starts with "Outline",
|
|
13
13
|
* then remove that heading plus every subsequent top-level node until
|
|
14
|
-
* the next H1 or H2 (non-inclusive) or the end of the document.
|
|
15
|
-
*
|
|
16
|
-
* because mdast traversal beats regex on structured content.
|
|
14
|
+
* the next H1 or H2 (non-inclusive) or the end of the document. Uses
|
|
15
|
+
* mdast traversal (beats regex on structured content).
|
|
17
16
|
*
|
|
18
17
|
* No-op when the document has no outline section.
|
|
19
18
|
*/
|
package/dist/rename-slug.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Renames the per-post directory at `<contentDir>/<slug>/` to its new
|
|
5
5
|
* name, updates the calendar entry's slug, syncs slug on matching
|
|
6
6
|
* distribution records, and (optionally) appends a 301 redirect block
|
|
7
|
-
* to the
|
|
7
|
+
* to the `_redirects` file named by the entry's lane (`lane.redirectsPath`,
|
|
8
|
+
* Phase 39c c4). UUID identity keeps workflows,
|
|
8
9
|
* distribution records, and journal history joined through `entry.id`
|
|
9
10
|
* across the rename.
|
|
10
11
|
*
|
|
@@ -18,13 +19,12 @@ import type { DeskworkConfig } from './config.ts';
|
|
|
18
19
|
export interface RenameSlugOptions {
|
|
19
20
|
projectRoot: string;
|
|
20
21
|
config: DeskworkConfig;
|
|
21
|
-
site: string;
|
|
22
22
|
oldSlug: string;
|
|
23
23
|
newSlug: string;
|
|
24
24
|
dryRun?: boolean;
|
|
25
25
|
}
|
|
26
26
|
export interface RenameSlugPlanAction {
|
|
27
|
-
kind: 'dir-rename' | 'calendar-slug-change' | 'distribution-slug-sync' | 'redirect-append';
|
|
27
|
+
kind: 'dir-rename' | 'calendar-slug-change' | 'distribution-slug-sync' | 'redirect-append' | 'redirect-skipped';
|
|
28
28
|
summary: string;
|
|
29
29
|
details?: string;
|
|
30
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rename-slug.d.ts","sourceRoot":"","sources":["../src/rename-slug.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rename-slug.d.ts","sourceRoot":"","sources":["../src/rename-slug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AA8ClD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EACA,YAAY,GACZ,sBAAsB,GACtB,wBAAwB,GACxB,iBAAiB,GACjB,kBAAkB,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,MAAM,EAAE,OAAO,CAAC;CACjB;AAID,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI/C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAS3E;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CA8LvE"}
|
package/dist/rename-slug.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* Renames the per-post directory at `<contentDir>/<slug>/` to its new
|
|
5
5
|
* name, updates the calendar entry's slug, syncs slug on matching
|
|
6
6
|
* distribution records, and (optionally) appends a 301 redirect block
|
|
7
|
-
* to the
|
|
7
|
+
* to the `_redirects` file named by the entry's lane (`lane.redirectsPath`,
|
|
8
|
+
* Phase 39c c4). UUID identity keeps workflows,
|
|
8
9
|
* distribution records, and journal history joined through `entry.id`
|
|
9
10
|
* across the rename.
|
|
10
11
|
*
|
|
@@ -16,9 +17,45 @@
|
|
|
16
17
|
*/
|
|
17
18
|
import { existsSync, renameSync, writeFileSync, appendFileSync } from 'node:fs';
|
|
18
19
|
import { join } from 'node:path';
|
|
19
|
-
import {
|
|
20
|
+
import { resolveCalendarPath } from "./paths.js";
|
|
20
21
|
import { readCalendar, writeCalendar } from "./calendar.js";
|
|
21
|
-
import {
|
|
22
|
+
import { readSidecarSync } from "./sidecar/read.js";
|
|
23
|
+
import { writeSidecarSync } from "./sidecar/write.js";
|
|
24
|
+
import { sidecarPath } from "./sidecar/paths.js";
|
|
25
|
+
import { loadLaneConfigSchemaOnly } from "./lanes/loader.js";
|
|
26
|
+
/**
|
|
27
|
+
* Detect a slug rename's filesystem shape from the entry's stored
|
|
28
|
+
* (POSIX, relative) `artifactPath` (Phase 39c-2b(a), spec AUDIT-36).
|
|
29
|
+
*
|
|
30
|
+
* - `…/<slug>/index.md` → move the per-post DIR `<base>/<slug>`
|
|
31
|
+
* - `…/<slug>/README.md` → move the per-post DIR `<base>/<slug>`
|
|
32
|
+
* - `…/<slug>.<ext>` → move the FILE
|
|
33
|
+
*
|
|
34
|
+
* Returns the relative move source + target (dir or file) plus the new
|
|
35
|
+
* relative artifactPath. No naive slug-substring replacement — only the
|
|
36
|
+
* slug segment changes, never a same-named ancestor directory.
|
|
37
|
+
*/
|
|
38
|
+
function planArtifactMove(artifactPath, newSlug) {
|
|
39
|
+
const segments = artifactPath.split('/');
|
|
40
|
+
const filename = segments[segments.length - 1];
|
|
41
|
+
const isDirLayout = filename === 'index.md' || filename === 'README.md';
|
|
42
|
+
if (isDirLayout) {
|
|
43
|
+
// <base>/<slug>/<filename> → move the <slug> dir.
|
|
44
|
+
const slugDirRel = segments.slice(0, -1).join('/'); // <base>/<slug>
|
|
45
|
+
const baseRel = segments.slice(0, -2).join('/'); // <base>
|
|
46
|
+
const toDirRel = baseRel === '' ? newSlug : `${baseRel}/${newSlug}`;
|
|
47
|
+
return {
|
|
48
|
+
fromRel: slugDirRel,
|
|
49
|
+
toRel: toDirRel,
|
|
50
|
+
newArtifactRel: `${toDirRel}/${filename}`,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Flat: <base>/<slug>.<ext> → move the file.
|
|
54
|
+
const ext = filename.slice(filename.lastIndexOf('.'));
|
|
55
|
+
const baseRel = segments.slice(0, -1).join('/');
|
|
56
|
+
const toFileRel = baseRel === '' ? `${newSlug}${ext}` : `${baseRel}/${newSlug}${ext}`;
|
|
57
|
+
return { fromRel: artifactPath, toRel: toFileRel, newArtifactRel: toFileRel };
|
|
58
|
+
}
|
|
22
59
|
const SLUG_RE = /^[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)*$/;
|
|
23
60
|
export function validateSlug(slug) {
|
|
24
61
|
if (!SLUG_RE.test(slug)) {
|
|
@@ -40,29 +77,21 @@ export function buildRedirectBlock(oldSlug, newSlug) {
|
|
|
40
77
|
'',
|
|
41
78
|
].join('\n');
|
|
42
79
|
}
|
|
43
|
-
function siteEntry(config, site) {
|
|
44
|
-
if (!(site in config.sites)) {
|
|
45
|
-
const known = Object.keys(config.sites).join(', ');
|
|
46
|
-
throw new Error(`unknown site "${site}". Configured sites: ${known}`);
|
|
47
|
-
}
|
|
48
|
-
return config.sites[site];
|
|
49
|
-
}
|
|
50
80
|
/**
|
|
51
81
|
* Execute (or dry-run) a slug rename.
|
|
52
82
|
*/
|
|
53
83
|
export function renameSlug(options) {
|
|
54
|
-
const { projectRoot, config,
|
|
84
|
+
const { projectRoot, config, oldSlug, newSlug, dryRun = false } = options;
|
|
55
85
|
validateSlug(oldSlug);
|
|
56
86
|
validateSlug(newSlug);
|
|
57
87
|
if (oldSlug === newSlug) {
|
|
58
88
|
throw new Error('oldSlug and newSlug are identical — nothing to do');
|
|
59
89
|
}
|
|
60
|
-
const
|
|
61
|
-
const calendarPath = resolveCalendarPath(projectRoot, config, site);
|
|
90
|
+
const calendarPath = resolveCalendarPath(projectRoot, config);
|
|
62
91
|
const calendar = readCalendar(calendarPath);
|
|
63
92
|
const entry = calendar.entries.find((e) => e.slug === oldSlug);
|
|
64
93
|
if (!entry) {
|
|
65
|
-
throw new Error(`no calendar entry with slug "${oldSlug}"
|
|
94
|
+
throw new Error(`no calendar entry with slug "${oldSlug}"`);
|
|
66
95
|
}
|
|
67
96
|
if (!entry.id) {
|
|
68
97
|
throw new Error(`entry "${oldSlug}" has no UUID — re-save the calendar to backfill`);
|
|
@@ -72,35 +101,90 @@ export function renameSlug(options) {
|
|
|
72
101
|
throw new Error(`slug "${newSlug}" is already taken by entry ${collision.id ?? '(no id)'} (${collision.title})`);
|
|
73
102
|
}
|
|
74
103
|
const actions = [];
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (existsSync(
|
|
95
|
-
throw
|
|
104
|
+
// 1. Move the artifact. Phase 39c-2b(a) / spec AUDIT-36: the move is
|
|
105
|
+
// derived from the entry's STORED artifactPath (layout-detected) —
|
|
106
|
+
// not a slug-template dir. For an index/README layout the per-post
|
|
107
|
+
// DIR moves (carrying co-located assets in one mv); for a flat
|
|
108
|
+
// layout the FILE moves. The sidecar's artifactPath is rewritten to
|
|
109
|
+
// the new location. No naive slug-substring replacement.
|
|
110
|
+
// AUDIT-20260604-02: an entry with a calendar row but no sidecar file is
|
|
111
|
+
// a drift case like any other — surface the actionable doctor --fix
|
|
112
|
+
// guidance, not the raw `sidecar not found` ENOENT from readSidecarSync.
|
|
113
|
+
// AUDIT-20260604-05: distinguish a MISSING sidecar (file absent) from a
|
|
114
|
+
// CORRUPT one (file present but invalid JSON/schema). A bare catch would
|
|
115
|
+
// misreport corruption as "no sidecar on disk" and send the operator to
|
|
116
|
+
// the wrong remedy — re-throw readSidecarSync's accurate diagnosis when
|
|
117
|
+
// the file actually exists.
|
|
118
|
+
let sidecar;
|
|
119
|
+
try {
|
|
120
|
+
sidecar = readSidecarSync(projectRoot, entry.id);
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
if (existsSync(sidecarPath(projectRoot, entry.id))) {
|
|
124
|
+
throw err;
|
|
96
125
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
126
|
+
throw new Error(`entry "${oldSlug}" (${entry.id}) has a calendar row but no sidecar on ` +
|
|
127
|
+
`disk — run \`deskwork doctor --fix\` to reconcile before renaming.`);
|
|
128
|
+
}
|
|
129
|
+
if (sidecar.artifactPath === undefined) {
|
|
130
|
+
throw new Error(`entry "${oldSlug}" (${entry.id}) has no artifactPath — run ` +
|
|
131
|
+
`\`deskwork doctor --fix\` to backfill it before renaming.`);
|
|
132
|
+
}
|
|
133
|
+
// Resolve the optional 301-redirect target (lane.redirectsPath, spec
|
|
134
|
+
// Decision #23) BEFORE any filesystem/calendar mutation (AUDIT-20260604-08
|
|
135
|
+
// — resolving here, not at the append step, means a resolution failure can
|
|
136
|
+
// never leave a half-applied rename).
|
|
137
|
+
//
|
|
138
|
+
// The redirect lives on the lane, but the pipeline template is ORTHOGONAL
|
|
139
|
+
// to it: read the lane SCHEMA-ONLY (AUDIT-20260604-19) so a valid
|
|
140
|
+
// `redirectsPath` still appends when the lane's pipeline is mid-edit /
|
|
141
|
+
// renamed / deleted. `loadLaneConfig` would cross-validate the pipeline and
|
|
142
|
+
// throw — silently dropping a perfectly valid redirect because an unrelated
|
|
143
|
+
// field didn't resolve (the SEO regression AUDIT-17/19 filed).
|
|
144
|
+
//
|
|
145
|
+
// When the lane cannot be consulted at all — config absent (archived /
|
|
146
|
+
// purged / legacy-stale) or unreadable/corrupt — the optional redirect is
|
|
147
|
+
// skipped, but VISIBLY: a `redirect-skipped` action records the reason so
|
|
148
|
+
// the omission surfaces in the result the caller renders (AUDIT-20260604-19
|
|
149
|
+
// — no silent drop, no phantom "attributability"). A renamed entry whose
|
|
150
|
+
// lane is gone is still a valid rename; the broken lane is surfaced by the
|
|
151
|
+
// lane-config doctor rules, not by crashing the rename.
|
|
152
|
+
let redirectsPath;
|
|
153
|
+
if (sidecar.lane !== undefined) {
|
|
154
|
+
try {
|
|
155
|
+
redirectsPath = loadLaneConfigSchemaOnly(sidecar.lane, projectRoot).redirectsPath;
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
159
|
+
actions.push({
|
|
160
|
+
kind: 'redirect-skipped',
|
|
161
|
+
summary: `skipped 301 redirect — lane "${sidecar.lane}" could not be read`,
|
|
162
|
+
details: reason,
|
|
163
|
+
});
|
|
164
|
+
redirectsPath = undefined;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const move = planArtifactMove(sidecar.artifactPath, newSlug);
|
|
168
|
+
const fromAbs = join(projectRoot, move.fromRel);
|
|
169
|
+
const toAbs = join(projectRoot, move.toRel);
|
|
170
|
+
if (!existsSync(fromAbs)) {
|
|
171
|
+
throw new Error(`entry "${oldSlug}" artifactPath ${sidecar.artifactPath} does not exist on disk at ` +
|
|
172
|
+
`${fromAbs}. The calendar/sidecar has drifted from disk — reconcile before renaming.`);
|
|
173
|
+
}
|
|
174
|
+
if (existsSync(toAbs)) {
|
|
175
|
+
throw new Error(`target already exists: ${toAbs}`);
|
|
176
|
+
}
|
|
177
|
+
actions.push({
|
|
178
|
+
kind: 'dir-rename',
|
|
179
|
+
summary: 'move post artifact',
|
|
180
|
+
details: `${fromAbs}\n → ${toAbs}`,
|
|
181
|
+
});
|
|
182
|
+
if (!dryRun) {
|
|
183
|
+
renameSync(fromAbs, toAbs);
|
|
184
|
+
writeSidecarSync(projectRoot, {
|
|
185
|
+
...sidecar,
|
|
186
|
+
artifactPath: move.newArtifactRel,
|
|
101
187
|
});
|
|
102
|
-
if (!dryRun)
|
|
103
|
-
renameSync(oldDir, newDir);
|
|
104
188
|
}
|
|
105
189
|
// 2. Calendar entry slug change
|
|
106
190
|
actions.push({
|
|
@@ -129,9 +213,18 @@ export function renameSlug(options) {
|
|
|
129
213
|
if (!dryRun) {
|
|
130
214
|
writeCalendar(calendarPath, calendar);
|
|
131
215
|
}
|
|
132
|
-
// 4. _redirects append (when
|
|
133
|
-
|
|
134
|
-
|
|
216
|
+
// 4. _redirects append (when the entry's LANE has redirectsPath
|
|
217
|
+
// configured). Phase 39c (c4 / spec Decision #23): `redirectsPath`
|
|
218
|
+
// re-homed from the retired `SiteConfig` onto the lane (an optional
|
|
219
|
+
// sibling of `lane.host`), mirroring Decision #2's `host` re-home.
|
|
220
|
+
// The renamed entry's lane is resolved from its sidecar's `lane`
|
|
221
|
+
// field. When the sidecar carries no lane, or the lane declares no
|
|
222
|
+
// redirectsPath, the append is SKIPPED — this is optional
|
|
223
|
+
// website-publishing metadata; skipping is the correct unset
|
|
224
|
+
// behavior, NOT an error (a collection without a renderer is valid).
|
|
225
|
+
// `redirectsPath` was resolved before the mutation steps (see above).
|
|
226
|
+
if (redirectsPath !== undefined) {
|
|
227
|
+
const redirectsFile = join(projectRoot, redirectsPath);
|
|
135
228
|
const block = buildRedirectBlock(oldSlug, newSlug);
|
|
136
229
|
actions.push({
|
|
137
230
|
kind: 'redirect-append',
|
package/dist/rename-slug.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rename-slug.js","sourceRoot":"","sources":["../src/rename-slug.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rename-slug.js","sourceRoot":"","sources":["../src/rename-slug.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAE7D;;;;;;;;;;;GAWG;AACH,SAAS,gBAAgB,CACvB,YAAoB,EACpB,OAAe;IAEf,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/C,MAAM,WAAW,GAAG,QAAQ,KAAK,UAAU,IAAI,QAAQ,KAAK,WAAW,CAAC;IACxE,IAAI,WAAW,EAAE,CAAC;QAChB,kDAAkD;QAClD,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB;QACpE,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;QAC1D,MAAM,QAAQ,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,OAAO,EAAE,CAAC;QACpE,OAAO;YACL,OAAO,EAAE,UAAU;YACnB,KAAK,EAAE,QAAQ;YACf,cAAc,EAAE,GAAG,QAAQ,IAAI,QAAQ,EAAE;SAC1C,CAAC;IACJ,CAAC;IACD,6CAA6C;IAC7C,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,SAAS,GACb,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,OAAO,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;IACtE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,CAAC;AAChF,CAAC;AA6BD,MAAM,OAAO,GAAG,6CAA6C,CAAC;AAE9D,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,kBAAkB,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,OAAe;IACjE,OAAO;QACL,EAAE;QACF,wBAAwB,OAAO,aAAa,OAAO,GAAG;QACtD,SAAS,OAAO,iBAAiB,OAAO,gBAAgB;QACxD,SAAS,OAAO,iBAAiB,OAAO,gBAAgB;QACxD,SAAS,OAAO,iBAAiB,OAAO,gBAAgB;QACxD,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,OAA0B;IACnD,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1E,YAAY,CAAC,OAAO,CAAC,CAAC;IACtB,YAAY,CAAC,OAAO,CAAC,CAAC;IACtB,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;IAC/D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,gCAAgC,OAAO,GAAG,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,UAAU,OAAO,kDAAkD,CACpE,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CACrC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAC/C,CAAC;IACF,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CACb,SAAS,OAAO,+BAA+B,SAAS,CAAC,EAAE,IAAI,SAAS,KAAK,SAAS,CAAC,KAAK,GAAG,CAChG,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAA2B,EAAE,CAAC;IAE3C,qEAAqE;IACrE,sEAAsE;IACtE,sEAAsE;IACtE,kEAAkE;IAClE,uEAAuE;IACvE,4DAA4D;IAC5D,yEAAyE;IACzE,oEAAoE;IACpE,yEAAyE;IACzE,wEAAwE;IACxE,yEAAyE;IACzE,wEAAwE;IACxE,wEAAwE;IACxE,4BAA4B;IAC5B,IAAI,OAAO,CAAC;IACZ,IAAI,CAAC;QACH,OAAO,GAAG,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,UAAU,CAAC,WAAW,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;YACnD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,IAAI,KAAK,CACb,UAAU,OAAO,MAAM,KAAK,CAAC,EAAE,yCAAyC;YACtE,oEAAoE,CACvE,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CACb,UAAU,OAAO,MAAM,KAAK,CAAC,EAAE,8BAA8B;YAC3D,2DAA2D,CAC9D,CAAC;IACJ,CAAC;IAED,qEAAqE;IACrE,2EAA2E;IAC3E,2EAA2E;IAC3E,sCAAsC;IACtC,EAAE;IACF,0EAA0E;IAC1E,kEAAkE;IAClE,uEAAuE;IACvE,4EAA4E;IAC5E,4EAA4E;IAC5E,+DAA+D;IAC/D,EAAE;IACF,uEAAuE;IACvE,0EAA0E;IAC1E,0EAA0E;IAC1E,4EAA4E;IAC5E,yEAAyE;IACzE,2EAA2E;IAC3E,wDAAwD;IACxD,IAAI,aAAiC,CAAC;IACtC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,aAAa,GAAG,wBAAwB,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,aAAa,CAAC;QACpF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,gCAAgC,OAAO,CAAC,IAAI,qBAAqB;gBAC1E,OAAO,EAAE,MAAM;aAChB,CAAC,CAAC;YACH,aAAa,GAAG,SAAS,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,UAAU,OAAO,kBAAkB,OAAO,CAAC,YAAY,6BAA6B;YAClF,GAAG,OAAO,2EAA2E,CACxF,CAAC;IACJ,CAAC;IACD,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,oBAAoB;QAC7B,OAAO,EAAE,GAAG,OAAO,gBAAgB,KAAK,EAAE;KAC3C,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC3B,gBAAgB,CAAC,WAAW,EAAE;YAC5B,GAAG,OAAO;YACV,YAAY,EAAE,IAAI,CAAC,cAAc;SAClC,CAAC,CAAC;IACL,CAAC;IAED,gCAAgC;IAChC,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,sBAAsB;QAC5B,OAAO,EAAE,yBAAyB,OAAO,QAAQ,OAAO,GAAG;QAC3D,OAAO,EAAE,YAAY,KAAK,CAAC,EAAE,wDAAwD;KACtF,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,GAAG,OAAO,CAAC;IACvB,CAAC;IAED,+DAA+D;IAC/D,MAAM,qBAAqB,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CACzD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,EAAE,CAC9B,CAAC;IACF,IAAI,qBAAqB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,wBAAwB;YAC9B,OAAO,EAAE,gBAAgB,qBAAqB,CAAC,MAAM,yBAAyB;YAC9E,OAAO,EAAE,qBAAqB;iBAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,OAAO,GAAG,CAAC;iBACpF,IAAI,CAAC,IAAI,CAAC;SACd,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,KAAK,MAAM,CAAC,IAAI,qBAAqB;gBAAE,CAAC,CAAC,IAAI,GAAG,OAAO,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,aAAa,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,gEAAgE;IAChE,sEAAsE;IACtE,uEAAuE;IACvE,sEAAsE;IACtE,oEAAoE;IACpE,sEAAsE;IACtE,6DAA6D;IAC7D,gEAAgE;IAChE,wEAAwE;IACxE,sEAAsE;IACtE,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACnD,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,yCAAyC;YAClD,OAAO,EAAE,WAAW,aAAa,KAAK,KAAK;iBACxC,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC;iBAC3B,IAAI,CAAC,IAAI,CAAC,EAAE;SAChB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC/B,aAAa,CAAC,aAAa,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,cAAc,CAAC,aAAa,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,EAAE;QACjB,OAAO;QACP,OAAO;QACP,OAAO;QACP,MAAM;KACP,CAAC;AACJ,CAAC"}
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* { status, body } results that routes serialize as JSON.
|
|
5
5
|
*
|
|
6
6
|
* Ported from audiocontrol.org's scripts/lib/editorial-review/handlers.ts.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Phase 39c-2b(a): the longform + shortform file paths now resolve from
|
|
8
|
+
* the entry's stored `artifactPath` (via `review/workflow-paths.ts`), not
|
|
9
|
+
* the slug-template — see that module.
|
|
10
10
|
*/
|
|
11
11
|
import type { DeskworkConfig } from '../config.ts';
|
|
12
12
|
import { type HandlerResult } from './result.ts';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/review/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAanD,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE1D,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAUhF,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,aAAa,CAoJf;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC3D,aAAa,CAWf;AAOD,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,aAAa,CAaf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE;IACL,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,GACA,aAAa,
|
|
1
|
+
{"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/review/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAanD,OAAO,EAAW,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE1D,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAUhF,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,aAAa,CAoJf;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC3D,aAAa,CAWf;AAOD,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,aAAa,CAaf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE;IACL,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,GACA,aAAa,CAmDf;AAQD;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,OAAO,GACZ,aAAa,CA6Df;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBrD;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQlD"}
|
package/dist/review/handlers.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* { status, body } results that routes serialize as JSON.
|
|
5
5
|
*
|
|
6
6
|
* Ported from audiocontrol.org's scripts/lib/editorial-review/handlers.ts.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Phase 39c-2b(a): the longform + shortform file paths now resolve from
|
|
8
|
+
* the entry's stored `artifactPath` (via `review/workflow-paths.ts`), not
|
|
9
|
+
* the slug-template — see that module.
|
|
10
10
|
*/
|
|
11
11
|
import { existsSync, writeFileSync } from 'node:fs';
|
|
12
12
|
import { appendAnnotation, appendVersion, mintAnnotation, readAnnotations, readVersions, readWorkflow, readWorkflows, transitionState, } from "./pipeline.js";
|
|
@@ -212,10 +212,9 @@ export function handleGetWorkflow(projectRoot, config, query) {
|
|
|
212
212
|
if (!query.entryId && (!query.site || !query.slug)) {
|
|
213
213
|
return err(400, 'either id, entryId, or (site & slug) query params are required');
|
|
214
214
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
215
|
+
// Phase 39c c3 (spec Decision #22): `site` is an opaque recorded label,
|
|
216
|
+
// no longer validated against `config.sites`. An unrecognized site for
|
|
217
|
+
// which no workflow matches falls through to the existing 404 below.
|
|
219
218
|
const contentKind = (query.contentKind ?? 'longform');
|
|
220
219
|
const candidates = readWorkflows(projectRoot, config).filter((w) => {
|
|
221
220
|
// Stable-identity join when entryId is present on both sides;
|
|
@@ -279,13 +278,18 @@ export function handleCreateVersion(projectRoot, config, body) {
|
|
|
279
278
|
// shortform) are disk-backed — Phase 21a removed the shortform special
|
|
280
279
|
// case so the studio's save/iterate/approve handlers see one shape.
|
|
281
280
|
//
|
|
282
|
-
//
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
281
|
+
// Phase 39c-2b(a): workflowFilePath resolves via the entry's stored
|
|
282
|
+
// artifactPath and throws doctor --fix for an unmigrated entry — surface
|
|
283
|
+
// that as a save error rather than crashing the route handler.
|
|
284
|
+
let targetFile;
|
|
285
|
+
try {
|
|
286
|
+
targetFile = workflowFilePath(projectRoot, config, workflow);
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
return err(500, e instanceof Error ? e.message : String(e));
|
|
290
|
+
}
|
|
291
|
+
if (!existsSync(targetFile)) {
|
|
292
|
+
const shown = targetFile;
|
|
289
293
|
if (workflow.contentKind === 'shortform') {
|
|
290
294
|
return err(500, `cannot save: shortform file missing at ${shown}. ` +
|
|
291
295
|
`Run /deskwork:shortform-start to scaffold the file before saving edits.`);
|