@symbo.ls/mermaid 3.14.4 → 3.14.6

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/src/bundle.js DELETED
@@ -1,217 +0,0 @@
1
- import log from './utils/logger.js';
2
- // The smbls IIFE source as a string. Single source of truth — generated by
3
- // `smbls build:iife` and exported from the smbls package itself, so this works
4
- // identically in Node, CF Workers, and any other runtime as a static import.
5
- import smblsIife from 'smbls/iife-string';
6
-
7
- log.debug(`[BUNDLE] smbls IIFE loaded from smbls/iife-string (${smblsIife.length} bytes)`);
8
-
9
- // FRAMEWORK-6: any `<` in JSON that gets inlined into a `<script>` block can
10
- // terminate the wrapper (`</script>`) or trigger HTML-comment / CDATA modes
11
- // (`<!--`, `<![CDATA[`) inside the script-data state. Replacing every `<`
12
- // with the JS unicode escape `<` neutralizes HTML interpretation while
13
- // preserving the runtime string value — `<` decodes to `<` at JS parse
14
- // time, so the embedded object is identical to the original. Standard
15
- // mitigation: see OWASP §RULE-3.1, React's renderToString, Rails json_escape.
16
- const escapeJsonForScript = (s) => s.replace(/</g, '\\u003c');
17
-
18
- export function getSmblsIife() {
19
- return smblsIife;
20
- }
21
-
22
- /**
23
- * Generate the client-side bundle script that creates the Smbls app.
24
- * Uses the same approach as templates/start.js runFromJson:
25
- * Smbls.deepDestringifyFunctions(data) -> Smbls.create(app, context)
26
- */
27
- export function getBundleScript(appKey, data, req, { ProjectDataService } = {}) {
28
- log.debug(`[BUNDLE] getBundleScript started for ${appKey}`);
29
-
30
- if (!data) {
31
- log.warn('[BUNDLE] No data provided, returning empty');
32
- return '';
33
- }
34
-
35
- // Ensure data is fully merged before bundling
36
- try {
37
- if (data.merged) {
38
- Object.assign(data, data.merged);
39
- log.debug(`[BUNDLE] Applied precomputed merged data for project ${data.id}`);
40
- } else if (
41
- ProjectDataService &&
42
- Array.isArray(data.sharedLibraries) &&
43
- data.sharedLibraries.length > 0
44
- ) {
45
- const mergedData = ProjectDataService.mergeSharedLibraries(
46
- data,
47
- data.sharedLibraries,
48
- data.projectType,
49
- );
50
- Object.assign(data, mergedData);
51
- log.debug(`[BUNDLE] Merged ${data.sharedLibraries.length} shared libraries`);
52
- }
53
- } catch (mergeError) {
54
- log.error(`[BUNDLE] Failed to ensure merged data: ${mergeError.message}`);
55
- }
56
-
57
- // Serialize the project data as JSON (same as templates __mockedData approach).
58
- // escapeJsonForScript: see FRAMEWORK-6 — any `</script>` (or `<!--`) inside a
59
- // string value would break out of the inline `<script>` wrapper.
60
- const jsonData = escapeJsonForScript(JSON.stringify(data));
61
-
62
- /*
63
- * Determine liveSync setting.
64
- *
65
- * Realtime is enabled on Symbols-managed dev hosts (any *.symbo.ls env,
66
- * *.preview.symbols.app, and localhost / *.localhost for local dev) and
67
- * disabled on custom domains so we don't leak the realtime backend to
68
- * third-party hosts. Post-§45 the appkey is a bare `owner/key` slug, so
69
- * gating on `appKey.endsWith('symbo.ls')` would always evaluate false
70
- * (rendering all browsers blind). The signal we want is the request host.
71
- */
72
- // Resolve incoming host across Express (req.headers.host),
73
- // Cloudflare Workers / Fetch (req.headers is a Headers instance with .get),
74
- // and plain options bags ({ host }). Falls back to req.url parsing so a
75
- // missing Host header still degrades to a URL-based read instead of ''.
76
- const rawHost =
77
- (req?.headers && typeof req.headers.get === 'function' && req.headers.get('host')) ||
78
- req?.headers?.host ||
79
- req?.host ||
80
- (req?.url && (() => { try { return new URL(req.url).host; } catch { return ''; } })()) ||
81
- '';
82
- const host = String(rawHost).toLowerCase().split(':')[0];
83
- // Express exposes parsed query on req.query; CF Workers expose req.url only —
84
- // pull `release` / `version` from a parsed URL when the structured form is
85
- // absent so feature flags work in both runtimes.
86
- const queryFrom = (() => {
87
- if (req?.query) return req.query;
88
- try { return Object.fromEntries(new URL(req?.url || '', 'http://x').searchParams); } catch { return {}; }
89
- })();
90
- const release = queryFrom.release || false;
91
- const version = queryFrom.version;
92
- const isSymboLs =
93
- host.endsWith('.symbo.ls') ||
94
- host.endsWith('.preview.symbols.app') ||
95
- host === 'localhost' ||
96
- host.endsWith('.localhost');
97
- const shouldDisableLiveSync = Boolean(release) || Boolean(version) || !isSymboLs;
98
-
99
- // Inject the persisted `_id` so the realtime client can hand the socket
100
- // server a concrete projectId (mongo _id) on handshake. Without this, the
101
- // server falls back to a `/v1/projects/by-key/...` lookup that 404s on the
102
- // current API and drops the connection mid-join.
103
- const projectId =
104
- (data && (data._id || data.projectId || data.id)) ||
105
- null;
106
- const settingsStr = isSymboLs
107
- ? escapeJsonForScript(JSON.stringify({
108
- ...data.settings,
109
- liveSync: !shouldDisableLiveSync,
110
- inspect: true,
111
- verbose: !shouldDisableLiveSync,
112
- projectId,
113
- }))
114
- : 'null';
115
-
116
- // Generate the client script - same pattern as templates/start.js runFromJson
117
- const script = `
118
- var context = Smbls.deepDestringifyFunctions(${jsonData});
119
- context.packageManager = 'esm.sh';
120
- if (context.db && context.db.createClient) delete context.db.createClient;
121
- if (context.fetch && context.fetch.createClient) delete context.fetch.createClient;
122
-
123
- // Runtime defaults (these are baked into the starter-kit/frank but missing from server data)
124
- if (context.useReset === undefined) context.useReset = true;
125
- if (context.useVariable === undefined) context.useVariable = true;
126
- if (context.useFontImport === undefined) context.useFontImport = true;
127
- if (context.useIconSprite === undefined) context.useIconSprite = true;
128
- if (context.useSvgSprite === undefined) context.useSvgSprite = true;
129
- if (context.useDefaultConfig === undefined) context.useDefaultConfig = true;
130
- if (context.useDocumentTheme === undefined) context.useDocumentTheme = true;
131
- if (!context.routerOptions) context.routerOptions = { scrollToTop: true };
132
-
133
- var app = context.app || {};
134
- delete context.app;
135
- var settings = ${settingsStr};
136
- if (settings) context.editor = settings;
137
-
138
- // Theme initialization: set data-theme before app renders to avoid FOUC.
139
- // Mirrors the precedence in resolveAndApplyTheme (packages/smbls/src/index.js):
140
- // 1. URL param ?globalTheme=<name> — embedded-iframe override
141
- // 2. localStorage[themeStorageKey] — persisted user choice
142
- // 3. context.globalTheme — project's configured value
143
- // 4. matchMedia(prefers-color-scheme: dark) — OS fallback (when 'auto')
144
- //
145
- // Writing the literal 'auto' as data-theme would leave the
146
- // [data-theme="dark"] / [data-theme="light"] CSS selectors unmatched and
147
- // the page would render with system defaults instead of the chosen
148
- // theme — so 'auto' is always resolved to a concrete light/dark value.
149
- //
150
- // PRIOR BUG: this script previously ignored context.globalTheme entirely
151
- // and went straight to matchMedia after localStorage. A project with
152
- // globalTheme: light in config.js would render in OS dark on a
153
- // dark-mode device because the inline boot ran first and locked in
154
- // dark before resolveAndApplyTheme could correct it.
155
- //
156
- // IDEMPOTENT GUARD: the SSR-side render in @symbo.ls/mermaid/render.js
157
- // now bakes data-theme onto <html> for concrete-theme projects and
158
- // emits an inline <head> script that runs synchronously before paint.
159
- // When that path runs, data-theme is already set by the time the bundle
160
- // executes — skip this block to avoid re-resolving and overwriting
161
- // (which would race with URL/localStorage values the head script
162
- // already honored). Kept as a safety net for older renders / cases
163
- // where the head script couldn't run.
164
- if (
165
- context.useDocumentTheme !== false &&
166
- !document.documentElement.hasAttribute('data-theme')
167
- ) {
168
- (function() {
169
- try {
170
- var t = null;
171
- // 1. URL param wins (used by preview/canvas iframe embedding).
172
- try {
173
- var p = new URLSearchParams(window.location.search).get('globalTheme');
174
- if (p) t = p;
175
- } catch(e) {}
176
- // 2. localStorage — only consulted when no forcing source above.
177
- if (!t) {
178
- var key = context.themeStorageKey;
179
- if (key) { try { t = localStorage.getItem(key) } catch(e) {} }
180
- }
181
- // 3. Project's configured globalTheme (config.js export). Honors an
182
- // explicit 'light'/'dark'/<scheme> instead of falling through to OS.
183
- if (!t && context.globalTheme && context.globalTheme !== 'auto') {
184
- t = context.globalTheme;
185
- }
186
- // 4. OS preference fallback — only when nothing concrete chose.
187
- if (!t || t === 'auto') {
188
- t = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
189
- }
190
- if (t) document.documentElement.setAttribute('data-theme', t);
191
- } catch(e) {}
192
- })();
193
- }
194
-
195
- // When brender pre-rendered this page, adopt the existing DOM nodes
196
- // instead of tearing them down. The [data-ssr] wrapper is moved out of
197
- // the way so pre-rendered children land at document.body level — matching
198
- // the level at which DOMQL creates its own elements. The __BRENDER__ flag
199
- // and __BR_REGISTRY__ map were injected into <head> by the server.
200
- var ssrShell = document.querySelector('[data-ssr]');
201
- if (window.__BRENDER__ && ssrShell) {
202
- // Move pre-rendered children to body before DOMQL skeleton creation
203
- var frag = document.createDocumentFragment();
204
- while (ssrShell.firstChild) frag.appendChild(ssrShell.firstChild);
205
- document.body.insertBefore(frag, ssrShell);
206
- ssrShell.remove();
207
- } else if (ssrShell) {
208
- // No brender hydration — remove empty/stale SSR shell
209
- ssrShell.remove();
210
- }
211
-
212
- Smbls.create(app, context);
213
- `;
214
-
215
- log.debug(`[BUNDLE] Script generated (${script.length} bytes)`);
216
- return script;
217
- }