homebridge-plugin-utils 2.0.0 → 2.2.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 (99) hide show
  1. package/build/tsconfig.json +15 -0
  2. package/dist/backpressure.d.ts +1 -1
  3. package/dist/backpressure.js +4 -4
  4. package/dist/backpressure.js.map +1 -1
  5. package/dist/cli/index.d.ts +93 -5
  6. package/dist/cli/index.js +296 -30
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/clock-double.js +2 -2
  9. package/dist/clock-double.js.map +1 -1
  10. package/dist/disposable-stack.d.ts +59 -0
  11. package/dist/disposable-stack.js +155 -0
  12. package/dist/disposable-stack.js.map +1 -0
  13. package/dist/docChrome.d.ts +263 -0
  14. package/dist/docChrome.js +364 -0
  15. package/dist/docChrome.js.map +1 -0
  16. package/dist/featureOptions-docs.d.ts +2 -2
  17. package/dist/featureOptions-docs.js +1 -1
  18. package/dist/featureOptions.d.ts +5 -5
  19. package/dist/featureOptions.js +6 -6
  20. package/dist/ffmpeg/exec.d.ts +2 -2
  21. package/dist/ffmpeg/exec.js +2 -2
  22. package/dist/ffmpeg/fmp4-builders.d.ts +77 -0
  23. package/dist/ffmpeg/fmp4-builders.js +163 -0
  24. package/dist/ffmpeg/fmp4-builders.js.map +1 -0
  25. package/dist/ffmpeg/index.d.ts +1 -0
  26. package/dist/ffmpeg/index.js +1 -0
  27. package/dist/ffmpeg/index.js.map +1 -1
  28. package/dist/ffmpeg/mp4-assembler.d.ts +45 -4
  29. package/dist/ffmpeg/mp4-assembler.js +45 -8
  30. package/dist/ffmpeg/mp4-assembler.js.map +1 -1
  31. package/dist/ffmpeg/mp4-parser.d.ts +1 -1
  32. package/dist/ffmpeg/mp4-parser.js +1 -1
  33. package/dist/ffmpeg/options.d.ts +17 -0
  34. package/dist/ffmpeg/options.js +50 -25
  35. package/dist/ffmpeg/options.js.map +1 -1
  36. package/dist/ffmpeg/process.d.ts +1 -1
  37. package/dist/ffmpeg/process.js +4 -4
  38. package/dist/ffmpeg/process.js.map +1 -1
  39. package/dist/ffmpeg/record.d.ts +26 -1
  40. package/dist/ffmpeg/record.js +12 -0
  41. package/dist/ffmpeg/record.js.map +1 -1
  42. package/dist/ffmpeg/recording-process-double.d.ts +13 -0
  43. package/dist/ffmpeg/recording-process-double.js +22 -0
  44. package/dist/ffmpeg/recording-process-double.js.map +1 -1
  45. package/dist/ffmpeg/rtp-parser.d.ts +1 -1
  46. package/dist/ffmpeg/rtp-parser.js +1 -1
  47. package/dist/ffmpeg/rtp.d.ts +4 -4
  48. package/dist/ffmpeg/rtp.js +7 -6
  49. package/dist/ffmpeg/rtp.js.map +1 -1
  50. package/dist/ffmpeg/stream.js +1 -1
  51. package/dist/ffmpeg/stream.js.map +1 -1
  52. package/dist/index.d.ts +3 -0
  53. package/dist/index.js +3 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/logclient/auth.d.ts +3 -3
  56. package/dist/logclient/auth.js +2 -2
  57. package/dist/logclient/auth.js.map +1 -1
  58. package/dist/logclient/cli-run.js +2 -2
  59. package/dist/logclient/cli-run.js.map +1 -1
  60. package/dist/logclient/cli.js +2 -2
  61. package/dist/logclient/client.js +2 -2
  62. package/dist/logclient/client.js.map +1 -1
  63. package/dist/logclient/rest.js +1 -1
  64. package/dist/logclient/socket-double.d.ts +1 -1
  65. package/dist/logclient/socket-double.js +1 -1
  66. package/dist/logclient/socket.js +5 -5
  67. package/dist/logclient/socket.js.map +1 -1
  68. package/dist/logclient/time-window.d.ts +1 -1
  69. package/dist/mqttClient.d.ts +1 -1
  70. package/dist/mqttClient.js +3 -3
  71. package/dist/mqttClient.js.map +1 -1
  72. package/dist/timer-registry.d.ts +100 -0
  73. package/dist/timer-registry.js +184 -0
  74. package/dist/timer-registry.js.map +1 -0
  75. package/dist/ui/featureOptions.js +6 -6
  76. package/dist/ui/pluginConfigSession.mjs +1 -1
  77. package/dist/ui/webUi-featureOptions/effects/persist.mjs +2 -2
  78. package/dist/ui/webUi-featureOptions/effects/theme.mjs +1 -1
  79. package/dist/ui/webUi-featureOptions/effects/tokens.mjs +1 -1
  80. package/dist/ui/webUi-featureOptions/rendering.mjs +2 -2
  81. package/dist/ui/webUi-featureOptions/selectors.mjs +3 -3
  82. package/dist/ui/webUi-featureOptions/state.mjs +72 -15
  83. package/dist/ui/webUi-featureOptions/store.mjs +1 -1
  84. package/dist/ui/webUi-featureOptions/utils.mjs +25 -1
  85. package/dist/ui/webUi-featureOptions/views/connectionError.mjs +28 -11
  86. package/dist/ui/webUi-featureOptions/views/deviceInfo.mjs +7 -0
  87. package/dist/ui/webUi-featureOptions/views/header.mjs +22 -5
  88. package/dist/ui/webUi-featureOptions/views/nav.mjs +39 -32
  89. package/dist/ui/webUi-featureOptions/views/options.mjs +13 -7
  90. package/dist/ui/webUi-featureOptions/views/search.mjs +2 -2
  91. package/dist/ui/webUi-featureOptions.mjs +109 -61
  92. package/dist/ui/webUi.mjs +48 -11
  93. package/dist/util.d.ts +118 -16
  94. package/dist/util.js +96 -14
  95. package/dist/util.js.map +1 -1
  96. package/dist/webui-loader.d.ts +80 -0
  97. package/dist/webui-loader.js +373 -0
  98. package/dist/webui-loader.js.map +1 -0
  99. package/package.json +10 -10
@@ -0,0 +1,364 @@
1
+ /* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * docChrome.ts: Pure, isomorphic renderers that project a per-plugin documentation-chrome manifest into the masthead, navigation index, dashboard badges, and project
4
+ * list that every family plugin repeats by hand across its README, its docs, and its webUI.
5
+ */
6
+ /**
7
+ * The opening marker of the auto-generated masthead region. See {@link renderMasthead}. The text doubles as an in-document warning not to edit the region by hand.
8
+ *
9
+ * @category Doc Chrome
10
+ */
11
+ export const MASTHEAD_BEGIN = "<!-- MASTHEAD:BEGIN - Auto-generated by homebridge-plugin-utils. Do not edit this region by hand. -->";
12
+ /**
13
+ * The closing marker of the auto-generated masthead region. See {@link MASTHEAD_BEGIN}.
14
+ *
15
+ * @category Doc Chrome
16
+ */
17
+ export const MASTHEAD_END = "<!-- MASTHEAD:END -->";
18
+ /**
19
+ * The opening marker of the auto-generated documentation-index region, used in the README's documentation section, each content doc's footer, and the webUI's Support
20
+ * tab. See {@link renderDocIndex}.
21
+ *
22
+ * @category Doc Chrome
23
+ */
24
+ export const DOCUMENTATION_BEGIN = "<!-- DOCUMENTATION:BEGIN - Auto-generated by homebridge-plugin-utils. Do not edit this region by hand. -->";
25
+ /**
26
+ * The closing marker of the auto-generated documentation-index region. See {@link DOCUMENTATION_BEGIN}.
27
+ *
28
+ * @category Doc Chrome
29
+ */
30
+ export const DOCUMENTATION_END = "<!-- DOCUMENTATION:END -->";
31
+ /**
32
+ * The opening marker of the auto-generated development-dashboard badge region (README only). See {@link renderDevBadges}.
33
+ *
34
+ * @category Doc Chrome
35
+ */
36
+ export const DEV_BADGES_BEGIN = "<!-- DEV BADGES:BEGIN - Auto-generated by homebridge-plugin-utils. Do not edit this region by hand. -->";
37
+ /**
38
+ * The closing marker of the auto-generated development-dashboard badge region. See {@link DEV_BADGES_BEGIN}.
39
+ *
40
+ * @category Doc Chrome
41
+ */
42
+ export const DEV_BADGES_END = "<!-- DEV BADGES:END -->";
43
+ /**
44
+ * The opening marker of the auto-generated project-list region (webUI only). See {@link renderProjects}.
45
+ *
46
+ * @category Doc Chrome
47
+ */
48
+ export const PROJECTS_BEGIN = "<!-- PROJECTS:BEGIN - Auto-generated by homebridge-plugin-utils. Do not edit this region by hand. -->";
49
+ /**
50
+ * The closing marker of the auto-generated project-list region. See {@link PROJECTS_BEGIN}.
51
+ *
52
+ * @category Doc Chrome
53
+ */
54
+ export const PROJECTS_END = "<!-- PROJECTS:END -->";
55
+ // Escape text destined for an HTML text node. We neutralize "&", "<", and ">" so a blurb containing any of them renders as literal text rather than being parsed as
56
+ // markup. "&" is replaced first so the entities we introduce are not themselves re-escaped. This applies only to the webUI (HTML) surface; the markdown surfaces embed
57
+ // author-owned prose verbatim, matching the hand-authored source the renderer reproduces.
58
+ function escapeHtmlText(text) {
59
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
60
+ }
61
+ // Escape a value destined for a double-quoted HTML attribute (an href). It builds on the text-node escaping - the shared single source of truth for the "&"/"<"/">"
62
+ // entities - and adds the double quote that would otherwise terminate the attribute early. The quote substitution runs after the text escaping, so the "&" it introduces
63
+ // is not itself re-escaped.
64
+ function escapeHtmlAttr(value) {
65
+ return escapeHtmlText(value).replaceAll("\"", "&quot;");
66
+ }
67
+ // The absolute GitHub blob base for a repository, the prefix every non-local navigation href is built on.
68
+ function blobBaseUrl(repo) {
69
+ return "https://github.com/" + repo.owner + "/" + repo.name + "/blob/" + repo.branch;
70
+ }
71
+ // Render a single badge as a linked markdown image, the shape both the masthead and the dashboard badges share.
72
+ function renderBadge(badge) {
73
+ return "[![" + badge.alt + "](" + badge.image + ")](" + badge.link + ")";
74
+ }
75
+ // Derive the href for a documentation entry on a given surface. On the README itself a `readme-anchor` entry is an in-page anchor; viewed from anywhere else it must
76
+ // resolve to the README's absolute blob URL. A `doc` entry is always an absolute blob URL to its file, identical on every surface - which is what collapses the
77
+ // per-surface href drift the hand-maintained lists suffer from.
78
+ function docEntryHref(entry, repo, local) {
79
+ if (entry.kind === "readme-anchor") {
80
+ return local ? ("#" + entry.anchor) : (blobBaseUrl(repo) + "/README.md#" + entry.anchor);
81
+ }
82
+ return blobBaseUrl(repo) + "/" + entry.file;
83
+ }
84
+ /**
85
+ * Render the masthead - the centered logo, the H1 title, the ordered badge row, and the H2 tagline - as the markdown/HTML block a plugin embeds at the top of its README
86
+ * and every content doc. The block is identical across those surfaces, so a single manifest drives all of them and the hand-copied mastheads collapse to one.
87
+ *
88
+ * @param manifest - The documentation-chrome manifest.
89
+ *
90
+ * @returns The rendered masthead block.
91
+ *
92
+ * @category Doc Chrome
93
+ */
94
+ export function renderMasthead(manifest) {
95
+ const { badges, logo, tagline, title } = manifest.masthead;
96
+ // Assemble the block line by line: the centering wrapper, the linked logo image, the title, one linked badge image per badge, and the tagline. A pushed empty string
97
+ // is a blank line, keeping the vertical rhythm explicit rather than threading "\n\n" through concatenations.
98
+ const lines = [
99
+ "<SPAN ALIGN=\"CENTER\" STYLE=\"text-align:center\">",
100
+ "<DIV ALIGN=\"CENTER\" STYLE=\"text-align:center\">",
101
+ "",
102
+ "[![" + logo.alt + "](" + logo.src + ")](" + logo.href + ")",
103
+ "",
104
+ "# " + title,
105
+ "",
106
+ ...badges.map(renderBadge),
107
+ "",
108
+ "## " + tagline,
109
+ "</DIV>",
110
+ "</SPAN>"
111
+ ];
112
+ return lines.join("\n");
113
+ }
114
+ /**
115
+ * Render the development-dashboard badges - the README-only badge row (license, build status, dependencies, and the like) that sits apart from the masthead. Returns an
116
+ * empty string when the manifest declares no dashboard badges.
117
+ *
118
+ * @param manifest - The documentation-chrome manifest.
119
+ *
120
+ * @returns The rendered badge rows, one badge per line.
121
+ *
122
+ * @category Doc Chrome
123
+ */
124
+ export function renderDevBadges(manifest) {
125
+ return (manifest.devBadges ?? []).map(renderBadge).join("\n");
126
+ }
127
+ /**
128
+ * Render the documentation index for a surface. On the markdown surfaces (`"readme"`, `"doc-footer"`) the output is one bullet per section with a nested bullet per
129
+ * entry; on `"webui"` it is one `<h5>` heading and `<ul>` per section. Href derivation follows the surface: in-README anchors on `"readme"`, absolute blob URLs
130
+ * elsewhere. A `"doc-footer"` render omits the current document (via `currentFile`) and drops any section left empty by that omission, so a doc's own footer never links
131
+ * back to itself.
132
+ *
133
+ * @param input
134
+ * @param input.currentFile - The doc file being rendered into, relative to the plugin root. Only consulted for the `"doc-footer"` surface, to omit the self-link.
135
+ * @param input.manifest - The documentation-chrome manifest.
136
+ * @param input.surface - The surface to render for.
137
+ *
138
+ * @returns The rendered documentation index.
139
+ *
140
+ * @category Doc Chrome
141
+ */
142
+ export function renderDocIndex({ currentFile, manifest, surface }) {
143
+ if (surface === "webui") {
144
+ // The webUI surface renders HTML and never omits a self-link (it is not itself a doc). Each entry links to an absolute blob URL.
145
+ const sections = manifest.nav.map((section) => {
146
+ const items = section.entries.map((entry) => {
147
+ return " <li><a target=\"_blank\" href=\"" + escapeHtmlAttr(docEntryHref(entry, manifest.repo, false)) + "\">" + escapeHtmlText(entry.title) + "</a>: " +
148
+ escapeHtmlText(entry.blurb) + "</li>";
149
+ });
150
+ return "<h5>" + escapeHtmlText(section.title) + "</h5>\n<ul dir=\"auto\">\n" + items.join("\n") + "\n</ul>";
151
+ });
152
+ return sections.join("\n\n");
153
+ }
154
+ // The markdown surfaces. `"readme"` uses in-page anchors for anchor entries; `"doc-footer"` uses absolute URLs and omits the current doc.
155
+ const local = surface === "readme";
156
+ const omitFile = (surface === "doc-footer") ? currentFile : undefined;
157
+ const sections = [];
158
+ for (const section of manifest.nav) {
159
+ const bullets = section.entries
160
+ .filter((entry) => !((entry.kind === "doc") && (entry.file === omitFile)))
161
+ .map((entry) => " * [" + entry.title + "](" + docEntryHref(entry, manifest.repo, local) + "): " + entry.blurb);
162
+ // Drop a section that the self-omission emptied, so a footer never renders a heading with no entries beneath it.
163
+ if (bullets.length === 0) {
164
+ continue;
165
+ }
166
+ sections.push(["* " + section.title, ...bullets].join("\n"));
167
+ }
168
+ return sections.join("\n\n");
169
+ }
170
+ /**
171
+ * Render the webUI project list - the "other projects" links - as an HTML `<ul>`. The CLI resolves the manifest's project source (inline, local file, or remote URL) to
172
+ * this array before calling; the renderer never performs I/O. Entries are rendered in alphabetical order by title, so the list reads predictably no matter what order the
173
+ * source - often a shared, remotely-fetched file every plugin points at - happens to hold. Each entry's link text is `{title}: {blurb}`.
174
+ *
175
+ * @param projects - The resolved project entries. Their order is not significant; the renderer sorts them by title.
176
+ *
177
+ * @returns The rendered project list.
178
+ *
179
+ * @category Doc Chrome
180
+ */
181
+ export function renderProjects(projects) {
182
+ // Sort a copy by lowercased title so the output is alphabetical and consistent regardless of the source order, without disturbing the caller's array. We compare
183
+ // lowercased titles directly rather than through localeCompare so the ordering is identical on every build machine, independent of the host's locale.
184
+ const ordered = projects.toSorted((left, right) => {
185
+ const leftTitle = left.title.toLowerCase();
186
+ const rightTitle = right.title.toLowerCase();
187
+ if (leftTitle < rightTitle) {
188
+ return -1;
189
+ }
190
+ if (leftTitle > rightTitle) {
191
+ return 1;
192
+ }
193
+ return 0;
194
+ });
195
+ const items = ordered.map((project) => {
196
+ return " <li><a target=\"_blank\" href=\"" + escapeHtmlAttr(project.href) + "\">" + escapeHtmlText(project.title) + ": " +
197
+ escapeHtmlText(project.blurb) + "</a></li>";
198
+ });
199
+ return "<ul dir=\"auto\">\n" + items.join("\n") + "\n</ul>";
200
+ }
201
+ // Throw a uniformly framed validation error naming the manifest source and the offending path, mirroring the diagnostics the catalog validation in the CLI produces.
202
+ function fail(source, detail) {
203
+ throw new Error("Doc-chrome manifest " + source + " " + detail + ".");
204
+ }
205
+ // Assert that a value is a non-empty string at a named path, else fail with a framed diagnostic.
206
+ function assertString(value, path, source) {
207
+ if ((typeof value !== "string") || (value.length === 0)) {
208
+ fail(source, "field `" + path + "` must be a non-empty string");
209
+ }
210
+ }
211
+ // Validate a badge's shape. Shared by the masthead badges and the optional dashboard badges.
212
+ function assertBadge(value, path, source) {
213
+ if ((typeof value !== "object") || (value === null)) {
214
+ fail(source, "field `" + path + "` must be an object");
215
+ }
216
+ const badge = value;
217
+ assertString(badge.alt, path + ".alt", source);
218
+ assertString(badge.image, path + ".image", source);
219
+ assertString(badge.link, path + ".link", source);
220
+ }
221
+ // Validate one documentation entry. The `kind` tag selects which additional field is required.
222
+ function assertDocEntry(value, path, source) {
223
+ if ((typeof value !== "object") || (value === null)) {
224
+ fail(source, "field `" + path + "` must be an object");
225
+ }
226
+ const entry = value;
227
+ assertString(entry.title, path + ".title", source);
228
+ assertString(entry.blurb, path + ".blurb", source);
229
+ switch (entry.kind) {
230
+ case "doc": {
231
+ assertString(entry.file, path + ".file", source);
232
+ if ((entry.footer !== undefined) && (typeof entry.footer !== "boolean")) {
233
+ fail(source, "field `" + path + ".footer` must be a boolean when present");
234
+ }
235
+ if ((entry.masthead !== undefined) && (typeof entry.masthead !== "boolean")) {
236
+ fail(source, "field `" + path + ".masthead` must be a boolean when present");
237
+ }
238
+ return;
239
+ }
240
+ case "readme-anchor": {
241
+ assertString(entry.anchor, path + ".anchor", source);
242
+ return;
243
+ }
244
+ default: {
245
+ fail(source, "field `" + path + ".kind` must be \"doc\" or \"readme-anchor\"");
246
+ }
247
+ }
248
+ }
249
+ /**
250
+ * Validate a loaded value and return it typed as a well-formed {@link DocChromeManifest}, failing fast with a framed diagnostic that names the manifest source and the
251
+ * offending field rather than surfacing an opaque error deep inside a renderer or a bare `resolve()` type error in the CLI. Validates every field the renderer or the
252
+ * CLI consumes - `masthead`, `nav`, `repo`, and the optional `devBadges` and `surfaces`. The project source is validated separately at resolution time, since it may be
253
+ * a remote reference rather than inline data.
254
+ *
255
+ * @param value - The loaded manifest value, untrusted until validated.
256
+ * @param source - The manifest's path or module specifier, for diagnostics.
257
+ *
258
+ * @returns The same value, now typed as a validated {@link DocChromeManifest}.
259
+ *
260
+ * @category Doc Chrome
261
+ */
262
+ export function parseDocChromeManifest(value, source) {
263
+ if ((typeof value !== "object") || (value === null)) {
264
+ fail(source, "must export a manifest object");
265
+ }
266
+ const manifest = value;
267
+ // The masthead and its nested logo and badge row.
268
+ if ((typeof manifest.masthead !== "object") || (manifest.masthead === null)) {
269
+ fail(source, "field `masthead` must be an object");
270
+ }
271
+ const masthead = manifest.masthead;
272
+ assertString(masthead.title, "masthead.title", source);
273
+ assertString(masthead.tagline, "masthead.tagline", source);
274
+ if ((typeof masthead.logo !== "object") || (masthead.logo === null)) {
275
+ fail(source, "field `masthead.logo` must be an object");
276
+ }
277
+ const logo = masthead.logo;
278
+ assertString(logo.alt, "masthead.logo.alt", source);
279
+ assertString(logo.href, "masthead.logo.href", source);
280
+ assertString(logo.src, "masthead.logo.src", source);
281
+ if (!Array.isArray(masthead.badges)) {
282
+ fail(source, "field `masthead.badges` must be an array");
283
+ }
284
+ for (const [index, badge] of masthead.badges.entries()) {
285
+ assertBadge(badge, "masthead.badges[" + String(index) + "]", source);
286
+ }
287
+ // The repository coordinates.
288
+ if ((typeof manifest.repo !== "object") || (manifest.repo === null)) {
289
+ fail(source, "field `repo` must be an object");
290
+ }
291
+ const repo = manifest.repo;
292
+ assertString(repo.branch, "repo.branch", source);
293
+ assertString(repo.name, "repo.name", source);
294
+ assertString(repo.owner, "repo.owner", source);
295
+ // The navigation sections and their entries.
296
+ if (!Array.isArray(manifest.nav)) {
297
+ fail(source, "field `nav` must be an array");
298
+ }
299
+ for (const [sectionIndex, section] of manifest.nav.entries()) {
300
+ if ((typeof section !== "object") || (section === null)) {
301
+ fail(source, "field `nav[" + String(sectionIndex) + "]` must be an object");
302
+ }
303
+ const navSection = section;
304
+ assertString(navSection.title, "nav[" + String(sectionIndex) + "].title", source);
305
+ if (!Array.isArray(navSection.entries)) {
306
+ fail(source, "field `nav[" + String(sectionIndex) + "].entries` must be an array");
307
+ }
308
+ for (const [entryIndex, entry] of navSection.entries.entries()) {
309
+ assertDocEntry(entry, "nav[" + String(sectionIndex) + "].entries[" + String(entryIndex) + "]", source);
310
+ }
311
+ }
312
+ // The optional dashboard badges.
313
+ if (manifest.devBadges !== undefined) {
314
+ if (!Array.isArray(manifest.devBadges)) {
315
+ fail(source, "field `devBadges` must be an array when present");
316
+ }
317
+ for (const [index, badge] of manifest.devBadges.entries()) {
318
+ assertBadge(badge, "devBadges[" + String(index) + "]", source);
319
+ }
320
+ }
321
+ // The optional per-surface path overrides. These are consumed by the CLI's `resolve()` calls, so validating them here keeps the framed-diagnostic guarantee whole for
322
+ // a JSON-authored manifest that could otherwise smuggle a non-string path past the type system.
323
+ if (manifest.surfaces !== undefined) {
324
+ if ((typeof manifest.surfaces !== "object") || (manifest.surfaces === null)) {
325
+ fail(source, "field `surfaces` must be an object when present");
326
+ }
327
+ const surfaces = manifest.surfaces;
328
+ if ((surfaces.readme !== undefined) && ((typeof surfaces.readme !== "string") || (surfaces.readme.length === 0))) {
329
+ fail(source, "field `surfaces.readme` must be a non-empty string when present");
330
+ }
331
+ if ((surfaces.webui !== undefined) && ((typeof surfaces.webui !== "string") || (surfaces.webui.length === 0))) {
332
+ fail(source, "field `surfaces.webui` must be a non-empty string when present");
333
+ }
334
+ }
335
+ return value;
336
+ }
337
+ /**
338
+ * Validate a resolved project source and return it typed as an array of {@link ProjectEntry}. The CLI calls this after resolving the manifest's project source - which
339
+ * may be inline data, a local file, or a remote URL - so a malformed external list fails fast with a framed diagnostic naming the source rather than rendering broken
340
+ * markup.
341
+ *
342
+ * @param value - The resolved project value, untrusted until validated.
343
+ * @param source - The project source (a path or URL) for diagnostics.
344
+ *
345
+ * @returns The same value, now typed as a validated array of {@link ProjectEntry}.
346
+ *
347
+ * @category Doc Chrome
348
+ */
349
+ export function parseProjectEntries(value, source) {
350
+ if (!Array.isArray(value)) {
351
+ fail(source, "project source must resolve to an array of project entries");
352
+ }
353
+ for (const [index, entry] of value.entries()) {
354
+ if ((typeof entry !== "object") || (entry === null)) {
355
+ fail(source, "project entry [" + String(index) + "] must be an object");
356
+ }
357
+ const project = entry;
358
+ assertString(project.blurb, "project entry [" + String(index) + "].blurb", source);
359
+ assertString(project.href, "project entry [" + String(index) + "].href", source);
360
+ assertString(project.title, "project entry [" + String(index) + "].title", source);
361
+ }
362
+ return value;
363
+ }
364
+ //# sourceMappingURL=docChrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docChrome.js","sourceRoot":"","sources":["../src/docChrome.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAiIH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,uGAAuG,CAAC;AAEtI;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,4GAA4G,CAAC;AAEhJ;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,yGAAyG,CAAC;AAE1I;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,yBAAyB,CAAC;AAExD;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,uGAAuG,CAAC;AAEtI;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAC;AAEpD,oKAAoK;AACpK,uKAAuK;AACvK,0FAA0F;AAC1F,SAAS,cAAc,CAAC,IAAY;IAElC,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACvF,CAAC;AAED,oKAAoK;AACpK,yKAAyK;AACzK,4BAA4B;AAC5B,SAAS,cAAc,CAAC,KAAa;IAEnC,OAAO,cAAc,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED,0GAA0G;AAC1G,SAAS,WAAW,CAAC,IAAqB;IAExC,OAAO,qBAAqB,GAAG,IAAI,CAAC,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC;AACvF,CAAC;AAED,gHAAgH;AAChH,SAAS,WAAW,CAAC,KAAY;IAE/B,OAAO,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,IAAI,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC;AAC3E,CAAC;AAED,qKAAqK;AACrK,gKAAgK;AAChK,gEAAgE;AAChE,SAAS,YAAY,CAAC,KAAe,EAAE,IAAqB,EAAE,KAAc;IAE1E,IAAG,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QAElC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC;AAC9C,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,QAA2B;IAExD,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,QAAQ,CAAC;IAE3D,qKAAqK;IACrK,6GAA6G;IAC7G,MAAM,KAAK,GAAG;QAEZ,qDAAqD;QACrD,oDAAoD;QACpD,EAAE;QACF,KAAK,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,GAAG,KAAK,GAAG,IAAI,CAAC,IAAI,GAAG,GAAG;QAC5D,EAAE;QACF,IAAI,GAAG,KAAK;QACZ,EAAE;QACF,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;QAC1B,EAAE;QACF,KAAK,GAAG,OAAO;QACf,QAAQ;QACR,SAAS;KACV,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,QAA2B;IAEzD,OAAO,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAA8E;IAE3I,IAAG,OAAO,KAAK,OAAO,EAAE,CAAC;QAEvB,iIAAiI;QACjI,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;YAE5C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBAE1C,OAAO,oCAAoC,GAAG,cAAc,CAAC,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,KAAK,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,QAAQ;oBACtJ,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;YAC1C,CAAC,CAAC,CAAC;YAEH,OAAO,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,4BAA4B,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;QAC9G,CAAC,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC;IAED,0IAA0I;IAC1I,MAAM,KAAK,GAAG,OAAO,KAAK,QAAQ,CAAC;IACnC,MAAM,QAAQ,GAAG,CAAC,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;IACtE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAI,MAAM,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE,CAAC;QAElC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;aAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC;aACzE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,GAAG,KAAK,CAAC,KAAK,GAAG,IAAI,GAAG,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QAElH,iHAAiH;QACjH,IAAG,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAExB,SAAS;QACX,CAAC;QAED,QAAQ,CAAC,IAAI,CAAC,CAAE,IAAI,GAAG,OAAO,CAAC,KAAK,EAAE,GAAG,OAAO,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,QAAiC;IAE9D,iKAAiK;IACjK,sJAAsJ;IACtJ,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAEhD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;QAE7C,IAAG,SAAS,GAAG,UAAU,EAAE,CAAC;YAE1B,OAAO,CAAC,CAAC,CAAC;QACZ,CAAC;QAED,IAAG,SAAS,GAAG,UAAU,EAAE,CAAC;YAE1B,OAAO,CAAC,CAAC;QACX,CAAC;QAED,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAEpC,OAAO,oCAAoC,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,IAAI;YACvH,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,OAAO,qBAAqB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;AAC9D,CAAC;AAED,qKAAqK;AACrK,SAAS,IAAI,CAAC,MAAc,EAAE,MAAc;IAE1C,MAAM,IAAI,KAAK,CAAC,sBAAsB,GAAG,MAAM,GAAG,GAAG,GAAG,MAAM,GAAG,GAAG,CAAC,CAAC;AACxE,CAAC;AAED,iGAAiG;AACjG,SAAS,YAAY,CAAC,KAAc,EAAE,IAAY,EAAE,MAAc;IAEhE,IAAG,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QAEvD,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,8BAA8B,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,6FAA6F;AAC7F,SAAS,WAAW,CAAC,KAAc,EAAE,IAAY,EAAE,MAAc;IAE/D,IAAG,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC;QAEnD,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,qBAAqB,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,KAAK,GAAG,KAA2D,CAAC;IAE1E,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnD,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;AACnD,CAAC;AAED,+FAA+F;AAC/F,SAAS,cAAc,CAAC,KAAc,EAAE,IAAY,EAAE,MAAc;IAElE,IAAG,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC;QAEnD,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,qBAAqB,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,KAAK,GAAG,KAAqI,CAAC;IAEpJ,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnD,YAAY,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,GAAG,QAAQ,EAAE,MAAM,CAAC,CAAC;IAEnD,QAAO,KAAK,CAAC,IAAI,EAAE,CAAC;QAElB,KAAK,KAAK,CAAC,CAAC,CAAC;YAEX,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC;YAEjD,IAAG,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,EAAE,CAAC;gBAEvE,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,yCAAyC,CAAC,CAAC;YAC7E,CAAC;YAED,IAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,EAAE,CAAC;gBAE3E,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,2CAA2C,CAAC,CAAC;YAC/E,CAAC;YAED,OAAO;QACT,CAAC;QAED,KAAK,eAAe,CAAC,CAAC,CAAC;YAErB,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC;YAErD,OAAO;QACT,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YAER,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,6CAA6C,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc,EAAE,MAAc;IAEnE,IAAG,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC;QAEnD,IAAI,CAAC,MAAM,EAAE,+BAA+B,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,QAAQ,GAAG,KAAuG,CAAC;IAEzH,kDAAkD;IAClD,IAAG,CAAC,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,EAAE,CAAC;QAE3E,IAAI,CAAC,MAAM,EAAE,oCAAoC,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAoF,CAAC;IAE/G,YAAY,CAAC,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACvD,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,EAAE,MAAM,CAAC,CAAC;IAE3D,IAAG,CAAC,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAEnE,IAAI,CAAC,MAAM,EAAE,yCAAyC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAwD,CAAC;IAE/E,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;IACpD,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,oBAAoB,EAAE,MAAM,CAAC,CAAC;IACtD,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAEpD,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAEnC,IAAI,CAAC,MAAM,EAAE,0CAA0C,CAAC,CAAC;IAC3D,CAAC;IAED,KAAI,MAAM,CAAE,KAAK,EAAE,KAAK,CAAE,IAAI,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAExD,WAAW,CAAC,KAAK,EAAE,kBAAkB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,CAAC,CAAC;IACvE,CAAC;IAED,8BAA8B;IAC9B,IAAG,CAAC,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QAEnE,IAAI,CAAC,MAAM,EAAE,gCAAgC,CAAC,CAAC;IACjD,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,IAA6D,CAAC;IAEpF,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,CAAC;IACjD,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAC7C,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAE/C,6CAA6C;IAC7C,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAEhC,IAAI,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC;IAC/C,CAAC;IAED,KAAI,MAAM,CAAE,YAAY,EAAE,OAAO,CAAE,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;QAE9D,IAAG,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAC,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;YAEvD,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,sBAAsB,CAAC,CAAC;QAC9E,CAAC;QAED,MAAM,UAAU,GAAG,OAAiD,CAAC;QAErE,YAAY,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC;QAElF,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAEtC,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,6BAA6B,CAAC,CAAC;QACrF,CAAC;QAED,KAAI,MAAM,CAAE,UAAU,EAAE,KAAK,CAAE,IAAI,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YAEhE,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,YAAY,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,GAAG,EAAE,MAAM,CAAC,CAAC;QACzG,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,IAAG,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAEpC,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAEtC,IAAI,CAAC,MAAM,EAAE,iDAAiD,CAAC,CAAC;QAClE,CAAC;QAED,KAAI,MAAM,CAAE,KAAK,EAAE,KAAK,CAAE,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;YAE3D,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,sKAAsK;IACtK,gGAAgG;IAChG,IAAG,QAAQ,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAEnC,IAAG,CAAC,OAAO,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,KAAK,IAAI,CAAC,EAAE,CAAC;YAE3E,IAAI,CAAC,MAAM,EAAE,iDAAiD,CAAC,CAAC;QAClE,CAAC;QAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAiD,CAAC;QAE5E,IAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAEhH,IAAI,CAAC,MAAM,EAAE,iEAAiE,CAAC,CAAC;QAClF,CAAC;QAED,IAAG,CAAC,QAAQ,CAAC,KAAK,KAAK,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAE7G,IAAI,CAAC,MAAM,EAAE,gEAAgE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,OAAO,KAA0B,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAc,EAAE,MAAc;IAEhE,IAAG,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAEzB,IAAI,CAAC,MAAM,EAAE,4DAA4D,CAAC,CAAC;IAC7E,CAAC;IAED,KAAI,MAAM,CAAE,KAAK,EAAE,KAAK,CAAE,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAE9C,IAAG,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,EAAE,CAAC;YAEnD,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,qBAAqB,CAAC,CAAC;QAC1E,CAAC;QAED,MAAM,OAAO,GAAG,KAA6D,CAAC;QAE9E,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC;QACnF,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,EAAE,MAAM,CAAC,CAAC;QACjF,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,EAAE,MAAM,CAAC,CAAC;IACrF,CAAC;IAED,OAAO,KAAgC,CAAC;AAC1C,CAAC"}
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Every plugin in the family used to ship a near-duplicate `*-gendocs.ts` script that walked its feature-options catalog and printed a markdown category index plus
5
5
  * per-category option tables, then pasted the result into its `docs/FeatureOptions.md` by hand. ~95% of each script was identical, and each hand-rolled the dotted-key
6
- * construction and the value-vs-toggle discrimination that this library already owns as single-source-of-truth helpers. This module collapses all of that into one
6
+ * construction and the value-vs-toggle distinction that this library already owns as single-source-of-truth helpers. This module collapses all of that into one
7
7
  * elegant renderer so the documentation becomes a pure projection of the live catalog.
8
8
  *
9
9
  * The module exports two pure string functions:
@@ -75,7 +75,7 @@ export declare function renderFeatureOptionsReference<TOptionMeta = unknown, TCa
75
75
  * `writeFile` around it.
76
76
  *
77
77
  * The replacement inserts a newline before and after `content`, so a marker pair on its own lines stays on its own lines and the rendered fragment is cleanly framed.
78
- * The operation is idempotent: splicing the same `content` into an already-spliced document reproduces it byte-for-byte. Each marker may be overridden through the
78
+ * The operation is repeatable: splicing the same `content` into an already-spliced document reproduces it byte-for-byte. Each marker may be overridden through the
79
79
  * options object for documents that use a different convention, though the defaults match what every plugin in the family embeds.
80
80
  *
81
81
  * @param source - The full document text.
@@ -149,7 +149,7 @@ export function renderFeatureOptionsReference(input) {
149
149
  * `writeFile` around it.
150
150
  *
151
151
  * The replacement inserts a newline before and after `content`, so a marker pair on its own lines stays on its own lines and the rendered fragment is cleanly framed.
152
- * The operation is idempotent: splicing the same `content` into an already-spliced document reproduces it byte-for-byte. Each marker may be overridden through the
152
+ * The operation is repeatable: splicing the same `content` into an already-spliced document reproduces it byte-for-byte. Each marker may be overridden through the
153
153
  * options object for documents that use a different convention, though the defaults match what every plugin in the family embeds.
154
154
  *
155
155
  * @param source - The full document text.
@@ -26,7 +26,7 @@ import type { HomebridgePluginLogging, Nullable } from "./util.ts";
26
26
  * available for bespoke needs that the registry does not cover.
27
27
  *
28
28
  * The set targets the unit categories that recur across plugin catalogs: bitrate (in either of the two common storage conventions), data size, percentages, and
29
- * durations. Extend the union when a new shared format becomes load-bearing across multiple plugins. Resist adding a formatter speculatively - the function escape
29
+ * durations. Extend the union when a new format is genuinely shared across multiple plugins. Resist adding a formatter speculatively - the function escape
30
30
  * hatch already covers one-off needs, and an unused formatter is dead surface that downstream plugins still see in their IDE autocomplete.
31
31
  *
32
32
  * @category Feature Options
@@ -43,7 +43,7 @@ export type FeatureOptionFormatter = "bps" | "bytes" | "kbps" | "ms" | "percent"
43
43
  * @property meta - Optional. An opaque, plugin-private annotation channel the core never interprets. HBPU's types deliberately cannot see inside `TMeta`;
44
44
  * the value is carried verbatim through the catalog and forwarded to the documentation renderer's closures (the only surface that knows its
45
45
  * concrete shape). This mirrors the OpenAPI `x-*` extension discipline, made type-safe: a plugin parameterizes the entry with its own
46
- * annotation type, the core treats it as `unknown`, and the round-trip stays structurally invariant rather than a naming convention.
46
+ * annotation type, the core treats it as `unknown`, and the round-trip stays structurally unchanged rather than a naming convention.
47
47
  * @property name - Name of the feature option (used in option strings).
48
48
  * @property render - Optional. Maps the raw stored value of a value-centric option to a display string. Either a {@link FeatureOptionFormatter} string naming
49
49
  * a built-in formatter (preferred when the format already exists in the registry, since this keeps the enclosing catalog JSON-serializable
@@ -105,7 +105,7 @@ export interface ResolvedOptionEntry {
105
105
  * Immutable derived index over the catalog inputs ({@link FeatureCategoryEntry}[] + the options map). Every field except `categories` / `options` is derived from
106
106
  * those two; the index bundles them with their derivations so a single value carries everything any caller needs to make catalog-level decisions in O(1).
107
107
  *
108
- * The index is built once per catalog at {@link buildCatalogIndex}; it is invariant across configured-options mutations, so a consumer that holds a stable
108
+ * The index is built once per catalog at {@link buildCatalogIndex}; it is unchanged across configured-options mutations, so a consumer that holds a stable
109
109
  * reference can rely on its query results until the catalog itself changes. The {@link FeatureOptions} class holds one internally; consumers driving reducers
110
110
  * directly hold it as state and reuse it across every dispatch that does not touch the catalog.
111
111
  *
@@ -188,7 +188,7 @@ export declare function expandOption(category: FeatureCategoryEntry | string, op
188
188
  * name on a `render` declaration does not resolve, surfacing the misconfiguration at load time rather than silently degrading the log-emission path.
189
189
  *
190
190
  * The index is the catalog-side input to every other pure helper in this module. Build it once per catalog; reuse it across every configured-options mutation
191
- * because the catalog is invariant across those mutations. Categories without an entry in the options map are skipped silently (a plugin defines a category for
191
+ * because the catalog is unchanged across those mutations. Categories without an entry in the options map are skipped silently (a plugin defines a category for
192
192
  * future expansion before any option has migrated into it).
193
193
  *
194
194
  * @param categories - The raw category list.
@@ -557,7 +557,7 @@ export declare class FeatureOptions {
557
557
  *
558
558
  * Callers express intent ("forget any configuration for option X at scope Y") and the model owns the entry-format end-to-end. The match is value-aware: for
559
559
  * value-centric options it covers both the bare scoped entry and any entry carrying a single trailing value segment, so a subsequent {@link setOption} cleanly
560
- * replaces whatever was there. No-op when no entry addresses the target scope, so callers can treat this as an idempotent reset.
560
+ * replaces whatever was there. No-op when no entry addresses the target scope, so callers can treat this as a repeatable reset.
561
561
  *
562
562
  * @param args - The addressing intent: option key and optional scope id. See {@link ClearOptionArgs}.
563
563
  *
@@ -136,7 +136,7 @@ function entryAddressesScope({ catalog, rawEntry, target }) {
136
136
  * name on a `render` declaration does not resolve, surfacing the misconfiguration at load time rather than silently degrading the log-emission path.
137
137
  *
138
138
  * The index is the catalog-side input to every other pure helper in this module. Build it once per catalog; reuse it across every configured-options mutation
139
- * because the catalog is invariant across those mutations. Categories without an entry in the options map are skipped silently (a plugin defines a category for
139
+ * because the catalog is unchanged across those mutations. Categories without an entry in the options map are skipped silently (a plugin defines a category for
140
140
  * future expansion before any option has migrated into it).
141
141
  *
142
142
  * @param categories - The raw category list.
@@ -681,7 +681,7 @@ export class FeatureOptions {
681
681
  *
682
682
  * Callers express intent ("forget any configuration for option X at scope Y") and the model owns the entry-format end-to-end. The match is value-aware: for
683
683
  * value-centric options it covers both the bare scoped entry and any entry carrying a single trailing value segment, so a subsequent {@link setOption} cleanly
684
- * replaces whatever was there. No-op when no entry addresses the target scope, so callers can treat this as an idempotent reset.
684
+ * replaces whatever was there. No-op when no entry addresses the target scope, so callers can treat this as a repeatable reset.
685
685
  *
686
686
  * @param args - The addressing intent: option key and optional scope id. See {@link ClearOptionArgs}.
687
687
  *
@@ -700,7 +700,7 @@ export class FeatureOptions {
700
700
  return;
701
701
  }
702
702
  this.#configuredOptions = next;
703
- // Only the index depends on the configured-options array; the catalog-derived state is invariant across config mutations and need not be touched here.
703
+ // Only the index depends on the configured-options array; the catalog-derived state is unchanged across config mutations and need not be touched here.
704
704
  this.#configIndex = buildConfigIndex(this.#catalog, this.#configuredOptions);
705
705
  }
706
706
  /**
@@ -725,7 +725,7 @@ export class FeatureOptions {
725
725
  */
726
726
  setOption(args) {
727
727
  this.#configuredOptions = applySetOption({ args, catalog: this.#catalog, configuredOptions: this.#configuredOptions });
728
- // Only the index depends on the configured-options array; the catalog-derived state is invariant across config mutations and need not be touched here.
728
+ // Only the index depends on the configured-options array; the catalog-derived state is unchanged across config mutations and need not be touched here.
729
729
  this.#configIndex = buildConfigIndex(this.#catalog, this.#configuredOptions);
730
730
  }
731
731
  /**
@@ -791,7 +791,7 @@ export class FeatureOptions {
791
791
  * @returns Returns the current list of available feature option categories.
792
792
  */
793
793
  get categories() {
794
- // The catalog stores the categories as readonly to encode the immutability invariant the pure functional core relies on. The public getter returns the
794
+ // The catalog stores the categories as readonly to encode the immutability guarantee the pure functional core relies on. The public getter returns the
795
795
  // historical mutable type for backward compatibility - the array is the same identity the caller passed at construction (or via the setter), so consumers
796
796
  // mutating it would be mutating the catalog regardless of the return type. The readonly annotation is the discipline, not a runtime enforcement.
797
797
  return this.#catalog.categories;
@@ -823,7 +823,7 @@ export class FeatureOptions {
823
823
  */
824
824
  set configuredOptions(options) {
825
825
  this.#configuredOptions = options ?? [];
826
- // The catalog-derived state is invariant across config mutations; only the lookup index needs rebuilding.
826
+ // The catalog-derived state is unchanged across config mutations; only the lookup index needs rebuilding.
827
827
  this.#configIndex = buildConfigIndex(this.#catalog, this.#configuredOptions);
828
828
  }
829
829
  /**
@@ -98,7 +98,7 @@ export declare class FfmpegExec extends FfmpegProcess {
98
98
  * path does not normally produce a stream error, so an aborted run yields whatever bytes happened to arrive before the kill landed - which may be all of them, some
99
99
  * of them, or none of them.
100
100
  *
101
- * This promise is the data channel only. To discriminate "the child wrote nothing" from "the run was aborted before the child could write anything," consult
101
+ * This promise is the data channel only. To distinguish "the child wrote nothing" from "the run was aborted before the child could write anything," consult
102
102
  * {@link FfmpegExec.exited} (`exitCode` / `exitSignal`) and {@link FfmpegProcess.signal} (`signal.reason`). Those are the single source of truth for process
103
103
  * disposition; an empty buffer carries no disposition meaning on its own.
104
104
  */
@@ -117,7 +117,7 @@ export declare class FfmpegExec extends FfmpegProcess {
117
117
  * Await the process to completion and return the bundled result.
118
118
  *
119
119
  * Resolves once both the stdout collector and the base class's `exited` promise settle. Rejects with the same reason `exited` would reject with (today, only when
120
- * the child never spawned - e.g., ENOENT). On any normal exit, including non-zero exit codes, this method resolves; callers discriminate outcomes by inspecting
120
+ * the child never spawned - e.g., ENOENT). On any normal exit, including non-zero exit codes, this method resolves; callers distinguish outcomes by inspecting
121
121
  * `exitCode` and `exitSignal` in the result, or the derived `hasError` getter on the instance.
122
122
  *
123
123
  * @returns A promise resolving to an {@link ExecResult} bundling stdout, exit code, exit signal, and the accumulated stderr log.
@@ -47,7 +47,7 @@ export class FfmpegExec extends FfmpegProcess {
47
47
  * path does not normally produce a stream error, so an aborted run yields whatever bytes happened to arrive before the kill landed - which may be all of them, some
48
48
  * of them, or none of them.
49
49
  *
50
- * This promise is the data channel only. To discriminate "the child wrote nothing" from "the run was aborted before the child could write anything," consult
50
+ * This promise is the data channel only. To distinguish "the child wrote nothing" from "the run was aborted before the child could write anything," consult
51
51
  * {@link FfmpegExec.exited} (`exitCode` / `exitSignal`) and {@link FfmpegProcess.signal} (`signal.reason`). Those are the single source of truth for process
52
52
  * disposition; an empty buffer carries no disposition meaning on its own.
53
53
  */
@@ -88,7 +88,7 @@ export class FfmpegExec extends FfmpegProcess {
88
88
  * Await the process to completion and return the bundled result.
89
89
  *
90
90
  * Resolves once both the stdout collector and the base class's `exited` promise settle. Rejects with the same reason `exited` would reject with (today, only when
91
- * the child never spawned - e.g., ENOENT). On any normal exit, including non-zero exit codes, this method resolves; callers discriminate outcomes by inspecting
91
+ * the child never spawned - e.g., ENOENT). On any normal exit, including non-zero exit codes, this method resolves; callers distinguish outcomes by inspecting
92
92
  * `exitCode` and `exitSignal` in the result, or the derived `hasError` getter on the instance.
93
93
  *
94
94
  * @returns A promise resolving to an {@link ExecResult} bundling stdout, exit code, exit signal, and the accumulated stderr log.
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Handler-type code for video tracks in ISO BMFF `hdlr` boxes: ASCII `"vide"` encoded as a 32-bit big-endian integer. Defined here rather than in `fmp4.ts` because
3
+ * production never inspects it - `hasAudioTrack` compares each track's handler_type against `HDLR_TYPE_SOUN` and any non-match (including `"vide"`) is treated
4
+ * uniformly as "not audio." Tests need a concrete non-audio value to exercise the negative path of that predicate, so the constant lives with the test builders.
5
+ */
6
+ export declare const HDLR_TYPE_VIDE = 1986618469;
7
+ /**
8
+ * Synthesize a minimal ISO BMFF box. Produces a Buffer whose first 4 bytes are the big-endian total size, the next 4 bytes are the 4-character ASCII `type`, and the
9
+ * remainder is the supplied payload. Each test file wants the same construction helper to exercise different stream shapes; keeping it in one place eliminates drift
10
+ * between the parser and assembler test suites.
11
+ *
12
+ * @param type - The 4-character ASCII box type (`"ftyp"`, `"moov"`, `"moof"`, `"mdat"`, ...). Any other length fails the check.
13
+ * @param payload - Optional opaque payload bytes. Defaults to an empty buffer.
14
+ *
15
+ * @returns A complete box (header + payload) suitable for feeding to the parser or writing to an in-memory Readable fixture.
16
+ */
17
+ export declare function makeBox(type: string, payload?: Buffer): Buffer;
18
+ /**
19
+ * Convenience wrapper that builds a container box from a sequence of already-constructed child boxes. The payload is just the concatenation of children, so the
20
+ * result is equivalent to `makeBox(type, Buffer.concat(children))` - this helper's value is readability at the call site, where nested structures like
21
+ * `moov -> [ trak -> [ mdia -> [ hdlr ] ] ]` become one-liners instead of a visual ladder of `Buffer.concat` calls.
22
+ *
23
+ * @param type - The 4-character ASCII box type.
24
+ * @param children - The pre-built child boxes to include as the container's payload.
25
+ *
26
+ * @returns A complete container box.
27
+ *
28
+ * @example
29
+ *
30
+ * ```ts
31
+ * const hdlr = makeHdlrBox(HDLR_TYPE_SOUN);
32
+ * const mdia = makeContainer("mdia", [hdlr]);
33
+ * const trak = makeContainer("trak", [mdia]);
34
+ * const moov = makeContainer("moov", [trak]);
35
+ * ```
36
+ */
37
+ export declare function makeContainer(type: string, children: Buffer[]): Buffer;
38
+ /**
39
+ * Build a `hdlr` fullbox with the supplied handler_type code. The fullbox layout (box header + version/flags + pre_defined + handler_type + reserved[3] + name) is
40
+ * exactly what `hasAudioTrack` reads when deciding whether a track is audio. Tests compose an initialization segment by wrapping an hdlr inside `moov -> trak -> mdia`,
41
+ * with the handler_type selecting the track media type under test.
42
+ *
43
+ * @param handlerType - The 32-bit big-endian handler type code. Use `HDLR_TYPE_SOUN` for audio, {@link HDLR_TYPE_VIDE} for video, or any other value to
44
+ * exercise the "unknown handler" negative path.
45
+ * @param truncate - Optional. When `true`, emits an hdlr whose payload is smaller than the minimum `hasAudioTrack` reads (stops before the handler_type field). Used
46
+ * by the "undersized hdlr" negative test; defaults to `false`.
47
+ *
48
+ * @returns A complete `hdlr` box.
49
+ */
50
+ export declare function makeHdlrBox(handlerType: number, truncate?: boolean): Buffer;
51
+ /**
52
+ * Build a `trun` fullbox describing a single sample, with the flags and sample-flags bits set to select one of the keyframe-detection paths `isKeyframe` supports.
53
+ * `isKeyframe` reads the flags to decide whether to consult `first_sample_flags` (common path, when `frag_keyframe` movflag is set) or fall back to the per-sample
54
+ * `sample_flags` field. The helper composes the optional fields in the exact order the spec mandates so the predicate's offset arithmetic matches.
55
+ *
56
+ * @param options - Options that shape the emitted box.
57
+ * @param options.useFirstSampleFlags - When `true`, set `TRUN_FLAG_FIRST_SAMPLE_FLAGS` and emit the first-sample-flags field after the optional data_offset.
58
+ * @param options.usePerSampleFlags - When `true`, set `TRUN_FLAG_SAMPLE_FLAGS` and emit a single per-sample flags field after any duration/size slots. Ignored when
59
+ * `useFirstSampleFlags` is also `true` because the predicate consults first_sample_flags first.
60
+ * @param options.includeDuration - When `true`, set `TRUN_FLAG_SAMPLE_DURATION` and reserve a 4-byte per-sample duration slot before the flags slot.
61
+ * @param options.includeSize - When `true`, set `TRUN_FLAG_SAMPLE_SIZE` and reserve a 4-byte per-sample size slot before the flags slot.
62
+ * @param options.sampleFlagsValue - The 32-bit value to write into the chosen flags field (first_sample_flags or per-sample flags). Use `0` for a keyframe; use
63
+ * `SAMPLE_FLAG_NON_SYNC` for a non-keyframe.
64
+ * @param options.truncate - When `true`, emit a trun whose payload stops short of the flags field the predicate would read. Exercises the "insufficient bytes"
65
+ * guard inside `isKeyframe` without changing the declared box size.
66
+ *
67
+ * @returns A complete `trun` box.
68
+ */
69
+ export declare function makeTrunBox(options: {
70
+ includeDuration?: boolean;
71
+ includeSize?: boolean;
72
+ sampleFlagsValue: number;
73
+ truncate?: boolean;
74
+ useFirstSampleFlags?: boolean;
75
+ usePerSampleFlags?: boolean;
76
+ }): Buffer;
77
+ //# sourceMappingURL=fmp4-builders.d.ts.map