@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,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classify whether a scaffolded blog post's body is still the placeholder
|
|
3
|
+
* or has been replaced with real prose.
|
|
4
|
+
*
|
|
5
|
+
* The scaffold produced by `scaffoldBlogPost` writes an H1, optionally a
|
|
6
|
+
* `## Outline` section, and a `<!-- Write your post here -->` placeholder.
|
|
7
|
+
* The outline is a legitimate authored artifact during Outlining; its
|
|
8
|
+
* presence must NOT make the body look written. This helper strips the
|
|
9
|
+
* outline and classifies what remains:
|
|
10
|
+
*
|
|
11
|
+
* - `missing` — file does not exist
|
|
12
|
+
* - `placeholder` — file exists but body (minus outline) is only the
|
|
13
|
+
* placeholder marker and whitespace
|
|
14
|
+
* - `written` — file exists and prose remains after placeholder + outline
|
|
15
|
+
* are accounted for
|
|
16
|
+
*
|
|
17
|
+
* Used by the review/studio surfaces to branch on whether a post has
|
|
18
|
+
* actually been drafted, not just scaffolded.
|
|
19
|
+
*/
|
|
20
|
+
export type BodyState = 'missing' | 'placeholder' | 'written';
|
|
21
|
+
/** The body-placeholder marker written by scaffoldBlogPost. Exact string. */
|
|
22
|
+
export declare const PLACEHOLDER_MARKER = "<!-- Write your post here -->";
|
|
23
|
+
/**
|
|
24
|
+
* Classify the body of a scaffolded blog post at `filePath`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function bodyState(filePath: string): BodyState;
|
|
27
|
+
//# sourceMappingURL=body-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-state.d.ts","sourceRoot":"","sources":["../src/body-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;AAE9D,6EAA6E;AAC7E,eAAO,MAAM,kBAAkB,kCAAkC,CAAC;AAoBlE;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAkBrD"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classify whether a scaffolded blog post's body is still the placeholder
|
|
3
|
+
* or has been replaced with real prose.
|
|
4
|
+
*
|
|
5
|
+
* The scaffold produced by `scaffoldBlogPost` writes an H1, optionally a
|
|
6
|
+
* `## Outline` section, and a `<!-- Write your post here -->` placeholder.
|
|
7
|
+
* The outline is a legitimate authored artifact during Outlining; its
|
|
8
|
+
* presence must NOT make the body look written. This helper strips the
|
|
9
|
+
* outline and classifies what remains:
|
|
10
|
+
*
|
|
11
|
+
* - `missing` — file does not exist
|
|
12
|
+
* - `placeholder` — file exists but body (minus outline) is only the
|
|
13
|
+
* placeholder marker and whitespace
|
|
14
|
+
* - `written` — file exists and prose remains after placeholder + outline
|
|
15
|
+
* are accounted for
|
|
16
|
+
*
|
|
17
|
+
* Used by the review/studio surfaces to branch on whether a post has
|
|
18
|
+
* actually been drafted, not just scaffolded.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
21
|
+
/** The body-placeholder marker written by scaffoldBlogPost. Exact string. */
|
|
22
|
+
export const PLACEHOLDER_MARKER = '<!-- Write your post here -->';
|
|
23
|
+
/**
|
|
24
|
+
* Strip the `## Outline` section (heading through the next H2 or EOF)
|
|
25
|
+
* so its content doesn't masquerade as body prose.
|
|
26
|
+
*/
|
|
27
|
+
function stripOutlineSection(body) {
|
|
28
|
+
const lines = body.split('\n');
|
|
29
|
+
const startIdx = lines.findIndex((line) => /^##[ \t]+Outline\b/.test(line));
|
|
30
|
+
if (startIdx < 0)
|
|
31
|
+
return body;
|
|
32
|
+
let endIdx = lines.length;
|
|
33
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
34
|
+
if (/^##[ \t]+/.test(lines[i])) {
|
|
35
|
+
endIdx = i;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return [...lines.slice(0, startIdx), ...lines.slice(endIdx)].join('\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Classify the body of a scaffolded blog post at `filePath`.
|
|
43
|
+
*/
|
|
44
|
+
export function bodyState(filePath) {
|
|
45
|
+
if (!existsSync(filePath))
|
|
46
|
+
return 'missing';
|
|
47
|
+
const content = readFileSync(filePath, 'utf8');
|
|
48
|
+
// `\r?\n` mirrors `frontmatter.ts`'s FRONTMATTER_RE so files saved
|
|
49
|
+
// with Windows line endings classify the same way as `\n`-only files.
|
|
50
|
+
const fmMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
51
|
+
const body = fmMatch ? content.slice(fmMatch[0].length) : content;
|
|
52
|
+
const withoutH1 = body.replace(/^\s*#[^\n]*\n?/, '');
|
|
53
|
+
const withoutOutline = stripOutlineSection(withoutH1);
|
|
54
|
+
const trimmed = withoutOutline.trim();
|
|
55
|
+
if (trimmed === PLACEHOLDER_MARKER)
|
|
56
|
+
return 'placeholder';
|
|
57
|
+
if (trimmed === '')
|
|
58
|
+
return 'placeholder';
|
|
59
|
+
const withoutPlaceholder = trimmed.replace(PLACEHOLDER_MARKER, '').trim();
|
|
60
|
+
return withoutPlaceholder.length > 0 ? 'written' : 'placeholder';
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=body-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-state.js","sourceRoot":"","sources":["../src/body-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAInD,6EAA6E;AAC7E,MAAM,CAAC,MAAM,kBAAkB,GAAG,+BAA+B,CAAC;AAElE;;;GAGG;AACH,SAAS,mBAAmB,CAAC,IAAY;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5E,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,MAAM,GAAG,CAAC,CAAC;YACX,MAAM;QACR,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE/C,mEAAmE;IACnE,sEAAsE;IACtE,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAElE,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;IACrD,MAAM,cAAc,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAEtD,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC;IACtC,IAAI,OAAO,KAAK,kBAAkB;QAAE,OAAO,aAAa,CAAC;IACzD,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,aAAa,CAAC;IAEzC,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,OAAO,kBAAkB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure mutations on an in-memory EditorialCalendar.
|
|
3
|
+
*
|
|
4
|
+
* None of these touch disk — callers parse the calendar, mutate, then write
|
|
5
|
+
* it back. Each mutation validates stage invariants (an entry must be in
|
|
6
|
+
* Planned to be drafted, etc.) and throws a descriptive Error if violated.
|
|
7
|
+
*/
|
|
8
|
+
import { type CalendarEntry, type ContentType, type DistributionRecord, type EditorialCalendar, type Platform } from './types.ts';
|
|
9
|
+
/** Convert a title to a URL-safe slug. */
|
|
10
|
+
export declare function slugify(title: string): string;
|
|
11
|
+
/** Add a new entry to the Ideas stage. */
|
|
12
|
+
export declare function addEntry(calendar: EditorialCalendar, title: string, opts?: {
|
|
13
|
+
description?: string;
|
|
14
|
+
source?: CalendarEntry['source'];
|
|
15
|
+
contentType?: ContentType;
|
|
16
|
+
contentUrl?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Explicit slug override. Use this to capture hierarchical entries
|
|
19
|
+
* (e.g. "the-outbound/characters/strivers") whose slug shouldn't be
|
|
20
|
+
* derived from the title. When omitted, the slug is `slugify(title)`.
|
|
21
|
+
*/
|
|
22
|
+
slug?: string;
|
|
23
|
+
}): CalendarEntry;
|
|
24
|
+
/**
|
|
25
|
+
* Find a calendar entry by its stable UUID. Prefer this over `findEntry`
|
|
26
|
+
* (slug lookup) anywhere the caller has an entry already and wants the
|
|
27
|
+
* join to survive a future slug rename.
|
|
28
|
+
*/
|
|
29
|
+
export declare function findEntryById(calendar: EditorialCalendar, id: string): CalendarEntry | undefined;
|
|
30
|
+
/** Move an entry to Planned and set target keywords (and optionally topics). */
|
|
31
|
+
export declare function planEntry(calendar: EditorialCalendar, slug: string, keywords: string[], opts?: {
|
|
32
|
+
topics?: string[];
|
|
33
|
+
}): CalendarEntry;
|
|
34
|
+
/**
|
|
35
|
+
* Set or clear an entry's `contentUrl`. Used when late-setting a URL on a
|
|
36
|
+
* youtube or tool entry before publishing. Pass `undefined` to unset.
|
|
37
|
+
*/
|
|
38
|
+
export declare function setContentUrl(calendar: EditorialCalendar, slug: string, url: string | undefined): CalendarEntry;
|
|
39
|
+
/**
|
|
40
|
+
* Move an entry to Outlining. Precondition: Planned.
|
|
41
|
+
*
|
|
42
|
+
* The outline skill scaffolds the blog file (for blog entries) and
|
|
43
|
+
* advances the entry to this stage; an outline review can happen
|
|
44
|
+
* before the entry moves on to Drafting.
|
|
45
|
+
*/
|
|
46
|
+
export declare function outlineEntry(calendar: EditorialCalendar, slug: string): CalendarEntry;
|
|
47
|
+
/**
|
|
48
|
+
* Move an entry to Drafting. Precondition: Outlining (the approved
|
|
49
|
+
* outline is the handoff into body-drafting). The issueNumber
|
|
50
|
+
* argument records a previously-created GitHub issue if the caller
|
|
51
|
+
* opened one; the helper does not call gh itself.
|
|
52
|
+
*/
|
|
53
|
+
export declare function draftEntry(calendar: EditorialCalendar, slug: string, issueNumber?: number): CalendarEntry;
|
|
54
|
+
/**
|
|
55
|
+
* Move an entry to `Paused`, recording its prior stage so `unpauseEntry`
|
|
56
|
+
* can restore it. Refuses to pause an already-Paused entry (would lose
|
|
57
|
+
* the original `pausedFrom`) and refuses to pause a `Published` entry
|
|
58
|
+
* (terminal; a shipped post can't be "in progress again"). See #27.
|
|
59
|
+
*/
|
|
60
|
+
export declare function pauseEntry(calendar: EditorialCalendar, slug: string): CalendarEntry;
|
|
61
|
+
/**
|
|
62
|
+
* Restore a `Paused` entry to its `pausedFrom` stage. Throws if the
|
|
63
|
+
* entry isn't Paused, or if `pausedFrom` is missing (legacy / corrupt
|
|
64
|
+
* state — operator must move the entry by hand). See #27.
|
|
65
|
+
*/
|
|
66
|
+
export declare function unpauseEntry(calendar: EditorialCalendar, slug: string): CalendarEntry;
|
|
67
|
+
/** Mark an entry Published with the given date (defaults to today). */
|
|
68
|
+
export declare function publishEntry(calendar: EditorialCalendar, slug: string, datePublished?: string): CalendarEntry;
|
|
69
|
+
/** Find an entry by slug. */
|
|
70
|
+
export declare function findEntry(calendar: EditorialCalendar, slug: string): CalendarEntry | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* CLI-arg friendly lookup: tries `id` first (stable across slug
|
|
73
|
+
* renames), falls back to `slug`. Use this anywhere an operator-typed
|
|
74
|
+
* argument might be either form — e.g. `deskwork doctor --entry <X>`.
|
|
75
|
+
*
|
|
76
|
+
* Returns `undefined` when neither match. Empty / whitespace-only
|
|
77
|
+
* input also returns undefined.
|
|
78
|
+
*/
|
|
79
|
+
export declare function findEntryBySlugOrId(calendar: EditorialCalendar, slugOrId: string): CalendarEntry | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Append a distribution record for a published post. The referenced entry
|
|
82
|
+
* must exist and be in the Published stage — we don't record shares for
|
|
83
|
+
* posts that haven't shipped yet.
|
|
84
|
+
*
|
|
85
|
+
* Resolves by entryId when present (stable) or falls back to slug
|
|
86
|
+
* (legacy callers). On success, stamps entryId onto the record so
|
|
87
|
+
* downstream joins use the stable identity even if the slug later
|
|
88
|
+
* changes.
|
|
89
|
+
*
|
|
90
|
+
* Phase 21a: `record.url` may be an empty string at creation time. The
|
|
91
|
+
* shortform flow `addDistribution`s a placeholder record (URL gets filled
|
|
92
|
+
* in by `updateDistributionUrl` once the operator has posted to the
|
|
93
|
+
* platform). The DistributionRecord.url type stays `string` — there is
|
|
94
|
+
* no runtime non-empty check, so the existing surface is unchanged.
|
|
95
|
+
*/
|
|
96
|
+
export declare function addDistribution(calendar: EditorialCalendar, record: DistributionRecord): DistributionRecord;
|
|
97
|
+
/**
|
|
98
|
+
* Set / update the URL on a distribution record. Used after the operator
|
|
99
|
+
* has manually posted a shortform to the platform and obtained the share
|
|
100
|
+
* URL.
|
|
101
|
+
*
|
|
102
|
+
* Match precedence: `entryId` (stable) → `(slug, platform, channel?)`
|
|
103
|
+
* (legacy fallback). When no record exists yet the helper creates one via
|
|
104
|
+
* `addDistribution` so the URL becomes the first thing recorded for that
|
|
105
|
+
* distribution.
|
|
106
|
+
*
|
|
107
|
+
* Channel comparison is case-insensitive (matches the existing approve
|
|
108
|
+
* helper's behavior). Pass `channel: undefined` to match a record that
|
|
109
|
+
* has no channel set.
|
|
110
|
+
*
|
|
111
|
+
* Only fields explicitly supplied are written — leaving `notes`
|
|
112
|
+
* undefined preserves any existing note. `dateShared` defaults to today
|
|
113
|
+
* (UTC) when omitted, both for new records and as the explicit set when
|
|
114
|
+
* updating an existing one.
|
|
115
|
+
*
|
|
116
|
+
* @returns the updated DistributionRecord (always present after this call).
|
|
117
|
+
*/
|
|
118
|
+
export declare function updateDistributionUrl(calendar: EditorialCalendar, match: {
|
|
119
|
+
entryId?: string;
|
|
120
|
+
slug?: string;
|
|
121
|
+
platform: Platform;
|
|
122
|
+
channel?: string;
|
|
123
|
+
}, url: string, dateShared?: string, notes?: string): DistributionRecord;
|
|
124
|
+
//# sourceMappingURL=calendar-mutations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-mutations.d.ts","sourceRoot":"","sources":["../src/calendar-mutations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure mutations on an in-memory EditorialCalendar.
|
|
3
|
+
*
|
|
4
|
+
* None of these touch disk — callers parse the calendar, mutate, then write
|
|
5
|
+
* it back. Each mutation validates stage invariants (an entry must be in
|
|
6
|
+
* Planned to be drafted, etc.) and throws a descriptive Error if violated.
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import { isPausable, } from "./types.js";
|
|
10
|
+
/** Convert a title to a URL-safe slug. */
|
|
11
|
+
export function slugify(title) {
|
|
12
|
+
return title
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
15
|
+
.replace(/^-|-$/g, '');
|
|
16
|
+
}
|
|
17
|
+
/** Add a new entry to the Ideas stage. */
|
|
18
|
+
export function addEntry(calendar, title, opts) {
|
|
19
|
+
const slug = opts?.slug ?? slugify(title);
|
|
20
|
+
const existing = calendar.entries.find((e) => e.slug === slug);
|
|
21
|
+
if (existing) {
|
|
22
|
+
throw new Error(`Entry with slug "${slug}" already exists in stage "${existing.stage}"`);
|
|
23
|
+
}
|
|
24
|
+
const entry = {
|
|
25
|
+
id: randomUUID(),
|
|
26
|
+
slug,
|
|
27
|
+
title,
|
|
28
|
+
description: opts?.description ?? '',
|
|
29
|
+
stage: 'Ideas',
|
|
30
|
+
targetKeywords: [],
|
|
31
|
+
source: opts?.source ?? 'manual',
|
|
32
|
+
};
|
|
33
|
+
if (opts?.contentType !== undefined)
|
|
34
|
+
entry.contentType = opts.contentType;
|
|
35
|
+
if (opts?.contentUrl !== undefined && opts.contentUrl.length > 0) {
|
|
36
|
+
entry.contentUrl = opts.contentUrl;
|
|
37
|
+
}
|
|
38
|
+
calendar.entries.push(entry);
|
|
39
|
+
return entry;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Find a calendar entry by its stable UUID. Prefer this over `findEntry`
|
|
43
|
+
* (slug lookup) anywhere the caller has an entry already and wants the
|
|
44
|
+
* join to survive a future slug rename.
|
|
45
|
+
*/
|
|
46
|
+
export function findEntryById(calendar, id) {
|
|
47
|
+
if (!id)
|
|
48
|
+
return undefined;
|
|
49
|
+
return calendar.entries.find((e) => e.id === id);
|
|
50
|
+
}
|
|
51
|
+
/** Move an entry to Planned and set target keywords (and optionally topics). */
|
|
52
|
+
export function planEntry(calendar, slug, keywords, opts) {
|
|
53
|
+
const entry = calendar.entries.find((e) => e.slug === slug);
|
|
54
|
+
if (!entry) {
|
|
55
|
+
throw new Error(`No calendar entry found with slug: ${slug}`);
|
|
56
|
+
}
|
|
57
|
+
if (entry.stage !== 'Ideas') {
|
|
58
|
+
throw new Error(`Entry "${slug}" is in stage "${entry.stage}" — must be in Ideas to plan`);
|
|
59
|
+
}
|
|
60
|
+
entry.stage = 'Planned';
|
|
61
|
+
entry.targetKeywords = keywords;
|
|
62
|
+
if (opts?.topics !== undefined && opts.topics.length > 0) {
|
|
63
|
+
entry.topics = opts.topics;
|
|
64
|
+
}
|
|
65
|
+
return entry;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Set or clear an entry's `contentUrl`. Used when late-setting a URL on a
|
|
69
|
+
* youtube or tool entry before publishing. Pass `undefined` to unset.
|
|
70
|
+
*/
|
|
71
|
+
export function setContentUrl(calendar, slug, url) {
|
|
72
|
+
const entry = calendar.entries.find((e) => e.slug === slug);
|
|
73
|
+
if (!entry) {
|
|
74
|
+
throw new Error(`No calendar entry found with slug: ${slug}`);
|
|
75
|
+
}
|
|
76
|
+
if (url === undefined || url.length === 0) {
|
|
77
|
+
delete entry.contentUrl;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
entry.contentUrl = url;
|
|
81
|
+
}
|
|
82
|
+
return entry;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Move an entry to Outlining. Precondition: Planned.
|
|
86
|
+
*
|
|
87
|
+
* The outline skill scaffolds the blog file (for blog entries) and
|
|
88
|
+
* advances the entry to this stage; an outline review can happen
|
|
89
|
+
* before the entry moves on to Drafting.
|
|
90
|
+
*/
|
|
91
|
+
export function outlineEntry(calendar, slug) {
|
|
92
|
+
const entry = calendar.entries.find((e) => e.slug === slug);
|
|
93
|
+
if (!entry) {
|
|
94
|
+
throw new Error(`No calendar entry found with slug: ${slug}`);
|
|
95
|
+
}
|
|
96
|
+
if (entry.stage !== 'Planned') {
|
|
97
|
+
throw new Error(`Entry "${slug}" is in stage "${entry.stage}" — must be in Planned to outline`);
|
|
98
|
+
}
|
|
99
|
+
entry.stage = 'Outlining';
|
|
100
|
+
return entry;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Move an entry to Drafting. Precondition: Outlining (the approved
|
|
104
|
+
* outline is the handoff into body-drafting). The issueNumber
|
|
105
|
+
* argument records a previously-created GitHub issue if the caller
|
|
106
|
+
* opened one; the helper does not call gh itself.
|
|
107
|
+
*/
|
|
108
|
+
export function draftEntry(calendar, slug, issueNumber) {
|
|
109
|
+
const entry = calendar.entries.find((e) => e.slug === slug);
|
|
110
|
+
if (!entry) {
|
|
111
|
+
throw new Error(`No calendar entry found with slug: ${slug}`);
|
|
112
|
+
}
|
|
113
|
+
if (entry.stage !== 'Outlining') {
|
|
114
|
+
throw new Error(`Entry "${slug}" is in stage "${entry.stage}" — must be in Outlining to draft`);
|
|
115
|
+
}
|
|
116
|
+
entry.stage = 'Drafting';
|
|
117
|
+
if (issueNumber !== undefined) {
|
|
118
|
+
entry.issueNumber = issueNumber;
|
|
119
|
+
}
|
|
120
|
+
return entry;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Move an entry to `Paused`, recording its prior stage so `unpauseEntry`
|
|
124
|
+
* can restore it. Refuses to pause an already-Paused entry (would lose
|
|
125
|
+
* the original `pausedFrom`) and refuses to pause a `Published` entry
|
|
126
|
+
* (terminal; a shipped post can't be "in progress again"). See #27.
|
|
127
|
+
*/
|
|
128
|
+
export function pauseEntry(calendar, slug) {
|
|
129
|
+
const entry = calendar.entries.find((e) => e.slug === slug);
|
|
130
|
+
if (!entry) {
|
|
131
|
+
throw new Error(`No calendar entry found with slug: ${slug}`);
|
|
132
|
+
}
|
|
133
|
+
if (entry.stage === 'Paused') {
|
|
134
|
+
throw new Error(`Entry "${slug}" is already Paused.`);
|
|
135
|
+
}
|
|
136
|
+
if (!isPausable(entry.stage)) {
|
|
137
|
+
throw new Error(`Entry "${slug}" is in stage "${entry.stage}" — only non-terminal stages (Ideas / Planned / Outlining / Drafting / Review) can be paused.`);
|
|
138
|
+
}
|
|
139
|
+
entry.pausedFrom = entry.stage;
|
|
140
|
+
entry.stage = 'Paused';
|
|
141
|
+
return entry;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Restore a `Paused` entry to its `pausedFrom` stage. Throws if the
|
|
145
|
+
* entry isn't Paused, or if `pausedFrom` is missing (legacy / corrupt
|
|
146
|
+
* state — operator must move the entry by hand). See #27.
|
|
147
|
+
*/
|
|
148
|
+
export function unpauseEntry(calendar, slug) {
|
|
149
|
+
const entry = calendar.entries.find((e) => e.slug === slug);
|
|
150
|
+
if (!entry) {
|
|
151
|
+
throw new Error(`No calendar entry found with slug: ${slug}`);
|
|
152
|
+
}
|
|
153
|
+
if (entry.stage !== 'Paused') {
|
|
154
|
+
throw new Error(`Entry "${slug}" is in stage "${entry.stage}" — only Paused entries can be resumed.`);
|
|
155
|
+
}
|
|
156
|
+
if (entry.pausedFrom === undefined) {
|
|
157
|
+
throw new Error(`Entry "${slug}" is Paused but has no pausedFrom — cannot resume automatically. Edit the calendar by hand to move it back to the right stage.`);
|
|
158
|
+
}
|
|
159
|
+
entry.stage = entry.pausedFrom;
|
|
160
|
+
delete entry.pausedFrom;
|
|
161
|
+
return entry;
|
|
162
|
+
}
|
|
163
|
+
/** Mark an entry Published with the given date (defaults to today). */
|
|
164
|
+
export function publishEntry(calendar, slug, datePublished) {
|
|
165
|
+
const entry = calendar.entries.find((e) => e.slug === slug);
|
|
166
|
+
if (!entry) {
|
|
167
|
+
throw new Error(`No calendar entry found with slug: ${slug}`);
|
|
168
|
+
}
|
|
169
|
+
entry.stage = 'Published';
|
|
170
|
+
entry.datePublished =
|
|
171
|
+
datePublished ?? new Date().toISOString().slice(0, 10);
|
|
172
|
+
return entry;
|
|
173
|
+
}
|
|
174
|
+
/** Find an entry by slug. */
|
|
175
|
+
export function findEntry(calendar, slug) {
|
|
176
|
+
return calendar.entries.find((e) => e.slug === slug);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* CLI-arg friendly lookup: tries `id` first (stable across slug
|
|
180
|
+
* renames), falls back to `slug`. Use this anywhere an operator-typed
|
|
181
|
+
* argument might be either form — e.g. `deskwork doctor --entry <X>`.
|
|
182
|
+
*
|
|
183
|
+
* Returns `undefined` when neither match. Empty / whitespace-only
|
|
184
|
+
* input also returns undefined.
|
|
185
|
+
*/
|
|
186
|
+
export function findEntryBySlugOrId(calendar, slugOrId) {
|
|
187
|
+
if (slugOrId === undefined || slugOrId === null)
|
|
188
|
+
return undefined;
|
|
189
|
+
const trimmed = slugOrId.trim();
|
|
190
|
+
if (trimmed === '')
|
|
191
|
+
return undefined;
|
|
192
|
+
return findEntryById(calendar, trimmed) ?? findEntry(calendar, trimmed);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Append a distribution record for a published post. The referenced entry
|
|
196
|
+
* must exist and be in the Published stage — we don't record shares for
|
|
197
|
+
* posts that haven't shipped yet.
|
|
198
|
+
*
|
|
199
|
+
* Resolves by entryId when present (stable) or falls back to slug
|
|
200
|
+
* (legacy callers). On success, stamps entryId onto the record so
|
|
201
|
+
* downstream joins use the stable identity even if the slug later
|
|
202
|
+
* changes.
|
|
203
|
+
*
|
|
204
|
+
* Phase 21a: `record.url` may be an empty string at creation time. The
|
|
205
|
+
* shortform flow `addDistribution`s a placeholder record (URL gets filled
|
|
206
|
+
* in by `updateDistributionUrl` once the operator has posted to the
|
|
207
|
+
* platform). The DistributionRecord.url type stays `string` — there is
|
|
208
|
+
* no runtime non-empty check, so the existing surface is unchanged.
|
|
209
|
+
*/
|
|
210
|
+
export function addDistribution(calendar, record) {
|
|
211
|
+
const entry = (record.entryId && calendar.entries.find((e) => e.id === record.entryId)) ||
|
|
212
|
+
calendar.entries.find((e) => e.slug === record.slug);
|
|
213
|
+
if (!entry) {
|
|
214
|
+
throw new Error(`No calendar entry found with entryId "${record.entryId ?? ''}" or slug "${record.slug}"`);
|
|
215
|
+
}
|
|
216
|
+
if (entry.stage !== 'Published') {
|
|
217
|
+
throw new Error(`Entry "${entry.slug}" is in stage "${entry.stage}" — must be Published to record a distribution`);
|
|
218
|
+
}
|
|
219
|
+
if (entry.id !== undefined)
|
|
220
|
+
record.entryId = entry.id;
|
|
221
|
+
record.slug = entry.slug;
|
|
222
|
+
calendar.distributions.push(record);
|
|
223
|
+
return record;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Set / update the URL on a distribution record. Used after the operator
|
|
227
|
+
* has manually posted a shortform to the platform and obtained the share
|
|
228
|
+
* URL.
|
|
229
|
+
*
|
|
230
|
+
* Match precedence: `entryId` (stable) → `(slug, platform, channel?)`
|
|
231
|
+
* (legacy fallback). When no record exists yet the helper creates one via
|
|
232
|
+
* `addDistribution` so the URL becomes the first thing recorded for that
|
|
233
|
+
* distribution.
|
|
234
|
+
*
|
|
235
|
+
* Channel comparison is case-insensitive (matches the existing approve
|
|
236
|
+
* helper's behavior). Pass `channel: undefined` to match a record that
|
|
237
|
+
* has no channel set.
|
|
238
|
+
*
|
|
239
|
+
* Only fields explicitly supplied are written — leaving `notes`
|
|
240
|
+
* undefined preserves any existing note. `dateShared` defaults to today
|
|
241
|
+
* (UTC) when omitted, both for new records and as the explicit set when
|
|
242
|
+
* updating an existing one.
|
|
243
|
+
*
|
|
244
|
+
* @returns the updated DistributionRecord (always present after this call).
|
|
245
|
+
*/
|
|
246
|
+
export function updateDistributionUrl(calendar, match, url, dateShared, notes) {
|
|
247
|
+
if (!match.platform) {
|
|
248
|
+
throw new Error('updateDistributionUrl: platform is required');
|
|
249
|
+
}
|
|
250
|
+
if ((match.entryId === undefined || match.entryId === '') &&
|
|
251
|
+
(match.slug === undefined || match.slug === '')) {
|
|
252
|
+
throw new Error('updateDistributionUrl: entryId or slug is required');
|
|
253
|
+
}
|
|
254
|
+
const channelKey = match.channel?.toLowerCase();
|
|
255
|
+
const channelMatches = (recChannel) => {
|
|
256
|
+
if (channelKey === undefined)
|
|
257
|
+
return !recChannel;
|
|
258
|
+
return (recChannel?.toLowerCase() ?? '') === channelKey;
|
|
259
|
+
};
|
|
260
|
+
// Match by entryId first (stable across slug renames) then by
|
|
261
|
+
// (slug, platform, channel?) as the legacy fallback.
|
|
262
|
+
const existing = calendar.distributions.find((d) => {
|
|
263
|
+
if (d.platform !== match.platform)
|
|
264
|
+
return false;
|
|
265
|
+
if (!channelMatches(d.channel))
|
|
266
|
+
return false;
|
|
267
|
+
if (match.entryId !== undefined && match.entryId !== '' && d.entryId === match.entryId) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
if (match.slug !== undefined && match.slug !== '' && d.slug === match.slug) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
});
|
|
275
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
276
|
+
if (existing) {
|
|
277
|
+
existing.url = url;
|
|
278
|
+
existing.dateShared = dateShared ?? existing.dateShared ?? today;
|
|
279
|
+
if (notes !== undefined) {
|
|
280
|
+
existing.notes = notes;
|
|
281
|
+
}
|
|
282
|
+
return existing;
|
|
283
|
+
}
|
|
284
|
+
// No record yet — fall through to addDistribution so the entry's
|
|
285
|
+
// Published-stage invariant is enforced and entryId/slug get stamped
|
|
286
|
+
// from the calendar entry.
|
|
287
|
+
const slug = match.slug ?? '';
|
|
288
|
+
const record = {
|
|
289
|
+
slug,
|
|
290
|
+
platform: match.platform,
|
|
291
|
+
url,
|
|
292
|
+
dateShared: dateShared ?? today,
|
|
293
|
+
};
|
|
294
|
+
if (match.entryId !== undefined && match.entryId !== '') {
|
|
295
|
+
record.entryId = match.entryId;
|
|
296
|
+
}
|
|
297
|
+
if (match.channel !== undefined && match.channel !== '') {
|
|
298
|
+
record.channel = match.channel;
|
|
299
|
+
}
|
|
300
|
+
if (notes !== undefined) {
|
|
301
|
+
record.notes = notes;
|
|
302
|
+
}
|
|
303
|
+
return addDistribution(calendar, record);
|
|
304
|
+
}
|
|
305
|
+
//# sourceMappingURL=calendar-mutations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-mutations.js","sourceRoot":"","sources":["../src/calendar-mutations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown editorial calendar parser and writer.
|
|
3
|
+
*
|
|
4
|
+
* Each site's calendar is a human-readable markdown file with one table per
|
|
5
|
+
* stage. This module round-trips between that format and the in-memory
|
|
6
|
+
* EditorialCalendar type.
|
|
7
|
+
*
|
|
8
|
+
* ## Markdown format
|
|
9
|
+
*
|
|
10
|
+
* ```markdown
|
|
11
|
+
* # Editorial Calendar
|
|
12
|
+
*
|
|
13
|
+
* ## Ideas
|
|
14
|
+
*
|
|
15
|
+
* | UUID | Slug | Title | Description | Keywords | Source |
|
|
16
|
+
* |------|------|-------|-------------|----------|--------|
|
|
17
|
+
* | abc-123 | my-post | My Post | A post about things | kw1, kw2 | manual |
|
|
18
|
+
*
|
|
19
|
+
* ## Planned
|
|
20
|
+
* ...
|
|
21
|
+
*
|
|
22
|
+
* ## Published
|
|
23
|
+
*
|
|
24
|
+
* | UUID | Slug | Title | Description | Keywords | Source | Published | Issue |
|
|
25
|
+
* |------|------|-------|-------------|----------|--------|-----------|-------|
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Optional columns (Topics, Type, URL) are emitted only when any entry uses
|
|
29
|
+
* them, so a calendar with no cross-posting stays visually simple. Published
|
|
30
|
+
* entries always include Published and Issue columns. The UUID column is
|
|
31
|
+
* always emitted at render time — pre-UUID calendars get backfilled lazily
|
|
32
|
+
* (parser assigns missing UUIDs in-memory; the next write persists them).
|
|
33
|
+
*/
|
|
34
|
+
import { PLATFORMS, STAGES, type EditorialCalendar } from './types.ts';
|
|
35
|
+
/** Parse the editorial calendar markdown file into an EditorialCalendar. */
|
|
36
|
+
export declare function parseCalendar(markdown: string): EditorialCalendar;
|
|
37
|
+
/**
|
|
38
|
+
* Read and parse the editorial calendar from an absolute path.
|
|
39
|
+
*
|
|
40
|
+
* A non-existent file is treated as a logically empty calendar — the user
|
|
41
|
+
* hasn't written anything yet. Any other error (e.g. unreadable, malformed)
|
|
42
|
+
* propagates so the operator sees the problem instead of a silently-empty
|
|
43
|
+
* dashboard.
|
|
44
|
+
*/
|
|
45
|
+
export declare function readCalendar(calendarPath: string): EditorialCalendar;
|
|
46
|
+
/** Render the full editorial calendar as markdown. */
|
|
47
|
+
export declare function renderCalendar(calendar: EditorialCalendar): string;
|
|
48
|
+
/** Write the editorial calendar to an absolute path. */
|
|
49
|
+
export declare function writeCalendar(calendarPath: string, calendar: EditorialCalendar): void;
|
|
50
|
+
/** Render an empty calendar — used by the install skill to seed a new file. */
|
|
51
|
+
export declare function renderEmptyCalendar(): string;
|
|
52
|
+
/** Suppress unused-import warnings for re-exported constants available to callers. */
|
|
53
|
+
export { PLATFORMS, STAGES };
|
|
54
|
+
//# sourceMappingURL=calendar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar.d.ts","sourceRoot":"","sources":["../src/calendar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,EACL,SAAS,EACT,MAAM,EAQN,KAAK,iBAAiB,EAEvB,MAAM,YAAY,CAAC;AAsMpB,4EAA4E;AAC5E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAoEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,iBAAiB,CAWpE;AAyFD,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,CAqClE;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,iBAAiB,GAC1B,IAAI,CAEN;AAED,+EAA+E;AAC/E,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,sFAAsF;AACtF,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC"}
|