senac-chat
v0.1.0
Published
Client library for SENAC classroom chat over Supabase Realtime
Readme
senac-chat
TypeScript client for a classroom messaging exercise: each student builds a PWA, all apps share one Supabase project, and users identify themselves by phone number (WhatsApp-style, no OTP).
Install
yarn add senac-chat @supabase/supabase-jsTeacher setup (once)
- Create one Supabase project for the class.
- Run
supabase/schema.sqlin the SQL editor. - Enable Realtime for
public.profilesandpublic.messages. - Share with students only:
- project URL
- anon key
Never share the service-role key.
Student app usage
import { createSenacChat } from "senac-chat";
const chat = createSenacChat({
url: import.meta.env.VITE_SUPABASE_URL,
anonKey: import.meta.env.VITE_SUPABASE_ANON_KEY,
});
// First visit: register with phone as public ID
const me = await chat.register({
phone: "11999990000",
displayName: "Ana",
appName: "AnaChat", // optional: name of this student's PWA
});
localStorage.setItem("senac-phone", me.phone);
// Next visits: restore session
// await chat.identify(localStorage.getItem("senac-phone")!);
const contacts = await chat.listContacts();
await chat.sendMessage({
to: "11988880000", // classmate phone
text: "Hello from my PWA!",
});
const thread = await chat.getHistory({ with: "11988880000" });
const unsubscribe = chat.onMessage((message) => {
// Only messages where to_phone === my phone
console.log(message.from_phone, message.text);
});
unsubscribe();Data model
| Table | Role |
|-------|------|
| profiles | Registered users (phone PK, display_name, app_name) |
| messages | Direct messages (from_phone → to_phone, text) |
Phones are normalized to digits only (11 99999-0000 → 11999990000).
Security note
This is intentionally open for a supervised classroom exercise (no Auth / no OTP). Anyone with the anon key can register any phone. Do not use this pattern for a real production messenger.
Scripts
yarn typecheck
yarn test
yarn buildAPI
| Method | Description |
|--------|-------------|
| createSenacChat(config) | Creates the client |
| register({ phone, displayName, appName? }) | Registers / updates profile and starts session |
| identify(phone) | Restores session for an existing phone |
| getMe() | Current profile or null |
| listContacts() | All registered classmates |
| sendMessage({ to, text }) | Sends a direct message |
| getHistory({ with?, limit? }) | Inbox or 1:1 thread |
| onMessage(callback) | Live inbox for the current phone |
| normalizePhone(phone) | Digits-only helper |
