layoutbridge-light 1.0.0 → 1.0.1

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 (35) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/README.de.md +31 -2
  3. package/README.md +31 -3
  4. package/THIRD-PARTY-NOTICES.md +2 -2
  5. package/dist/src/mcp/catalog.js +9 -4
  6. package/dist/src/mcp/content-credentials.js +65 -0
  7. package/dist/src/mcp/dispatch.js +8 -1
  8. package/dist/src/mcp/license/polar-ids.js +1 -1
  9. package/dist/src/mcp/pdf-postprocess.js +798 -0
  10. package/dist/src/mcp/server.js +1 -1
  11. package/dist/src/mcp/target-folder.js +25 -0
  12. package/dist/src/mcp/tools.js +275 -20
  13. package/dist/src/mcp/typography-rules.js +1 -1
  14. package/dist/src/transport/com.js +224 -38
  15. package/dist/src/transport/transport.js +22 -0
  16. package/indesign/commands/25-export.js +258 -11
  17. package/indesign/commands/26-export-image.js +1 -1
  18. package/indesign/commands/31-view.js +100 -0
  19. package/indesign/commands/35-parents.js +44 -24
  20. package/indesign/commands/40-frames.js +35 -0
  21. package/indesign/commands/47-anchored.js +142 -0
  22. package/indesign/commands/50-modify.js +25 -3
  23. package/indesign/commands/60-images.js +28 -2
  24. package/indesign/commands/69-placeholders.js +7 -2
  25. package/indesign/commands/70-text.js +39 -4
  26. package/indesign/commands/75-tables.js +27 -1
  27. package/indesign/commands/78-frame-lines.js +59 -0
  28. package/indesign/commands/79-check-typography.js +143 -4
  29. package/indesign/commands/80-styles.js +105 -10
  30. package/indesign/commands/89-toc-index.js +61 -4
  31. package/indesign/commands/91-epub.js +92 -9
  32. package/indesign/commands/92-accessibility.js +1131 -0
  33. package/indesign/interpreter.js +51 -1
  34. package/package.json +1 -1
  35. package/rules/typography-rules.json +3 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,89 @@ All notable changes to `layoutbridge-light`. Format: Keep a Changelog;
4
4
  versions follow Semantic Versioning. Within a major version, tool names,
5
5
  parameters and return fields remain stable; additions are additive.
6
6
 
7
+ ## [1.0.1] - 2026-11-11
8
+
9
+ Update round for accessible PDF and book typesetting: 85 tools (1.0.0: 77).
10
+ New features are additive (new tools, new optional parameters, new return
11
+ fields); existing tool names, parameters and the error format are
12
+ unchanged. Tested with macOS 27.2, InDesign 2026 (21.6.0.57), Claude
13
+ Desktop 5.1231.2 and Claude Code 2.1.322; Windows 11 with InDesign 2026
14
+ (21.6) for the Windows changes.
15
+
16
+ ### Added
17
+
18
+ - Accessible PDF (tested with InDesign 2026):
19
+ - New tools `list_alt_texts` and `set_alt_text`: read and set the
20
+ alternative text of images (own text, XMP fields of the image file,
21
+ decorative), actual text and the tag type (artifact for lines,
22
+ ornaments and decorative images) – also for many objects in one call.
23
+ - New tools `list_articles`, `define_article` and `delete_article`: the
24
+ reading order of a tagged PDF through articles.
25
+ - New tool `check_accessibility`: images without alternative text,
26
+ paragraph styles that look like headings but have no PDF tag, missing
27
+ document title, text or PDF language, untagged paths, articles; lists
28
+ the steps that remain manual in Adobe Acrobat.
29
+ - New tool `export_interactive_pdf`: interactive PDF with tags, tab order
30
+ from the structure, document title, PDF language.
31
+ - `export_pdf`: new optional `tagged`, `bookmarks`, `display_title`,
32
+ `pdf_language` (unknown values are `invalid_argument` with
33
+ `details.code` `unknown_pdf_language`), `title`,
34
+ `artifact_untagged_paths` (on by default with `tagged: true`, `false`
35
+ switches it off) and `pdfua_identifier` (only when
36
+ `check_accessibility` reports no error – otherwise `invalid_state` with
37
+ `details.code` `accessibility_check_failed`).
38
+ - `define_paragraph_style`, `define_character_style`: `pdf_tag` (P, H,
39
+ H1–H6, Artifact); `list_paragraph_styles` returns `pdf_tag`.
40
+ - New tool `set_anchored_object_options`: change the options of an anchored
41
+ object (position, alignment, space before/after, offset, reference points)
42
+ without releasing and re-anchoring it.
43
+ - `export_epub`: `split_by_paragraph_style` also takes a list of paragraph
44
+ styles; new options `image_alignment` and `image_size` for images in the
45
+ text flow.
46
+ - `apply_character_format_to_range`: `language` sets the language
47
+ (dictionary) of a text range. `check_typography`: new rule
48
+ `foreign_language_in_text`.
49
+ - `define_paragraph_style`: `align_to_baseline_grid` and `capitalization`;
50
+ `define_character_style`: `capitalization`.
51
+ - `set_frame_inset`: `first_baseline_offset` and `min_first_baseline_pt`.
52
+ - Text placeholders `{{odd_page_break}}` and `{{even_page_break}}`.
53
+ - `apply_parent_to_page`: `page_ids` applies a master to several pages at
54
+ once.
55
+ - `export_page_image`: `create_folder` creates a missing target folder.
56
+ - `find_replace`: `scope: "story"` searches the whole story of a frame,
57
+ overset included; new warnings for `scope: "frame"`, merged paragraphs
58
+ and GREP lookbehinds with a break character.
59
+ - `list_links`: `without_object` marks links that InDesign keeps without any
60
+ object, with a warning that names the remedy.
61
+ - `anchor_frame_in_text`: warning when the anchored object reaches above
62
+ the top of its text frame.
63
+ - `place_image`: warning when the XMP metadata of the image points to a
64
+ remote Content Credentials manifest.
65
+
66
+ ### Fixed
67
+
68
+ - Windows: tool calls no longer write two new script files per call; with
69
+ real-time virus scanning calls took 12 s to over two minutes. The server
70
+ log records the time shares of each call (`timing_ms`).
71
+ - Tools taking `frame_id` answer `not_found` with `details.found_class` for
72
+ the id of a placed graphic or of an object that is not on a page
73
+ (InDesign could quit on such a call).
74
+ - `get_frame_lines`: correct baseline for the first line of a paragraph
75
+ with a drop cap; new warning when space before has no effect at the top
76
+ of a column.
77
+ - `check_epub` and `export_epub` no longer report text frames holding
78
+ anchored images as objects that are not anchored.
79
+ - `check_typography` (`heading_at_column_end`): a one-line dialogue line in
80
+ the body style is no longer reported as a heading.
81
+ - `define_toc_style` can change the entries of an existing TOC style;
82
+ `create_toc` with `replace` and another TOC style replaces the existing
83
+ table of contents.
84
+ - `define_character_style`: `font_style` reads `null` instead of
85
+ `"NOTHING"` when the style sets no font style.
86
+ - Tools that add pages or define styles no longer make the layout window
87
+ jump while they run.
88
+ - Same server code as LayoutBridge for Adobe InDesign® 1.0.5.
89
+
7
90
  ## [1.0.0] - 2026-11-01
8
91
 
9
92
  ### Added
package/README.de.md CHANGED
@@ -9,10 +9,11 @@ Dokumente und Seiten, Musterseiten und Abschnitte, Satzspiegel, Textrahmen
9
9
  und Autofluss ganzer Manuskripte, Text, Absatz- und Zeichenformate mit
10
10
  Initialen, Bilder und verankerte Objekte, Inhaltsverzeichnis und Register,
11
11
  Typografieprüfung, Prüfung für Print-on-Demand und Buchumschlag, Preflight
12
- und Export (PDF, Seitenbilder und E-Books als EPUB, mit Prüfung des EPUB).
12
+ und Export (PDF – auch barrierefrei, getaggt und interaktiv –, Seitenbilder
13
+ und E-Books als EPUB, mit Prüfung des EPUB).
13
14
 
14
15
  LayoutBridge Light ist die Buchausgabe von LayoutBridge for Adobe InDesign®:
15
- derselbe Server, 77 seiner Werkzeuge. Nicht enthalten sind Tabellen, Ebenen,
16
+ derselbe Server, 85 seiner Werkzeuge. Nicht enthalten sind Tabellen, Ebenen,
16
17
  Gruppen und Objektformate, Formen, Linien und Effekte, das Umsortieren von
17
18
  Seiten, das Verpacken und die Werkzeuge zum Übernehmen fremder Dokumente. Es braucht einen eigenen
18
19
  Lizenzschlüssel; ein Schlüssel für LayoutBridge for Adobe InDesign® gilt
@@ -282,6 +283,34 @@ Servers. Danach wieder auf `basic` stellen.
282
283
  als **30 Tage** sind. Andere Frist über `LAYOUTBRIDGE_LIGHT_LOG_KEEP_DAYS`
283
284
  (Zahl in Tagen; `0` schaltet das Löschen ab).
284
285
 
286
+ ## Barrierefreie PDFs
287
+
288
+ LayoutBridge bereitet Dokumente für barrierefreie PDFs (PDF/UA, WCAG) vor
289
+ und exportiert getaggte PDFs:
290
+
291
+ - Alternativtexte für Bilder (`list_alt_texts`, `set_alt_text` – auch für
292
+ viele Bilder in einem Aufruf und aus den XMP-Metadaten der Bilddatei;
293
+ dekorative Bilder werden Artefakte),
294
+ - PDF-Tags der Absatz- und Zeichenformate (`pdf_tag` in
295
+ `define_paragraph_style` und `define_character_style`),
296
+ - die Lesereihenfolge über Artikel (`list_articles`, `define_article`,
297
+ `delete_article`),
298
+ - eine Prüfung vor dem Export (`check_accessibility`),
299
+ - den Export als getaggtes PDF (`export_pdf` mit `tagged`, `pdf_language`,
300
+ `title`, `display_title`, `bookmarks`) oder als interaktives PDF
301
+ (`export_interactive_pdf`). Konturen und Flächen von Textrahmen und die
302
+ Konturen von Tabellen, die InDesign ungetaggt lässt, werden bei jedem
303
+ getaggten Export in der fertigen Datei als Artefakt markiert
304
+ (`artifact_untagged_paths: false` schaltet das ab).
305
+
306
+ Die PDF/UA-Kennung wird nur auf Wunsch geschrieben (`pdfua_identifier`) und
307
+ nur, wenn `check_accessibility` keinen Fehler meldet. Sie behauptet eine
308
+ Konformität, die der Connector nicht vollständig prüfen kann. Prüfen Sie
309
+ jede Datei mit einem PDF/UA-Prüfprogramm und einem Bildschirmleser; einige
310
+ Schritte bleiben Handarbeit in Adobe Acrobat: Bildunterschriften ihren
311
+ Bildern zuordnen, komplexe Tabellen (verbundene Zellen, Zeilenköpfe) und
312
+ Kontraste. Geprüft mit InDesign 2026.
313
+
285
314
  ## License terms
286
315
 
287
316
  Die Nutzung von LayoutBridge Light for Adobe InDesign® richtet sich nach den
package/README.md CHANGED
@@ -8,11 +8,12 @@ provides the tools a book needs, for any MCP-capable AI: documents and
8
8
  pages, master pages and sections, layout grid, text frames and autoflow of
9
9
  whole manuscripts, text, paragraph and character styles with drop caps,
10
10
  images and anchored objects, table of contents and index, typography
11
- checks, print-on-demand checks and book covers, preflight and export (PDF,
12
- page images and e-books as EPUB, with a check of the EPUB).
11
+ checks, print-on-demand checks and book covers, preflight and export (PDF –
12
+ also accessible, tagged PDF and interactive PDF –, page images and e-books
13
+ as EPUB, with a check of the EPUB).
13
14
 
14
15
  LayoutBridge Light is the book edition of LayoutBridge for Adobe InDesign®:
15
- same server, 77 of its tools. Not included are tables, layers, groups and
16
+ same server, 85 of its tools. Not included are tables, layers, groups and
16
17
  object styles, shapes, lines and effects, page reordering, packaging and the
17
18
  tools for taking over third-party documents. It needs its own licence key; a key for
18
19
  LayoutBridge for Adobe InDesign® does not work here, and vice versa.
@@ -270,6 +271,33 @@ message say so. Afterwards switch back to `basic`.
270
271
  days**. Other period via `LAYOUTBRIDGE_LIGHT_LOG_KEEP_DAYS` (number of days;
271
272
  `0` switches deletion off).
272
273
 
274
+ ## Accessible PDF
275
+
276
+ LayoutBridge prepares documents for accessible PDF (PDF/UA, WCAG) and
277
+ exports tagged PDFs:
278
+
279
+ - alternative texts for images (`list_alt_texts`, `set_alt_text` – also for
280
+ many images in one call and from the XMP metadata of the image file;
281
+ decorative images become artifacts),
282
+ - PDF tags of paragraph and character styles (`pdf_tag` in
283
+ `define_paragraph_style` and `define_character_style`),
284
+ - the reading order through articles (`list_articles`, `define_article`,
285
+ `delete_article`),
286
+ - a check before the export (`check_accessibility`),
287
+ - the export as tagged PDF (`export_pdf` with `tagged`, `pdf_language`,
288
+ `title`, `display_title`, `bookmarks`) or as interactive PDF
289
+ (`export_interactive_pdf`). Strokes and fills of text frames and the
290
+ strokes of tables, which InDesign leaves untagged, are marked as artifacts
291
+ in the finished file of every tagged export
292
+ (`artifact_untagged_paths: false` switches that off).
293
+
294
+ The PDF/UA identifier is written only on request (`pdfua_identifier`) and
295
+ only when `check_accessibility` reports no error. It claims a conformance
296
+ that the connector cannot fully check. Check every file with a PDF/UA
297
+ checker and a screen reader; some steps remain manual in Adobe Acrobat:
298
+ linking captions to their figures, complex tables (merged cells, row
299
+ headers) and contrast. Tested with InDesign 2026.
300
+
273
301
  ## License terms
274
302
 
275
303
  Use of LayoutBridge Light for Adobe InDesign® is governed by the
@@ -1,11 +1,11 @@
1
- # Third-party notices – layoutbridge-spm 1.0.4
1
+ # Third-party notices – layoutbridge-spm 1.0.5
2
2
 
3
3
  This package includes the following third-party software (runtime dependencies,
4
4
  including transitive packages). Each entry lists name, version, licence identifier,
5
5
  copyright line and the full licence text as shipped in the package. Development
6
6
  dependencies are not part of the published package and are not listed.
7
7
 
8
- Generated on 2026-10-30 with `node scripts/third-party-notices.mjs` (dependency list from `npm ls --omit=dev --all --parseable`).
8
+ Generated on 2026-11-11 with `node scripts/third-party-notices.mjs` (dependency list from `npm ls --omit=dev --all --parseable`).
9
9
 
10
10
  Packages: 92. Licences: MIT (82), BSD-3-Clause (2), ISC (7), BSD-2-Clause (1).
11
11
 
@@ -1,6 +1,6 @@
1
1
  import { PRODUCT } from './product.js';
2
2
  import { TOOLS } from './tools.js';
3
- /** Die 77 Werkzeuge von LayoutBridge Light (Wahl Sascha 30.10.2026, a), in der Reihenfolge des Katalogs. */
3
+ /** Die 85 Werkzeuge von LayoutBridge Light (Wahl des Anbieters 30.10.2026, a; dazu set_anchored_object_options, Wahl des Anbieters 11.11.2026, Rueckfrage 2 b; dazu die sieben Werkzeuge fuer barrierefreie PDFs, A-71, Entscheidung des Anbieters 06.11.2026), in der Reihenfolge des Katalogs. */
4
4
  export const LIGHT_TOOLS = [
5
5
  'get_app_version', 'list_documents', 'open_document', 'close_document', 'create_document', 'save_document',
6
6
  'export_pdf', 'preflight_document', 'document_text_defaults', 'set_document_bleed',
@@ -14,13 +14,15 @@ export const LIGHT_TOOLS = [
14
14
  'list_paragraph_styles', 'define_paragraph_style', 'define_character_style', 'apply_paragraph_style_to_range', 'apply_character_style_to_range', 'apply_character_format_to_range',
15
15
  'set_paragraph_options', 'get_font_info_at_index', 'find_font_usage', 'debug_inspect_run', 'create_swatch',
16
16
  'get_frame_options', 'list_swatches', 'export_page_image', 'place_text_file', 'fit_frame',
17
- 'list_stories', 'anchor_frame_in_text', 'list_sections', 'set_section',
17
+ 'list_stories', 'anchor_frame_in_text', 'set_anchored_object_options', 'list_sections', 'set_section',
18
18
  'set_document_setup', 'list_text_variables', 'create_text_variable', 'insert_text_variable',
19
19
  'save_document_as', 'set_story_options', 'get_frame_lines', 'check_typography',
20
20
  'position_image_in_frame', 'list_toc_styles', 'define_toc_style', 'create_toc', 'add_index_entry', 'list_index', 'generate_index',
21
21
  'check_print_on_demand', 'create_book_cover', 'export_epub', 'check_epub', 'autoflow_story',
22
+ /* A-71 (Sitzung 82): barrierefreie PDFs, in beiden Produkten */
23
+ 'list_alt_texts', 'set_alt_text', 'list_articles', 'define_article', 'delete_article', 'check_accessibility', 'export_interactive_pdf',
22
24
  ];
23
- /** Die 46 Werkzeuge, die Light ausdruecklich nicht hat. */
25
+ /** Die 47 Werkzeuge, die Light ausdruecklich nicht hat. */
24
26
  export const LIGHT_EXCLUDED = [
25
27
  /* Gestaltung */
26
28
  'create_ellipse', 'create_polygon', 'set_corner_options', 'create_gradient_swatch', 'set_frame_effects',
@@ -35,10 +37,12 @@ export const LIGHT_EXCLUDED = [
35
37
  'align_objects', 'list_guides', 'delete_guides', 'clear_frame_content', 'get_selection', 'set_selection',
36
38
  /* Tabellen */
37
39
  'create_table', 'set_table_cells', 'set_table_style', 'get_table',
38
- /* Im Buchsatz mit Umweg erledigt (Wahl Sascha 30.10.2026, a) */
40
+ /* Im Buchsatz mit Umweg erledigt (Wahl des Anbieters 30.10.2026, a) */
39
41
  'duplicate_parent_page', 'override_parent_item_on_page', 'show_page', 'duplicate_frame', 'create_line',
40
42
  'unanchor_frame', 'import_styles', 'package_document', 'relink_links', 'get_image_link_info',
41
43
  'scale_image_in_frame', 'apply_paragraph_style',
44
+ /* Sitzung 81 (A-68): nur im Vollprodukt (Wahl des Anbieters 11.11.2026, Rueckfrage 2 b) */
45
+ 'set_view_options',
42
46
  ];
43
47
  /**
44
48
  * Gezielte Ersetzungen fuer Light: Halbsaetze, die auf ein fehlendes
@@ -57,6 +61,7 @@ export const LIGHT_TEXT_REPLACEMENTS = [
57
61
  [' in its rotated space; set set_frame_rotation 0 first.', ' in its rotated space.'],
58
62
  ["(print: the document's CMYK swatches, see list_swatches, create_gradient_swatch)", "(print: the document's CMYK swatches, see list_swatches)"],
59
63
  [' – delete_link removes it, list_pasteboard_items lists all pasteboard objects.', '.'],
64
+ [' – it does not print, delete_link cannot reach it, and a warning names', ' – it does not print, and a warning names'],
60
65
  ['exact name from list_layers.', 'exact name of an existing layer.'],
61
66
  [', or call show_page.', '.'],
62
67
  [' to stay on the master, or call show_page with one of the page_ids to work on it visibly.', ' to stay on the master.'],
@@ -0,0 +1,65 @@
1
+ /**
2
+ * A-76 (Sitzung 80): Verweis auf ein entferntes Inhaltsnachweis-Manifest
3
+ * (Content Credentials, C2PA) im XMP einer Bilddatei erkennen.
4
+ *
5
+ * Anlass A-75 (10.11.2026, InDesign 21.6.0.57, Mac): Ein Bild mit
6
+ * `dcterms:provenance` auf ein Manifest im Netz liess InDesign beim Export
7
+ * (JPEG, PDF) minutenlang im Modul fuer Inhaltsnachweise haengen; ohne XMP
8
+ * (Pixel gleich) exportierte dasselbe Dokument in Sekunden. place_image
9
+ * warnt deshalb; die Datei wird nur gelesen, nie veraendert.
10
+ */
11
+ import { closeSync, fstatSync, openSync, readSync } from 'node:fs';
12
+ /** So viel wird vom Anfang und vom Ende der Datei gelesen (XMP steht dort). */
13
+ const HEAD_BYTES = 4 * 1024 * 1024;
14
+ const TAIL_BYTES = 1024 * 1024;
15
+ const PATTERNS = [
16
+ /dcterms:provenance\s*=\s*["'](https:\/\/[^"'\s<>]+)["']/i,
17
+ /<dcterms:provenance[^>]*>\s*(https:\/\/[^<\s]+)\s*</i,
18
+ ];
19
+ /** URL des entfernten Manifests im Text, sonst null. */
20
+ export function findRemoteManifest(text) {
21
+ for (const pattern of PATTERNS) {
22
+ const match = pattern.exec(text);
23
+ if (match !== null && match[1] !== undefined)
24
+ return match[1];
25
+ }
26
+ return null;
27
+ }
28
+ function readSlice(fd, start, length) {
29
+ const buffer = Buffer.alloc(length);
30
+ const read = readSync(fd, buffer, 0, length, start);
31
+ return buffer.subarray(0, read).toString('latin1');
32
+ }
33
+ /** Liest Anfang und Ende der Datei und sucht den Verweis; Lesefehler ergeben null. */
34
+ export function remoteManifestOfFile(path) {
35
+ let fd = null;
36
+ try {
37
+ fd = openSync(path, 'r');
38
+ const size = fstatSync(fd).size;
39
+ const head = readSlice(fd, 0, Math.min(size, HEAD_BYTES));
40
+ const found = findRemoteManifest(head);
41
+ if (found !== null || size <= HEAD_BYTES)
42
+ return found;
43
+ const tailStart = Math.max(HEAD_BYTES, size - TAIL_BYTES);
44
+ return findRemoteManifest(readSlice(fd, tailStart, size - tailStart));
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ finally {
50
+ if (fd !== null) {
51
+ try {
52
+ closeSync(fd);
53
+ }
54
+ catch {
55
+ /* egal */
56
+ }
57
+ }
58
+ }
59
+ }
60
+ export function remoteManifestWarning(url) {
61
+ return 'The image refers in its XMP metadata (dcterms:provenance) to a remote Content Credentials manifest (' + url +
62
+ '). InDesign can hang for minutes while it loads such a manifest on export; place a copy of the image without XMP metadata ' +
63
+ '(pixels unchanged) if exports stall. The file itself was not changed.';
64
+ }
65
+ //# sourceMappingURL=content-credentials.js.map
@@ -7,7 +7,7 @@ import { homedir } from 'node:os';
7
7
  import { z } from 'zod';
8
8
  import { applyConfigDefaults } from './config.js';
9
9
  import { maskHome } from './logging.js';
10
- import { transportError, } from '../transport/transport.js';
10
+ import { takeTiming, transportError, } from '../transport/transport.js';
11
11
  /** Entfernt Felder ohne Wert, damit der Befehl keine leeren Schluessel traegt. */
12
12
  export function dropUndefined(input) {
13
13
  const out = {};
@@ -134,6 +134,12 @@ export async function runTool(definition, rawInput, transport, logger, options =
134
134
  result = transportError('script_error', 'Transport failed: ' + String(error), null);
135
135
  }
136
136
  const duration = Date.now() - startedAt;
137
+ /* A-74: Zeitanteile des Transports nur ins Protokoll, nie in die Antwort. */
138
+ const timing = takeTiming(result);
139
+ if (timing !== null && typeof timing['begin'] === 'number') {
140
+ timing['wait'] = Math.max(0, timing['begin'] - startedAt);
141
+ delete timing['begin'];
142
+ }
137
143
  if (result.ok && definition.after) {
138
144
  try {
139
145
  result = definition.after(built.command.args, result);
@@ -158,6 +164,7 @@ export async function runTool(definition, rawInput, transport, logger, options =
158
164
  error_message: result.error?.message ?? null,
159
165
  warnings: Array.isArray(result.warnings) ? result.warnings : [],
160
166
  result_keys: Object.keys(result).filter((key) => key !== 'ok' && key !== 'warnings'),
167
+ ...(timing !== null ? { timing_ms: timing } : {}),
161
168
  });
162
169
  return result;
163
170
  }
@@ -31,7 +31,7 @@ export const POLAR_IDS = {
31
31
  /**
32
32
  * Kennungen von LayoutBridge Light (A-70, E-109): dieselbe Organisation,
33
33
  * eigener Benefit des eigenen Polar-Produkts ("LayoutBridge Light licence
34
- * key", Schluesselpraefix LBL, 1 Aktivierung, kein Ablauf; Angabe Sascha
34
+ * key", Schluesselpraefix LBL, 1 Aktivierung, kein Ablauf; Angabe des Anbieters
35
35
  * 01.11.2026, Nachtrag E-109). Ohne Benefit wiese Light jeden Schluessel ab
36
36
  * (requireBenefit im Verifier), und scripts/light-package.mjs baute kein
37
37
  * Paket.