@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.
Files changed (188) hide show
  1. package/dist/body-state.d.ts +27 -0
  2. package/dist/body-state.d.ts.map +1 -0
  3. package/dist/body-state.js +62 -0
  4. package/dist/body-state.js.map +1 -0
  5. package/dist/calendar-mutations.d.ts +124 -0
  6. package/dist/calendar-mutations.d.ts.map +1 -0
  7. package/dist/calendar-mutations.js +305 -0
  8. package/dist/calendar-mutations.js.map +1 -0
  9. package/dist/calendar.d.ts +54 -0
  10. package/dist/calendar.d.ts.map +1 -0
  11. package/dist/calendar.js +430 -0
  12. package/dist/calendar.js.map +1 -0
  13. package/dist/cli.d.ts +38 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +72 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/config.d.ts +91 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +216 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/content-index.d.ts +74 -0
  22. package/dist/content-index.d.ts.map +1 -0
  23. package/dist/content-index.js +205 -0
  24. package/dist/content-index.js.map +1 -0
  25. package/dist/content-tree-fs-walk.d.ts +54 -0
  26. package/dist/content-tree-fs-walk.d.ts.map +1 -0
  27. package/dist/content-tree-fs-walk.js +112 -0
  28. package/dist/content-tree-fs-walk.js.map +1 -0
  29. package/dist/content-tree-helpers.d.ts +52 -0
  30. package/dist/content-tree-helpers.d.ts.map +1 -0
  31. package/dist/content-tree-helpers.js +116 -0
  32. package/dist/content-tree-helpers.js.map +1 -0
  33. package/dist/content-tree-types.d.ts +175 -0
  34. package/dist/content-tree-types.d.ts.map +1 -0
  35. package/dist/content-tree-types.js +10 -0
  36. package/dist/content-tree-types.js.map +1 -0
  37. package/dist/content-tree.d.ts +93 -0
  38. package/dist/content-tree.d.ts.map +1 -0
  39. package/dist/content-tree.js +385 -0
  40. package/dist/content-tree.js.map +1 -0
  41. package/dist/doctor/index.d.ts +11 -0
  42. package/dist/doctor/index.d.ts.map +1 -0
  43. package/dist/doctor/index.js +10 -0
  44. package/dist/doctor/index.js.map +1 -0
  45. package/dist/doctor/project-rules.d.ts +59 -0
  46. package/dist/doctor/project-rules.d.ts.map +1 -0
  47. package/dist/doctor/project-rules.js +143 -0
  48. package/dist/doctor/project-rules.js.map +1 -0
  49. package/dist/doctor/rules/calendar-uuid-missing.d.ts +19 -0
  50. package/dist/doctor/rules/calendar-uuid-missing.d.ts.map +1 -0
  51. package/dist/doctor/rules/calendar-uuid-missing.js +176 -0
  52. package/dist/doctor/rules/calendar-uuid-missing.js.map +1 -0
  53. package/dist/doctor/rules/duplicate-id.d.ts +27 -0
  54. package/dist/doctor/rules/duplicate-id.d.ts.map +1 -0
  55. package/dist/doctor/rules/duplicate-id.js +157 -0
  56. package/dist/doctor/rules/duplicate-id.js.map +1 -0
  57. package/dist/doctor/rules/legacy-top-level-id-migration.d.ts +40 -0
  58. package/dist/doctor/rules/legacy-top-level-id-migration.d.ts.map +1 -0
  59. package/dist/doctor/rules/legacy-top-level-id-migration.js +232 -0
  60. package/dist/doctor/rules/legacy-top-level-id-migration.js.map +1 -0
  61. package/dist/doctor/rules/missing-frontmatter-id.d.ts +45 -0
  62. package/dist/doctor/rules/missing-frontmatter-id.d.ts.map +1 -0
  63. package/dist/doctor/rules/missing-frontmatter-id.js +283 -0
  64. package/dist/doctor/rules/missing-frontmatter-id.js.map +1 -0
  65. package/dist/doctor/rules/orphan-frontmatter-id.d.ts +18 -0
  66. package/dist/doctor/rules/orphan-frontmatter-id.d.ts.map +1 -0
  67. package/dist/doctor/rules/orphan-frontmatter-id.js +154 -0
  68. package/dist/doctor/rules/orphan-frontmatter-id.js.map +1 -0
  69. package/dist/doctor/rules/schema-rejected.d.ts +20 -0
  70. package/dist/doctor/rules/schema-rejected.d.ts.map +1 -0
  71. package/dist/doctor/rules/schema-rejected.js +44 -0
  72. package/dist/doctor/rules/schema-rejected.js.map +1 -0
  73. package/dist/doctor/rules/slug-collision.d.ts +18 -0
  74. package/dist/doctor/rules/slug-collision.d.ts.map +1 -0
  75. package/dist/doctor/rules/slug-collision.js +65 -0
  76. package/dist/doctor/rules/slug-collision.js.map +1 -0
  77. package/dist/doctor/rules/workflow-stale.d.ts +20 -0
  78. package/dist/doctor/rules/workflow-stale.d.ts.map +1 -0
  79. package/dist/doctor/rules/workflow-stale.js +136 -0
  80. package/dist/doctor/rules/workflow-stale.js.map +1 -0
  81. package/dist/doctor/runner.d.ts +75 -0
  82. package/dist/doctor/runner.d.ts.map +1 -0
  83. package/dist/doctor/runner.js +289 -0
  84. package/dist/doctor/runner.js.map +1 -0
  85. package/dist/doctor/schema-patch.d.ts +21 -0
  86. package/dist/doctor/schema-patch.d.ts.map +1 -0
  87. package/dist/doctor/schema-patch.js +92 -0
  88. package/dist/doctor/schema-patch.js.map +1 -0
  89. package/dist/doctor/types.d.ts +185 -0
  90. package/dist/doctor/types.d.ts.map +1 -0
  91. package/dist/doctor/types.js +13 -0
  92. package/dist/doctor/types.js.map +1 -0
  93. package/dist/frontmatter.d.ts +103 -0
  94. package/dist/frontmatter.d.ts.map +1 -0
  95. package/dist/frontmatter.js +306 -0
  96. package/dist/frontmatter.js.map +1 -0
  97. package/dist/index.d.ts +27 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +27 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/ingest-derive.d.ts +79 -0
  102. package/dist/ingest-derive.d.ts.map +1 -0
  103. package/dist/ingest-derive.js +299 -0
  104. package/dist/ingest-derive.js.map +1 -0
  105. package/dist/ingest-paths.d.ts +37 -0
  106. package/dist/ingest-paths.d.ts.map +1 -0
  107. package/dist/ingest-paths.js +176 -0
  108. package/dist/ingest-paths.js.map +1 -0
  109. package/dist/ingest.d.ts +162 -0
  110. package/dist/ingest.d.ts.map +1 -0
  111. package/dist/ingest.js +269 -0
  112. package/dist/ingest.js.map +1 -0
  113. package/dist/journal.d.ts +49 -0
  114. package/dist/journal.d.ts.map +1 -0
  115. package/dist/journal.js +113 -0
  116. package/dist/journal.js.map +1 -0
  117. package/dist/outline-split.d.ts +38 -0
  118. package/dist/outline-split.d.ts.map +1 -0
  119. package/dist/outline-split.js +84 -0
  120. package/dist/outline-split.js.map +1 -0
  121. package/dist/overrides.d.ts +83 -0
  122. package/dist/overrides.d.ts.map +1 -0
  123. package/dist/overrides.js +88 -0
  124. package/dist/overrides.js.map +1 -0
  125. package/dist/paths.d.ts +183 -0
  126. package/dist/paths.d.ts.map +1 -0
  127. package/dist/paths.js +266 -0
  128. package/dist/paths.js.map +1 -0
  129. package/dist/remark-image-figure.mjs +77 -0
  130. package/dist/remark-strip-first-h1.mjs +26 -0
  131. package/dist/remark-strip-outline.mjs +44 -0
  132. package/dist/rename-slug.d.ts +49 -0
  133. package/dist/rename-slug.d.ts.map +1 -0
  134. package/dist/rename-slug.js +161 -0
  135. package/dist/rename-slug.js.map +1 -0
  136. package/dist/review/handlers.d.ts +55 -0
  137. package/dist/review/handlers.d.ts.map +1 -0
  138. package/dist/review/handlers.js +307 -0
  139. package/dist/review/handlers.js.map +1 -0
  140. package/dist/review/index.d.ts +14 -0
  141. package/dist/review/index.d.ts.map +1 -0
  142. package/dist/review/index.js +13 -0
  143. package/dist/review/index.js.map +1 -0
  144. package/dist/review/journal-mappers.d.ts +35 -0
  145. package/dist/review/journal-mappers.d.ts.map +1 -0
  146. package/dist/review/journal-mappers.js +48 -0
  147. package/dist/review/journal-mappers.js.map +1 -0
  148. package/dist/review/pipeline.d.ts +79 -0
  149. package/dist/review/pipeline.d.ts.map +1 -0
  150. package/dist/review/pipeline.js +234 -0
  151. package/dist/review/pipeline.js.map +1 -0
  152. package/dist/review/render.d.ts +27 -0
  153. package/dist/review/render.d.ts.map +1 -0
  154. package/dist/review/render.js +42 -0
  155. package/dist/review/render.js.map +1 -0
  156. package/dist/review/report.d.ts +50 -0
  157. package/dist/review/report.d.ts.map +1 -0
  158. package/dist/review/report.js +164 -0
  159. package/dist/review/report.js.map +1 -0
  160. package/dist/review/result.d.ts +12 -0
  161. package/dist/review/result.d.ts.map +1 -0
  162. package/dist/review/result.js +12 -0
  163. package/dist/review/result.js.map +1 -0
  164. package/dist/review/start-handlers.d.ts +62 -0
  165. package/dist/review/start-handlers.d.ts.map +1 -0
  166. package/dist/review/start-handlers.js +223 -0
  167. package/dist/review/start-handlers.js.map +1 -0
  168. package/dist/review/types.d.ts +169 -0
  169. package/dist/review/types.d.ts.map +1 -0
  170. package/dist/review/types.js +26 -0
  171. package/dist/review/types.js.map +1 -0
  172. package/dist/review/workflow-paths.d.ts +68 -0
  173. package/dist/review/workflow-paths.d.ts.map +1 -0
  174. package/dist/review/workflow-paths.js +112 -0
  175. package/dist/review/workflow-paths.js.map +1 -0
  176. package/dist/scaffold.d.ts +67 -0
  177. package/dist/scaffold.d.ts.map +1 -0
  178. package/dist/scaffold.js +122 -0
  179. package/dist/scaffold.js.map +1 -0
  180. package/dist/scrapbook.d.ts +229 -0
  181. package/dist/scrapbook.d.ts.map +1 -0
  182. package/dist/scrapbook.js +500 -0
  183. package/dist/scrapbook.js.map +1 -0
  184. package/dist/types.d.ts +197 -0
  185. package/dist/types.d.ts.map +1 -0
  186. package/dist/types.js +120 -0
  187. package/dist/types.js.map +1 -0
  188. package/package.json +160 -0
@@ -0,0 +1,500 @@
1
+ /**
2
+ * Scrapbook helpers — the per-article `<contentDir>/<slug>/scrapbook/`
3
+ * directory. The scrapbook is a working-notes home for receipts,
4
+ * research, and references attached to an in-flight article. Committed
5
+ * to git alongside the article; not baked to the public site.
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
13
+ *
14
+ * The API endpoints that wrap these helpers should 404 in PROD; this
15
+ * library contains no PROD check of its own (enforcement stays at the
16
+ * endpoint boundary).
17
+ */
18
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from 'node:fs';
19
+ import { dirname, extname, join, resolve } from 'node:path';
20
+ import { findEntryFile, resolveBlogPostDir, resolveContentDir } from "./paths.js";
21
+ /** Well-known subdirectory name for editorially-private scrapbook items. */
22
+ export const SECRET_SUBDIR = 'secret';
23
+ // ---------------------------------------------------------------------------
24
+ // Validation
25
+ // ---------------------------------------------------------------------------
26
+ /**
27
+ * A single slug segment — kebab-case lowercase. Used both for flat
28
+ * slugs and as the building block of hierarchical paths.
29
+ */
30
+ const SLUG_SEGMENT_RE = /^[a-z0-9][a-z0-9-]*$/;
31
+ /**
32
+ * A full slug path — one or more `/`-separated kebab-case segments.
33
+ * Accepts both legacy flat slugs ("scsi-over-wifi") and hierarchical
34
+ * paths ("the-outbound/characters/strivers"). No leading or trailing
35
+ * slash; no empty segments.
36
+ */
37
+ const SLUG_RE = /^[a-z0-9][a-z0-9-]*(\/[a-z0-9][a-z0-9-]*)*$/;
38
+ const FILENAME_RE = /^[a-zA-Z0-9._-][a-zA-Z0-9._ -]*$/;
39
+ export function assertSlug(slug) {
40
+ if (!SLUG_RE.test(slug)) {
41
+ throw new Error(`invalid slug "${slug}" — must match ${SLUG_RE}`);
42
+ }
43
+ }
44
+ /**
45
+ * Split a hierarchical slug into its segments. Each segment is a
46
+ * standalone kebab-case identifier.
47
+ */
48
+ export function slugSegments(slug) {
49
+ return slug.split('/');
50
+ }
51
+ /**
52
+ * True if a slug refers to a nested entry (has at least one `/`).
53
+ */
54
+ export function isNestedSlug(slug) {
55
+ return slug.includes('/');
56
+ }
57
+ // `SLUG_SEGMENT_RE` is exported for callers that need to validate one
58
+ // segment at a time (e.g. when assembling a path interactively).
59
+ export { SLUG_SEGMENT_RE };
60
+ export function assertFilename(name) {
61
+ if (!name || name === '.' || name === '..') {
62
+ throw new Error(`invalid filename "${name}"`);
63
+ }
64
+ if (name.includes('/') || name.includes('\\') || name.includes('\0')) {
65
+ throw new Error(`filename may not contain path separators: "${name}"`);
66
+ }
67
+ if (name.startsWith('.')) {
68
+ // Dotfiles are suspicious for a dev-only operator UI. Reject.
69
+ throw new Error(`filename may not start with a dot: "${name}"`);
70
+ }
71
+ if (!FILENAME_RE.test(name)) {
72
+ throw new Error(`filename may only contain [A-Za-z0-9._ -]: "${name}"`);
73
+ }
74
+ if (name.length > 200) {
75
+ throw new Error(`filename too long (> 200 chars): "${name}"`);
76
+ }
77
+ }
78
+ /**
79
+ * Resolve the scrapbook directory for (site, slug) and ensure the
80
+ * return path stays inside the site's content directory.
81
+ * Doesn't require the directory to exist.
82
+ */
83
+ export function scrapbookDir(projectRoot, config, site, slug) {
84
+ assertSlug(slug);
85
+ const articleDir = resolveBlogPostDir(projectRoot, config, site, slug);
86
+ return join(articleDir, 'scrapbook');
87
+ }
88
+ /**
89
+ * Resolve the scrapbook directory for an arbitrary path under the site's
90
+ * content directory. Used by Phase 19c+ callers (e.g. the studio) that
91
+ * already know the fs-relative path of an organizational or tracked node
92
+ * and don't want to re-derive it through the slug regex. The path may
93
+ * contain `/` segments; no `..` or absolute paths allowed.
94
+ *
95
+ * Path-shape validation matches `assertSlug` since the on-disk layout
96
+ * is the same shape — kebab-case segments separated by `/`. Different
97
+ * helper, same constraint.
98
+ */
99
+ export function scrapbookDirAtPath(projectRoot, config, site, relPath) {
100
+ assertSlug(relPath);
101
+ const articleDir = join(resolveContentDir(projectRoot, config, site), relPath);
102
+ return join(articleDir, 'scrapbook');
103
+ }
104
+ /**
105
+ * Resolve the scrapbook directory for a tracked calendar entry.
106
+ *
107
+ * Derives the scrapbook location from the parent directory of the
108
+ * entry's content file (located via `findEntryFile`, which prefers the
109
+ * frontmatter-id index over the slug template). Falls back to today's
110
+ * slug-based addressing for entries that haven't been bound to
111
+ * frontmatter yet (pre-doctor state).
112
+ *
113
+ * Refactor-proof: when the operator renames an entry's directory on
114
+ * disk, the next request rebuilds the index and the scrapbook now
115
+ * lives at the new path automatically.
116
+ *
117
+ * @param entry Calendar entry — `id` preferred (Phase 19+); `slug` is
118
+ * used both as the legacy fallback and to locate the
119
+ * `<dirname>/scrapbook/` when the entry has no id yet.
120
+ * @param index Optional pre-built index (per-request memoization). When
121
+ * omitted, this function builds one.
122
+ */
123
+ export function scrapbookDirForEntry(projectRoot, config, site, entry, index) {
124
+ const entryId = entry.id ?? '';
125
+ const file = findEntryFile(projectRoot, config, site, entryId, index,
126
+ // Legacy fallback ON — we want a usable path even for pre-doctor entries.
127
+ { slug: entry.slug });
128
+ if (file === undefined) {
129
+ // No id binding AND no template fallback resolved (template should
130
+ // always resolve since it's just slug substitution; this branch is
131
+ // defensive for empty slugs / future template variants).
132
+ throw new Error(`Cannot resolve scrapbook dir: entry has no id binding and no template fallback (slug="${entry.slug}")`);
133
+ }
134
+ return join(dirname(file), 'scrapbook');
135
+ }
136
+ /**
137
+ * Resolve a filename INSIDE a scrapbook dir and return the absolute
138
+ * path. Throws if the resolved path escapes the scrapbook dir (guards
139
+ * against `..` sequences that slipped through assertFilename).
140
+ *
141
+ * When `opts.secret` is true, the returned path is rooted at
142
+ * `<scrapbook>/secret/<filename>` instead of `<scrapbook>/<filename>`.
143
+ */
144
+ export function scrapbookFilePath(projectRoot, config, site, slug, filename, opts = {}) {
145
+ assertFilename(filename);
146
+ const dir = scrapbookDir(projectRoot, config, site, slug);
147
+ const target = opts.secret ? join(dir, SECRET_SUBDIR) : dir;
148
+ const abs = resolve(target, filename);
149
+ // Guard either against `..` escape from secret/ AND escape from the
150
+ // top-level scrapbook dir. The base of the containment check is the
151
+ // top-level dir so secret/ always sits inside it.
152
+ if (!abs.startsWith(dir + '/') && abs !== dir) {
153
+ throw new Error(`resolved path escapes scrapbook dir: "${filename}" → ${abs}`);
154
+ }
155
+ return abs;
156
+ }
157
+ // ---------------------------------------------------------------------------
158
+ // Type classification
159
+ // ---------------------------------------------------------------------------
160
+ export function classify(filename) {
161
+ const ext = extname(filename).toLowerCase();
162
+ switch (ext) {
163
+ case '.md':
164
+ case '.markdown':
165
+ return 'md';
166
+ case '.json':
167
+ case '.jsonl':
168
+ return 'json';
169
+ case '.js':
170
+ case '.mjs':
171
+ case '.cjs':
172
+ case '.ts':
173
+ case '.tsx':
174
+ case '.mts':
175
+ return 'js';
176
+ case '.png':
177
+ case '.jpg':
178
+ case '.jpeg':
179
+ case '.gif':
180
+ case '.webp':
181
+ case '.svg':
182
+ return 'img';
183
+ case '.txt':
184
+ case '.log':
185
+ return 'txt';
186
+ default:
187
+ return 'other';
188
+ }
189
+ }
190
+ // ---------------------------------------------------------------------------
191
+ // Listing
192
+ // ---------------------------------------------------------------------------
193
+ /**
194
+ * List the items in a scrapbook, sorted newest-mtime first. Returns
195
+ * both public items (top-level files) and secret items (files inside
196
+ * `scrapbook/secret/`). Subdirectories at the top level OTHER than
197
+ * `secret/` are ignored — deskwork doesn't recurse into arbitrary
198
+ * trees inside a scrapbook.
199
+ */
200
+ export function listScrapbook(projectRoot, config, site, slug) {
201
+ const dir = scrapbookDir(projectRoot, config, site, slug);
202
+ return listScrapbookAtDir(site, slug, dir);
203
+ }
204
+ /**
205
+ * List a scrapbook by absolute directory path. Used by callers that
206
+ * have already resolved the on-disk path via `scrapbookDirForEntry`
207
+ * (id-driven) or `scrapbookDirAtPath` (fs-path-driven) and don't want
208
+ * to re-derive through the slug template. The `slug` parameter is only
209
+ * used to populate the returned summary's identifier field — it does
210
+ * not influence path resolution.
211
+ *
212
+ * Internal primitive shared by `listScrapbook` (slug-based) and
213
+ * `listScrapbookForEntry` (id-driven).
214
+ */
215
+ export function listScrapbookAtDir(site, slug, dir) {
216
+ if (!existsSync(dir)) {
217
+ return { site, slug, dir, exists: false, items: [], secretItems: [] };
218
+ }
219
+ const items = listFilesInDir(dir);
220
+ const secretDir = join(dir, SECRET_SUBDIR);
221
+ const secretItems = existsSync(secretDir) ? listFilesInDir(secretDir) : [];
222
+ return { site, slug, dir, exists: true, items, secretItems };
223
+ }
224
+ /**
225
+ * List scrapbook items for a tracked calendar entry. Resolves the
226
+ * scrapbook directory via the content index when available (id binding),
227
+ * falling back to slug-based addressing for entries that haven't been
228
+ * bound to frontmatter yet (pre-doctor state).
229
+ *
230
+ * Mirrors the shape of `countScrapbookForEntry`. Used by the studio
231
+ * review-page drawer + content-detail panel so writingcontrol-shape
232
+ * entries (where the file path diverges from the slug template) list
233
+ * items at the correct on-disk location.
234
+ *
235
+ * @param entry Calendar entry — `id` preferred; `slug` is both the
236
+ * legacy fallback and the disambiguator the underlying
237
+ * resolver uses when the index is incomplete.
238
+ * @param index Optional pre-built per-request index. When omitted, the
239
+ * resolver builds one on demand.
240
+ */
241
+ export function listScrapbookForEntry(projectRoot, config, site, entry, index) {
242
+ const dir = scrapbookDirForEntry(projectRoot, config, site, entry, index);
243
+ return listScrapbookAtDir(site, entry.slug, dir);
244
+ }
245
+ /** Internal helper — list files (not subdirs/dotfiles) at a given path. */
246
+ function listFilesInDir(dir) {
247
+ const items = [];
248
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
249
+ if (!e.isFile())
250
+ continue;
251
+ if (e.name.startsWith('.'))
252
+ continue;
253
+ const abs = join(dir, e.name);
254
+ const st = statSync(abs);
255
+ items.push({
256
+ name: e.name,
257
+ kind: classify(e.name),
258
+ size: st.size,
259
+ mtime: st.mtime.toISOString(),
260
+ });
261
+ }
262
+ items.sort((a, b) => b.mtime.localeCompare(a.mtime));
263
+ return items;
264
+ }
265
+ /**
266
+ * Count items inside an absolute scrapbook directory — files at the top
267
+ * level plus files inside the `secret/` subdirectory. Returns 0 if the
268
+ * directory doesn't exist; tolerates fs errors so a transient permission
269
+ * issue or race never crashes the dashboard render. Internal primitive
270
+ * shared by `countScrapbook` (slug-based) and `countScrapbookForEntry`
271
+ * (id-driven).
272
+ */
273
+ function countScrapbookAtDir(dir) {
274
+ try {
275
+ if (!existsSync(dir))
276
+ return 0;
277
+ const top = listFilesInDir(dir);
278
+ const secretDir = join(dir, SECRET_SUBDIR);
279
+ const secret = existsSync(secretDir) ? listFilesInDir(secretDir) : [];
280
+ return top.length + secret.length;
281
+ }
282
+ catch {
283
+ return 0;
284
+ }
285
+ }
286
+ /**
287
+ * Total item count (public + secret). Used by the studio chip for the
288
+ * badge — operators want a single "has scrapbook content" signal that
289
+ * counts everything attached to this entry.
290
+ *
291
+ * Slug-based addressing: resolves `<contentDir>/<slug>/scrapbook/`. For
292
+ * entries whose on-disk path doesn't match the slug template (e.g.
293
+ * writingcontrol-shape projects where slug `the-outbound` lives at
294
+ * `projects/the-outbound/index.md`), use `countScrapbookForEntry`
295
+ * instead — it derives the path from the bound file via the content
296
+ * index.
297
+ */
298
+ export function countScrapbook(projectRoot, config, site, slug) {
299
+ try {
300
+ const dir = scrapbookDir(projectRoot, config, site, slug);
301
+ return countScrapbookAtDir(dir);
302
+ }
303
+ catch {
304
+ return 0;
305
+ }
306
+ }
307
+ /**
308
+ * Count scrapbook items for a tracked calendar entry. Resolves the
309
+ * scrapbook directory via the content index when available (id binding),
310
+ * falling back to slug-based addressing for entries that haven't been
311
+ * bound to frontmatter yet (pre-doctor state).
312
+ *
313
+ * Mirrors the shape of `scrapbookDirForEntry` — same resolver, same
314
+ * legacy-slug fallback. Used by the studio dashboard chip so writing-
315
+ * control-shape entries (where the file path diverges from the slug
316
+ * template) report the correct count.
317
+ *
318
+ * @param entry Calendar entry — `id` preferred (Phase 19+); `slug` is
319
+ * both the legacy fallback and the disambiguator the
320
+ * underlying resolver uses when the index is incomplete.
321
+ * @param index Optional pre-built per-request index. When omitted, the
322
+ * resolver builds one on demand.
323
+ */
324
+ export function countScrapbookForEntry(projectRoot, config, site, entry, index) {
325
+ try {
326
+ const dir = scrapbookDirForEntry(projectRoot, config, site, entry, index);
327
+ return countScrapbookAtDir(dir);
328
+ }
329
+ catch {
330
+ return 0;
331
+ }
332
+ }
333
+ // ---------------------------------------------------------------------------
334
+ // CRUD
335
+ // ---------------------------------------------------------------------------
336
+ export function readScrapbookFile(projectRoot, config, site, slug, filename, opts = {}) {
337
+ const abs = scrapbookFilePath(projectRoot, config, site, slug, filename, opts);
338
+ if (!existsSync(abs))
339
+ throw new Error(`not found: ${filename}`);
340
+ const st = statSync(abs);
341
+ if (!st.isFile())
342
+ throw new Error(`not a file: ${filename}`);
343
+ const content = readFileSync(abs);
344
+ return {
345
+ name: filename,
346
+ kind: classify(filename),
347
+ size: st.size,
348
+ mtime: st.mtime.toISOString(),
349
+ content,
350
+ };
351
+ }
352
+ /**
353
+ * Create a new markdown note in the scrapbook. Creates the scrapbook
354
+ * dir (and `secret/` subdir, if needed) if it doesn't exist. Refuses
355
+ * to overwrite existing files.
356
+ */
357
+ export function createScrapbookMarkdown(projectRoot, config, site, slug, filename, body, opts = {}) {
358
+ if (!filename.endsWith('.md')) {
359
+ throw new Error(`create endpoint only accepts .md files: "${filename}"`);
360
+ }
361
+ const abs = scrapbookFilePath(projectRoot, config, site, slug, filename, opts);
362
+ if (existsSync(abs)) {
363
+ throw new Error(`file already exists: "${filename}"`);
364
+ }
365
+ mkdirSync(dirname(abs), { recursive: true });
366
+ writeFileSync(abs, body, 'utf-8');
367
+ const st = statSync(abs);
368
+ return {
369
+ name: filename,
370
+ kind: 'md',
371
+ size: st.size,
372
+ mtime: st.mtime.toISOString(),
373
+ };
374
+ }
375
+ /** Overwrite an existing file's contents. Refuses if the file is absent. */
376
+ export function saveScrapbookFile(projectRoot, config, site, slug, filename, body, opts = {}) {
377
+ const abs = scrapbookFilePath(projectRoot, config, site, slug, filename, opts);
378
+ if (!existsSync(abs))
379
+ throw new Error(`file not found: "${filename}"`);
380
+ writeFileSync(abs, body);
381
+ const st = statSync(abs);
382
+ return {
383
+ name: filename,
384
+ kind: classify(filename),
385
+ size: st.size,
386
+ mtime: st.mtime.toISOString(),
387
+ };
388
+ }
389
+ export function renameScrapbookFile(projectRoot, config, site, slug, oldName, newName, opts = {}) {
390
+ const oldAbs = scrapbookFilePath(projectRoot, config, site, slug, oldName, opts);
391
+ const newAbs = scrapbookFilePath(projectRoot, config, site, slug, newName, opts);
392
+ if (!existsSync(oldAbs))
393
+ throw new Error(`file not found: "${oldName}"`);
394
+ if (existsSync(newAbs) && oldAbs !== newAbs) {
395
+ throw new Error(`target name already exists: "${newName}"`);
396
+ }
397
+ renameSync(oldAbs, newAbs);
398
+ const st = statSync(newAbs);
399
+ return {
400
+ name: newName,
401
+ kind: classify(newName),
402
+ size: st.size,
403
+ mtime: st.mtime.toISOString(),
404
+ };
405
+ }
406
+ export function deleteScrapbookFile(projectRoot, config, site, slug, filename, opts = {}) {
407
+ const abs = scrapbookFilePath(projectRoot, config, site, slug, filename, opts);
408
+ if (!existsSync(abs))
409
+ throw new Error(`file not found: "${filename}"`);
410
+ rmSync(abs);
411
+ }
412
+ /**
413
+ * Seed a scrapbook's `README.md` at plan time. Idempotent — if the
414
+ * README already exists, returns null without touching it. Used by
415
+ * the plan skill so every Planned article gets a scrapbook home with
416
+ * a template that names the article and invites receipts.
417
+ */
418
+ export function seedScrapbookReadme(projectRoot, config, site, slug, title) {
419
+ const abs = scrapbookFilePath(projectRoot, config, site, slug, 'README.md');
420
+ if (existsSync(abs))
421
+ return null;
422
+ const now = new Date().toISOString().slice(0, 10);
423
+ const body = [
424
+ `# Scrapbook — ${title}`,
425
+ '',
426
+ `Planned ${now}. Working notes, research, receipts, and references`,
427
+ `for the \`${slug}\` dispatch. Committed to git alongside the article;`,
428
+ 'not baked to the public site.',
429
+ '',
430
+ '## Receipts',
431
+ '',
432
+ '- ',
433
+ '',
434
+ '## Notes',
435
+ '',
436
+ '- ',
437
+ '',
438
+ '## References',
439
+ '',
440
+ '- ',
441
+ '',
442
+ ].join('\n');
443
+ return createScrapbookMarkdown(projectRoot, config, site, slug, 'README.md', body);
444
+ }
445
+ /**
446
+ * Write an uploaded file into the scrapbook. Filename + content come
447
+ * from the multipart body upstream; we validate and persist.
448
+ */
449
+ export function writeScrapbookUpload(projectRoot, config, site, slug, filename, content, opts = {}) {
450
+ const abs = scrapbookFilePath(projectRoot, config, site, slug, filename, opts);
451
+ if (existsSync(abs)) {
452
+ throw new Error(`file already exists: "${filename}" — rename first`);
453
+ }
454
+ mkdirSync(dirname(abs), { recursive: true });
455
+ writeFileSync(abs, content);
456
+ const st = statSync(abs);
457
+ return {
458
+ name: filename,
459
+ kind: classify(filename),
460
+ size: st.size,
461
+ mtime: st.mtime.toISOString(),
462
+ };
463
+ }
464
+ // ---------------------------------------------------------------------------
465
+ // Formatting helpers (for the UI / chip)
466
+ // ---------------------------------------------------------------------------
467
+ export function formatRelativeTime(iso, now = new Date()) {
468
+ const then = new Date(iso).getTime();
469
+ const diff = now.getTime() - then;
470
+ if (diff < 0)
471
+ return 'just now';
472
+ const s = Math.floor(diff / 1000);
473
+ if (s < 60)
474
+ return `${s}s ago`;
475
+ const m = Math.floor(s / 60);
476
+ if (m < 60)
477
+ return `${m}m ago`;
478
+ const h = Math.floor(m / 60);
479
+ if (h < 48)
480
+ return `${h}h ago`;
481
+ const d = Math.floor(h / 24);
482
+ if (d < 14)
483
+ return `${d}d ago`;
484
+ const w = Math.floor(d / 7);
485
+ if (w < 9)
486
+ return `${w}w ago`;
487
+ const months = Math.floor(d / 30);
488
+ if (months < 18)
489
+ return `${months}mo ago`;
490
+ const y = Math.floor(d / 365);
491
+ return `${y}y ago`;
492
+ }
493
+ export function formatSize(bytes) {
494
+ if (bytes < 1024)
495
+ return `${bytes} B`;
496
+ if (bytes < 1024 * 1024)
497
+ return `${(bytes / 1024).toFixed(1)} KB`;
498
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
499
+ }
500
+ //# sourceMappingURL=scrapbook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrapbook.js","sourceRoot":"","sources":["../src/scrapbook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,MAAM,EACN,QAAQ,EACR,aAAa,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA2ClF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC;AAEtC,8EAA8E;AAC9E,aAAa;AACb,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;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAC1B,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY;IAEZ,UAAU,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,UAAU,GAAG,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACvE,OAAO,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAChC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,OAAe;IAEf,UAAU,CAAC,OAAO,CAAC,CAAC;IACpB,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,oBAAoB,CAClC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,KAAoC,EACpC,KAAoB;IAEpB,MAAM,OAAO,GAAG,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,aAAa,CACxB,WAAW,EACX,MAAM,EACN,IAAI,EACJ,OAAO,EACP,KAAK;IACL,0EAA0E;IAC1E,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CACrB,CAAC;IACF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,mEAAmE;QACnE,mEAAmE;QACnE,yDAAyD;QACzD,MAAM,IAAI,KAAK,CACb,yFAAyF,KAAK,CAAC,IAAI,IAAI,CACxG,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC;AAC1C,CAAC;AAQD;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,OAA0B,EAAE;IAE5B,cAAc,CAAC,QAAQ,CAAC,CAAC;IACzB,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtC,oEAAoE;IACpE,oEAAoE;IACpE,kDAAkD;IAClD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,yCAAyC,QAAQ,OAAO,GAAG,EAAE,CAC9D,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,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;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY;IAEZ,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1D,OAAO,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,IAAY,EACZ,GAAW;IAEX,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IACxE,CAAC;IACD,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3E,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAC/D,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,qBAAqB,CACnC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,KAAoC,EACpC,KAAoB;IAEpB,MAAM,GAAG,GAAG,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAC1E,OAAO,kBAAkB,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACnD,CAAC;AAED,2EAA2E;AAC3E,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE;YAAE,SAAS;QAC1B,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YACtB,IAAI,EAAE,EAAE,CAAC,IAAI;YACb,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE;SAC9B,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,mBAAmB,CAAC,GAAW;IACtC,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC5B,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY;IAEZ,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAC1D,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,KAAoC,EACpC,KAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAC1E,OAAO,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,OAAO;AACP,8EAA8E;AAE9E,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,OAA0B,EAAE;IAQ5B,MAAM,GAAG,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,cAAc,QAAQ,EAAE,CAAC,CAAC;IAChE,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACzB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,EAAE,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC;QACxB,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE;QAC7B,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACrC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,IAAY,EACZ,OAA0B,EAAE;IAE5B,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,4CAA4C,QAAQ,GAAG,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,GAAG,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/E,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,GAAG,CAAC,CAAC;IACxD,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,iBAAiB,CAC/B,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,IAAqB,EACrB,OAA0B,EAAE;IAE5B,MAAM,GAAG,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,GAAG,CAAC,CAAC;IACvE,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzB,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC;QACxB,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,OAAe,EACf,OAAe,EACf,OAA0B,EAAE;IAE5B,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACjF,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACjF,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,OAAO,GAAG,CAAC,CAAC;IACzE,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,gCAAgC,OAAO,GAAG,CAAC,CAAC;IAC9D,CAAC;IACD,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,MAAM,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO;QACL,IAAI,EAAE,OAAO;QACb,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC;QACvB,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,OAA0B,EAAE;IAE5B,MAAM,GAAG,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,GAAG,CAAC,CAAC;IACvE,MAAM,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,KAAa;IAEb,MAAM,GAAG,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IAC5E,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG;QACX,iBAAiB,KAAK,EAAE;QACxB,EAAE;QACF,WAAW,GAAG,qDAAqD;QACnE,aAAa,IAAI,sDAAsD;QACvE,+BAA+B;QAC/B,EAAE;QACF,aAAa;QACb,EAAE;QACF,IAAI;QACJ,EAAE;QACF,UAAU;QACV,EAAE;QACF,IAAI;QACJ,EAAE;QACF,eAAe;QACf,EAAE;QACF,IAAI;QACJ,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,uBAAuB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;AACrF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,WAAmB,EACnB,MAAsB,EACtB,IAAY,EACZ,IAAY,EACZ,QAAgB,EAChB,OAAe,EACf,OAA0B,EAAE;IAE5B,MAAM,GAAG,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC/E,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,kBAAkB,CAAC,CAAC;IACvE,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC5B,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO;QACL,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ,CAAC,QAAQ,CAAC;QACxB,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE;KAC9B,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,yCAAyC;AACzC,8EAA8E;AAE9E,MAAM,UAAU,kBAAkB,CAAC,GAAW,EAAE,MAAY,IAAI,IAAI,EAAE;IACpE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;IAClC,IAAI,IAAI,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC;IAChC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,OAAO,CAAC;IAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,EAAE;QAAE,OAAO,GAAG,MAAM,QAAQ,CAAC;IAC1C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAC;IACtC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;IAClE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;AACpD,CAAC"}