@deskwork/core 0.15.0 → 0.17.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.
Files changed (84) hide show
  1. package/dist/doctor/rules/legacy-stage-artifact-path.d.ts +28 -0
  2. package/dist/doctor/rules/legacy-stage-artifact-path.d.ts.map +1 -0
  3. package/dist/doctor/rules/legacy-stage-artifact-path.js +232 -0
  4. package/dist/doctor/rules/legacy-stage-artifact-path.js.map +1 -0
  5. package/dist/doctor/rules/legacy-stage-artifact-path.ts +285 -0
  6. package/dist/doctor/runner.d.ts.map +1 -1
  7. package/dist/doctor/runner.js +2 -0
  8. package/dist/doctor/runner.js.map +1 -1
  9. package/dist/entry/annotations.d.ts +38 -3
  10. package/dist/entry/annotations.d.ts.map +1 -1
  11. package/dist/entry/annotations.js +164 -3
  12. package/dist/entry/annotations.js.map +1 -1
  13. package/dist/entry/approve.d.ts +21 -4
  14. package/dist/entry/approve.d.ts.map +1 -1
  15. package/dist/entry/approve.js +74 -7
  16. package/dist/entry/approve.js.map +1 -1
  17. package/dist/entry/snapshot.d.ts +51 -0
  18. package/dist/entry/snapshot.d.ts.map +1 -0
  19. package/dist/entry/snapshot.js +95 -0
  20. package/dist/entry/snapshot.js.map +1 -0
  21. package/dist/ingest-derive.d.ts +1 -1
  22. package/dist/ingest-derive.d.ts.map +1 -1
  23. package/dist/ingest-derive.js +10 -7
  24. package/dist/ingest-derive.js.map +1 -1
  25. package/dist/ingest-id.d.ts +33 -0
  26. package/dist/ingest-id.d.ts.map +1 -0
  27. package/dist/ingest-id.js +60 -0
  28. package/dist/ingest-id.js.map +1 -0
  29. package/dist/ingest.d.ts.map +1 -1
  30. package/dist/ingest.js +23 -0
  31. package/dist/ingest.js.map +1 -1
  32. package/dist/iterate/iterate.d.ts.map +1 -1
  33. package/dist/iterate/iterate.js +37 -25
  34. package/dist/iterate/iterate.js.map +1 -1
  35. package/dist/review/types.d.ts +56 -1
  36. package/dist/review/types.d.ts.map +1 -1
  37. package/dist/review/types.js.map +1 -1
  38. package/dist/schema/draft-annotation.d.ts +108 -24
  39. package/dist/schema/draft-annotation.d.ts.map +1 -1
  40. package/dist/schema/draft-annotation.js +23 -0
  41. package/dist/schema/draft-annotation.js.map +1 -1
  42. package/dist/schema/journal-events.d.ts +240 -104
  43. package/dist/schema/journal-events.d.ts.map +1 -1
  44. package/dist/scrapbook/crud-at-dir.d.ts +47 -0
  45. package/dist/scrapbook/crud-at-dir.d.ts.map +1 -0
  46. package/dist/scrapbook/crud-at-dir.js +114 -0
  47. package/dist/scrapbook/crud-at-dir.js.map +1 -0
  48. package/dist/scrapbook/crud-slug.d.ts +33 -0
  49. package/dist/scrapbook/crud-slug.d.ts.map +1 -0
  50. package/dist/scrapbook/crud-slug.js +99 -0
  51. package/dist/scrapbook/crud-slug.js.map +1 -0
  52. package/dist/scrapbook/format.d.ts +10 -0
  53. package/dist/scrapbook/format.d.ts.map +1 -0
  54. package/dist/scrapbook/format.js +41 -0
  55. package/dist/scrapbook/format.js.map +1 -0
  56. package/dist/scrapbook/listing.d.ts +94 -0
  57. package/dist/scrapbook/listing.d.ts.map +1 -0
  58. package/dist/scrapbook/listing.js +167 -0
  59. package/dist/scrapbook/listing.js.map +1 -0
  60. package/dist/scrapbook/paths.d.ts +115 -0
  61. package/dist/scrapbook/paths.d.ts.map +1 -0
  62. package/dist/scrapbook/paths.js +149 -0
  63. package/dist/scrapbook/paths.js.map +1 -0
  64. package/dist/scrapbook/read.d.ts +54 -0
  65. package/dist/scrapbook/read.d.ts.map +1 -0
  66. package/dist/scrapbook/read.js +62 -0
  67. package/dist/scrapbook/read.js.map +1 -0
  68. package/dist/scrapbook/seed.d.ts +19 -0
  69. package/dist/scrapbook/seed.d.ts.map +1 -0
  70. package/dist/scrapbook/seed.js +46 -0
  71. package/dist/scrapbook/seed.js.map +1 -0
  72. package/dist/scrapbook/types.d.ts +44 -0
  73. package/dist/scrapbook/types.d.ts.map +1 -0
  74. package/dist/scrapbook/types.js +11 -0
  75. package/dist/scrapbook/types.js.map +1 -0
  76. package/dist/scrapbook/validation.d.ts +28 -0
  77. package/dist/scrapbook/validation.d.ts.map +1 -0
  78. package/dist/scrapbook/validation.js +98 -0
  79. package/dist/scrapbook/validation.js.map +1 -0
  80. package/dist/scrapbook.d.ts +43 -277
  81. package/dist/scrapbook.d.ts.map +1 -1
  82. package/dist/scrapbook.js +42 -535
  83. package/dist/scrapbook.js.map +1 -1
  84. package/package.json +5 -1
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Scrapbook validation + classification helpers.
3
+ *
4
+ * Slug + filename validation runs at every fs entry point; a `..` or
5
+ * absolute path that slips through here would let a malicious request
6
+ * read or write outside the scrapbook tree. The route layer relies on
7
+ * these helpers for traversal protection.
8
+ */
9
+ import { extname } from 'node:path';
10
+ // ---------------------------------------------------------------------------
11
+ // Slug + path validation
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * A single slug segment — kebab-case lowercase. Used both for flat
15
+ * slugs and as the building block of hierarchical paths.
16
+ */
17
+ const SLUG_SEGMENT_RE = /^[a-z0-9][a-z0-9-]*$/;
18
+ /**
19
+ * A full slug path — one or more `/`-separated kebab-case segments.
20
+ * Accepts both legacy flat slugs ("scsi-over-wifi") and hierarchical
21
+ * paths ("the-outbound/characters/strivers"). No leading or trailing
22
+ * slash; no empty segments.
23
+ */
24
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)*$/;
25
+ const FILENAME_RE = /^[a-zA-Z0-9._-][a-zA-Z0-9._ -]*$/;
26
+ export function assertSlug(slug) {
27
+ if (!SLUG_RE.test(slug)) {
28
+ throw new Error(`invalid slug "${slug}" — must match ${SLUG_RE}`);
29
+ }
30
+ }
31
+ /**
32
+ * Split a hierarchical slug into its segments. Each segment is a
33
+ * standalone kebab-case identifier.
34
+ */
35
+ export function slugSegments(slug) {
36
+ return slug.split('/');
37
+ }
38
+ /**
39
+ * True if a slug refers to a nested entry (has at least one `/`).
40
+ */
41
+ export function isNestedSlug(slug) {
42
+ return slug.includes('/');
43
+ }
44
+ // `SLUG_SEGMENT_RE` is exported for callers that need to validate one
45
+ // segment at a time (e.g. when assembling a path interactively).
46
+ export { SLUG_SEGMENT_RE };
47
+ export function assertFilename(name) {
48
+ if (!name || name === '.' || name === '..') {
49
+ throw new Error(`invalid filename "${name}"`);
50
+ }
51
+ if (name.includes('/') || name.includes('\\') || name.includes('\0')) {
52
+ throw new Error(`filename may not contain path separators: "${name}"`);
53
+ }
54
+ if (name.startsWith('.')) {
55
+ // Dotfiles are suspicious for a dev-only operator UI. Reject.
56
+ throw new Error(`filename may not start with a dot: "${name}"`);
57
+ }
58
+ if (!FILENAME_RE.test(name)) {
59
+ throw new Error(`filename may only contain [A-Za-z0-9._ -]: "${name}"`);
60
+ }
61
+ if (name.length > 200) {
62
+ throw new Error(`filename too long (> 200 chars): "${name}"`);
63
+ }
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Type classification
67
+ // ---------------------------------------------------------------------------
68
+ export function classify(filename) {
69
+ const ext = extname(filename).toLowerCase();
70
+ switch (ext) {
71
+ case '.md':
72
+ case '.markdown':
73
+ return 'md';
74
+ case '.json':
75
+ case '.jsonl':
76
+ return 'json';
77
+ case '.js':
78
+ case '.mjs':
79
+ case '.cjs':
80
+ case '.ts':
81
+ case '.tsx':
82
+ case '.mts':
83
+ return 'js';
84
+ case '.png':
85
+ case '.jpg':
86
+ case '.jpeg':
87
+ case '.gif':
88
+ case '.webp':
89
+ case '.svg':
90
+ return 'img';
91
+ case '.txt':
92
+ case '.log':
93
+ return 'txt';
94
+ default:
95
+ return 'other';
96
+ }
97
+ }
98
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/scrapbook/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,eAAe,GAAG,sBAAsB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,OAAO,GAAG,6CAA6C,CAAC;AAC9D,MAAM,WAAW,GAAG,kCAAkC,CAAC;AAEvD,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,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;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,sEAAsE;AACtE,iEAAiE;AACjE,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,GAAG,CAAC,CAAC;IAChD,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,8CAA8C,IAAI,GAAG,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,8DAA8D;QAC9D,MAAM,IAAI,KAAK,CAAC,uCAAuC,IAAI,GAAG,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,+CAA+C,IAAI,GAAG,CACvD,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,GAAG,CAAC,CAAC;IAChE,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,MAAM,UAAU,QAAQ,CAAC,QAAgB;IACvC,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,KAAK,CAAC;QACX,KAAK,WAAW;YACd,OAAO,IAAI,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC;QAChB,KAAK,KAAK,CAAC;QACX,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,KAAK,CAAC;QACX,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,IAAI,CAAC;QACd,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf,KAAK,MAAM,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,KAAK,CAAC;QACf;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -4,286 +4,52 @@
4
4
  * research, and references attached to an in-flight article. Committed
5
5
  * to git alongside the article; not baked to the public site.
6
6
  *
7
- * Responsibilities:
8
- * - Resolve + validate slug + filename (reject `..`, absolute paths,
9
- * and anything outside the article's scrapbook dir)
10
- * - List + read + mutate files inside one scrapbook
11
- * - Classify files by extension into the design type buckets
12
- * - Format relative mtime + total size for the studio chip / viewer
7
+ * Responsibilities (split across sibling modules under `scrapbook/`
8
+ * per #202; this file is a barrel re-export so existing
9
+ * `import from '@deskwork/core/scrapbook'` sites stay unchanged):
10
+ *
11
+ * - `scrapbook/types.ts` — public types
12
+ * - `scrapbook/validation.ts` — slug + filename validation, classify
13
+ * - `scrapbook/paths.ts` — public (entry-aware + path-aware) +
14
+ * private (slug-template) resolvers
15
+ * - `scrapbook/listing.ts` — list + count
16
+ * - `scrapbook/read.ts` — read primitives
17
+ * - `scrapbook/crud-at-dir.ts` — entry-aware CRUD primitives (post-#191)
18
+ * - `scrapbook/crud-slug.ts` — INTERNAL slug-template CRUD primitives
19
+ * - `scrapbook/seed.ts` — plan-time scaffolding
20
+ * - `scrapbook/format.ts` — UI formatters
21
+ *
22
+ * Public API surface (#192 — slug-template mutators are NO LONGER
23
+ * exported; callers go through `scrapbookDirForEntry` + the `*AtDir`
24
+ * family instead):
25
+ *
26
+ * - Types: `ScrapbookItemKind`, `ScrapbookItem`, `ScrapbookSummary`,
27
+ * `ScrapbookLocation`, `SECRET_SUBDIR`
28
+ * - Validation: `assertSlug`, `assertFilename`, `slugSegments`,
29
+ * `isNestedSlug`, `SLUG_SEGMENT_RE`, `classify`
30
+ * - Paths: `scrapbookDirAtPath`, `scrapbookDirForEntry`,
31
+ * `scrapbookFilePathAtDir`
32
+ * - Listing: `listScrapbook`, `listScrapbookAtDir`,
33
+ * `listScrapbookForEntry`, `countScrapbook`, `countScrapbookForEntry`
34
+ * - Read: `readScrapbookFile`, `readScrapbookFileAtDir`,
35
+ * `readScrapbookFileForEntry`
36
+ * - CRUD: `createScrapbookMarkdownAtDir`, `saveScrapbookFileAtDir`,
37
+ * `renameScrapbookFileAtDir`, `deleteScrapbookFileAtDir`,
38
+ * `writeScrapbookUploadAtDir`
39
+ * - Seed: `seedScrapbookReadme`
40
+ * - Format: `formatRelativeTime`, `formatSize`
13
41
  *
14
42
  * The API endpoints that wrap these helpers should 404 in PROD; this
15
43
  * library contains no PROD check of its own (enforcement stays at the
16
44
  * endpoint boundary).
17
45
  */
18
- import type { DeskworkConfig } from './config.ts';
19
- import type { ContentIndex } from './content-index.ts';
20
- /** Type buckets for scrapbook entries. */
21
- export type ScrapbookItemKind = 'md' | 'json' | 'js' | 'img' | 'txt' | 'other';
22
- export interface ScrapbookItem {
23
- name: string;
24
- kind: ScrapbookItemKind;
25
- size: number;
26
- mtime: string;
27
- }
28
- export interface ScrapbookSummary {
29
- site: string;
30
- /**
31
- * The scrapbook's location identifier — a slug for entries tied to a
32
- * calendar row, or any directory path within `contentDir` for
33
- * scrapbooks that hang off purely organizational nodes (e.g. an
34
- * intermediate project directory that isn't itself a calendar entry).
35
- */
36
- slug: string;
37
- dir: string;
38
- exists: boolean;
39
- /** Files at the top of `scrapbook/` (public/published-side notes). */
40
- items: ScrapbookItem[];
41
- /**
42
- * Files inside `scrapbook/secret/` — never to be published. Operators
43
- * can drop research, drafts, or sensitive notes here knowing the host
44
- * project's content collection patterns won't pick them up.
45
- */
46
- secretItems: ScrapbookItem[];
47
- }
48
- /** Well-known subdirectory name for editorially-private scrapbook items. */
49
- export declare const SECRET_SUBDIR = "secret";
50
- /**
51
- * A single slug segment — kebab-case lowercase. Used both for flat
52
- * slugs and as the building block of hierarchical paths.
53
- */
54
- declare const SLUG_SEGMENT_RE: RegExp;
55
- export declare function assertSlug(slug: string): void;
56
- /**
57
- * Split a hierarchical slug into its segments. Each segment is a
58
- * standalone kebab-case identifier.
59
- */
60
- export declare function slugSegments(slug: string): string[];
61
- /**
62
- * True if a slug refers to a nested entry (has at least one `/`).
63
- */
64
- export declare function isNestedSlug(slug: string): boolean;
65
- export { SLUG_SEGMENT_RE };
66
- export declare function assertFilename(name: string): void;
67
- /**
68
- * Resolve the scrapbook directory for (site, slug) and ensure the
69
- * return path stays inside the site's content directory.
70
- * Doesn't require the directory to exist.
71
- */
72
- export declare function scrapbookDir(projectRoot: string, config: DeskworkConfig, site: string, slug: string): string;
73
- /**
74
- * Resolve the scrapbook directory for an arbitrary path under the site's
75
- * content directory. Used by Phase 19c+ callers (e.g. the studio) that
76
- * already know the fs-relative path of an organizational or tracked node
77
- * and don't want to re-derive it through the slug regex. The path may
78
- * contain `/` segments; no `..` or absolute paths allowed.
79
- *
80
- * Path-shape validation matches `assertSlug` since the on-disk layout
81
- * is the same shape — kebab-case segments separated by `/`. Different
82
- * helper, same constraint.
83
- */
84
- export declare function scrapbookDirAtPath(projectRoot: string, config: DeskworkConfig, site: string, relPath: string): string;
85
- /**
86
- * Resolve the scrapbook directory for a tracked calendar entry.
87
- *
88
- * Derives the scrapbook location from the parent directory of the
89
- * entry's content file (located via `findEntryFile`, which prefers the
90
- * frontmatter-id index over the slug template). Falls back to today's
91
- * slug-based addressing for entries that haven't been bound to
92
- * frontmatter yet (pre-doctor state).
93
- *
94
- * Refactor-proof: when the operator renames an entry's directory on
95
- * disk, the next request rebuilds the index and the scrapbook now
96
- * lives at the new path automatically.
97
- *
98
- * @param entry Calendar entry — `id` preferred (Phase 19+); `slug` is
99
- * used both as the legacy fallback and to locate the
100
- * `<dirname>/scrapbook/` when the entry has no id yet.
101
- * @param index Optional pre-built index (per-request memoization). When
102
- * omitted, this function builds one.
103
- */
104
- export declare function scrapbookDirForEntry(projectRoot: string, config: DeskworkConfig, site: string, entry: {
105
- id?: string;
106
- slug: string;
107
- }, index?: ContentIndex): string;
108
- /** Options that select between the public scrapbook root and `secret/`. */
109
- export interface ScrapbookLocation {
110
- /** When true, the file lives under `scrapbook/secret/`. Default: false. */
111
- secret?: boolean;
112
- }
113
- /**
114
- * Resolve a filename INSIDE a scrapbook dir and return the absolute
115
- * path. Throws if the resolved path escapes the scrapbook dir (guards
116
- * against `..` sequences that slipped through assertFilename).
117
- *
118
- * When `opts.secret` is true, the returned path is rooted at
119
- * `<scrapbook>/secret/<filename>` instead of `<scrapbook>/<filename>`.
120
- */
121
- export declare function scrapbookFilePath(projectRoot: string, config: DeskworkConfig, site: string, slug: string, filename: string, opts?: ScrapbookLocation): string;
122
- /**
123
- * Resolve a filename inside an already-resolved scrapbook directory.
124
- * Mirrors `listScrapbookAtDir` — used by callers that have already
125
- * resolved the on-disk dir via `scrapbookDirForEntry` (id-driven) or
126
- * `scrapbookDirAtPath` (fs-path-driven) and don't want to re-derive
127
- * through the slug template.
128
- *
129
- * Same security guards as `scrapbookFilePath`:
130
- * - `assertFilename` blocks dotfiles / `..` / absolute paths in the filename
131
- * - the `startsWith(dir + '/')` containment check blocks any traversal
132
- * that slipped through (so `secret/` always sits inside the top-level
133
- * scrapbook dir)
134
- *
135
- * The slug-shape validator is bypassed because the caller has already
136
- * proven the directory exists in the content tree by other means.
137
- */
138
- export declare function scrapbookFilePathAtDir(scrapbookDirAbs: string, filename: string, opts?: ScrapbookLocation): string;
139
- export declare function classify(filename: string): ScrapbookItemKind;
140
- /**
141
- * List the items in a scrapbook, sorted newest-mtime first. Returns
142
- * both public items (top-level files) and secret items (files inside
143
- * `scrapbook/secret/`). Subdirectories at the top level OTHER than
144
- * `secret/` are ignored — deskwork doesn't recurse into arbitrary
145
- * trees inside a scrapbook.
146
- */
147
- export declare function listScrapbook(projectRoot: string, config: DeskworkConfig, site: string, slug: string): ScrapbookSummary;
148
- /**
149
- * List a scrapbook by absolute directory path. Used by callers that
150
- * have already resolved the on-disk path via `scrapbookDirForEntry`
151
- * (id-driven) or `scrapbookDirAtPath` (fs-path-driven) and don't want
152
- * to re-derive through the slug template. The `slug` parameter is only
153
- * used to populate the returned summary's identifier field — it does
154
- * not influence path resolution.
155
- *
156
- * Internal primitive shared by `listScrapbook` (slug-based) and
157
- * `listScrapbookForEntry` (id-driven).
158
- */
159
- export declare function listScrapbookAtDir(site: string, slug: string, dir: string): ScrapbookSummary;
160
- /**
161
- * List scrapbook items for a tracked calendar entry. Resolves the
162
- * scrapbook directory via the content index when available (id binding),
163
- * falling back to slug-based addressing for entries that haven't been
164
- * bound to frontmatter yet (pre-doctor state).
165
- *
166
- * Mirrors the shape of `countScrapbookForEntry`. Used by the studio
167
- * review-page drawer + content-detail panel so writingcontrol-shape
168
- * entries (where the file path diverges from the slug template) list
169
- * items at the correct on-disk location.
170
- *
171
- * @param entry Calendar entry — `id` preferred; `slug` is both the
172
- * legacy fallback and the disambiguator the underlying
173
- * resolver uses when the index is incomplete.
174
- * @param index Optional pre-built per-request index. When omitted, the
175
- * resolver builds one on demand.
176
- */
177
- export declare function listScrapbookForEntry(projectRoot: string, config: DeskworkConfig, site: string, entry: {
178
- id?: string;
179
- slug: string;
180
- }, index?: ContentIndex): ScrapbookSummary;
181
- /**
182
- * Total item count (public + secret). Used by the studio chip for the
183
- * badge — operators want a single "has scrapbook content" signal that
184
- * counts everything attached to this entry.
185
- *
186
- * Slug-based addressing: resolves `<contentDir>/<slug>/scrapbook/`. For
187
- * entries whose on-disk path doesn't match the slug template (e.g.
188
- * writingcontrol-shape projects where slug `the-outbound` lives at
189
- * `projects/the-outbound/index.md`), use `countScrapbookForEntry`
190
- * instead — it derives the path from the bound file via the content
191
- * index.
192
- */
193
- export declare function countScrapbook(projectRoot: string, config: DeskworkConfig, site: string, slug: string): number;
194
- /**
195
- * Count scrapbook items for a tracked calendar entry. Resolves the
196
- * scrapbook directory via the content index when available (id binding),
197
- * falling back to slug-based addressing for entries that haven't been
198
- * bound to frontmatter yet (pre-doctor state).
199
- *
200
- * Mirrors the shape of `scrapbookDirForEntry` — same resolver, same
201
- * legacy-slug fallback. Used by the studio dashboard chip so writing-
202
- * control-shape entries (where the file path diverges from the slug
203
- * template) report the correct count.
204
- *
205
- * @param entry Calendar entry — `id` preferred (Phase 19+); `slug` is
206
- * both the legacy fallback and the disambiguator the
207
- * underlying resolver uses when the index is incomplete.
208
- * @param index Optional pre-built per-request index. When omitted, the
209
- * resolver builds one on demand.
210
- */
211
- export declare function countScrapbookForEntry(projectRoot: string, config: DeskworkConfig, site: string, entry: {
212
- id?: string;
213
- slug: string;
214
- }, index?: ContentIndex): number;
215
- export declare function readScrapbookFile(projectRoot: string, config: DeskworkConfig, site: string, slug: string, filename: string, opts?: ScrapbookLocation): {
216
- name: string;
217
- kind: ScrapbookItemKind;
218
- size: number;
219
- mtime: string;
220
- content: Buffer;
221
- };
222
- /**
223
- * Read a scrapbook file given the absolute scrapbook directory. Used
224
- * by callers that have already resolved the on-disk dir via
225
- * `scrapbookDirForEntry` (id-driven) or `scrapbookDirAtPath`
226
- * (fs-path-driven) and don't want to re-derive through the slug
227
- * template. Mirrors the listing-side primitive `listScrapbookAtDir`.
228
- *
229
- * Same security guards as `readScrapbookFile` (filename validation +
230
- * path-traversal containment) via `scrapbookFilePathAtDir`.
231
- */
232
- export declare function readScrapbookFileAtDir(scrapbookDirAbs: string, filename: string, opts?: ScrapbookLocation): {
233
- name: string;
234
- kind: ScrapbookItemKind;
235
- size: number;
236
- mtime: string;
237
- content: Buffer;
238
- };
239
- /**
240
- * Read a scrapbook file for a tracked calendar entry. Mirrors
241
- * `listScrapbookForEntry` / `countScrapbookForEntry` — id-driven
242
- * resolution via `scrapbookDirForEntry`, slug fallback for pre-bound
243
- * entries. Used by the studio's `/api/dev/scrapbook-file?entryId=...`
244
- * variant so projects whose feature-doc layout doesn't match the
245
- * kebab-case slug template (e.g. `docs/<version>/<status>/<feature>/`)
246
- * can still serve scrapbook assets — `scrapbookDirAtPath`'s slug
247
- * validator would otherwise reject any path with dots or uppercase
248
- * segments.
249
- *
250
- * Same security guards as the slug-shape variant: `assertFilename`
251
- * blocks dotfiles / `..` / absolute paths in the filename; the
252
- * `startsWith(dir + '/')` containment check blocks any traversal that
253
- * slipped through.
254
- */
255
- export declare function readScrapbookFileForEntry(projectRoot: string, config: DeskworkConfig, site: string, entry: {
256
- id?: string;
257
- slug: string;
258
- }, filename: string, opts?: ScrapbookLocation, index?: ContentIndex): {
259
- name: string;
260
- kind: ScrapbookItemKind;
261
- size: number;
262
- mtime: string;
263
- content: Buffer;
264
- };
265
- /**
266
- * Create a new markdown note in the scrapbook. Creates the scrapbook
267
- * dir (and `secret/` subdir, if needed) if it doesn't exist. Refuses
268
- * to overwrite existing files.
269
- */
270
- export declare function createScrapbookMarkdown(projectRoot: string, config: DeskworkConfig, site: string, slug: string, filename: string, body: string, opts?: ScrapbookLocation): ScrapbookItem;
271
- /** Overwrite an existing file's contents. Refuses if the file is absent. */
272
- export declare function saveScrapbookFile(projectRoot: string, config: DeskworkConfig, site: string, slug: string, filename: string, body: string | Buffer, opts?: ScrapbookLocation): ScrapbookItem;
273
- export declare function renameScrapbookFile(projectRoot: string, config: DeskworkConfig, site: string, slug: string, oldName: string, newName: string, opts?: ScrapbookLocation): ScrapbookItem;
274
- export declare function deleteScrapbookFile(projectRoot: string, config: DeskworkConfig, site: string, slug: string, filename: string, opts?: ScrapbookLocation): void;
275
- /**
276
- * Seed a scrapbook's `README.md` at plan time. Idempotent — if the
277
- * README already exists, returns null without touching it. Used by
278
- * the plan skill so every Planned article gets a scrapbook home with
279
- * a template that names the article and invites receipts.
280
- */
281
- export declare function seedScrapbookReadme(projectRoot: string, config: DeskworkConfig, site: string, slug: string, title: string): ScrapbookItem | null;
282
- /**
283
- * Write an uploaded file into the scrapbook. Filename + content come
284
- * from the multipart body upstream; we validate and persist.
285
- */
286
- export declare function writeScrapbookUpload(projectRoot: string, config: DeskworkConfig, site: string, slug: string, filename: string, content: Buffer, opts?: ScrapbookLocation): ScrapbookItem;
287
- export declare function formatRelativeTime(iso: string, now?: Date): string;
288
- export declare function formatSize(bytes: number): string;
46
+ export type { ScrapbookItem, ScrapbookItemKind, ScrapbookLocation, ScrapbookSummary, } from './scrapbook/types.ts';
47
+ export { SECRET_SUBDIR } from './scrapbook/types.ts';
48
+ export { assertFilename, assertSlug, classify, isNestedSlug, SLUG_SEGMENT_RE, slugSegments, } from './scrapbook/validation.ts';
49
+ export { scrapbookDirAtPath, scrapbookDirForEntry, scrapbookFilePathAtDir, } from './scrapbook/paths.ts';
50
+ export { countScrapbook, countScrapbookForEntry, listScrapbook, listScrapbookAtDir, listScrapbookForEntry, } from './scrapbook/listing.ts';
51
+ export { readScrapbookFile, readScrapbookFileAtDir, readScrapbookFileForEntry, } from './scrapbook/read.ts';
52
+ export { createScrapbookMarkdownAtDir, deleteScrapbookFileAtDir, renameScrapbookFileAtDir, saveScrapbookFileAtDir, writeScrapbookUploadAtDir, } from './scrapbook/crud-at-dir.ts';
53
+ export { seedScrapbookReadme } from './scrapbook/seed.ts';
54
+ export { formatRelativeTime, formatSize } from './scrapbook/format.ts';
289
55
  //# sourceMappingURL=scrapbook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scrapbook.d.ts","sourceRoot":"","sources":["../src/scrapbook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scrapbook.d.ts","sourceRoot":"","sources":["../src/scrapbook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,YAAY,EACV,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EACL,cAAc,EACd,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,YAAY,GACb,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC"}