"""The GODVERSE modules — canonical naming per GODVERSE.md (the brand bible; lanes name to match it). StoreGod is the BASE store-management app (vanilla: inventory, POS, store ops, universal identify+value over the BaseGod brain — any goods). Each MODULE (RecordGod, ToolGod, VaultGod…) is a specialist ADD-ON unlocked on top, deepening the store for one shopper-world. A store unlocks the modules for what it sells — MULTIPLE at once (a pawnbroker enables records + tools + jewellery). Enabled via `STOREGOD_MODULES` (csv); the key's scope/features on the DealGod side gate access + billing. Branding: one module → that BrandGod; many/none → "StoreGod". Umbrella by shopper-world + data source (GODVERSE.md §naming): media (TMDb video), tech (electronics), vault (graded collectibles = comics+cards+collectible toys), kid (family/kids resale). Cross-cutting FEATURES (scangod/meltgod/stagegod/comps/lore/history) are capabilities, NOT modules — they ride on a key's `features`. """ import os from dataclasses import dataclass BASE_BRAND = "StoreGod" BASE_FEATURES = ("identify", "value", "inventory", "pos", "supply") @dataclass(frozen=True) class Module: key: str # STOREGOD_MODULES token brand: str # BrandGod name shown when this is the only module unlocked domains: tuple # the DealGod scope domain(s) it unlocks (umbrellas carry several) condition_vocab: str # grading vocab adds: tuple # specialist features layered onto the base threed: bool # Roster per GODVERSE.md. domains use DG1's published scope vocab # (apparel·books·comics·computing·games·instruments·jewellery·music·phones·photo·tcg·tools·toys·video). MODULES = { "records": Module("records", "RecordGod", ("music",), "goldmine", ("lore", "discogs_intake", "wishlist", "heal"), True), "media": Module("media", "MediaGod", ("video",), "disc", ("lore",), False), "books": Module("books", "BookGod", ("books",), "generic", ("lore",), False), "tech": Module("tech", "TechGod", ("computing", "phones", "photo"), "grade", ("comps", "lore"), False), "tools": Module("tools", "ToolGod", ("tools",), "tested", ("lore",), False), "play": Module("play", "PlayGod", ("instruments",), "gear", ("lore",), True), "games": Module("games", "GameGod", ("games",), "CIB", ("comps", "lore"), False), "jewellery": Module("jewellery", "JewelGod", ("jewellery",), "hallmark", ("melt",), False), "vault": Module("vault", "VaultGod", ("comics", "tcg", "toys"), "graded", ("lore", "comps"), False), "kids": Module("kids", "KidGod", ("toys", "apparel"), "generic", ("comps",), False), "kicks": Module("kicks", "KicksGod", ("apparel",), "grade", ("comps",), False), "drip": Module("drip", "DripGod", ("apparel",), "generic", ("comps",), False), } # legacy STOREGOD_PACK / module aliases → canonical key (back-compat for running containers + folded gods) ALIASES = {"store": None, "": None, "instruments": "play", "computing": "tech", "phones": "tech", "comics": "vault", "cards": "vault", "tcg": "vault", "toys": "vault", "jewel": "jewellery"} def _resolve(token: str): token = ALIASES.get(token, token) return MODULES.get(token) if token else None def active_modules() -> list: """The unlocked modules. STOREGOD_MODULES (csv) preferred; falls back to legacy single STOREGOD_PACK ('store'/'' = vanilla base, no module). Unknown/folded tokens resolve via ALIASES.""" env = os.getenv("STOREGOD_MODULES") tokens = ([t.strip() for t in env.split(",")] if env is not None else [os.getenv("STOREGOD_PACK", "records")]) out, seen = [], set() for t in tokens: m = _resolve(t.strip()) if m and m.key not in seen: out.append(m) seen.add(m.key) return out def brand() -> str: m = active_modules() return m[0].brand if len(m) == 1 else BASE_BRAND def active_scope() -> list: m = active_modules() if not m: return ["*"] # no module = StoreGod base = every domain return sorted({d for mod in m for d in mod.domains}) # union of all unlocked modules' domains def features() -> set: f = set(BASE_FEATURES) for m in active_modules(): f |= set(m.adds) return f def has_module(key: str) -> bool: mod = _resolve(key) return bool(mod) and any(m.key == mod.key for m in active_modules()) def has_feature(feat: str) -> bool: return feat in features() def threed() -> bool: return any(m.threed for m in active_modules()) def info() -> dict: return {"brand": brand(), "base": BASE_BRAND, "modules": [m.key for m in active_modules()], "scope": active_scope(), "features": sorted(features()), "threed": threed()} def _selfcheck(): assert "identify" in features() and "value" in features() # base always on os.environ["STOREGOD_MODULES"] = "records,tools" assert has_module("records") and has_module("tools") assert brand() == "StoreGod" and set(active_scope()) == {"music", "tools"} os.environ["STOREGOD_MODULES"] = "records" assert brand() == "RecordGod" and "discogs_intake" in features() os.environ["STOREGOD_MODULES"] = "vault" # umbrella → multi-domain assert brand() == "VaultGod" and set(active_scope()) == {"comics", "tcg", "toys"} os.environ["STOREGOD_MODULES"] = "comics" # folded alias → vault assert has_module("vault") os.environ["STOREGOD_MODULES"] = "" assert brand() == "StoreGod" and active_scope() == ["*"] del os.environ["STOREGOD_MODULES"] print("ok") if __name__ == "__main__": _selfcheck()