dsh-tiddlywiki 0.26.6 → 0.26.7

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/lib/index.js CHANGED
@@ -2574,10 +2574,40 @@ function rejectCrossSiteWrite(req, res, allowedMethods) {
2574
2574
  }, 403);
2575
2575
  return true;
2576
2576
  }
2577
- /** Read-only route guard: GET/HEAD only, no side effects on any other method. */
2577
+ /**
2578
+ * Read-only route guard: GET/HEAD only, no side effects on any other method. */
2578
2579
  function rejectNonRead(req, res) {
2579
2580
  return rejectCrossSiteWrite(req, res, ["GET", "HEAD"]);
2580
2581
  }
2582
+ /**
2583
+ * Absolute loopback base for a same-origin proxy path, from a request's `Host`
2584
+ * header (v0.26.7). Returns undefined when the header is unusable, in which
2585
+ * case the caller simply omits the absolute twin and clients keep using the
2586
+ * relative path.
2587
+ *
2588
+ * WHY THIS EXISTS: the DSH **desktop** app serves its renderer from the custom
2589
+ * `dsh-app:` scheme, and TiddlyWiki's own TiddlyWeb sync adaptor refuses to load
2590
+ * on a document whose protocol does not start with `http`
2591
+ * (`plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js`:
2592
+ * `if($tw.browser && document.location.protocol.substr(0,4) === "http")`).
2593
+ * Embedded from `dsh-app://app/…` the wiki therefore had NO sync adaptor:
2594
+ * `$:/status/IsReadOnly` was never written, TW's read-only stylesheet (which
2595
+ * treats a MISSING status as read-only) hid 添加条目 / 日志 / 导入 / 管理器 /
2596
+ * 编辑 …, and the saver fell back to local-only dirty tracking — an edit made
2597
+ * there could not be saved either. The client falls back to this absolute URL
2598
+ * only while its own document is not on http(s) (see `resolveTwUrl` in
2599
+ * `src/client/endpoints.ts`), so http(s) embedders — loopback, LAN, Tailscale,
2600
+ * domain, HTTPS — are never moved off their own origin.
2601
+ *
2602
+ * `http` is deliberate rather than `x-forwarded-proto`: the desktop app's host
2603
+ * always listens on plain HTTP loopback, and only a renderer able to reach it
2604
+ * ever reads this value.
2605
+ */
2606
+ function absoluteHostBase(hostHeader, proxyPath) {
2607
+ const host = typeof hostHeader === "string" ? hostHeader.trim() : Array.isArray(hostHeader) ? String(hostHeader[0] ?? "").trim() : "";
2608
+ if (!/^[A-Za-z0-9.\-[\]:]+$/.test(host)) return void 0;
2609
+ return `http://${host}${proxyPath}`;
2610
+ }
2581
2611
  //#endregion
2582
2612
  //#region src/host/sanitize.ts
2583
2613
  /**
@@ -6679,6 +6709,14 @@ function registerRoutes(ctx, deps) {
6679
6709
  * tags, it cannot know the text is a secret.
6680
6710
  */
6681
6711
  const referencesBlockedTitle = (value) => value !== void 0 && BLOCKED_PROXY_TITLE_PREFIXES.some((prefix) => value.includes(prefix));
6712
+ /**
6713
+ * Absolute twin of `twProxy` for embedders whose own document is not on
6714
+ * http(s) — the DSH desktop app's `dsh-app:` renderer (v0.26.7). See
6715
+ * `absoluteHostBase()` in http.ts for the full why; clients only consult it
6716
+ * while their own page is not http(s), so nothing changes for http(s)
6717
+ * deployments.
6718
+ */
6719
+ const twProxyAbsoluteBase = (req) => absoluteHostBase(req.headers.host, TW_PROXY_PATH);
6682
6720
  const handleStatus = async (req, res) => {
6683
6721
  if (rejectNonRead(req, res)) return;
6684
6722
  const view = deps.server.status();
@@ -6688,6 +6726,7 @@ function registerRoutes(ctx, deps) {
6688
6726
  ...view,
6689
6727
  logs: redactLogLines(view.logs),
6690
6728
  twProxy: TW_PROXY_PATH,
6729
+ twProxyAbsolute: twProxyAbsoluteBase(req),
6691
6730
  git: gitSummary,
6692
6731
  note: { tag: deps.noteDefaults().tag },
6693
6732
  ui: deps.uiDefaults()
@@ -6765,7 +6804,8 @@ function registerRoutes(ctx, deps) {
6765
6804
  json(res, {
6766
6805
  ok: true,
6767
6806
  ...await pendingSummary,
6768
- twUrl: TW_PROXY_PATH
6807
+ twUrl: TW_PROXY_PATH,
6808
+ twUrlAbsolute: twProxyAbsoluteBase(req)
6769
6809
  });
6770
6810
  } catch (err) {
6771
6811
  json(res, {
@@ -7186,7 +7226,8 @@ function registerRoutes(ctx, deps) {
7186
7226
  json(res, {
7187
7227
  ok: true,
7188
7228
  ...result,
7189
- twUrl: TW_PROXY_PATH
7229
+ twUrl: TW_PROXY_PATH,
7230
+ twUrlAbsolute: twProxyAbsoluteBase(req)
7190
7231
  });
7191
7232
  } catch (err) {
7192
7233
  if (err instanceof WriteConflictError) {