Humuhumu33 commited on
Commit
359fc7e
Β·
verified Β·
1 Parent(s): 1b30ade

holo-evicted-publish: player +5 object(s)

Browse files
b/23e4f84b126619414ef90eba73a97fc398e2629b8ced8c90d5390df35caaca26 ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // holo-media-item.mjs β€” the ONE render-item shape for Holo Player, and the TMDb β†’ item normalizers.
2
+ //
3
+ // Holo Player has always had a single item shape (holo-jellyfin.js: native ΞΊ-store + real Jellyfin).
4
+ // This module is the SOURCE OF TRUTH for that shape's superset β€” it adds, additively:
5
+ // β€’ a series hierarchy (kind: movie|series|season|episode; seriesId/seasonNumber/episodeNumber/parentId)
6
+ // β€’ a rich metadata block (overview, genres, cast, rating, backdrop, logo, tagline, trailer)
7
+ // β€’ "where to watch" (providers[] from TMDb watch-providers β€” the Netflix/Hulu *feel*)
8
+ // β€’ honest availability (metadata β‰  bytes: a title is browsable without being playable)
9
+ // Every field the existing player already reads (name, topics, channel, quality, posterUrl, backdrop,
10
+ // blurb, runtimeSec, kappa, playSrc, source, provider, license …) is preserved, so pool()/HoloRank/rails
11
+ // consume a TMDb item with ZERO rail-code change. The shape IS the contract.
12
+ //
13
+ // Pure ESM, no globals, no network β€” so Node can witness the normalizers exactly. holo-tmdb.mjs wires the
14
+ // network + ΞΊ-cache on top; this module never fetches.
15
+
16
+ // TMDb genre id β†’ human name (movie + tv ids unioned). Used to label rails and feed affinity.
17
+ export const TMDB_GENRES = {
18
+ 28: "Action", 12: "Adventure", 16: "Animation", 35: "Comedy", 80: "Crime", 99: "Documentary",
19
+ 18: "Drama", 10751: "Family", 14: "Fantasy", 36: "History", 27: "Horror", 10402: "Music",
20
+ 9648: "Mystery", 10749: "Romance", 878: "Science Fiction", 10770: "TV Movie", 53: "Thriller",
21
+ 10752: "War", 37: "Western", 10759: "Action & Adventure", 10762: "Kids", 10763: "News",
22
+ 10764: "Reality", 10765: "Sci-Fi & Fantasy", 10766: "Soap", 10767: "Talk", 10768: "War & Politics",
23
+ };
24
+
25
+ // Human genre β†’ the player's existing topic keys (so genre rails + HoloRank affinity light up). Genres
26
+ // without a legacy key still flow through as their lowercased name (the player makes genre rails dynamic).
27
+ const GENRE_TO_TOPIC = {
28
+ "Science Fiction": "scifi", "Sci-Fi & Fantasy": "scifi", Animation: "animation", Fantasy: "fantasy",
29
+ Comedy: "comedy", Action: "action", "Action & Adventure": "action", Music: "music",
30
+ Documentary: "nature", Family: "comedy", Adventure: "action",
31
+ };
32
+
33
+ // TMDb image CDN β€” no API key needed for images. Sized per surface so cards are crisp without overfetch.
34
+ export const TMDB_IMG = "https://image.tmdb.org/t/p";
35
+ export const posterURL = (p, w = "w500") => (p ? `${TMDB_IMG}/${w}${p}` : null);
36
+ export const backdropURL = (p, w = "w1280") => (p ? `${TMDB_IMG}/${w}${p}` : null);
37
+ // Key-free artwork fallback (Stremio metahub, by IMDb id) β€” gives every catalog title real, beautiful
38
+ // posters/backdrops with NO TMDb key (the same CDN Cinemeta uses). Used only when TMDb ships no image
39
+ // (the offline seed): a live TMDb fetch, which has its own poster_path, always wins.
40
+ export const METAHUB = "https://images.metahub.space";
41
+ export const metahubPoster = (imdb) => (imdb && /^tt\d+$/.test(imdb) ? `${METAHUB}/poster/medium/${imdb}/img` : null);
42
+ export const metahubBackdrop = (imdb) => (imdb && /^tt\d+$/.test(imdb) ? `${METAHUB}/background/medium/${imdb}/img` : null);
43
+ const yearOf = (d) => (d && /^\d{4}/.test(d) ? +d.slice(0, 4) : null);
44
+
45
+ const genreNames = (t) => {
46
+ if (Array.isArray(t.genres)) return t.genres.map((g) => g.name).filter(Boolean);
47
+ if (Array.isArray(t.genre_ids)) return t.genre_ids.map((id) => TMDB_GENRES[id]).filter(Boolean);
48
+ return [];
49
+ };
50
+ // topics = legacy keys (for affinity/genre rails) βˆͺ lowercased raw genres (for dynamic rails). Deduped.
51
+ const topicsOf = (names) => [...new Set(names.flatMap((n) => [GENRE_TO_TOPIC[n], n.toLowerCase()]).filter(Boolean))];
52
+
53
+ // The honest seam: metadata is not bytes. A freshly-discovered title is browsable, not playable, until a
54
+ // source binds (Phase 2). Callers can pass a pre-resolved source; default = browse-only.
55
+ export const browseOnly = () => ({ playable: false, source: null, kappa: "", playSrc: "", type: "" });
56
+
57
+ // ── deep-metadata extractors (tolerant: return empty when the append_to_response field is absent) ──────────
58
+ export const DEFAULT_REGION = "US";
59
+ const headshot = (p) => (p ? posterURL(p, "w185") : null);
60
+ // official trailer/teaser key (YouTube) for the ambient hero.
61
+ export function trailerOf(t) {
62
+ const v = (t.videos && t.videos.results) || [];
63
+ const f = v.find((x) => x.site === "YouTube" && /Trailer/i.test(x.type)) || v.find((x) => x.site === "YouTube" && /Teaser/i.test(x.type)) || v.find((x) => x.site === "YouTube");
64
+ return f ? f.key : (t.trailerKey || null);
65
+ }
66
+ function castOf(t) { return ((t.credits && t.credits.cast) || []).slice(0, 12).map((p) => ({ id: p.id, name: p.name, character: p.character || "", profile: headshot(p.profile_path) })); }
67
+ function crewOf(t, kind) {
68
+ if (kind === "series") return ((t.created_by) || []).map((p) => p.name);
69
+ return ((t.credits && t.credits.crew) || []).filter((p) => p.job === "Director").map((p) => p.name);
70
+ }
71
+ function certOf(t, kind, region = DEFAULT_REGION) {
72
+ if (kind === "series") { const r = (t.content_ratings && t.content_ratings.results) || []; const m = r.find((x) => x.iso_3166_1 === region) || r[0]; return (m && m.rating) || ""; }
73
+ const rd = (t.release_dates && t.release_dates.results) || []; const m = rd.find((x) => x.iso_3166_1 === region) || rd[0];
74
+ return m ? ((m.release_dates || []).map((x) => x.certification).find(Boolean) || "") : "";
75
+ }
76
+ function logoOf(t) {
77
+ const lg = (t.images && t.images.logos) || [];
78
+ if (lg.length) { const en = lg.find((x) => x.iso_639_1 === "en" && /\.png$/i.test(x.file_path)) || lg.find((x) => x.iso_639_1 === "en") || lg[0]; return posterURL(en.file_path, "w300"); }
79
+ return t.logo_path ? posterURL(t.logo_path, "w300") : null;
80
+ }
81
+ function keywordsOf(t) { const k = (t.keywords && (t.keywords.keywords || t.keywords.results)) || []; return k.slice(0, 10).map((x) => x.name); }
82
+ function imdbOf(t) { return (t.external_ids && t.external_ids.imdb_id) || t.imdb_id || null; }
83
+ // a lightweight item for "More like this" (no recursive append extraction).
84
+ function liteItem(x) {
85
+ const isTv = !!(x.first_air_date) || (x.name && !x.title) || x.media_type === "tv";
86
+ const names = genreNames(x);
87
+ return { id: "tmdb:" + (isTv ? "series" : "movie") + ":" + x.id, tmdbId: x.id, kind: isTv ? "series" : "movie",
88
+ name: x.title || x.name || "Untitled", year: yearOf(x.release_date || x.first_air_date),
89
+ posterUrl: posterURL(x.poster_path), backdrop: backdropURL(x.backdrop_path), rating: typeof x.vote_average === "number" ? x.vote_average : null,
90
+ topics: topicsOf(names), genres: names, channel: "TheMovieDB", quality: x.vote_average ? Math.min(1, x.vote_average / 10) : 0.7,
91
+ source: "tmdb", provider: "tmdb", kappa: "", holoKappa: "tmdb:" + (isTv ? "series" : "movie") + ":" + x.id, availability: browseOnly() };
92
+ }
93
+ function recsOf(t) {
94
+ const all = [...(((t.recommendations && t.recommendations.results) || [])), ...(((t.similar && t.similar.results) || []))];
95
+ const seen = new Set(), out = [];
96
+ for (const x of all) { const it = liteItem(x); if (seen.has(it.id)) continue; seen.add(it.id); out.push(it); if (out.length >= 12) break; }
97
+ return out;
98
+ }
99
+
100
+ // ── normalizers β€” every one returns the canonical shape ──────────────────────────────────────────────────
101
+ export function normalizeMovie(t, { availability, region } = {}) {
102
+ const names = genreNames(t);
103
+ const cd = castOf(t);
104
+ const imdb = imdbOf(t);
105
+ return {
106
+ id: "tmdb:movie:" + t.id, tmdbId: t.id, kind: "movie", name: t.title || t.name || "Untitled",
107
+ year: yearOf(t.release_date), releaseDate: t.release_date || "",
108
+ overview: t.overview || "", blurb: t.overview || "", tagline: t.tagline || "",
109
+ genres: names, topics: topicsOf(names),
110
+ cast: cd.length ? cd.map((c) => c.name) : (t.cast || []).slice(0, 8), castDetail: cd, directors: crewOf(t, "movie"),
111
+ rating: typeof t.vote_average === "number" ? t.vote_average : null, certification: certOf(t, "movie", region),
112
+ keywords: keywordsOf(t), recommendations: recsOf(t), imdbId: imdb,
113
+ runtimeSec: t.runtime ? t.runtime * 60 : 0,
114
+ posterUrl: posterURL(t.poster_path) || metahubPoster(imdb), backdrop: backdropURL(t.backdrop_path) || metahubBackdrop(imdb), logoUrl: logoOf(t),
115
+ trailerKey: trailerOf(t),
116
+ providers: t.providers || [],
117
+ channel: "TheMovieDB", quality: t.vote_average ? Math.min(1, t.vote_average / 10) : 0.7, license: "",
118
+ source: "tmdb", provider: "tmdb", kappa: "", holoKappa: "tmdb:movie:" + t.id,
119
+ availability: availability || browseOnly(),
120
+ };
121
+ }
122
+
123
+ export function normalizeSeries(t, { availability, region } = {}) {
124
+ const names = genreNames(t);
125
+ const imdb = imdbOf(t);
126
+ return {
127
+ id: "tmdb:series:" + t.id, tmdbId: t.id, kind: "series", name: t.name || t.title || "Untitled",
128
+ year: yearOf(t.first_air_date), releaseDate: t.first_air_date || "",
129
+ overview: t.overview || "", blurb: t.overview || "", tagline: t.tagline || "",
130
+ genres: names, topics: topicsOf(names),
131
+ cast: (() => { const cd = castOf(t); return cd.length ? cd.map((c) => c.name) : (t.cast || []).slice(0, 8); })(), castDetail: castOf(t), creators: crewOf(t, "series"),
132
+ rating: typeof t.vote_average === "number" ? t.vote_average : null, certification: certOf(t, "series", region),
133
+ keywords: keywordsOf(t), recommendations: recsOf(t), imdbId: imdb, networks: (t.networks || []).map((n) => n.name),
134
+ seasonCount: t.number_of_seasons || (Array.isArray(t.seasons) ? t.seasons.filter((s) => s.season_number > 0).length : 0),
135
+ episodeCount: t.number_of_episodes || 0,
136
+ seasons: Array.isArray(t.seasons)
137
+ ? t.seasons.filter((s) => s.season_number != null).map((s) => ({
138
+ id: "tmdb:season:" + t.id + ":" + s.season_number, seriesId: "tmdb:series:" + t.id, seasonNumber: s.season_number,
139
+ name: s.name || "Season " + s.season_number, episodeCount: s.episode_count || 0,
140
+ posterUrl: posterURL(s.poster_path), overview: s.overview || "",
141
+ }))
142
+ : [],
143
+ runtimeSec: 0,
144
+ posterUrl: posterURL(t.poster_path) || metahubPoster(imdb), backdrop: backdropURL(t.backdrop_path) || metahubBackdrop(imdb), logoUrl: logoOf(t),
145
+ trailerKey: trailerOf(t),
146
+ providers: t.providers || [],
147
+ channel: "TheMovieDB", quality: t.vote_average ? Math.min(1, t.vote_average / 10) : 0.7, license: "",
148
+ source: "tmdb", provider: "tmdb", kappa: "", holoKappa: "tmdb:series:" + t.id,
149
+ availability: availability || browseOnly(),
150
+ };
151
+ }
152
+
153
+ export function normalizeEpisode(ep, series, { availability } = {}) {
154
+ const sid = "tmdb:series:" + (series.tmdbId ?? series.id);
155
+ return {
156
+ id: "tmdb:ep:" + (series.tmdbId ?? series.id) + ":" + ep.season_number + ":" + ep.episode_number,
157
+ tmdbId: ep.id, kind: "episode",
158
+ name: ep.name || `Episode ${ep.episode_number}`,
159
+ seriesId: sid, seriesName: series.name, parentId: "tmdb:season:" + (series.tmdbId ?? series.id) + ":" + ep.season_number,
160
+ seasonNumber: ep.season_number, episodeNumber: ep.episode_number,
161
+ overview: ep.overview || "", blurb: ep.overview || "",
162
+ rating: typeof ep.vote_average === "number" ? ep.vote_average : null,
163
+ runtimeSec: ep.runtime ? ep.runtime * 60 : 0, releaseDate: ep.air_date || "",
164
+ posterUrl: backdropURL(ep.still_path, "w300") || series.posterUrl, backdrop: series.backdrop,
165
+ topics: series.topics || [], genres: series.genres || [],
166
+ channel: "TheMovieDB", quality: series.quality ?? 0.7, license: "",
167
+ source: "tmdb", provider: "tmdb", kappa: "", holoKappa: "tmdb:ep:" + (series.tmdbId ?? series.id) + ":" + ep.season_number + ":" + ep.episode_number,
168
+ availability: availability || browseOnly(),
169
+ };
170
+ }
171
+
172
+ // TMDb /watch/providers β†’ a flat, region-stamped list. Honest: provider availability is region- AND
173
+ // time-variant, so the caller stamps the fetch time; we stamp the region here.
174
+ export function normalizeWatchProviders(payload, region = "US") {
175
+ const r = payload && payload.results && payload.results[region];
176
+ if (!r) return [];
177
+ const out = [];
178
+ for (const type of ["flatrate", "free", "ads", "rent", "buy"]) {
179
+ for (const p of r[type] || []) out.push({ name: p.provider_name, type, region, logo: posterURL(p.logo_path, "w92"), link: r.link || "" });
180
+ }
181
+ // de-dup by provider name keeping the best (flatrate/free) tier
182
+ const seen = new Map();
183
+ for (const p of out) if (!seen.has(p.name)) seen.set(p.name, p);
184
+ return [...seen.values()];
185
+ }
186
+
187
+ // generic dispatch for a /trending or /search/multi mixed list
188
+ export function normalizeAny(t, opts) {
189
+ const mt = t.media_type || (t.title ? "movie" : t.name && t.first_air_date !== undefined ? "tv" : t.title ? "movie" : "tv");
190
+ if (mt === "tv" || t.first_air_date != null || t.number_of_seasons != null) return normalizeSeries(t, opts);
191
+ return normalizeMovie(t, opts);
192
+ }
193
+
194
+ // "Where to watch" β†’ a "go watch it there" link. TMDb gives platform NAMES + ONE aggregate JustWatch link per
195
+ // title (no per-provider deep-links), so we open each platform's SEARCH for the title β€” the honest take-me-there.
196
+ // Unknown platform β†’ the title's JustWatch link, else a web search. Makes the where-to-watch chips ACTIONABLE.
197
+ const WATCH_URLS = {
198
+ "netflix": (q) => "https://www.netflix.com/search?q=" + q,
199
+ "disney plus": (q) => "https://www.disneyplus.com/search?q=" + q,
200
+ "hulu": (q) => "https://www.hulu.com/search?q=" + q,
201
+ "amazon prime video": (q) => "https://www.amazon.com/s?k=" + q + "&i=instant-video",
202
+ "amazon video": (q) => "https://www.amazon.com/s?k=" + q + "&i=instant-video",
203
+ "max": (q) => "https://play.max.com/search?q=" + q,
204
+ "hbo max": (q) => "https://play.max.com/search?q=" + q,
205
+ "apple tv": (q) => "https://tv.apple.com/search?term=" + q,
206
+ "apple tv plus": (q) => "https://tv.apple.com/search?term=" + q,
207
+ "youtube": (q) => "https://www.youtube.com/results?search_query=" + q,
208
+ "peacock": (q) => "https://www.peacocktv.com/search?q=" + q,
209
+ "paramount plus": (q) => "https://www.paramountplus.com/search/?query=" + q,
210
+ "paramount+": (q) => "https://www.paramountplus.com/search/?query=" + q,
211
+ "crunchyroll": (q) => "https://www.crunchyroll.com/search?q=" + q,
212
+ };
213
+ export function watchLink(providerName, item, fallbackLink) {
214
+ const q = encodeURIComponent((item && item.name) || "");
215
+ const key = String(providerName || "").toLowerCase().replace(/\s+with\s+ads$/, "").trim();
216
+ const fn = WATCH_URLS[key];
217
+ if (fn) return fn(q);
218
+ if (fallbackLink) return fallbackLink; // TMDb's aggregate JustWatch link for the title
219
+ return "https://www.google.com/search?q=" + q + "%20watch%20online"; // last resort
220
+ }
221
+
222
+ export default { normalizeMovie, normalizeSeries, normalizeEpisode, normalizeWatchProviders, normalizeAny, browseOnly, posterURL, backdropURL, watchLink, TMDB_GENRES, TMDB_IMG };
b/52cf82d28c69b7ff9b5ae3df553c65957cf10295013dd6837a1c78e427874ef5 ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "when": "2026-07-11T08:29:23.457Z",
3
+ "pass": true,
4
+ "checks": {
5
+ "loadsUniverse": true,
6
+ "verifyRefuse": true,
7
+ "worksOffline": true,
8
+ "facetAlgebra": true,
9
+ "pivotUnderFrame": true,
10
+ "searchFast": true,
11
+ "personPivot": true
12
+ },
13
+ "fail": [],
14
+ "p95ms": 1.91,
15
+ "searchMs": 1.89
16
+ }
b/5f654b793ccf0c8d9b4e6bc530dd15f7a032fd440f7bb353496cbf6692b7374f ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$comment": "Holo TV Library root β€” the ΞΊ-addressed universe catalog. Each shard/persons object lives on the ΞΊ-mirror at b/<blake3>; sha256 is the verify key (SubtleCrypto at runtime, verify-or-refuse). Rows are compact: i/k/t/y/dec/g[]/m[](moods)/r/v/p/b/rt/imdb/col/c3/dir. Mood taxonomy is a fixed rule table in holo-library-forge.mjs.",
3
+ "version": 1,
4
+ "moodTaxonomyVersion": 1,
5
+ "generatedAt": "2026-07-11",
6
+ "counts": {
7
+ "titles": 18712,
8
+ "movies": 14474,
9
+ "series": 4238,
10
+ "persons": 1856
11
+ },
12
+ "mirror": "https://huggingface.co/HOLOGRAMTECH/holo-messenger-shell/resolve/main/b/",
13
+ "moods": [
14
+ "feelgood",
15
+ "dark",
16
+ "mindbending",
17
+ "cozy",
18
+ "adrenaline",
19
+ "tearjerker",
20
+ "scary",
21
+ "romantic",
22
+ "epic",
23
+ "quirky",
24
+ "truestory",
25
+ "family"
26
+ ],
27
+ "shards": [
28
+ {
29
+ "sha256": "899d357cb5004de5addb181458ec52a4e282fabfd8cebb62ccf589a8ef1d8dcf",
30
+ "blake3": "57268b6ebc99004d38c72b7f966ccd8dd8fe3cccb5dd0f62a3d9a15ac1cdd150",
31
+ "bytes": 267733
32
+ },
33
+ {
34
+ "sha256": "e3fa688522ea3ce62d92cd6f4673729dce85883952d4dd9f5b6de8dfa6d0a20f",
35
+ "blake3": "6a6a84772313bcfd73301c11c78754cf1d685b1546d7d0420f30b12423b0390b",
36
+ "bytes": 273045
37
+ },
38
+ {
39
+ "sha256": "b9f4f511bbe1e52f388ee5c2a1cdb54783cd10661b25516f3bdf4eec2c3604b2",
40
+ "blake3": "a37412bc4a78d83579e7dd734d4d37790a8093db4c0c36926162c4a79be68361",
41
+ "bytes": 261298
42
+ },
43
+ {
44
+ "sha256": "fcdec94954cf10119e6eebf88986a59911894c862528953f64c82721fcb55adb",
45
+ "blake3": "15ed6838730d14422d136357d4a4c2f23decb4cc4b8ff37d3043022252fedad7",
46
+ "bytes": 269554
47
+ },
48
+ {
49
+ "sha256": "1c0d04b28758892b8df09f004c00247f14b6005dd22d7cddcc0c88aad7a0ec3c",
50
+ "blake3": "b077e38fe5b37f0ca78f07e03fb4753e820d0e5bf176b5c9a1d449c3c7e1ecda",
51
+ "bytes": 257809
52
+ },
53
+ {
54
+ "sha256": "0ecb222e1fc41a7bfc42f0840eafc951405c4ebb923a8e075d5f9a8e23994b1c",
55
+ "blake3": "e108d9fa3b23f072cd603b083e689212a17efb410cf5ca44e83031a8f35315fc",
56
+ "bytes": 271457
57
+ },
58
+ {
59
+ "sha256": "a9359b860826572f5ca186162152869ff62a674988a040bfcd820937e617a8cc",
60
+ "blake3": "8db5d6e27b8bca40c7d09a8747f8ab10b48912a68408ada34e1d019eda2bdb99",
61
+ "bytes": 265943
62
+ },
63
+ {
64
+ "sha256": "307563503bdd8834ecab0d9b41c99e864f79ccb78e8603623361a306cdfa40c0",
65
+ "blake3": "285af202571902c3d5a6ef284ea419ecc37479eb1cdd5bfc8e616b48bec8f3ef",
66
+ "bytes": 249973
67
+ },
68
+ {
69
+ "sha256": "478f1a92f61e4807bab0c11b8f20b39cee74027287674ef11757187f5debeba1",
70
+ "blake3": "9cc700200ebfaf0c7c0bab7070de7c704122d4a7a2eac1f99be1ba25aa76958a",
71
+ "bytes": 255471
72
+ },
73
+ {
74
+ "sha256": "8d830344e9ace494855c063f44407868a21ccd0da178dfdac22fb17cf5b7e5ac",
75
+ "blake3": "bc51eb2b230db9ebe767cb0583c593e13af1a9bacb8bda30d189ec0c960413de",
76
+ "bytes": 259790
77
+ },
78
+ {
79
+ "sha256": "f05ee9415de028e9dba4de0d007a22ddfb249ea6a8af8580cb59cbe312d2772b",
80
+ "blake3": "31be625986b020bca96ecd931d97d67db93c003c5f93a7cf452ba6b4f4c290f3",
81
+ "bytes": 261008
82
+ },
83
+ {
84
+ "sha256": "e06268932c02d558fad1d36ce1327317190dbba086e5f8b74a9fa9fa5ef7307b",
85
+ "blake3": "7ef6dfe21b07713cc73e5188df264e5465de9ad49f93c07b23adda17c4779e40",
86
+ "bytes": 276469
87
+ },
88
+ {
89
+ "sha256": "622bf2bdc0dabd2ef04943988fa4f4a1c0b56e80d5cb56684528135cb53fb823",
90
+ "blake3": "3d5a39676cce4996cb7cac51d09f6c76ba4a9934033b950046be2ca687ba2738",
91
+ "bytes": 278653
92
+ },
93
+ {
94
+ "sha256": "062cf09c49de97e943a4973030d9e75cd56a2dac457dcc65fb470950773585da",
95
+ "blake3": "873b3e87caf5b6641a890d7e21f11d7afd44f3a0b4ab2868e1c6734bc3ec87ad",
96
+ "bytes": 255464
97
+ },
98
+ {
99
+ "sha256": "85a3270652ccaa4aa69e972da9be0f7dfab00280f96d2d7e10f0f97aabc7fb9b",
100
+ "blake3": "1f2875d3d4608a221fecb6624b18f79e9caf3283ecf0946f0524e8328045fa85",
101
+ "bytes": 255963
102
+ },
103
+ {
104
+ "sha256": "493b149742c721385f4d2aecec8c2196945bc6aed3df7535664c830823cc27ac",
105
+ "blake3": "e2b5fcf26615db3e673cf1e4c365e916d7a687f0016b5d9d457c25eee2478a39",
106
+ "bytes": 261255
107
+ }
108
+ ],
109
+ "persons": {
110
+ "sha256": "5d86ef6c1b5ec78c7e0ddd11a8d3ada0b24d6a879f2e7e5567b8a6dcb28a7531",
111
+ "blake3": "3c6d39d693bc0cb5e46d572037269e20b6379985350e4d72bb6fbfdf2831670e",
112
+ "bytes": 179005
113
+ }
114
+ }
b/a43249f4f27206cc010c369c220b1469ee0564c4d75f798d63d2c18425d063be ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // holo-library-forge.mjs β€” THE FORGE: the movie/TV universe β†’ ΞΊ (HOLO-TV-LIBRARY-PROMPT.md L1).
3
+ //
4
+ // Sweeps TMDb /discover by year Γ— kind (vote floors keep it real titles, not noise), enriches the head
5
+ // of the popularity curve with one append_to_response call each (runtime Β· imdb Β· collection Β· top cast Β·
6
+ // director · keywords→moods), and mints the whole catalog as CONTENT-ADDRESSED shards:
7
+ //
8
+ // rows β†’ 16 shards (hash(id)%16), each a raw-JSON ΞΊ-object (sha256 of bytes = verify key,
9
+ // blake3 of bytes = the ΞΊ-mirror path b/<hex>)
10
+ // persons β†’ ONE inverse posting object (person β†’ their title ids; persons with β‰₯3 titles)
11
+ // root β†’ feed/library-index.json (tiny, published in the Q repo): shard ΞΊs + counts + taxonomy
12
+ //
13
+ // Deterministic: same input β†’ same rows β†’ same ΞΊs (rows sorted by id; JSON.stringify of plain objects
14
+ // with fixed key order; undefined fields OMITTED β€” the RFC-8785 lesson). Moods are a FIXED rule table
15
+ // (versioned below), never a model call. Resumable: every network page lands in a checkpoint file first.
16
+ //
17
+ // node holo-library-forge.mjs sweep + enrich + mint + UPLOAD + write root
18
+ // node holo-library-forge.mjs --dry sweep + mint locally, print ΞΊs + sizes, no upload/root
19
+ // node holo-library-forge.mjs --no-net re-mint from the checkpoint only (deterministic re-derive)
20
+
21
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
22
+ import { createHash } from "node:crypto";
23
+ import os from "node:os";
24
+ import path from "node:path";
25
+ import { fileURLToPath, pathToFileURL } from "node:url";
26
+
27
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
28
+ const HOLOGRAM = path.resolve(HERE, "../../..");
29
+ const { blake3hex } = await import(pathToFileURL(path.join(HOLOGRAM, "holo-os/system/os/usr/lib/holo/holo-blake3.mjs")).href);
30
+
31
+ const DRY = process.argv.includes("--dry");
32
+ const NO_NET = process.argv.includes("--no-net");
33
+ const KEY = "45f791c5f6a6940a50403bb3890c7b86"; // the built-in community key (holo-tmdb.mjs)
34
+ const API = "https://api.themoviedb.org/3";
35
+ const CKPT = path.join(HERE, ".library-forge-checkpoint.json");
36
+ const SHARDS = 16;
37
+ const YEARS = { movie: [1940, new Date().getFullYear()], tv: [1950, new Date().getFullYear()] };
38
+ const FLOOR = { movie: 200, tv: 100 }; // vote_count floors (L0-probed: β‰ˆ19k titles)
39
+ const ENRICH_TOP = 5000; // head of the curve gets runtime/cast/keywords
40
+ const MIRROR = "https://huggingface.co/HOLOGRAMTECH/holo-messenger-shell/resolve/main/b/";
41
+ const HF_REPO = "HOLOGRAMTECH/holo-messenger-shell";
42
+
43
+ // ── mood taxonomy v1 β€” FIXED deterministic rules (genres βˆͺ keyword clusters); versioned, never a model ──
44
+ export const MOOD_TAXONOMY_VERSION = 1;
45
+ const KW = {
46
+ mindbending: ["time travel", "time loop", "parallel", "memory", "dream", "simulation", "plot twist", "identity", "surreal", "nonlinear"],
47
+ tearjerker: ["grief", "terminal illness", "cancer", "tragedy", "loss of", "death of", "holocaust", "euthanasia"],
48
+ quirky: ["surreal", "absurd", "black comedy", "dark comedy", "satire", "offbeat", "deadpan", "mockumentary"],
49
+ truestory: ["based on true story", "based on a true story", "biography", "based on real events", "true events", "biopic"],
50
+ };
51
+ const kwHit = (kws, list) => list.some((n) => kws.some((k) => k.includes(n)));
52
+ // g = tmdb genre ids (movie+tv unioned); rt minutes|0; r rating; v votes; kws lowercase keyword names
53
+ export function moodsOf({ g = [], rt = 0, r = 0, v = 0, kws = [] }) {
54
+ const has = (...ids) => ids.some((x) => g.includes(x));
55
+ const m = [];
56
+ if (has(35, 10402, 10751) && r >= 6.5) m.push("feelgood");
57
+ if (has(80, 53, 9648) && !has(10751, 16) ) m.push("dark");
58
+ if ((has(878, 10765) && has(9648)) || kwHit(kws, KW.mindbending)) m.push("mindbending");
59
+ if (has(10751, 16, 10749) && (!rt || rt <= 115) && r >= 6) m.push("cozy");
60
+ if (has(28, 53, 10759)) m.push("adrenaline");
61
+ if (has(18) && (kwHit(kws, KW.tearjerker) || (has(10749) && r >= 7.2))) m.push("tearjerker");
62
+ if (has(27)) m.push("scary");
63
+ if (has(10749)) m.push("romantic");
64
+ if (has(12, 10752, 36, 14, 10765, 10768) && ((rt >= 140) || v >= 5000)) m.push("epic");
65
+ if (has(35) && kwHit(kws, KW.quirky)) m.push("quirky");
66
+ if (kwHit(kws, KW.truestory) || has(36, 99)) m.push("truestory");
67
+ if (has(10751, 16, 10762)) m.push("family");
68
+ return m;
69
+ }
70
+ export const MOODS = ["feelgood", "dark", "mindbending", "cozy", "adrenaline", "tearjerker", "scary", "romantic", "epic", "quirky", "truestory", "family"];
71
+
72
+ // ── polite, resumable TMDb ───────────────────────────────────────────────────────────────────────────────
73
+ let inflight = 0, lastTick = 0;
74
+ async function tmdb(p, params = {}) {
75
+ const u = new URL(API + p); u.searchParams.set("api_key", KEY);
76
+ for (const [k, v] of Object.entries(params)) u.searchParams.set(k, String(v));
77
+ for (let attempt = 0; ; attempt++) {
78
+ const wait = Math.max(0, lastTick + 34 - Date.now()); // β‰ˆ29 req/s ceiling
79
+ lastTick = Math.max(Date.now(), lastTick + 34);
80
+ if (wait) await new Promise((r) => setTimeout(r, wait));
81
+ const res = await fetch(u).catch(() => null);
82
+ if (res && res.status === 429) { await new Promise((r) => setTimeout(r, 1500 * (attempt + 1))); continue; }
83
+ if (!res || !res.ok) { if (attempt >= 3) throw new Error("tmdb " + (res ? res.status : "net") + " " + p); await new Promise((r) => setTimeout(r, 800 * (attempt + 1))); continue; }
84
+ return res.json();
85
+ }
86
+ }
87
+
88
+ // ── the sweep (checkpointed) ─────────────────────────────────────────────────────────────────────────────
89
+ const ckpt = existsSync(CKPT) ? JSON.parse(readFileSync(CKPT, "utf8")) : { discover: {}, enrich: {} };
90
+ const saveCkpt = () => writeFileSync(CKPT, JSON.stringify(ckpt));
91
+
92
+ async function sweep() {
93
+ for (const kind of ["movie", "tv"]) {
94
+ const [y0, y1] = YEARS[kind];
95
+ for (let y = y0; y <= y1; y++) {
96
+ const ck = kind + ":" + y;
97
+ if (ckpt.discover[ck]) continue;
98
+ const yearParam = kind === "movie" ? { primary_release_year: y } : { first_air_date_year: y };
99
+ const base = { ...yearParam, "vote_count.gte": FLOOR[kind], sort_by: "vote_count.desc", include_adult: false };
100
+ const first = await tmdb(`/discover/${kind}`, { ...base, page: 1 });
101
+ const pages = Math.min(first.total_pages || 1, 500);
102
+ const rows = [...(first.results || [])];
103
+ for (let pg = 2; pg <= pages; pg++) rows.push(...((await tmdb(`/discover/${kind}`, { ...base, page: pg })).results || []));
104
+ ckpt.discover[ck] = rows.map((t) => ({
105
+ id: t.id, kind, title: kind === "movie" ? t.title : t.name,
106
+ year: parseInt((kind === "movie" ? t.release_date : t.first_air_date) || "0") || 0,
107
+ g: t.genre_ids || [], r: Math.round((t.vote_average || 0) * 10) / 10, v: t.vote_count || 0,
108
+ p: t.poster_path || null, b: t.backdrop_path || null, l: t.original_language || "",
109
+ }));
110
+ saveCkpt();
111
+ if (y % 10 === 0) console.log(`sweep ${ck}: ${rows.length} titles`);
112
+ }
113
+ }
114
+ }
115
+
116
+ async function enrich(all) {
117
+ const head = [...all].sort((a, b) => b.v - a.v).slice(0, ENRICH_TOP);
118
+ let done = 0;
119
+ for (const t of head) {
120
+ const ck = t.kind + ":" + t.id;
121
+ if (!(ck in ckpt.enrich)) {
122
+ try {
123
+ const d = await tmdb(`/${t.kind}/${t.id}`, { append_to_response: "keywords,credits,external_ids" });
124
+ const kws = ((d.keywords && (d.keywords.keywords || d.keywords.results)) || []).map((k) => (k.name || "").toLowerCase());
125
+ const cast = ((d.credits && d.credits.cast) || []).slice(0, 3).map((c) => [c.id, c.name]);
126
+ const dir = ((d.credits && d.credits.crew) || []).find((c) => c.job === "Director" || c.job === "Creator");
127
+ ckpt.enrich[ck] = {
128
+ rt: d.runtime || (Array.isArray(d.episode_run_time) && d.episode_run_time[0]) || 0,
129
+ imdb: (d.external_ids && d.external_ids.imdb_id) || d.imdb_id || null,
130
+ col: (d.belongs_to_collection && d.belongs_to_collection.id) || null,
131
+ colName: (d.belongs_to_collection && d.belongs_to_collection.name) || null,
132
+ c3: cast.length ? cast : null, dir: dir ? [dir.id, dir.name] : null, kws,
133
+ seasons: d.number_of_seasons || null,
134
+ };
135
+ } catch { ckpt.enrich[ck] = {}; }
136
+ if (++done % 250 === 0) { saveCkpt(); console.log(`enrich ${done}/${head.length}`); }
137
+ }
138
+ }
139
+ saveCkpt();
140
+ }
141
+
142
+ // ── mint: rows β†’ shards β†’ ΞΊ ──────────────────────────────────────────────────────────────────────────────
143
+ const sha256hex = (buf) => createHash("sha256").update(buf).digest("hex");
144
+ function mint() {
145
+ const seen = new Set(); const all = [];
146
+ for (const rows of Object.values(ckpt.discover)) for (const t of rows) {
147
+ const k = t.kind + ":" + t.id;
148
+ if (seen.has(k) || !t.year || !t.p) continue; // no art or no year = not library-grade
149
+ seen.add(k); all.push(t);
150
+ }
151
+ const persons = new Map(); // id β†’ { n: name, t: [rowKey…] }
152
+ const rows = all.map((t) => {
153
+ const e = ckpt.enrich[t.kind + ":" + t.id] || {};
154
+ const kws = e.kws || [];
155
+ const row = {
156
+ i: t.id, k: t.kind === "movie" ? "m" : "t", t: t.title, y: t.year, dec: Math.floor(t.year / 10) * 10,
157
+ g: t.g, m: moodsOf({ g: t.g, rt: e.rt || 0, r: t.r, v: t.v, kws }),
158
+ r: t.r, v: t.v, p: t.p, b: t.b || undefined, l: t.l || undefined,
159
+ rt: e.rt || undefined, imdb: e.imdb || undefined, col: e.col || undefined, cn: e.colName || undefined,
160
+ sn: e.seasons || undefined,
161
+ };
162
+ const key = row.k + row.i;
163
+ for (const pers of [...(e.c3 || []), ...(e.dir ? [e.dir] : [])]) {
164
+ const rec = persons.get(pers[0]) || { n: pers[1], t: [] };
165
+ rec.t.push(key); persons.set(pers[0], rec);
166
+ }
167
+ if (e.c3) row.c3 = e.c3.map((c) => c[0]);
168
+ if (e.dir) row.dir = e.dir[0];
169
+ return JSON.parse(JSON.stringify(row)); // strips undefined β€” ΞΊ-stable bytes
170
+ }).sort((a, b) => (a.k + a.i < b.k + b.i ? -1 : 1));
171
+
172
+ const shards = Array.from({ length: SHARDS }, () => []);
173
+ for (const r of rows) shards[(r.i + (r.k === "t" ? 7 : 0)) % SHARDS].push(r);
174
+ const personsObj = Object.fromEntries([...persons].filter(([, v]) => v.t.length >= 3).sort((a, b) => a[0] - b[0]));
175
+
176
+ const objects = shards.map((s, n) => ({ name: "shard" + n, bytes: Buffer.from(JSON.stringify({ v: 1, n, rows: s })) }));
177
+ objects.push({ name: "persons", bytes: Buffer.from(JSON.stringify({ v: 1, persons: personsObj })) });
178
+ for (const o of objects) { o.sha256 = sha256hex(o.bytes); o.blake3 = blake3hex(new Uint8Array(o.bytes)); }
179
+ return { rows, objects, personsCount: Object.keys(personsObj).length };
180
+ }
181
+
182
+ // ── upload + root ────────────────────────────────────────────────────────────────────────────────────────
183
+ async function upload(objects) {
184
+ const token = readFileSync(path.join(os.homedir(), ".cache/huggingface/token"), "utf8").trim();
185
+ const missing = [];
186
+ for (const o of objects) if (!(await fetch(MIRROR + o.blake3, { method: "HEAD" })).ok) missing.push(o);
187
+ console.log(`mirror: ${objects.length - missing.length} present, ${missing.length} to upload`);
188
+ if (missing.length) {
189
+ const ndjson = [JSON.stringify({ key: "header", value: { summary: `holo-library-forge: ${missing.length} catalog object(s)` } })]
190
+ .concat(missing.map((o) => JSON.stringify({ key: "file", value: { path: "b/" + o.blake3, content: o.bytes.toString("base64"), encoding: "base64" } })))
191
+ .join("\n");
192
+ const r = await fetch(`https://huggingface.co/api/models/${HF_REPO}/commit/main`, {
193
+ method: "POST", headers: { Authorization: "Bearer " + token, "Content-Type": "application/x-ndjson" }, body: ndjson,
194
+ });
195
+ if (!r.ok) throw new Error("HF commit failed: " + r.status + " " + (await r.text()).slice(0, 200));
196
+ console.log("HF commit βœ“");
197
+ }
198
+ for (const o of objects) { // refuse-at-boundary: every object must re-derive from the mirror
199
+ const back = new Uint8Array(await (await fetch(MIRROR + o.blake3)).arrayBuffer());
200
+ if (blake3hex(back) !== o.blake3 || sha256hex(Buffer.from(back)) !== o.sha256) throw new Error("mirror does not re-derive: " + o.name);
201
+ }
202
+ console.log("mirror re-derives βœ“ all", objects.length);
203
+ }
204
+
205
+ // ── run ──────────────────────────────────────────────────────────────────────────────────────────────────
206
+ if (!NO_NET) { await sweep(); }
207
+ const preAll = Object.values(ckpt.discover).flat();
208
+ if (!NO_NET) { await enrich(preAll.filter((t) => t.p && t.year)); }
209
+ const { rows, objects, personsCount } = mint();
210
+ const total = objects.reduce((s, o) => s + o.bytes.length, 0);
211
+ console.log(`minted ${rows.length} titles (${rows.filter((r) => r.k === "m").length} movies Β· ${rows.filter((r) => r.k === "t").length} series) Β· ${personsCount} persons Β· ${objects.length} objects Β· ${(total / 1048576).toFixed(1)} MB`);
212
+ for (const o of objects) console.log(` ${o.name}: ${(o.bytes.length / 1024).toFixed(0)} KB sha256:${o.sha256.slice(0, 12)}… blake3:${o.blake3.slice(0, 12)}…`);
213
+
214
+ if (!DRY) {
215
+ await upload(objects);
216
+ const root = {
217
+ $comment: "Holo TV Library root β€” the ΞΊ-addressed universe catalog. Each shard/persons object lives on the ΞΊ-mirror at b/<blake3>; sha256 is the verify key (SubtleCrypto at runtime, verify-or-refuse). Rows are compact: i/k/t/y/dec/g[]/m[](moods)/r/v/p/b/rt/imdb/col/c3/dir. Mood taxonomy is a fixed rule table in holo-library-forge.mjs.",
218
+ version: 1, moodTaxonomyVersion: MOOD_TAXONOMY_VERSION, generatedAt: new Date().toISOString().slice(0, 10),
219
+ counts: { titles: rows.length, movies: rows.filter((r) => r.k === "m").length, series: rows.filter((r) => r.k === "t").length, persons: personsCount },
220
+ mirror: MIRROR, moods: MOODS,
221
+ shards: objects.filter((o) => o.name.startsWith("shard")).map((o) => ({ sha256: o.sha256, blake3: o.blake3, bytes: o.bytes.length })),
222
+ persons: (({ sha256, blake3, bytes }) => ({ sha256, blake3, bytes: bytes.length }))(objects.find((o) => o.name === "persons")),
223
+ };
224
+ writeFileSync(path.join(HERE, "feed/library-index.json"), JSON.stringify(root, null, 1) + "\n");
225
+ console.log("root written: feed/library-index.json βœ“");
226
+ }
b/e8e3471220a882a17984b9b6efb6a498bb3c82fcdb7a3547dc3905eed732c2fe ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // holo-library-witness.mjs β€” proves the Library plane: verify-or-refuse, offline, and under-a-frame pivots
3
+ // on a universe-scale synthetic catalog. Fake mirror + Map cache β€” no network, no key.
4
+ //
5
+ // Checks:
6
+ // 1 loadsUniverse β€” all shards load, rows concatenated, byKey resolves.
7
+ // 2 verifyRefuse β€” a TAMPERED cached shard is refused (re-fetched clean), a tampered MIRROR shard throws.
8
+ // 3 worksOffline β€” warm cache + dead network β†’ full load still succeeds (0 fetches).
9
+ // 4 facetAlgebra β€” genre ∩ decade ∩ mood ∩ runtime compose correctly (hand-checked expectations).
10
+ // 5 pivotUnderFrame β€” 6-way facet pivot over 19,000 rows completes in < 16 ms (p95 of 50 runs).
11
+ // 6 searchFast β€” search-as-you-type over 19k titles < 30 ms, prefix beats substring.
12
+ // 7 personPivot β€” person posting resolves to their titles via rowByKey.
13
+ //
14
+ // node holo-library-witness.mjs (from holo-apps/apps/player/)
15
+
16
+ import { writeFileSync } from "node:fs";
17
+ import { createHash } from "node:crypto";
18
+ import { fileURLToPath } from "node:url";
19
+ import { dirname, join } from "node:path";
20
+ import { createLibrary } from "./holo-library.mjs";
21
+
22
+ const here = dirname(fileURLToPath(import.meta.url));
23
+ const checks = {}; const fail = [];
24
+ const ok = (n, c, d = "") => { checks[n] = !!c; if (!c) fail.push(n + (d ? ` β€” ${d}` : "")); return !!c; };
25
+ const sha256hex = async (u8) => createHash("sha256").update(u8).digest("hex");
26
+
27
+ // ── synthetic universe: 19,000 deterministic rows across 16 shards + persons ────────────────────────────
28
+ const GENRES = [28, 35, 18, 27, 878, 10749, 80, 12, 16, 9648];
29
+ const MOODS = ["feelgood", "dark", "mindbending", "cozy", "adrenaline", "scary", "romantic", "epic"];
30
+ const N = 19000;
31
+ const mk = (i) => ({
32
+ i, k: i % 4 === 0 ? "t" : "m", t: "Title " + i + (i % 7 === 0 ? " Prefixable" : ""), y: 1940 + (i % 86),
33
+ dec: Math.floor((1940 + (i % 86)) / 10) * 10, g: [GENRES[i % 10], GENRES[(i + 3) % 10]],
34
+ m: [MOODS[i % 8]], r: 5 + (i % 50) / 10, v: 100 + (i % 9000), p: "/p" + i + ".jpg", rt: 60 + (i % 120),
35
+ });
36
+ const rows = Array.from({ length: N }, (_, i) => mk(i));
37
+ const shards = Array.from({ length: 16 }, () => []);
38
+ for (const r of rows) shards[(r.i + (r.k === "t" ? 7 : 0)) % 16].push(r);
39
+ const objects = shards.map((s, n) => Buffer.from(JSON.stringify({ v: 1, n, rows: s })));
40
+ const personsObj = { 7: { n: "Test Person", t: [rows[1].k + rows[1].i, rows[2].k + rows[2].i, rows[5].k + rows[5].i] } };
41
+ objects.push(Buffer.from(JSON.stringify({ v: 1, persons: personsObj })));
42
+ const refs = await Promise.all(objects.map(async (b) => ({ sha256: await sha256hex(b), blake3: "fake" + (await sha256hex(b)).slice(0, 8), bytes: b.length })));
43
+ const ROOT = { version: 1, mirror: "https://mirror.test/b/", shards: refs.slice(0, 16), persons: refs[16], counts: { titles: N } };
44
+ const byBlake = new Map(refs.map((r, idx) => [r.blake3, objects[idx]]));
45
+
46
+ let fetches = 0, netDown = false;
47
+ const fakeFetch = async (url) => {
48
+ if (netDown) throw new Error("network down");
49
+ fetches++;
50
+ const hex = String(url).split("/b/")[1];
51
+ const b = byBlake.get(hex);
52
+ return b ? { ok: true, arrayBuffer: async () => new Uint8Array(b).buffer } : { ok: false, status: 404 };
53
+ };
54
+ const kv = new Map();
55
+ const cacheGet = async (sha) => kv.get(sha) || null;
56
+ const cachePut = async (sha, bytes) => { kv.set(sha, bytes); };
57
+
58
+ // 1 loadsUniverse
59
+ const lib = createLibrary({ root: ROOT, fetch: fakeFetch, cacheGet, cachePut, sha256hex });
60
+ await lib.load();
61
+ ok("loadsUniverse", lib.rows.length === N && lib.rowByKey("m1") && lib.rowByKey("t4"), `rows=${lib.rows.length}`);
62
+
63
+ // 2 verifyRefuse β€” tamper a cache entry β†’ refused + healed from mirror; tamper the mirror β†’ throws
64
+ const someSha = ROOT.shards[3].sha256;
65
+ kv.set(someSha, new Uint8Array([1, 2, 3]));
66
+ const lib2 = createLibrary({ root: ROOT, fetch: fakeFetch, cacheGet, cachePut, sha256hex });
67
+ await lib2.load();
68
+ const healed = lib2.stats().refused >= 1 && lib2.rows.length === N && (await sha256hex(kv.get(someSha))) === someSha;
69
+ let mirrorRefused = false;
70
+ {
71
+ const badFetch = async () => ({ ok: true, arrayBuffer: async () => new Uint8Array([9, 9, 9]).buffer });
72
+ const lib3 = createLibrary({ root: ROOT, fetch: badFetch, cacheGet: async () => null, cachePut: null, sha256hex });
73
+ try { await lib3.load(); } catch (e) { mirrorRefused = /REFUSED/.test(String(e)); }
74
+ }
75
+ ok("verifyRefuse", healed && mirrorRefused, `healed=${healed} mirrorRefused=${mirrorRefused}`);
76
+
77
+ // 3 worksOffline β€” cache is warm from check 1; kill the network entirely
78
+ netDown = true; fetches = 0;
79
+ const lib4 = createLibrary({ root: ROOT, fetch: fakeFetch, cacheGet, cachePut, sha256hex });
80
+ await lib4.load();
81
+ ok("worksOffline", lib4.rows.length === N && fetches === 0, `rows=${lib4.rows.length} fetches=${fetches}`);
82
+ netDown = false;
83
+
84
+ // 4 facetAlgebra β€” hand-checked compositions
85
+ const q1 = lib.query({ genres: [35], decades: [1980] });
86
+ const truth1 = rows.filter((r) => r.g.includes(35) && r.dec === 1980).length;
87
+ const q2 = lib.query({ genres: [35], decades: [1980], moods: ["feelgood"], runtimeMax: 100 });
88
+ const truth2 = rows.filter((r) => r.g.includes(35) && r.dec === 1980 && r.m.includes("feelgood") && r.rt && r.rt <= 100).length;
89
+ const q3 = lib.query({ kind: "t", ratingMin: 8 });
90
+ const truth3 = rows.filter((r) => r.k === "t" && r.r >= 8).length;
91
+ ok("facetAlgebra", q1.length === truth1 && q2.length === truth2 && q3.length === truth3, `${q1.length}/${truth1} ${q2.length}/${truth2} ${q3.length}/${truth3}`);
92
+
93
+ // 5 pivotUnderFrame β€” p95 of 50 six-way pivots < 16ms
94
+ const times = [];
95
+ for (let i = 0; i < 50; i++) {
96
+ const t0 = performance.now();
97
+ lib.query({ kind: "m", genres: [GENRES[i % 10]], moods: [MOODS[i % 8]], decades: [1970 + (i % 5) * 10], runtimeMax: 150, ratingMin: 6, limit: 60 });
98
+ times.push(performance.now() - t0);
99
+ }
100
+ times.sort((a, b) => a - b);
101
+ const p95 = times[Math.floor(times.length * 0.95)];
102
+ ok("pivotUnderFrame", p95 < 16, `p95=${p95.toFixed(2)}ms`);
103
+
104
+ // 6 searchFast β€” prefix beats substring, < 30ms
105
+ const t0 = performance.now(); const sr = lib.search("title 7"); const searchMs = performance.now() - t0;
106
+ ok("searchFast", searchMs < 30 && sr.length > 0 && sr[0].t.toLowerCase().startsWith("title 7"), `ms=${searchMs.toFixed(2)} first=${sr[0] && sr[0].t}`);
107
+
108
+ // 7 personPivot
109
+ const p = await lib.person(7);
110
+ const titles = p ? p.t.map((k) => lib.rowByKey(k)).filter(Boolean) : [];
111
+ ok("personPivot", !!p && titles.length === 3 && p.n === "Test Person", JSON.stringify(p));
112
+
113
+ // ── verdict ─────────────────────────────────────────────────────────────────────────────────────────────
114
+ const pass = fail.length === 0;
115
+ console.log("\nholo-library witness β€” the ΞΊ-addressed universe plane: verified, offline, under-a-frame\n");
116
+ for (const [n, c] of Object.entries(checks)) console.log(` ${c ? "βœ“" : "βœ—"} ${n}`);
117
+ console.log(pass ? `\n WITNESSED βœ“ ${N} titles Β· pivot p95 ${p95.toFixed(1)}ms Β· search ${searchMs.toFixed(1)}ms Β· tamper refused Β· offline whole\n` : `\n FAILED: ${fail.join("; ")}\n`);
118
+ writeFileSync(join(here, "holo-library-witness.result.json"), JSON.stringify({ when: new Date().toISOString(), pass, checks, fail, p95ms: +p95.toFixed(2), searchMs: +searchMs.toFixed(2) }, null, 2));
119
+ process.exit(pass ? 0 : 1);