PageSourceSearch

https://selebox.com/js/public-browse.js?market=2026-09-30-1&chats=…&notifications=2026-09-24-3

js selebox.com collected 2026-10-07 23:57:18 UTC 3,641 bytes, 67 lines download raw bytes

1import {christmasTarget} from './christmas-route.js?v=2026-09-23-1';
2// ── True Public Browse Mode (Model B: session-less) ──────────────────────────
3// Pure, side-effect-free helpers so they can be unit-tested standalone (see
4// qa/selftest/public-browse.test.html) and imported by app.js without booting
5// the app. NO DOM, NO Supabase, NO auth calls here.
6//
7// Model B = logged-out visitors browse public surfaces with the anon Supabase
8// role (NO signInAnonymously). Writes/personal actions are login-gated. See
9// docs/superpowers/plans/2026-07-06-true-public-browse-mode-plan.md.
10
11// Code-side flag (never an app_config value — Codex amendment #2). It SHIPPED
12// default-OFF in PR #31 on main; this enablement branch INTENTIONALLY flips it
13// ON to activate public browse. Setting it back to `false` restores the wall.
14export const PUBLIC_BROWSE_WEB = true;
15
16export function isPublicBrowseEnabled() {
17  return PUBLIC_BROWSE_WEB === true;
18}
19
20// Boot destination. A live session always wins (full app). Otherwise the flag
21// decides: 'public' anon browse when enabled, else the legacy login 'wall'.
22export function resolveBootMode(hasSession, publicBrowseEnabled = PUBLIC_BROWSE_WEB) {
23  if (hasSession) return 'app';
24  return publicBrowseEnabled ? 'public' : 'wall';
25}
26
27// Feed data path. Personalized only when a real user id exists; anon visitors
28// get the 'public' path (direct posts select — never the personalized RPCs,
29// which require p_user_id).
30export function feedModeForUser(currentUser) {
31  return currentUser && currentUser.id ? 'personalized' : 'public';
32}
33
34// Login gate for a write/personal action. Returns true when allowed (a real
35// user is present); otherwise invokes onBlocked(message) (e.g. a toast / login
36// CTA) and returns false so the caller can bail out.
37export function requireLogin(currentUser, onBlocked, message = 'Please sign in') {
38  if (currentUser && currentUser.id) return true;
39  if (typeof onBlocked === 'function') onBlocked(message);
40  return false;
41}
42
43// ── Route auth ───────────────────────────────────────────────────────────────
44// Hash routes that require a logged-in user. Everything not listed here (home,
45// #post, #videos, #book[/*], #video/*, #radiobox, #profile/*, path book links)
46// is a public browse surface. #messages is included for completeness though it
47// is dispatched via its own hashchange handler (messages.js also self-gates).
48export const LOGIN_REQUIRED_HASHES = Object.freeze([
49  '#market', '#notifications', '#my-space', '#studio', '#author', '#store', '#earnings', '#bookmarks', '#messages', '#chats',
50]);
51// Prefix matches — the author book/chapter EDITORS (#author/book/<id>[/chapter/<n>]).
52export const LOGIN_REQUIRED_HASH_PREFIXES = Object.freeze(['#market/','#author/book/','#space/']);
53
54export function routeRequiresLogin(hash) {
55  if (!hash || typeof hash !== 'string') return false;
56  // Wishlist RPCs already enforce public/private Space access for anonymous readers.
57  if (christmasTarget(hash)) return false;
58  if (LOGIN_REQUIRED_HASHES.includes(hash)) return true;
59  return LOGIN_REQUIRED_HASH_PREFIXES.some(p => hash.startsWith(p));
60}
61
62// Decide routing for a hash given the viewer: 'allow' (proceed) or 'gate'
63// (anon hitting a login-only route → caller should prompt sign-in / redirect).
64export function routeAuthDecision(hash, currentUser) {
65  if (currentUser && currentUser.id) return 'allow';
66  return routeRequiresLogin(hash) ? 'gate' : 'allow';
67}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.