@forestadmin/agent-bff 1.18.0 → 1.19.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.
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const api_key_middleware_1 = require("../api-key/api-key-middleware");
4
+ const timezone_middleware_1 = require("../timezone/timezone-middleware");
5
+ /** Neutral and always valid, where a local zone would only be right for whoever generated it. */
6
+ const SAMPLE_TIMEZONE = 'UTC';
7
+ const KEY_VARIABLE = 'BFF_KEY';
8
+ /**
9
+ * Browser source, injected into the page: it decorates the fetched document with `x-codeSamples`,
10
+ * which Redoc renders as one tab per language, then hands it to `Redoc.init`.
11
+ *
12
+ * In the page rather than in the document, deliberately. Three samples per operation weigh ~73 KB on
13
+ * a 16-collection schema and several hundred KB on a large one, which every consumer of
14
+ * `/agent/openapi.json` would pay for an extension only a viewer reads — where this costs one
15
+ * function whatever the operation count. It also lets a sample carry the REAL origin: the document
16
+ * declares `servers: [{ url: '/' }]`, so a sample built into it could only hold a placeholder host,
17
+ * while the page knows where it is served from and emits a command that runs as pasted.
18
+ *
19
+ * The key is never inlined: each language reads it from the environment, so a copied sample cannot
20
+ * carry a credential into a shell history or a paste.
21
+ *
22
+ * Everything else is read from the document rather than assumed: the auth header comes from the
23
+ * security scheme the operation names, and the body carries exactly the properties its request
24
+ * schema makes required — `parentId` for a relation, `recordIds` for an action, nothing at all for a
25
+ * list, whose body is optional. The one header that cannot be read off an operation is the timezone,
26
+ * a component-level parameter reference; resolving it would buy nothing, since omitting it is a 400
27
+ * (`resolveTimezone` throws `missing_timezone` when header, body field and deployment default are
28
+ * all absent) and that is precisely what a hand-written sample forgets.
29
+ */
30
+ const SAMPLES_SCRIPT = `
31
+ var KEY_HEADER = ${JSON.stringify(api_key_middleware_1.BFF_KEY_HEADER)};
32
+ var TIMEZONE_HEADER = ${JSON.stringify(timezone_middleware_1.TIMEZONE_HEADER)};
33
+ var SAMPLE_TIMEZONE = ${JSON.stringify(SAMPLE_TIMEZONE)};
34
+ var KEY_VARIABLE = ${JSON.stringify(KEY_VARIABLE)};
35
+
36
+ /**
37
+ * Every walk below is depth-bounded, because a document can reference itself: a filter is a
38
+ * condition TREE, and nothing stops a \`$ref\` or an \`allOf\` from naming its own schema. An
39
+ * overflow here is not local — it throws out of the decoration, so every operation after the
40
+ * offending one loses its samples too.
41
+ */
42
+ var MAX_DEPTH = 6;
43
+
44
+ function schemaOf(spec, node, depth) {
45
+ if (!node || depth > MAX_DEPTH) return {};
46
+
47
+ if (node.$ref) {
48
+ var schemas = (spec.components || {}).schemas || {};
49
+
50
+ return schemaOf(spec, schemas[node.$ref.split('/').pop()], depth + 1);
51
+ }
52
+
53
+ return node;
54
+ }
55
+
56
+ // A relation request is its foreign collection request plus a parent id, expressed as allOf.
57
+ function flatten(spec, node, depth) {
58
+ var schema = schemaOf(spec, node, depth);
59
+
60
+ if (!schema.allOf || depth > MAX_DEPTH) return schema;
61
+
62
+ var merged = { properties: {}, required: [] };
63
+
64
+ schema.allOf.forEach(function (part) {
65
+ var flat = flatten(spec, part, depth + 1);
66
+
67
+ Object.keys(flat.properties || {}).forEach(function (name) {
68
+ merged.properties[name] = flat.properties[name];
69
+ });
70
+ merged.required = merged.required.concat(flat.required || []);
71
+ });
72
+
73
+ return merged;
74
+ }
75
+
76
+ function placeholder(spec, name, node, depth) {
77
+ if (depth > MAX_DEPTH) return '<' + name + '>';
78
+
79
+ var schema = schemaOf(spec, node, depth);
80
+ var alternatives = schema.anyOf || schema.oneOf;
81
+
82
+ if (alternatives && alternatives.length) {
83
+ return placeholder(spec, name, alternatives[0], depth + 1);
84
+ }
85
+
86
+ if (schema.enum && schema.enum.length) return schema.enum[0];
87
+ if (schema.type === 'array') return [placeholder(spec, name, schema.items, depth + 1)];
88
+ if (schema.type === 'number' || schema.type === 'integer') return 0;
89
+ if (schema.type === 'boolean') return true;
90
+ if (schema.type === 'object') return {};
91
+
92
+ return '<' + name + '>';
93
+ }
94
+
95
+ function exampleBody(spec, operation) {
96
+ var content = ((operation.requestBody || {}).content || {})['application/json'];
97
+
98
+ if (!content) return undefined;
99
+
100
+ var schema = flatten(spec, content.schema, 0);
101
+ var body = {};
102
+
103
+ (schema.required || []).forEach(function (name) {
104
+ body[name] = placeholder(spec, name, (schema.properties || {})[name], 0);
105
+ });
106
+
107
+ return body;
108
+ }
109
+
110
+ // The scheme the operation names, so a document that renames or re-types it stays right.
111
+ function authHeader(spec, operation) {
112
+ var schemes = (spec.components || {}).securitySchemes || {};
113
+ var requirements = operation.security || spec.security || [];
114
+ var header = null;
115
+
116
+ requirements.forEach(function (requirement) {
117
+ Object.keys(requirement).forEach(function (name) {
118
+ if (header) return;
119
+
120
+ var scheme = schemes[name] || {};
121
+
122
+ if (scheme.type === 'apiKey' && scheme.in === 'header') {
123
+ header = { name: scheme.name, prefix: '', secret: true };
124
+ } else if (scheme.type === 'http' && scheme.scheme === 'bearer') {
125
+ header = { name: 'Authorization', prefix: 'Bearer ', secret: true };
126
+ }
127
+ });
128
+ });
129
+
130
+ return header || { name: KEY_HEADER, prefix: '', secret: true };
131
+ }
132
+
133
+ /**
134
+ * The unfolded document has no path parameter left — its segments are the real names, already
135
+ * URL-encoded — but the generic one is all templates, and a sample cannot be made to invoke
136
+ * those: the generic document is served precisely BECAUSE the deployment cannot enumerate its
137
+ * collections, so there is no real name to substitute. Inventing one would read as runnable
138
+ * and answer 404. Each template becomes the same \`<name>\` placeholder the bodies use, so one
139
+ * notation across a snippet means "replace this" and none of it looks like API syntax.
140
+ */
141
+ function samplePath(spec, item, operation, path) {
142
+ var parameters = (item.parameters || []).concat(operation.parameters || []);
143
+ var components = (spec.components || {}).parameters || {};
144
+ var sampled = path;
145
+
146
+ parameters.forEach(function (parameter) {
147
+ var declared = parameter && parameter.$ref
148
+ ? components[parameter.$ref.split('/').pop()] || {}
149
+ : parameter || {};
150
+
151
+ if (declared.in !== 'path' || !declared.name) return;
152
+
153
+ sampled = sampled.split('{' + declared.name + '}').join('<' + declared.name + '>');
154
+ });
155
+
156
+ return sampled;
157
+ }
158
+
159
+ function headersOf(spec, operation, body) {
160
+ var headers = [authHeader(spec, operation)];
161
+
162
+ headers.push({ name: TIMEZONE_HEADER, value: SAMPLE_TIMEZONE });
163
+
164
+ if (body !== undefined) headers.push({ name: 'Content-Type', value: 'application/json' });
165
+
166
+ return headers;
167
+ }
168
+
169
+ /**
170
+ * A collection or action name reaches these samples with its apostrophes intact:
171
+ * \`encodeURIComponent\` leaves \`'\` alone, so a collection called \`John's orders\` is a path
172
+ * segment carrying one, and a field name or enum value can carry one into a body. Every
173
+ * interpolation therefore goes through the quoting of its target language.
174
+ */
175
+ function shellQuoted(value) {
176
+ return "'" + String(value).split("'").join("'\\\\''") + "'";
177
+ }
178
+
179
+ // For a value that must EXPAND: single quotes would send the literal '$BFF_KEY' and earn a
180
+ // 401. Only fixed text is ever placed here, but it is escaped for the context regardless.
181
+ function shellExpanding(text) {
182
+ return String(text).replace(/([\\\\"\`$])/g, '\\\\$1');
183
+ }
184
+
185
+ function rubyQuoted(value) {
186
+ return "'" + String(value).replace(/\\\\/g, '\\\\\\\\').split("'").join("\\\\'") + "'";
187
+ }
188
+
189
+ // Ruby interpolates #{...} inside double quotes, and a JSON literal is double-quoted.
190
+ function rubySafeJson(text) {
191
+ return String(text).split('#{').join('\\\\#{');
192
+ }
193
+
194
+ function curlSample(url, method, headers, body) {
195
+ var lines = ['curl -X ' + method + ' ' + shellQuoted(url)];
196
+
197
+ headers.forEach(function (header) {
198
+ if (header.secret) {
199
+ var expanded =
200
+ shellExpanding(header.name) +
201
+ ': ' +
202
+ shellExpanding(header.prefix) +
203
+ '$' +
204
+ KEY_VARIABLE;
205
+
206
+ lines.push(' -H "' + expanded + '"');
207
+
208
+ return;
209
+ }
210
+
211
+ lines.push(' -H ' + shellQuoted(header.name + ': ' + header.value));
212
+ });
213
+
214
+ if (body !== undefined) lines.push(' -d ' + shellQuoted(JSON.stringify(body)));
215
+
216
+ return lines.join(' \\\\\\n');
217
+ }
218
+
219
+ function nodeSample(url, method, headers, body) {
220
+ var lines = [
221
+ 'const response = await fetch(' + JSON.stringify(url) + ', {',
222
+ ' method: ' + JSON.stringify(method) + ',',
223
+ ' headers: {',
224
+ ];
225
+
226
+ headers.forEach(function (header) {
227
+ var value = header.secret
228
+ ? (header.prefix ? JSON.stringify(header.prefix) + ' + ' : '') +
229
+ 'process.env.' +
230
+ KEY_VARIABLE
231
+ : JSON.stringify(header.value);
232
+
233
+ lines.push(' ' + JSON.stringify(header.name) + ': ' + value + ',');
234
+ });
235
+
236
+ lines.push(' },');
237
+
238
+ if (body !== undefined) {
239
+ lines.push(' body: JSON.stringify(' + JSON.stringify(body) + '),');
240
+ }
241
+
242
+ lines.push('});');
243
+ lines.push('');
244
+ lines.push(
245
+ "if (!response.ok) throw new Error(response.status + ' ' + (await response.text()));",
246
+ );
247
+ lines.push('');
248
+ lines.push('console.log(await response.json());');
249
+
250
+ return lines.join('\\n');
251
+ }
252
+
253
+ function rubySample(url, method, headers, body) {
254
+ var verb = method.charAt(0) + method.slice(1).toLowerCase();
255
+ var lines = [
256
+ "require 'json'",
257
+ "require 'net/http'",
258
+ '',
259
+ 'uri = URI(' + rubyQuoted(url) + ')',
260
+ 'request = Net::HTTP::' + verb + '.new(uri)',
261
+ ];
262
+
263
+ headers.forEach(function (header) {
264
+ var read = "ENV.fetch('" + KEY_VARIABLE + "')";
265
+ var value = header.secret
266
+ ? (header.prefix ? '"' + header.prefix + '#{' + read + '}"' : read)
267
+ : rubyQuoted(header.value);
268
+
269
+ lines.push('request[' + rubyQuoted(header.name) + '] = ' + value);
270
+ });
271
+
272
+ if (body !== undefined) {
273
+ lines.push('request.body = JSON.generate(' + rubySafeJson(JSON.stringify(body)) + ')');
274
+ }
275
+
276
+ lines.push('');
277
+ lines.push(
278
+ "response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|",
279
+ );
280
+ lines.push(' http.request(request)');
281
+ lines.push('end');
282
+ lines.push('');
283
+ lines.push('puts response.body');
284
+
285
+ return lines.join('\\n');
286
+ }
287
+
288
+ function decorateWithSamples(spec, origin) {
289
+ var paths = spec.paths || {};
290
+
291
+ Object.keys(paths).forEach(function (path) {
292
+ var item = paths[path] || {};
293
+
294
+ Object.keys(item).forEach(function (method) {
295
+ var operation = item[method];
296
+
297
+ if (!operation || typeof operation !== 'object' || !operation.responses) return;
298
+
299
+ var body = exampleBody(spec, operation);
300
+ var headers = headersOf(spec, operation, body);
301
+ var url = origin + samplePath(spec, item, operation, path);
302
+ var verb = method.toUpperCase();
303
+
304
+ operation['x-codeSamples'] = [
305
+ { lang: 'cURL', source: curlSample(url, verb, headers, body) },
306
+ { lang: 'JavaScript', source: nodeSample(url, verb, headers, body) },
307
+ { lang: 'Ruby', source: rubySample(url, verb, headers, body) },
308
+ ];
309
+ });
310
+ });
311
+
312
+ return spec;
313
+ }
314
+
315
+ /**
316
+ * Samples are a convenience; the document is the point. A shape the generator cannot walk
317
+ * costs the reader its snippets, never the page — and reporting it as a Redoc render failure
318
+ * would send them looking in the wrong place.
319
+ */
320
+ function withSamples(spec) {
321
+ try {
322
+ return decorateWithSamples(spec, window.location.origin);
323
+ } catch (samplesError) {
324
+ return spec;
325
+ }
326
+ }
327
+ `;
328
+ exports.default = SAMPLES_SCRIPT;
329
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9jcy1zYW1wbGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2RvY3MvZG9jcy1zYW1wbGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsc0VBQStEO0FBQy9ELHlFQUFrRTtBQUVsRSxpR0FBaUc7QUFDakcsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDO0FBQzlCLE1BQU0sWUFBWSxHQUFHLFNBQVMsQ0FBQztBQUUvQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBcUJHO0FBQ0gsTUFBTSxjQUFjLEdBQUc7MkJBQ0ksSUFBSSxDQUFDLFNBQVMsQ0FBQyxtQ0FBYyxDQUFDO2dDQUN6QixJQUFJLENBQUMsU0FBUyxDQUFDLHFDQUFlLENBQUM7Z0NBQy9CLElBQUksQ0FBQyxTQUFTLENBQUMsZUFBZSxDQUFDOzZCQUNsQyxJQUFJLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Q0FxU3hELENBQUM7QUFFRixrQkFBZSxjQUFjLENBQUMifQ==
@@ -0,0 +1,84 @@
1
+ /** Passed to `Redoc.init`. Unknown keys are merged into Redoc's own defaults, so this is additive. */
2
+ export declare const REDOC_THEME: {
3
+ readonly colors: {
4
+ readonly primary: {
5
+ readonly main: "#62801a";
6
+ };
7
+ readonly success: {
8
+ readonly main: "#10b981";
9
+ };
10
+ readonly warning: {
11
+ readonly main: "#f59e0b";
12
+ };
13
+ readonly error: {
14
+ readonly main: "#bf3636";
15
+ };
16
+ readonly text: {
17
+ readonly primary: "#14171c";
18
+ readonly secondary: "#505d6f";
19
+ };
20
+ readonly border: {
21
+ readonly light: "#e0e3e8";
22
+ readonly dark: "#c1c7d1";
23
+ };
24
+ readonly http: {
25
+ readonly get: "#7ba01f";
26
+ readonly post: "#10b981";
27
+ readonly put: "#3b82f6";
28
+ readonly patch: "#f59e0b";
29
+ readonly delete: "#bf3636";
30
+ readonly options: "#505d6f";
31
+ readonly head: "#505d6f";
32
+ readonly basic: "#505d6f";
33
+ readonly link: "#3b82f6";
34
+ };
35
+ };
36
+ readonly typography: {
37
+ readonly fontSize: "15px";
38
+ readonly lineHeight: "1.6";
39
+ readonly fontFamily: "Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif";
40
+ readonly headings: {
41
+ readonly fontFamily: "Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif";
42
+ readonly fontWeight: "600";
43
+ };
44
+ readonly code: {
45
+ readonly fontFamily: "'Source Code Pro', ui-monospace, SFMono-Regular, Menlo, monospace";
46
+ readonly fontSize: "13px";
47
+ };
48
+ readonly links: {
49
+ readonly color: "#62801a";
50
+ readonly visited: "#62801a";
51
+ readonly hover: "#496015";
52
+ };
53
+ };
54
+ readonly sidebar: {
55
+ readonly backgroundColor: "#14171c";
56
+ readonly textColor: "#c1c7d1";
57
+ readonly activeTextColor: "#afdf3c";
58
+ readonly arrow: {
59
+ readonly color: "#8390a2";
60
+ };
61
+ };
62
+ readonly rightPanel: {
63
+ readonly backgroundColor: "#282e38";
64
+ readonly textColor: "#f0f1f3";
65
+ };
66
+ readonly schema: {
67
+ readonly typeNameColor: "#505d6f";
68
+ readonly typeTitleColor: "#62801a";
69
+ readonly requireLabelColor: "#bf3636";
70
+ };
71
+ };
72
+ /**
73
+ * The product logo, verbatim from the frontend's `public/img/logo.svg` minus its XML prolog — the
74
+ * mark itself, not a redrawing of it, so it cannot drift in geometry. The tree is negative space:
75
+ * the lime path covers everything the dark ground does not show through. Both colours are the logo's
76
+ * own, brighter and darker than any shade the interface palette carries.
77
+ *
78
+ * Inline rather than a file: this page must request nothing off-origin, and a favicon is a request
79
+ * like any other. It also needs no route of its own, and no bundle to exist.
80
+ */
81
+ export declare const FAVICON_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1080 1080\" fill=\"none\">\n <rect width=\"1080\" height=\"1080\" fill=\"#3F5B1E\"/>\n <path d=\"M0 0V1080H487.658V649.169L277.256 859.597L203.317 785.648L540 448.922L876.683 785.648L802.744 859.597L592.342 649.169V1080H1080V0H0ZM802.675 568.892L539.931 306.115L277.188 568.892L203.248 494.943L539.931 158.217L876.614 494.943L802.675 568.892Z\" fill=\"#DDF327\"/>\n</svg>";
82
+ /** The shell around Redoc: the key prompt, and the error box it writes into. */
83
+ export declare const PAGE_STYLES: string;
84
+ //# sourceMappingURL=docs-theme.d.ts.map
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PAGE_STYLES = exports.FAVICON_SVG = exports.REDOC_THEME = void 0;
4
+ /**
5
+ * The Forest palette, as the frontend defines it in `app/styles/common/palette.css`: `accent` is the
6
+ * lime ramp, the neutrals are `slate`. Copied rather than shared — this package depends on nothing in
7
+ * the frontend, and a viewer that trails a shade behind a redesign is not a defect.
8
+ *
9
+ * Only the shades this page uses are here. The pairings below are the accessible ones: lime 500 is
10
+ * the brand colour but it carries 1.96:1 against white, so it is a FILL with dark text on it, never
11
+ * text itself. Lime 700 (4.54:1) is the lightest shade usable as text on white, and the dark chrome
12
+ * takes lime 400 (11.5:1 on slate 1000).
13
+ */
14
+ const LIME = {
15
+ 400: '#afdf3c',
16
+ 500: '#99c924',
17
+ 600: '#7ba01f',
18
+ 700: '#62801a',
19
+ 800: '#496015',
20
+ };
21
+ const SLATE = {
22
+ 100: '#f0f1f3',
23
+ 200: '#e0e3e8',
24
+ 300: '#c1c7d1',
25
+ 500: '#8390a2',
26
+ 700: '#505d6f',
27
+ 900: '#282e38',
28
+ 1000: '#14171c',
29
+ };
30
+ const RED = { 100: '#fdecec', 700: '#bf3636' };
31
+ const EMERALD = { 600: '#10b981' };
32
+ const YELLOW = { 600: '#f59e0b' };
33
+ const BLUE = { 600: '#3b82f6' };
34
+ /**
35
+ * Inter and Source Code Pro are the frontend's faces, first in the stack so a machine that has them
36
+ * uses them. They are NOT fetched: a page holding an API key in memory must not talk to a font CDN,
37
+ * for the same reason the Redoc bundle is served from here rather than from one. Everyone else gets
38
+ * the system UI font, which is the price of that.
39
+ */
40
+ const SANS = "Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif";
41
+ const MONO = "'Source Code Pro', ui-monospace, SFMono-Regular, Menlo, monospace";
42
+ /** Passed to `Redoc.init`. Unknown keys are merged into Redoc's own defaults, so this is additive. */
43
+ exports.REDOC_THEME = {
44
+ colors: {
45
+ primary: { main: LIME[700] },
46
+ success: { main: EMERALD[600] },
47
+ warning: { main: YELLOW[600] },
48
+ error: { main: RED[700] },
49
+ text: { primary: SLATE[1000], secondary: SLATE[700] },
50
+ border: { light: SLATE[200], dark: SLATE[300] },
51
+ http: {
52
+ get: LIME[600],
53
+ post: EMERALD[600],
54
+ put: BLUE[600],
55
+ patch: YELLOW[600],
56
+ delete: RED[700],
57
+ options: SLATE[700],
58
+ head: SLATE[700],
59
+ basic: SLATE[700],
60
+ link: BLUE[600],
61
+ },
62
+ },
63
+ typography: {
64
+ fontSize: '15px',
65
+ lineHeight: '1.6',
66
+ fontFamily: SANS,
67
+ headings: { fontFamily: SANS, fontWeight: '600' },
68
+ code: { fontFamily: MONO, fontSize: '13px' },
69
+ links: { color: LIME[700], visited: LIME[700], hover: LIME[800] },
70
+ },
71
+ // Forest's own chrome is dark with lime accents; Redoc's two panels are where that reads.
72
+ sidebar: {
73
+ backgroundColor: SLATE[1000],
74
+ textColor: SLATE[300],
75
+ activeTextColor: LIME[400],
76
+ arrow: { color: SLATE[500] },
77
+ },
78
+ rightPanel: { backgroundColor: SLATE[900], textColor: SLATE[100] },
79
+ schema: { typeNameColor: SLATE[700], typeTitleColor: LIME[700], requireLabelColor: RED[700] },
80
+ };
81
+ /**
82
+ * The product logo, verbatim from the frontend's `public/img/logo.svg` minus its XML prolog — the
83
+ * mark itself, not a redrawing of it, so it cannot drift in geometry. The tree is negative space:
84
+ * the lime path covers everything the dark ground does not show through. Both colours are the logo's
85
+ * own, brighter and darker than any shade the interface palette carries.
86
+ *
87
+ * Inline rather than a file: this page must request nothing off-origin, and a favicon is a request
88
+ * like any other. It also needs no route of its own, and no bundle to exist.
89
+ */
90
+ exports.FAVICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 1080" fill="none">
91
+ <rect width="1080" height="1080" fill="#3F5B1E"/>
92
+ <path d="M0 0V1080H487.658V649.169L277.256 859.597L203.317 785.648L540 448.922L876.683 785.648L802.744 859.597L592.342 649.169V1080H1080V0H0ZM802.675 568.892L539.931 306.115L277.188 568.892L203.248 494.943L539.931 158.217L876.614 494.943L802.675 568.892Z" fill="#DDF327"/>
93
+ </svg>`;
94
+ /** The shell around Redoc: the key prompt, and the error box it writes into. */
95
+ exports.PAGE_STYLES = `
96
+ body { margin: 0; font-family: ${SANS}; color: ${SLATE[1000]}; }
97
+ #unlock { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; padding: 14px 20px; background: ${SLATE[1000]}; color: #fff; }
98
+ #unlock strong { font-size: 15px; font-weight: 600; margin-right: 4px; }
99
+ #unlock strong span { color: ${LIME[400]}; }
100
+ #unlock label { font-size: 13px; color: ${SLATE[300]}; }
101
+ #unlock input { flex: 1 1 280px; max-width: 420px; padding: 7px 10px; font: inherit; font-size: 14px; color: #fff; background: ${SLATE[900]}; border: 1px solid ${SLATE[700]}; border-radius: 4px; }
102
+ #unlock input:focus { outline: 2px solid ${LIME[500]}; outline-offset: 1px; }
103
+ #unlock button { padding: 7px 16px; font: inherit; font-size: 14px; font-weight: 600; color: ${SLATE[1000]}; background: ${LIME[500]}; border: 0; border-radius: 4px; cursor: pointer; }
104
+ #unlock button:hover { background: ${LIME[400]}; }
105
+ #error { display: none; margin: 20px; padding: 12px 14px; border-left: 3px solid ${RED[700]}; background: ${RED[100]}; color: ${RED[700]}; font-size: 14px; white-space: pre-wrap; }
106
+ #error[data-shown] { display: block; }
107
+ `;
108
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9jcy10aGVtZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9kb2NzL2RvY3MtdGhlbWUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUE7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxJQUFJLEdBQUc7SUFDWCxHQUFHLEVBQUUsU0FBUztJQUNkLEdBQUcsRUFBRSxTQUFTO0lBQ2QsR0FBRyxFQUFFLFNBQVM7SUFDZCxHQUFHLEVBQUUsU0FBUztJQUNkLEdBQUcsRUFBRSxTQUFTO0NBQ04sQ0FBQztBQUVYLE1BQU0sS0FBSyxHQUFHO0lBQ1osR0FBRyxFQUFFLFNBQVM7SUFDZCxHQUFHLEVBQUUsU0FBUztJQUNkLEdBQUcsRUFBRSxTQUFTO0lBQ2QsR0FBRyxFQUFFLFNBQVM7SUFDZCxHQUFHLEVBQUUsU0FBUztJQUNkLEdBQUcsRUFBRSxTQUFTO0lBQ2QsSUFBSSxFQUFFLFNBQVM7Q0FDUCxDQUFDO0FBRVgsTUFBTSxHQUFHLEdBQUcsRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQVcsQ0FBQztBQUN4RCxNQUFNLE9BQU8sR0FBRyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQVcsQ0FBQztBQUM1QyxNQUFNLE1BQU0sR0FBRyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQVcsQ0FBQztBQUMzQyxNQUFNLElBQUksR0FBRyxFQUFFLEdBQUcsRUFBRSxTQUFTLEVBQVcsQ0FBQztBQUV6Qzs7Ozs7R0FLRztBQUNILE1BQU0sSUFBSSxHQUFHLGlFQUFpRSxDQUFDO0FBQy9FLE1BQU0sSUFBSSxHQUFHLG1FQUFtRSxDQUFDO0FBRWpGLHNHQUFzRztBQUN6RixRQUFBLFdBQVcsR0FBRztJQUN6QixNQUFNLEVBQUU7UUFDTixPQUFPLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFO1FBQzVCLE9BQU8sRUFBRSxFQUFFLElBQUksRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLEVB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