layoutbridge-light 0.0.0-stage → 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.
- package/CHANGELOG.md +105 -0
- package/LICENSE +26 -0
- package/README.de.md +367 -0
- package/README.md +353 -2
- package/THIRD-PARTY-NOTICES.md +2878 -0
- package/dist/src/mcp/catalog.js +131 -0
- package/dist/src/mcp/config.js +211 -0
- package/dist/src/mcp/content-credentials.js +65 -0
- package/dist/src/mcp/dispatch.js +171 -0
- package/dist/src/mcp/epub-check.js +331 -0
- package/dist/src/mcp/epub-orientation.js +132 -0
- package/dist/src/mcp/license/free-tools.js +16 -0
- package/dist/src/mcp/license/gate.js +210 -0
- package/dist/src/mcp/license/index.js +107 -0
- package/dist/src/mcp/license/instance-name.js +57 -0
- package/dist/src/mcp/license/key-source.js +59 -0
- package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
- package/dist/src/mcp/license/placeholder-verifier.js +10 -0
- package/dist/src/mcp/license/polar-ids.js +100 -0
- package/dist/src/mcp/license/polar-verifier.js +285 -0
- package/dist/src/mcp/license/product-ids.js +73 -0
- package/dist/src/mcp/license/state-store.js +91 -0
- package/dist/src/mcp/license/types.js +11 -0
- package/dist/src/mcp/logging.js +271 -0
- package/dist/src/mcp/pdf-postprocess.js +798 -0
- package/dist/src/mcp/product-info.js +54 -0
- package/dist/src/mcp/product.js +56 -0
- package/dist/src/mcp/server.js +253 -0
- package/dist/src/mcp/setup.js +680 -0
- package/dist/src/mcp/start.cjs +117 -0
- package/dist/src/mcp/target-folder.js +25 -0
- package/dist/src/mcp/tool-definition.js +2 -0
- package/dist/src/mcp/tools.js +2680 -0
- package/dist/src/mcp/typography-rules.js +70 -0
- package/dist/src/transport/com.js +436 -0
- package/dist/src/transport/osascript.js +171 -0
- package/dist/src/transport/queue.js +19 -0
- package/dist/src/transport/script-bundle.js +92 -0
- package/dist/src/transport/transport.js +52 -0
- package/indesign/commands/10-app.js +6 -0
- package/indesign/commands/20-documents.js +517 -0
- package/indesign/commands/22-document-setup.js +204 -0
- package/indesign/commands/23-save-package.js +258 -0
- package/indesign/commands/25-export.js +695 -0
- package/indesign/commands/26-export-image.js +202 -0
- package/indesign/commands/27-textdefaults.js +151 -0
- package/indesign/commands/28-selection.js +174 -0
- package/indesign/commands/30-pages.js +1032 -0
- package/indesign/commands/31-view.js +310 -0
- package/indesign/commands/32-sections.js +203 -0
- package/indesign/commands/35-parents.js +448 -0
- package/indesign/commands/37-layout.js +837 -0
- package/indesign/commands/40-frames.js +886 -0
- package/indesign/commands/41-shapes.js +505 -0
- package/indesign/commands/42-groups.js +117 -0
- package/indesign/commands/43-layers.js +310 -0
- package/indesign/commands/44-effects.js +274 -0
- package/indesign/commands/45-frame-options.js +247 -0
- package/indesign/commands/47-anchored.js +473 -0
- package/indesign/commands/50-modify.js +803 -0
- package/indesign/commands/52-align.js +139 -0
- package/indesign/commands/55-fit.js +106 -0
- package/indesign/commands/60-images.js +1154 -0
- package/indesign/commands/62-embed.js +120 -0
- package/indesign/commands/69-placeholders.js +132 -0
- package/indesign/commands/70-text.js +710 -0
- package/indesign/commands/71-outlines.js +92 -0
- package/indesign/commands/72-place-text.js +476 -0
- package/indesign/commands/73-story-lock.js +157 -0
- package/indesign/commands/74-stories.js +121 -0
- package/indesign/commands/75-tables.js +702 -0
- package/indesign/commands/76-text-variables.js +260 -0
- package/indesign/commands/77-story-options.js +86 -0
- package/indesign/commands/78-frame-lines.js +511 -0
- package/indesign/commands/79-check-typography.js +480 -0
- package/indesign/commands/80-styles.js +1960 -0
- package/indesign/commands/81-composition.js +355 -0
- package/indesign/commands/82-object-styles.js +263 -0
- package/indesign/commands/83-drop-caps.js +87 -0
- package/indesign/commands/83-indents-tabs.js +264 -0
- package/indesign/commands/84-import-styles.js +142 -0
- package/indesign/commands/85-swatches.js +112 -0
- package/indesign/commands/86-gradients.js +185 -0
- package/indesign/commands/87-replace-swatch.js +180 -0
- package/indesign/commands/88-replace-font.js +212 -0
- package/indesign/commands/89-toc-index.js +1002 -0
- package/indesign/commands/90-print-on-demand.js +495 -0
- package/indesign/commands/91-epub.js +629 -0
- package/indesign/commands/92-accessibility.js +1131 -0
- package/indesign/interpreter.js +829 -0
- package/package.json +41 -4
- package/rules/typography-rules.json +32 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protokoll: eine Zeile je Aufruf (Spezifikation, allgemeine Anforderung 11).
|
|
3
|
+
* Jede Instanz schreibt in eine eigene Datei, damit sich mehrere Server nicht
|
|
4
|
+
* gegenseitig ins Protokoll schreiben.
|
|
5
|
+
*
|
|
6
|
+
* Wichtig: nie auf die Standardausgabe schreiben – dort laeuft das
|
|
7
|
+
* MCP-Protokoll. Nur in die Datei und notfalls auf die Standardfehlerausgabe.
|
|
8
|
+
*
|
|
9
|
+
* Datensparsamkeit (Auftrag A-8 des Vermarktungsprojekts, 13.09.2026,
|
|
10
|
+
* Vermerk E-64): Der Aufrufsatz `args` wird nicht mehr roh geschrieben.
|
|
11
|
+
* Festgehalten werden die Parameternamen; Werte nur dort, wo sie fuer die
|
|
12
|
+
* Fehlersuche noetig und harmlos sind – Zahlen, Wahrheitswerte, Kennungen,
|
|
13
|
+
* Indizes, Aufzaehlungswerte. Zeichenketten, die Dokumentinhalt sein
|
|
14
|
+
* koennen, werden durch eine Laengenangabe ersetzt. Pfade behalten ihre
|
|
15
|
+
* Form, das Benutzerverzeichnis wird durch "~" ersetzt. Wie tief
|
|
16
|
+
* protokolliert wird, steuert LAYOUTBRIDGE_SPM_LOG_LEVEL.
|
|
17
|
+
*/
|
|
18
|
+
import { appendFileSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
|
|
19
|
+
import { homedir, platform } from 'node:os';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { LICENSE_TERMS_URL, SUPPORT_EMAIL } from './product-info.js';
|
|
22
|
+
import { envName, PRODUCT } from './product.js';
|
|
23
|
+
export const DEFAULT_LOG_LEVEL = 'basic';
|
|
24
|
+
export const DEFAULT_LOG_KEEP_DAYS = 30;
|
|
25
|
+
/** Laengste Zeichenkette, die auch ausserhalb der Inhaltsfelder im Klartext bleibt. */
|
|
26
|
+
const MAX_PLAIN_STRING = 60;
|
|
27
|
+
/** Laengster Pfad, der im Klartext bleibt. */
|
|
28
|
+
const MAX_PATH_STRING = 300;
|
|
29
|
+
/** Hoechstzahl protokollierter Eintraege je Liste oder Objekt. */
|
|
30
|
+
const MAX_ITEMS = 20;
|
|
31
|
+
/**
|
|
32
|
+
* Parameter, deren Wert Dokumentinhalt sein kann. Ihr Wert wird immer
|
|
33
|
+
* ersetzt – bei Zeichenketten durch die Laengenangabe, bei Listen und
|
|
34
|
+
* Objekten durch die Zahl der Eintraege. Zahlen und Wahrheitswerte bleiben
|
|
35
|
+
* (z. B. `rows` als Zeilenzahl in create_table).
|
|
36
|
+
*/
|
|
37
|
+
const CONTENT_KEYS = [
|
|
38
|
+
'cells',
|
|
39
|
+
'content',
|
|
40
|
+
'find',
|
|
41
|
+
'initial_text',
|
|
42
|
+
'marker',
|
|
43
|
+
'replace',
|
|
44
|
+
'rows',
|
|
45
|
+
'style_map',
|
|
46
|
+
'template',
|
|
47
|
+
'text',
|
|
48
|
+
'text_after',
|
|
49
|
+
'text_before',
|
|
50
|
+
'values',
|
|
51
|
+
];
|
|
52
|
+
function isContentKey(key) {
|
|
53
|
+
if (CONTENT_KEYS.includes(key))
|
|
54
|
+
return true;
|
|
55
|
+
return key.endsWith('_text') || key.endsWith('_texts');
|
|
56
|
+
}
|
|
57
|
+
/** Zeichen zaehlen, nicht Bytes. */
|
|
58
|
+
export function charCount(value) {
|
|
59
|
+
return Array.from(value).length;
|
|
60
|
+
}
|
|
61
|
+
function textNote(value) {
|
|
62
|
+
return '<Text, ' + String(charCount(value)) + ' Zeichen>';
|
|
63
|
+
}
|
|
64
|
+
/** Benutzerverzeichnis in einem Text durch "~" ersetzen. */
|
|
65
|
+
export function maskHome(value, home = homedir()) {
|
|
66
|
+
if (home.length < 2)
|
|
67
|
+
return value;
|
|
68
|
+
return value.split(home).join('~');
|
|
69
|
+
}
|
|
70
|
+
function looksLikePath(value) {
|
|
71
|
+
return value.includes('/') || value.includes('\\') || /^[A-Za-z]:/.test(value);
|
|
72
|
+
}
|
|
73
|
+
function filterValue(key, value, home, depth) {
|
|
74
|
+
if (value === null)
|
|
75
|
+
return null;
|
|
76
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
77
|
+
return value;
|
|
78
|
+
if (isContentKey(key)) {
|
|
79
|
+
if (typeof value === 'string')
|
|
80
|
+
return textNote(value);
|
|
81
|
+
if (Array.isArray(value))
|
|
82
|
+
return '<Liste, ' + String(value.length) + ' Eintraege>';
|
|
83
|
+
if (typeof value === 'object')
|
|
84
|
+
return '<Objekt, ' + String(Object.keys(value).length) + ' Felder>';
|
|
85
|
+
return '<Wert>';
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === 'string') {
|
|
88
|
+
const masked = maskHome(value, home);
|
|
89
|
+
if (value.includes('\n'))
|
|
90
|
+
return textNote(value);
|
|
91
|
+
/*
|
|
92
|
+
* Pfade bleiben lesbar – ohne sie ist ein Ablagefehler nicht zu finden.
|
|
93
|
+
* Der Benutzername ist heraus, und eine Zeile ohne Umbruch, die einen
|
|
94
|
+
* Trenner enthaelt, ist kein Fliesstext.
|
|
95
|
+
*/
|
|
96
|
+
if (looksLikePath(value))
|
|
97
|
+
return charCount(value) > MAX_PATH_STRING ? textNote(value) : masked;
|
|
98
|
+
if (charCount(value) > MAX_PLAIN_STRING)
|
|
99
|
+
return textNote(value);
|
|
100
|
+
return masked;
|
|
101
|
+
}
|
|
102
|
+
if (depth >= 3)
|
|
103
|
+
return '<verschachtelt>';
|
|
104
|
+
if (Array.isArray(value)) {
|
|
105
|
+
const shown = value.slice(0, MAX_ITEMS).map((item) => filterValue(key, item, home, depth + 1));
|
|
106
|
+
if (value.length > MAX_ITEMS)
|
|
107
|
+
shown.push('<' + String(value.length - MAX_ITEMS) + ' weitere>');
|
|
108
|
+
return shown;
|
|
109
|
+
}
|
|
110
|
+
if (typeof value === 'object') {
|
|
111
|
+
const record = value;
|
|
112
|
+
const out = {};
|
|
113
|
+
let count = 0;
|
|
114
|
+
for (const innerKey of Object.keys(record)) {
|
|
115
|
+
if (count >= MAX_ITEMS) {
|
|
116
|
+
out['…'] = '<' + String(Object.keys(record).length - MAX_ITEMS) + ' weitere>';
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
out[innerKey] = filterValue(innerKey, record[innerKey], home, depth + 1);
|
|
120
|
+
count += 1;
|
|
121
|
+
}
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
return '<Wert>';
|
|
125
|
+
}
|
|
126
|
+
/** Aufrufsatz fuer das Protokoll entschaerfen (Stufe "basic"). */
|
|
127
|
+
export function filterArgs(args, home = homedir()) {
|
|
128
|
+
const out = {};
|
|
129
|
+
for (const key of Object.keys(args)) {
|
|
130
|
+
out[key] = filterValue(key, args[key], home, 0);
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
/** Protokollzeile fuer die Stufe "basic" aufbereiten. */
|
|
135
|
+
export function filterEntry(entry, home = homedir()) {
|
|
136
|
+
return {
|
|
137
|
+
...entry,
|
|
138
|
+
args: filterArgs(entry.args, home),
|
|
139
|
+
error_message: entry.error_message === null ? null : maskHome(entry.error_message, home),
|
|
140
|
+
warnings: entry.warnings.map((warning) => maskHome(warning, home)),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
export function readLogLevel(env = process.env, product = PRODUCT) {
|
|
144
|
+
const raw = env[envName('LOG_LEVEL', product)];
|
|
145
|
+
if (raw === undefined)
|
|
146
|
+
return DEFAULT_LOG_LEVEL;
|
|
147
|
+
const value = raw.trim().toLowerCase();
|
|
148
|
+
if (value === 'off' || value === 'basic' || value === 'full')
|
|
149
|
+
return value;
|
|
150
|
+
return DEFAULT_LOG_LEVEL;
|
|
151
|
+
}
|
|
152
|
+
/** Aufbewahrungsfrist in Tagen; 0 schaltet das Aufraeumen ab. */
|
|
153
|
+
export function readLogKeepDays(env = process.env, product = PRODUCT) {
|
|
154
|
+
const raw = env[envName('LOG_KEEP_DAYS', product)];
|
|
155
|
+
if (raw === undefined || raw.trim().length === 0)
|
|
156
|
+
return DEFAULT_LOG_KEEP_DAYS;
|
|
157
|
+
const value = Number(raw.trim());
|
|
158
|
+
if (!Number.isFinite(value) || value < 0)
|
|
159
|
+
return DEFAULT_LOG_KEEP_DAYS;
|
|
160
|
+
return Math.floor(value);
|
|
161
|
+
}
|
|
162
|
+
export function defaultLogDirectory(product = PRODUCT) {
|
|
163
|
+
const fromEnv = process.env[envName('LOG_DIR', product)];
|
|
164
|
+
if (fromEnv !== undefined && fromEnv.length > 0)
|
|
165
|
+
return fromEnv;
|
|
166
|
+
if (platform() === 'darwin') {
|
|
167
|
+
return join(homedir(), 'Library', 'Logs', product.dataDirName);
|
|
168
|
+
}
|
|
169
|
+
return join(homedir(), '.' + product.dataDirName, 'logs');
|
|
170
|
+
}
|
|
171
|
+
function twoDigits(value) {
|
|
172
|
+
return String(value).padStart(2, '0');
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Protokolldateien loeschen, die aelter als `keepDays` Tage sind. Das Datum
|
|
176
|
+
* kommt aus dem Dateinamen; gerechnet wird ab dem Ende des genannten Tages,
|
|
177
|
+
* damit nichts zu frueh verschwindet. Gibt die geloeschten Namen zurueck.
|
|
178
|
+
*/
|
|
179
|
+
export function removeOldLogs(directory, keepDays, now = new Date()) {
|
|
180
|
+
if (keepDays <= 0)
|
|
181
|
+
return [];
|
|
182
|
+
const cutoff = now.getTime() - keepDays * 24 * 60 * 60 * 1000;
|
|
183
|
+
let names;
|
|
184
|
+
try {
|
|
185
|
+
names = readdirSync(directory);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
const removed = [];
|
|
191
|
+
for (const name of names) {
|
|
192
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})_pid\d+\.log$/.exec(name);
|
|
193
|
+
if (match === null)
|
|
194
|
+
continue;
|
|
195
|
+
const endOfDay = Date.parse(match[1] + '-' + match[2] + '-' + match[3] + 'T23:59:59.999Z');
|
|
196
|
+
if (Number.isNaN(endOfDay) || endOfDay >= cutoff)
|
|
197
|
+
continue;
|
|
198
|
+
try {
|
|
199
|
+
unlinkSync(join(directory, name));
|
|
200
|
+
removed.push(name);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
/* schon weg oder nicht loeschbar – kein Grund fuer einen Abbruch */
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return removed;
|
|
207
|
+
}
|
|
208
|
+
export class CallLogger {
|
|
209
|
+
file;
|
|
210
|
+
level;
|
|
211
|
+
removedLogs;
|
|
212
|
+
home;
|
|
213
|
+
broken = false;
|
|
214
|
+
constructor(directory = defaultLogDirectory(), now = new Date(), options = {}) {
|
|
215
|
+
this.level = options.level ?? readLogLevel();
|
|
216
|
+
this.home = options.home ?? homedir();
|
|
217
|
+
const stamp = now.getFullYear() +
|
|
218
|
+
'-' + twoDigits(now.getMonth() + 1) +
|
|
219
|
+
'-' + twoDigits(now.getDate());
|
|
220
|
+
this.file = join(directory, `${stamp}_pid${process.pid}.log`);
|
|
221
|
+
this.removedLogs = removeOldLogs(directory, options.keepDays ?? readLogKeepDays(), now);
|
|
222
|
+
if (this.level === 'off') {
|
|
223
|
+
this.broken = true;
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
mkdirSync(directory, { recursive: true });
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
this.broken = true;
|
|
231
|
+
process.stderr.write(`[${PRODUCT.packageName}] no log directory: ${String(error)}\n`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
this.writeLine({
|
|
235
|
+
ts: new Date().toISOString(),
|
|
236
|
+
event: 'start',
|
|
237
|
+
log_level: this.level,
|
|
238
|
+
note: this.level === 'full'
|
|
239
|
+
? 'Log level "full": tool arguments are written unfiltered, including document text and paths with your user name. Set ' + envName('LOG_LEVEL') + '=basic when the support case is closed.'
|
|
240
|
+
: 'Log level "basic": argument names with harmless values only; text is reduced to its length, paths hide the user name.',
|
|
241
|
+
removed_old_logs: this.removedLogs.length,
|
|
242
|
+
/* Lizenzbedingungen und Support-Adresse in der Startzeile des Protokolls (A-11 Nr. 3, A-10 Nr. 1). */
|
|
243
|
+
license_terms: LICENSE_TERMS_URL,
|
|
244
|
+
support: SUPPORT_EMAIL,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
log(entry) {
|
|
248
|
+
if (this.broken)
|
|
249
|
+
return;
|
|
250
|
+
const prepared = this.level === 'full' ? entry : filterEntry(entry, this.home);
|
|
251
|
+
this.writeLine({ ts: new Date().toISOString(), ...prepared });
|
|
252
|
+
}
|
|
253
|
+
writeLine(record) {
|
|
254
|
+
if (this.broken)
|
|
255
|
+
return;
|
|
256
|
+
try {
|
|
257
|
+
appendFileSync(this.file, JSON.stringify(record) + '\n', 'utf8');
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
this.broken = true;
|
|
261
|
+
process.stderr.write(`[${PRODUCT.packageName}] cannot write log: ${String(error)}\n`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/** Protokollsenke, die nichts tut – fuer Tests. */
|
|
266
|
+
export class NullLogger {
|
|
267
|
+
log() {
|
|
268
|
+
/* absichtlich leer */
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=logging.js.map
|