GraphSX URL routing: s-expression URLs for sx-docs
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m50s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 5m50s
Replace path-based URLs with nested s-expression URLs across the sx app. URLs like /language/docs/introduction become /(language.(doc.introduction)), making the URL simultaneously a query, render instruction, and address. - Add sx_router.py: catch-all route evaluator with dot→space conversion, auto-quoting slugs, two-phase eval, streaming detection, 301 redirects - Add page-functions.sx: section + page functions for URL dispatch - Rewrite nav-data.sx: ~200 hrefs to SX expression format, tree-descent nav matching via has-descendant-href? (replaces prefix heuristics) - Convert ~120 old-style hrefs across 26 .sx content files - Add SX Protocol proposal (etc/plans/sx-protocol) - Wire catch-all route in app.py with before_request redirect handler Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
36
sx/app.py
36
sx/app.py
@@ -114,10 +114,37 @@ def create_app() -> "Quart":
|
||||
from shared.sx.pages import auto_mount_pages
|
||||
auto_mount_pages(app, "sx")
|
||||
|
||||
# --- GraphSX catch-all route: SX expression URLs ---
|
||||
from sxc.pages.sx_router import eval_sx_url, redirect_old_url
|
||||
|
||||
@app.before_request
|
||||
async def sx_url_redirect():
|
||||
"""Redirect old-style paths to SX expression URLs (301)."""
|
||||
from quart import request, redirect as q_redirect
|
||||
path = request.path
|
||||
# Skip non-page paths
|
||||
if path.startswith(("/static/", "/internal/", "/auth/", "/sx/")):
|
||||
return None
|
||||
# Skip SX expression URLs (already in new format)
|
||||
if path.startswith("/(") or path.startswith("/~"):
|
||||
return None
|
||||
# Skip API/handler paths
|
||||
if "/api/" in path:
|
||||
return None
|
||||
new_url = redirect_old_url(path)
|
||||
if new_url:
|
||||
qs = request.query_string.decode()
|
||||
if qs:
|
||||
new_url += "?" + qs
|
||||
return q_redirect(new_url, 301)
|
||||
|
||||
@app.before_request
|
||||
async def trailing_slash_redirect():
|
||||
from quart import request, redirect
|
||||
path = request.path
|
||||
# Skip SX expression URLs — they don't use trailing slashes
|
||||
if "(" in path or path.startswith("/~"):
|
||||
return None
|
||||
if (path != "/"
|
||||
and not path.endswith("/")
|
||||
and request.method == "GET"
|
||||
@@ -128,6 +155,15 @@ def create_app() -> "Quart":
|
||||
target = path + "/" + ("?" + qs if qs else "")
|
||||
return redirect(target, 301)
|
||||
|
||||
@app.get("/<path:expr>")
|
||||
async def sx_eval_route(expr):
|
||||
"""Catch-all: evaluate SX expression URLs."""
|
||||
result = await eval_sx_url(f"/{expr}")
|
||||
if result is None:
|
||||
from quart import abort
|
||||
abort(404)
|
||||
return result
|
||||
|
||||
@app.errorhandler(404)
|
||||
async def sx_not_found(e):
|
||||
from quart import request, make_response
|
||||
|
||||
Reference in New Issue
Block a user