homebridge-plugin-utils 1.35.0 → 2.0.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 (198) hide show
  1. package/README.md +130 -2
  2. package/build/eslint-plugin/README.md +164 -0
  3. package/build/eslint-plugin/config.mjs +308 -0
  4. package/build/eslint-plugin/index.mjs +7 -0
  5. package/build/eslint-plugin/plugin.mjs +46 -0
  6. package/build/eslint-plugin/rules/blank-line-after-open-brace.mjs +137 -0
  7. package/build/eslint-plugin/rules/blank-line-after-open-brace.test.mjs +112 -0
  8. package/build/eslint-plugin/rules/comment-style.mjs +190 -0
  9. package/build/eslint-plugin/rules/comment-style.test.mjs +190 -0
  10. package/build/eslint-plugin/rules/enforce-node-protocol.mjs +114 -0
  11. package/build/eslint-plugin/rules/enforce-node-protocol.test.mjs +116 -0
  12. package/build/eslint-plugin/rules/paren-comparisons-in-logical.mjs +94 -0
  13. package/build/eslint-plugin/rules/paren-comparisons-in-logical.test.mjs +107 -0
  14. package/build/eslint-plugin/rules/split-type-imports.mjs +354 -0
  15. package/build/eslint-plugin/rules/split-type-imports.test.mjs +268 -0
  16. package/build/eslint-plugin/test-setup.mjs +21 -0
  17. package/build/fs-ops.mjs +184 -0
  18. package/build/tsconfig.json +18 -3
  19. package/dist/backpressure.d.ts +94 -45
  20. package/dist/backpressure.js +229 -93
  21. package/dist/backpressure.js.map +1 -1
  22. package/dist/cli/index.d.ts +95 -0
  23. package/dist/cli/index.js +355 -0
  24. package/dist/cli/index.js.map +1 -0
  25. package/dist/clock-double.d.ts +93 -0
  26. package/dist/clock-double.js +141 -0
  27. package/dist/clock-double.js.map +1 -0
  28. package/dist/clock.d.ts +39 -0
  29. package/dist/clock.js +34 -0
  30. package/dist/clock.js.map +1 -0
  31. package/dist/eslint-plugin/config.d.mts +193 -0
  32. package/dist/eslint-plugin/index.d.mts +2 -0
  33. package/dist/eslint-plugin/plugin.d.mts +87 -0
  34. package/dist/eslint-plugin/rules/blank-line-after-open-brace.d.mts +19 -0
  35. package/dist/eslint-plugin/rules/comment-style.d.mts +15 -0
  36. package/dist/eslint-plugin/rules/enforce-node-protocol.d.mts +18 -0
  37. package/dist/eslint-plugin/rules/paren-comparisons-in-logical.d.mts +15 -0
  38. package/dist/eslint-plugin/rules/split-type-imports.d.mts +16 -0
  39. package/dist/featureOptions-docs.d.ts +96 -0
  40. package/dist/featureOptions-docs.js +193 -0
  41. package/dist/featureOptions-docs.js.map +1 -0
  42. package/dist/featureOptions.d.ts +674 -0
  43. package/dist/featureOptions.js +870 -0
  44. package/dist/featureOptions.js.map +1 -0
  45. package/dist/ffmpeg/codecs.d.ts +256 -72
  46. package/dist/ffmpeg/codecs.js +477 -262
  47. package/dist/ffmpeg/codecs.js.map +1 -1
  48. package/dist/ffmpeg/dgram-util.d.ts +46 -0
  49. package/dist/ffmpeg/dgram-util.js +38 -0
  50. package/dist/ffmpeg/dgram-util.js.map +1 -0
  51. package/dist/ffmpeg/exec.d.ts +83 -64
  52. package/dist/ffmpeg/exec.js +77 -86
  53. package/dist/ffmpeg/exec.js.map +1 -1
  54. package/dist/ffmpeg/fmp4.d.ts +49 -2
  55. package/dist/ffmpeg/fmp4.js +47 -11
  56. package/dist/ffmpeg/fmp4.js.map +1 -1
  57. package/dist/ffmpeg/hap-enums.d.ts +214 -0
  58. package/dist/ffmpeg/hap-enums.js +92 -0
  59. package/dist/ffmpeg/hap-enums.js.map +1 -0
  60. package/dist/ffmpeg/index.d.ts +16 -9
  61. package/dist/ffmpeg/index.js +6 -0
  62. package/dist/ffmpeg/index.js.map +1 -1
  63. package/dist/ffmpeg/mp4-assembler.d.ts +120 -0
  64. package/dist/ffmpeg/mp4-assembler.js +387 -0
  65. package/dist/ffmpeg/mp4-assembler.js.map +1 -0
  66. package/dist/ffmpeg/mp4-parser.d.ts +94 -0
  67. package/dist/ffmpeg/mp4-parser.js +130 -0
  68. package/dist/ffmpeg/mp4-parser.js.map +1 -0
  69. package/dist/ffmpeg/options.d.ts +62 -149
  70. package/dist/ffmpeg/options.js +608 -499
  71. package/dist/ffmpeg/options.js.map +1 -1
  72. package/dist/ffmpeg/process.d.ts +142 -96
  73. package/dist/ffmpeg/process.js +406 -278
  74. package/dist/ffmpeg/process.js.map +1 -1
  75. package/dist/ffmpeg/record.d.ts +302 -188
  76. package/dist/ffmpeg/record.js +408 -567
  77. package/dist/ffmpeg/record.js.map +1 -1
  78. package/dist/ffmpeg/recording-process-double.d.ts +144 -0
  79. package/dist/ffmpeg/recording-process-double.js +168 -0
  80. package/dist/ffmpeg/recording-process-double.js.map +1 -0
  81. package/dist/ffmpeg/rtp-parser.d.ts +70 -0
  82. package/dist/ffmpeg/rtp-parser.js +77 -0
  83. package/dist/ffmpeg/rtp-parser.js.map +1 -0
  84. package/dist/ffmpeg/rtp.d.ts +198 -141
  85. package/dist/ffmpeg/rtp.js +473 -251
  86. package/dist/ffmpeg/rtp.js.map +1 -1
  87. package/dist/ffmpeg/settings.d.ts +5 -2
  88. package/dist/ffmpeg/settings.js +20 -5
  89. package/dist/ffmpeg/settings.js.map +1 -1
  90. package/dist/ffmpeg/stream.d.ts +57 -107
  91. package/dist/ffmpeg/stream.js +121 -150
  92. package/dist/ffmpeg/stream.js.map +1 -1
  93. package/dist/formatters.d.ts +106 -0
  94. package/dist/formatters.js +174 -0
  95. package/dist/formatters.js.map +1 -0
  96. package/dist/homebridge-enums.d.ts +30 -0
  97. package/dist/homebridge-enums.js +17 -0
  98. package/dist/homebridge-enums.js.map +1 -0
  99. package/dist/index.d.ts +12 -6
  100. package/dist/index.js +7 -2
  101. package/dist/index.js.map +1 -1
  102. package/dist/logclient/auth.d.ts +114 -0
  103. package/dist/logclient/auth.js +199 -0
  104. package/dist/logclient/auth.js.map +1 -0
  105. package/dist/logclient/cli-run.d.ts +76 -0
  106. package/dist/logclient/cli-run.js +639 -0
  107. package/dist/logclient/cli-run.js.map +1 -0
  108. package/dist/logclient/cli.d.ts +3 -0
  109. package/dist/logclient/cli.js +97 -0
  110. package/dist/logclient/cli.js.map +1 -0
  111. package/dist/logclient/client.d.ts +145 -0
  112. package/dist/logclient/client.js +600 -0
  113. package/dist/logclient/client.js.map +1 -0
  114. package/dist/logclient/config.d.ts +173 -0
  115. package/dist/logclient/config.js +199 -0
  116. package/dist/logclient/config.js.map +1 -0
  117. package/dist/logclient/endpoints.d.ts +54 -0
  118. package/dist/logclient/endpoints.js +73 -0
  119. package/dist/logclient/endpoints.js.map +1 -0
  120. package/dist/logclient/filter.d.ts +45 -0
  121. package/dist/logclient/filter.js +51 -0
  122. package/dist/logclient/filter.js.map +1 -0
  123. package/dist/logclient/frame.d.ts +93 -0
  124. package/dist/logclient/frame.js +203 -0
  125. package/dist/logclient/frame.js.map +1 -0
  126. package/dist/logclient/index.d.ts +31 -0
  127. package/dist/logclient/index.js +12 -0
  128. package/dist/logclient/index.js.map +1 -0
  129. package/dist/logclient/parser.d.ts +211 -0
  130. package/dist/logclient/parser.js +393 -0
  131. package/dist/logclient/parser.js.map +1 -0
  132. package/dist/logclient/rest.d.ts +41 -0
  133. package/dist/logclient/rest.js +111 -0
  134. package/dist/logclient/rest.js.map +1 -0
  135. package/dist/logclient/settings.d.ts +15 -0
  136. package/dist/logclient/settings.js +64 -0
  137. package/dist/logclient/settings.js.map +1 -0
  138. package/dist/logclient/socket-double.d.ts +201 -0
  139. package/dist/logclient/socket-double.js +384 -0
  140. package/dist/logclient/socket-double.js.map +1 -0
  141. package/dist/logclient/socket.d.ts +257 -0
  142. package/dist/logclient/socket.js +620 -0
  143. package/dist/logclient/socket.js.map +1 -0
  144. package/dist/logclient/stitch.d.ts +83 -0
  145. package/dist/logclient/stitch.js +146 -0
  146. package/dist/logclient/stitch.js.map +1 -0
  147. package/dist/logclient/time-expression.d.ts +42 -0
  148. package/dist/logclient/time-expression.js +181 -0
  149. package/dist/logclient/time-expression.js.map +1 -0
  150. package/dist/logclient/time-window.d.ts +38 -0
  151. package/dist/logclient/time-window.js +53 -0
  152. package/dist/logclient/time-window.js.map +1 -0
  153. package/dist/logclient/types.d.ts +107 -0
  154. package/dist/logclient/types.js +6 -0
  155. package/dist/logclient/types.js.map +1 -0
  156. package/dist/mqttClient.d.ts +287 -0
  157. package/dist/mqttClient.js +433 -0
  158. package/dist/mqttClient.js.map +1 -0
  159. package/dist/service.d.ts +64 -15
  160. package/dist/service.js +93 -66
  161. package/dist/service.js.map +1 -1
  162. package/dist/ui/featureOptions.js +870 -0
  163. package/dist/ui/featureOptions.js.map +1 -0
  164. package/dist/ui/formatters.js +174 -0
  165. package/dist/ui/formatters.js.map +1 -0
  166. package/dist/ui/pluginConfigSession.mjs +141 -0
  167. package/dist/ui/webUi-featureOptions/categoryState.mjs +135 -0
  168. package/dist/ui/webUi-featureOptions/effects/keyboard.mjs +61 -0
  169. package/dist/ui/webUi-featureOptions/effects/persist.mjs +226 -0
  170. package/dist/ui/webUi-featureOptions/effects/theme.mjs +398 -0
  171. package/dist/ui/webUi-featureOptions/effects/tokens.mjs +152 -0
  172. package/dist/ui/webUi-featureOptions/rendering.mjs +431 -0
  173. package/dist/ui/webUi-featureOptions/selectors.mjs +360 -0
  174. package/dist/ui/webUi-featureOptions/state.mjs +319 -0
  175. package/dist/ui/webUi-featureOptions/store.mjs +181 -0
  176. package/dist/ui/webUi-featureOptions/utils.mjs +200 -0
  177. package/dist/ui/webUi-featureOptions/views/connectionError.mjs +152 -0
  178. package/dist/ui/webUi-featureOptions/views/deviceInfo.mjs +80 -0
  179. package/dist/ui/webUi-featureOptions/views/header.mjs +77 -0
  180. package/dist/ui/webUi-featureOptions/views/nav.mjs +327 -0
  181. package/dist/ui/webUi-featureOptions/views/options.mjs +521 -0
  182. package/dist/ui/webUi-featureOptions/views/search.mjs +395 -0
  183. package/dist/ui/webUi-featureOptions.mjs +677 -0
  184. package/dist/ui/webUi.mjs +192 -87
  185. package/dist/util.d.ts +602 -45
  186. package/dist/util.js +783 -78
  187. package/dist/util.js.map +1 -1
  188. package/package.json +33 -15
  189. package/build/eslint-rules.mjs +0 -511
  190. package/dist/featureoptions.d.ts +0 -264
  191. package/dist/featureoptions.js +0 -480
  192. package/dist/featureoptions.js.map +0 -1
  193. package/dist/mqttclient.d.ts +0 -178
  194. package/dist/mqttclient.js +0 -310
  195. package/dist/mqttclient.js.map +0 -1
  196. package/dist/ui/featureoptions.js +0 -480
  197. package/dist/ui/featureoptions.js.map +0 -1
  198. package/dist/ui/webUi-featureoptions.mjs +0 -3765
@@ -0,0 +1,193 @@
1
+ /* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * featureOptions-docs.ts: Pure, isomorphic renderer that projects a feature-options catalog into the markdown reference each plugin ships in its docs.
4
+ */
5
+ import { buildCatalogIndex, expandOption, isValueOption } from "./featureOptions.js";
6
+ /**
7
+ * The opening marker of the auto-generated region in a plugin's `docs/FeatureOptions.md`. {@link spliceMarkedRegion} replaces everything strictly between this marker
8
+ * and {@link FEATURE_OPTIONS_DOC_END}, preserving both markers and the hand-written prose around them. The text doubles as an in-document warning to maintainers not to
9
+ * edit the region by hand.
10
+ *
11
+ * @category Feature Options
12
+ */
13
+ export const FEATURE_OPTIONS_DOC_BEGIN = "<!-- FEATURE OPTIONS:BEGIN - Auto-generated by homebridge-plugin-utils. Do not edit this region by hand. -->";
14
+ /**
15
+ * The closing marker of the auto-generated region. See {@link FEATURE_OPTIONS_DOC_BEGIN}.
16
+ *
17
+ * @category Feature Options
18
+ */
19
+ export const FEATURE_OPTIONS_DOC_END = "<!-- FEATURE OPTIONS:END -->";
20
+ // The fixed width, in dashes, of the description-side divider segment. The Option-side width is auto-computed per table from the visible cell widths; the
21
+ // description side has no upper bound worth chasing (descriptions plus scope suffixes can be arbitrarily long), so it is a sensible constant rather than a second
22
+ // max pass. Padding is cosmetic - rendered markdown ignores it - so this is presentation polish, not part of the semantic contract.
23
+ const DESCRIPTION_DIVIDER_WIDTH = 61;
24
+ // One trailing space of breathing room added to the auto-computed Option-column width so the widest key cell does not sit flush against the column separator. Purely
25
+ // cosmetic alignment, matching the canonical rendering the family converges on.
26
+ const OPTION_COLUMN_PADDING = 1;
27
+ // The substitution shown in the default cell for a value option whose declared default is the empty string. We render "none" rather than an empty cell so the table
28
+ // communicates "this option defaults to no value" explicitly. The substitution happens at render time only; the catalog entry is never mutated.
29
+ const EMPTY_DEFAULT_LABEL = "none";
30
+ // Escape catalog-derived plain text for safe embedding in a markdown table cell, heading, or index line. We neutralize the column separator "|" (which would inject
31
+ // a phantom table column) and the HTML-significant "&", "<", ">" (which would otherwise be parsed as markup and swallowed). "&" is replaced first so the entities we
32
+ // introduce are not themselves re-escaped. This is applied ONLY to catalog-derived plain text (descriptions and the rendered default value); it is deliberately NOT
33
+ // applied to the dotted key (a constrained identifier that also lives inside a code span where entities would not decode), to the renderer's own structural HTML
34
+ // (the per-row/heading anchors) or the ".<value>" placeholder, nor to the scope-hook return values (the plugin owns that markup - e.g. a "<BR>" line break).
35
+ function escapeCellText(text) {
36
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("|", "\\|");
37
+ }
38
+ /**
39
+ * Render a feature-options catalog into the markdown reference fragment a plugin embeds in its `docs/FeatureOptions.md`. The output is a category index (one bullet per
40
+ * category, deep-linking to its detail section), an optional one-line legend explaining the `.<value>` notation, and then one detail section per category, each carrying
41
+ * an optional device-scope line and a flat table of option rows. The legend is emitted only when the catalog has at least one value option, since a toggle-only catalog
42
+ * never renders the `.<value>` placeholder the legend describes.
43
+ *
44
+ * The renderer owns all base-shaped scaffolding - the index, headings, per-row deep-link anchors, the key cell with its value/toggle placeholder, the default cell, the
45
+ * description cell, and the column math - purely from the base {@link FeatureOptionEntry} / {@link FeatureCategoryEntry} fields. The two optional hooks own *only* the
46
+ * plugin-private scope prose: `describeCategoryScope` contributes the device-scope sentence under a category heading, and `describeOptionScope` contributes a suffix
47
+ * appended to an option's description cell. A hook returning `undefined` omits its contribution cleanly - never an "undefined" literal, never a stray blank line.
48
+ *
49
+ * The catalog index is built once via {@link buildCatalogIndex}; value-ness is decided via {@link isValueOption}; the canonical dotted key is derived via
50
+ * {@link expandOption}. None of those is re-derived here - the renderer is a projection of the same single source of truth the rest of the module owns.
51
+ *
52
+ * @typeParam TOptionMeta - The concrete type of an option entry's opaque `meta` annotation, reconstituted at this boundary so `describeOptionScope` sees it typed.
53
+ * @typeParam TCategoryMeta - The concrete type of a category entry's opaque `meta` annotation, reconstituted so `describeCategoryScope` sees it typed.
54
+ * @param input
55
+ * @param input.categories - The catalog's category list, in the order the index and detail sections should follow.
56
+ * @param input.describeCategoryScope - Optional. Returns the device-scope sentence emitted under a category's heading, or `undefined` to omit it. The hook owns the full
57
+ * sentence including any leading or trailing text.
58
+ * @param input.describeOptionScope - Optional. Returns a suffix appended to an option's description cell, or `undefined` to omit it. The hook owns its full text
59
+ * including any leading separator.
60
+ * @param input.options - The catalog's options map keyed by category name.
61
+ *
62
+ * @returns The rendered markdown fragment.
63
+ */
64
+ export function renderFeatureOptionsReference(input) {
65
+ const { categories, describeCategoryScope, describeOptionScope, options } = input;
66
+ // Build the catalog index once. We never re-derive value-options, defaults, or the dotted key downstream - everything routes back through this single source of
67
+ // truth. The index accepts the typed arrays because a parameterized entry is structurally assignable to the base entry the index is declared over.
68
+ const catalog = buildCatalogIndex(categories, options);
69
+ // Accumulate the rendered lines. We assemble line-by-line and join with newlines at the end rather than concatenating strings, which keeps the blank-line discipline
70
+ // explicit (a pushed empty string is a blank line) and avoids a forest of "\n" literals.
71
+ const lines = [];
72
+ // Index pass: one bullet per category, deep-linking to the category's detail heading. We iterate the typed `categories` array directly - not `catalog.categories`,
73
+ // which is base-typed - and apply the same "skip a category with no options entry" rule the index builder uses, so the two traversals cannot disagree. The bullet's
74
+ // link text is the category name; the anchor is its lowercased form; the trailing text is the category description verbatim, with no appended suffix.
75
+ for (const category of categories) {
76
+ if (!options[category.name]) {
77
+ continue;
78
+ }
79
+ lines.push(" * [" + category.name + "](#" + category.name.toLowerCase() + "): " + escapeCellText(category.description));
80
+ }
81
+ // A single blank line separates the index from the detail sections.
82
+ lines.push("");
83
+ // Conditional legend. When the catalog has at least one value option, we explain the renderer's own ".<value>" notation - the suffix it appends to value-option keys -
84
+ // so a reader knows to substitute their setting rather than typing the literal placeholder, and that every other option is a simple on/off toggle. The legend is
85
+ // suppressed for a toggle-only catalog (no value options) where the ".<value>" notation never appears, so the explanation would document a notation the reader never
86
+ // sees. It sits after the index's trailing blank line and is itself followed by a blank line, keeping it cleanly framed before the first detail heading.
87
+ if (catalog.sortedValueOptionNames.length > 0) {
88
+ lines.push("Options whose key ends in `.<value>` take a value - replace `.<value>` with your setting; all other options are simple on/off toggles. " +
89
+ "The default shown for each option is what applies when you leave it unset.");
90
+ lines.push("");
91
+ }
92
+ // Detail pass: one section per category, in catalog order.
93
+ for (const category of categories) {
94
+ const categoryOptions = options[category.name];
95
+ // Skip a category with no options entry, matching the index builder's rule so the detail traversal stays in lockstep with the index.
96
+ if (!categoryOptions) {
97
+ continue;
98
+ }
99
+ // Heading with an invisible anchor so the index bullets can deep-link to it. The visible text is the category description, verbatim.
100
+ lines.push("#### <A NAME=\"" + category.name.toLowerCase() + "\"></A>" + escapeCellText(category.description));
101
+ lines.push("");
102
+ // Optional device-scope line. The hook owns the full sentence; when it returns `undefined` we omit the line and its trailing blank line entirely rather than
103
+ // emitting an empty row or the literal string "undefined".
104
+ const categoryScope = describeCategoryScope?.(category);
105
+ if (categoryScope !== undefined) {
106
+ lines.push(categoryScope);
107
+ lines.push("");
108
+ }
109
+ // Compose each option's two cells - the key cell and the description cell - up front so we can size the Option column to the widest visible key cell before we
110
+ // emit any table line. This is the single max pass the column math needs; nothing downstream re-walks the options.
111
+ const rows = categoryOptions.map((option) => {
112
+ // The canonical dotted key (single source of truth - never hand-concatenated). For a category-level option (empty name), `expandOption` returns the category
113
+ // name alone, which is exactly the bare-category key we want to render.
114
+ const key = expandOption(category, option);
115
+ // The key cell pairs an invisible per-row anchor (key case preserved, so deep links are stable) with a single backtick code span. Value-ness is signaled
116
+ // lexically by appending the universal ".<value>" placeholder inside the same span - no mixed HTML, faithful to the template the user fills in.
117
+ const keyCell = "<A NAME=\"" + key + "\"></A>`" + key + (isValueOption(catalog, key) ? ".<value>" : "") + "`";
118
+ // The default cell. A value option shows its raw declared default in the form the user would type - never formatted, since a formatted value (e.g. "30s") is an
119
+ // invalid config value. The empty-string default substitutes to "none" at render time, without mutating the entry. A toggle shows enabled / disabled.
120
+ const defaultCell = isValueOption(catalog, key) ?
121
+ (((option.defaultValue ?? "") === "") ? EMPTY_DEFAULT_LABEL : escapeCellText(String(option.defaultValue))) :
122
+ (option.default ? "enabled" : "disabled");
123
+ // The optional plugin-private scope suffix, appended to the description cell. Omitted cleanly when the hook returns `undefined`.
124
+ const scopeSuffix = describeOptionScope?.(option, category) ?? "";
125
+ // The full description cell: the option description, the bolded default, then any scope suffix. The description and the rendered default are catalog-derived plain
126
+ // text and are escaped for safe table embedding; the scope suffix is hook-owned markup the plugin controls (e.g. a "<BR>") and passes through verbatim.
127
+ const descriptionCell = escapeCellText(option.description) + " **(default: " + defaultCell + ")**." + scopeSuffix;
128
+ return { descriptionCell, keyCell };
129
+ });
130
+ // Auto-compute the Option-column width from the widest visible key cell and the header label, plus one space of breathing room. Padding is cosmetic - rendered
131
+ // markdown collapses it - so this is alignment polish for the raw source, not part of the semantic contract.
132
+ const optionColumnWidth = rows.reduce((width, row) => Math.max(width, row.keyCell.length), "Option".length) + OPTION_COLUMN_PADDING;
133
+ // Table header and divider. The Option-side divider width tracks the computed column (plus the two single-space gutters around the cell); the description-side
134
+ // divider is the fixed constant.
135
+ lines.push("| " + "Option".padEnd(optionColumnWidth) + " | Description");
136
+ lines.push("|" + "-".repeat(optionColumnWidth + 2) + "|" + "-".repeat(DESCRIPTION_DIVIDER_WIDTH));
137
+ // One flat row per option, in catalog order. No group nesting - the rows are flat by design.
138
+ for (const row of rows) {
139
+ lines.push("| " + row.keyCell.padEnd(optionColumnWidth) + " | " + row.descriptionCell);
140
+ }
141
+ // A trailing blank line after each table separates it from the next section.
142
+ lines.push("");
143
+ }
144
+ return lines.join("\n");
145
+ }
146
+ /**
147
+ * Replace the region strictly between {@link FEATURE_OPTIONS_DOC_BEGIN} and {@link FEATURE_OPTIONS_DOC_END} in `source` with `content`, leaving both markers and all
148
+ * surrounding prose untouched. This is the pure half of the in-place splice each plugin's build-script shim performs; the shim supplies the trivial `readFile` /
149
+ * `writeFile` around it.
150
+ *
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
153
+ * options object for documents that use a different convention, though the defaults match what every plugin in the family embeds.
154
+ *
155
+ * @param source - The full document text.
156
+ * @param content - The fragment to insert between the markers, typically the output of {@link renderFeatureOptionsReference}.
157
+ * @param markers
158
+ * @param markers.beginMarker - The opening marker to search for. Defaults to {@link FEATURE_OPTIONS_DOC_BEGIN}.
159
+ * @param markers.endMarker - The closing marker to search for. Defaults to {@link FEATURE_OPTIONS_DOC_END}.
160
+ *
161
+ * @returns `source` with the marked region's contents replaced by `content`.
162
+ *
163
+ * @throws `Error` naming the offending marker when either marker is absent, when the closing marker precedes the opening marker, or when the document is ambiguous - a
164
+ * second begin marker after the first, or a second end marker - since the marked region would not be uniquely identified.
165
+ */
166
+ export function spliceMarkedRegion(source, content, { beginMarker = FEATURE_OPTIONS_DOC_BEGIN, endMarker = FEATURE_OPTIONS_DOC_END } = {}) {
167
+ const beginIndex = source.indexOf(beginMarker);
168
+ if (beginIndex === -1) {
169
+ throw new Error("spliceMarkedRegion: begin marker not found in source: \"" + beginMarker + "\".");
170
+ }
171
+ const endIndex = source.indexOf(endMarker);
172
+ if (endIndex === -1) {
173
+ throw new Error("spliceMarkedRegion: end marker not found in source: \"" + endMarker + "\".");
174
+ }
175
+ // The opening marker must come before the closing marker. We compare against the end of the begin marker so an end marker that overlaps or sits immediately after
176
+ // the begin marker is still rejected as malformed rather than producing a negative-length region.
177
+ const regionStart = beginIndex + beginMarker.length;
178
+ if (endIndex < regionStart) {
179
+ throw new Error("spliceMarkedRegion: end marker \"" + endMarker + "\" precedes begin marker \"" + beginMarker + "\" in source.");
180
+ }
181
+ // Reject an ambiguous document. A second begin marker after the first - or a second end marker anywhere - means the marked region is not uniquely identified, and
182
+ // splicing into the first pair would silently leave the duplicate marker (and any stale content between the duplicates) behind. We search from just past the end of
183
+ // the first occurrence (the `includes` fromIndex argument) so an exact re-find of the same marker is not mistaken for a duplicate.
184
+ if (source.includes(beginMarker, beginIndex + beginMarker.length)) {
185
+ throw new Error("spliceMarkedRegion: multiple begin markers found in source; the marked region is ambiguous.");
186
+ }
187
+ if (source.includes(endMarker, endIndex + endMarker.length)) {
188
+ throw new Error("spliceMarkedRegion: multiple end markers found in source; the marked region is ambiguous.");
189
+ }
190
+ // Reassemble: everything up to and including the begin marker, a newline-framed copy of the new content, then everything from the end marker onward.
191
+ return source.slice(0, regionStart) + "\n" + content + "\n" + source.slice(endIndex);
192
+ }
193
+ //# sourceMappingURL=featureOptions-docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featureOptions-docs.js","sourceRoot":"","sources":["../src/featureOptions-docs.ts"],"names":[],"mappings":"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"}