appwrite
Version:
Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API
151 lines (119 loc) • 3.89 kB
text/typescript
interface Root {}
interface Database { _db: any }
interface Collection { _coll: any }
interface Document { _doc: any }
interface TablesDB { _tdb: any }
interface Table { _tbl: any }
interface Row { _row: any }
interface Bucket { _bkt: any }
interface File { _file: any }
interface Func { _fn: any }
interface Execution { _exec: any }
interface Team { _team: any }
interface Membership { _mem: any }
interface Resolved { _res: any }
type Actionable = Document | Row | File | Team | Membership;
function normalize(id: string): string {
const trimmed = id.trim();
return trimmed === "" ? "*" : trimmed;
}
export class Channel<T> {
declare _type: T;
private constructor(private readonly segments: string[]) {}
private next<N>(segment: string, id?: string): Channel<N> {
const segments =
id === undefined
? [...this.segments, segment]
: [...this.segments, segment, normalize(id)];
return new Channel<N>(segments) as any;
}
private resolve(action: string): Channel<Resolved> {
return new Channel<Resolved>([...this.segments, action]) as any;
}
toString(): string {
return this.segments.join(".");
}
// --- DATABASE ROUTE ---
// Only available on Channel<Database>
collection(this: Channel<Database>, id?: string): Channel<Collection> {
// Default: wildcard collection ID
return this.next<Collection>("collections", id ?? "*");
}
// Only available on Channel<Collection>
document(this: Channel<Collection>, id?: string): Channel<Document> {
// Default: no document ID segment
return this.next<Document>("documents", id);
}
// --- TABLESDB ROUTE ---
table(this: Channel<TablesDB>, id?: string): Channel<Table> {
// Default: wildcard table ID
return this.next<Table>("tables", id ?? "*");
}
row(this: Channel<Table>, id?: string): Channel<Row> {
// Default: no row ID segment
return this.next<Row>("rows", id);
}
// --- BUCKET ROUTE ---
file(this: Channel<Bucket>, id?: string): Channel<File> {
// Default: no file ID segment
return this.next<File>("files", id);
}
// --- TERMINAL ACTIONS ---
// Restricted to the Actionable union
create(this: Channel<Actionable>): Channel<Resolved> {
return this.resolve("create");
}
update(this: Channel<Actionable>): Channel<Resolved> {
return this.resolve("update");
}
delete(this: Channel<Actionable>): Channel<Resolved> {
return this.resolve("delete");
}
// --- ROOT FACTORIES ---
static database(id: string = "*") {
return new Channel<Database>(["databases", normalize(id)]);
}
static execution(id: string = "*") {
return new Channel<Execution>(["executions", normalize(id)]);
}
static tablesdb(id: string = "*") {
return new Channel<TablesDB>(["tablesdb", normalize(id)]);
}
static bucket(id: string = "*") {
return new Channel<Bucket>(["buckets", normalize(id)]);
}
static function(id: string = "*") {
return new Channel<Func>(["functions", normalize(id)]);
}
static team(id: string = "*") {
return new Channel<Team>(["teams", normalize(id)]);
}
static membership(id: string = "*") {
return new Channel<Membership>(["memberships", normalize(id)]);
}
static account(): string {
return "account";
}
// Global events
static documents(): string {
return "documents";
}
static rows(): string {
return "rows";
}
static files(): string {
return "files";
}
static executions(): string {
return "executions";
}
static teams(): string {
return "teams";
}
static memberships(): string {
return "memberships";
}
}
// Export types for backward compatibility with realtime
export type ActionableChannel = Channel<Document> | Channel<Row> | Channel<File> | Channel<Execution> | Channel<Team> | Channel<Membership>;
export type ResolvedChannel = Channel<Resolved>;