@symbo.ls/mermaid 3.14.0 → 3.14.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/mermaid",
3
- "version": "3.14.0",
3
+ "version": "3.14.1",
4
4
  "type": "module",
5
5
  "license": "CC-BY-NC-4.0",
6
6
  "description": "Self-sustaining server runner for Symbols projects from JSON. Renders DOMQL projects to HTML with optional brender SSR.",
@@ -17,7 +17,11 @@
17
17
  "./utils/logger": "./src/utils/logger.js",
18
18
  "./template.html": "./src/template.html"
19
19
  },
20
- "files": ["index.js", "src", "README.md"],
20
+ "files": [
21
+ "index.js",
22
+ "src",
23
+ "README.md"
24
+ ],
21
25
  "scripts": {
22
26
  "start": "node bin/start.js"
23
27
  },
package/src/bundle.js CHANGED
@@ -48,14 +48,58 @@ export function getBundleScript(appKey, data, req, { ProjectDataService } = {})
48
48
  // Serialize the project data as JSON (same as templates __mockedData approach)
49
49
  const jsonData = JSON.stringify(data);
50
50
 
51
- // Determine liveSync setting
52
- const release = req?.query?.release || false;
53
- const version = req?.query?.version;
54
- const isSymboLs = typeof appKey === 'string' && appKey.endsWith('symbo.ls');
51
+ /*
52
+ * Determine liveSync setting.
53
+ *
54
+ * Realtime is enabled on Symbols-managed dev hosts (any *.symbo.ls env,
55
+ * *.preview.symbols.app, and localhost / *.localhost for local dev) and
56
+ * disabled on custom domains so we don't leak the realtime backend to
57
+ * third-party hosts. Post-§45 the appkey is a bare `owner/key` slug, so
58
+ * gating on `appKey.endsWith('symbo.ls')` would always evaluate false
59
+ * (rendering all browsers blind). The signal we want is the request host.
60
+ */
61
+ // Resolve incoming host across Express (req.headers.host),
62
+ // Cloudflare Workers / Fetch (req.headers is a Headers instance with .get),
63
+ // and plain options bags ({ host }). Falls back to req.url parsing so a
64
+ // missing Host header still degrades to a URL-based read instead of ''.
65
+ const rawHost =
66
+ (req?.headers && typeof req.headers.get === 'function' && req.headers.get('host')) ||
67
+ req?.headers?.host ||
68
+ req?.host ||
69
+ (req?.url && (() => { try { return new URL(req.url).host; } catch { return ''; } })()) ||
70
+ '';
71
+ const host = String(rawHost).toLowerCase().split(':')[0];
72
+ // Express exposes parsed query on req.query; CF Workers expose req.url only —
73
+ // pull `release` / `version` from a parsed URL when the structured form is
74
+ // absent so feature flags work in both runtimes.
75
+ const queryFrom = (() => {
76
+ if (req?.query) return req.query;
77
+ try { return Object.fromEntries(new URL(req?.url || '', 'http://x').searchParams); } catch { return {}; }
78
+ })();
79
+ const release = queryFrom.release || false;
80
+ const version = queryFrom.version;
81
+ const isSymboLs =
82
+ host.endsWith('.symbo.ls') ||
83
+ host.endsWith('.preview.symbols.app') ||
84
+ host === 'localhost' ||
85
+ host.endsWith('.localhost');
55
86
  const shouldDisableLiveSync = Boolean(release) || Boolean(version) || !isSymboLs;
56
87
 
88
+ // Inject the persisted `_id` so the realtime client can hand the socket
89
+ // server a concrete projectId (mongo _id) on handshake. Without this, the
90
+ // server falls back to a `/v1/projects/by-key/...` lookup that 404s on the
91
+ // current API and drops the connection mid-join.
92
+ const projectId =
93
+ (data && (data._id || data.projectId || data.id)) ||
94
+ null;
57
95
  const settingsStr = isSymboLs
58
- ? JSON.stringify({ ...data.settings, liveSync: !shouldDisableLiveSync, inspect: true })
96
+ ? JSON.stringify({
97
+ ...data.settings,
98
+ liveSync: !shouldDisableLiveSync,
99
+ inspect: true,
100
+ verbose: !shouldDisableLiveSync,
101
+ projectId,
102
+ })
59
103
  : 'null';
60
104
 
61
105
  // Generate the client script - same pattern as templates/start.js runFromJson
@@ -85,14 +129,21 @@ export function getBundleScript(appKey, data, req, { ProjectDataService } = {})
85
129
  // way so pre-rendered children land at document.body level — matching the
86
130
  // level at which DOMQL creates its own elements. The __BRENDER__ flag and
87
131
  // __BR_REGISTRY__ map were injected into <head> by the server.
88
- // Theme initialization: set data-theme before app renders to avoid FOUC
132
+ // Theme initialization: set data-theme before app renders to avoid FOUC.
133
+ // 'auto' means "follow OS preference" — resolve through matchMedia and
134
+ // emit the concrete light/dark value. Writing the literal "auto" string
135
+ // would leave [data-theme="dark"] / [data-theme="light"] selectors
136
+ // unmatched + the page would render with system-default colors instead
137
+ // of the chosen theme. Matches resolveAndApplyTheme in smbls/src/index.js.
89
138
  if (context.useDocumentTheme !== false) {
90
139
  (function() {
91
140
  try {
92
141
  var t = null;
93
142
  var key = context.themeStorageKey;
94
143
  if (key) { try { t = localStorage.getItem(key) } catch(e) {} }
95
- if (!t) t = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
144
+ if (!t || t === 'auto') {
145
+ t = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
146
+ }
96
147
  if (t) document.documentElement.setAttribute('data-theme', t);
97
148
  } catch(e) {}
98
149
  })();
package/src/render.js CHANGED
@@ -309,7 +309,10 @@ export async function renderProjectHtml(data, options = {}) {
309
309
  const bundle = getBundleScript(appkey, data, req, { ProjectDataService });
310
310
 
311
311
  // ── Importmap ─────────────────────────────────────────────────────────────
312
- const importmap = getImportMapScript(data, data?.settings?.cdnProvider || 'skypack');
312
+ // esm.sh is the default — it resolves transitive deps correctly and matches
313
+ // the `context.packageManager = 'esm.sh'` set by bundle.js at runtime.
314
+ // Skypack frequently 500s on transitive deps (e.g. lit-html via ninja-keys).
315
+ const importmap = getImportMapScript(data, data?.settings?.cdnProvider || 'esmsh');
313
316
 
314
317
  // ── HTML template ─────────────────────────────────────────────────────────
315
318
  const indexHtmlFromFiles = data?.files && data.files['index.html'];
@@ -378,23 +381,35 @@ export async function renderProjectHtml(data, options = {}) {
378
381
  }
379
382
 
380
383
  /**
381
- * Render the "project not published" 404 fallback page.
384
+ * Render the "project not published" 404 fallback page. When `reason` is
385
+ * 'not_public', renders a tailored "site not yet public" message —
386
+ * specifically for paid custom domains pointing at a project whose
387
+ * visibility is still private.
382
388
  */
383
- export function renderNotPublishedHtml(appkey, channel = 'production') {
389
+ export function renderNotPublishedHtml(appkey, channel = 'production', reason = null) {
384
390
  const envLabel = channel !== 'production' ? ` (${channel})` : '';
391
+ const isNotPublic = reason === 'not_public';
392
+ const title = isNotPublic ? 'Site Not Yet Public' : 'Not Published';
393
+ const heading = isNotPublic
394
+ ? `This site isn't public yet${envLabel}`
395
+ : `This project hasn't been published yet${envLabel}`;
396
+ const body = isNotPublic
397
+ ? `The owner has connected this domain but the project's visibility is set to private. Once they switch it to public in the workspace settings, the site will appear here automatically.`
398
+ : `Run <code>smbls publish</code> to deploy your project, or visit <a href="https://symbols.app">symbols.app</a> to get started.`;
385
399
  return `<!doctype html>
386
400
  <html>
387
401
  <head>
388
402
  <meta charset="UTF-8">
389
403
  <meta name="viewport" content="width=device-width, initial-scale=1">
390
404
  <meta name="robots" content="noindex">
391
- <title>Not Published</title>
405
+ <title>${title}</title>
392
406
  <style>
393
407
  * { margin: 0; padding: 0; box-sizing: border-box; }
394
408
  body { min-height: 100vh; display: flex; align-items: center; justify-content: center; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #0a0a0a; color: #fafafa; }
395
- .container { text-align: center; max-width: 480px; padding: 2rem; }
409
+ .container { text-align: center; max-width: 520px; padding: 2rem; }
396
410
  h1 { font-size: 1.25rem; font-weight: 500; margin-bottom: 0.75rem; }
397
411
  p { color: #888; font-size: 0.875rem; line-height: 1.6; }
412
+ code { background: #1a1a1a; padding: 0.1rem 0.35rem; border-radius: 3px; font-size: 0.8rem; color: #ccc; }
398
413
  a { color: #888; text-decoration: underline; text-underline-offset: 2px; }
399
414
  a:hover { color: #fafafa; }
400
415
  .env { display: inline-block; font-size: 0.75rem; color: #555; background: #1a1a1a; padding: 0.2rem 0.5rem; border-radius: 4px; margin-top: 1rem; }
@@ -402,8 +417,8 @@ export function renderNotPublishedHtml(appkey, channel = 'production') {
402
417
  </head>
403
418
  <body>
404
419
  <div class="container">
405
- <h1>This project hasn't been published yet${envLabel}</h1>
406
- <p>Run <code>smbls publish</code> to deploy your project, or visit <a href="https://symbols.app">symbols.app</a> to get started.</p>
420
+ <h1>${heading}</h1>
421
+ <p>${body}</p>
407
422
  <span class="env">${appkey}${envLabel}</span>
408
423
  </div>
409
424
  </body>