Skip to content

API Node.js

Impor

ts
import { AingDB } from "@triyatna/aingdb";

AingOptions

ts
type AingOptions = {
  /** Jalur file absolut atau relatif untuk database (misalnya ./app.adb) */
  path: string;

  /** Enkripsi saat istirahat (diaktifkan secara default dalam contoh CLI). */
  encryption: {
    enabled: boolean; // must be true for encrypted files
    /** "aes-256-gcm" | "chacha20-poly1305" | "xchacha20-poly1305" */
    algorithm: string;
    /** "scrypt" | "argon2id" */
    kdf: string;
    /** Passphrase used to derive the encryption key (KDF). */
    passphrase: string;
  };

  /** Optional audit log (off by default). */
  audit?: {
    enabled?: boolean; // default: false
    file?: string; // defaults to <path>.audit.log when enabled
  };

  /** Optional observability hooks. */
  observability?: {
    prometheus?: {
      enabled?: boolean; // default: false
      port?: number; // e.g. 9108
    };
  };
};

Penyesuaian Scrypt (opsional)

AingDB mengatur penggunaan memori scrypt dan akan menurunkan faktor kerja secara otomatis pada sistem yang memiliki sumber daya terbatas. Anda dapat menyesuaikan perilaku ini melalui variabel lingkungan:

  • AINGDB_SCRYPT_N (default: 1<<14)
  • AINGDB_SCRYPT_R (default: 8)
  • AINGDB_SCRYPT_P (default: 1)
  • AINGDB_SCRYPT_MAXMEM (default: 67108864 = 64 MiB)

Buka atau Buat Basis Data

Membuka jalur yang tidak ada akan membuat file baru yang terenkripsi dan sudah diinisialisasi.

ts
import { AingDB } from "@triyatna/aingdb";

const db = await AingDB.open({
  path: "./app.adb",
  encryption: {
    enabled: true,
    algorithm: "aes-256-gcm",
    kdf: "scrypt",
    passphrase: "change-me",
  },
  // audit is disabled by default.
  // enable only when needed:
  // audit: { enabled: true }
});

Catatan: Jika Anda sebelumnya membuat file dengan CLI, gunakan kembali passphrase yang sama. Jika tidak, file tidak dapat didekripsi.


Lifecycle

ts
await db.close();

Schema API

AingDB melacak skema tabel ringan yang dapat Anda ubah saat runtime. Skema ini digunakan untuk hook TTL/masking dan alat bantu pengindeksan.

ts
type Column = {
  name: string;
  /** "string" | "number" | "boolean" | "uuid" | "text" | "json" | "vector" | "date" | "datetime" | "blob" */
  type: string;
  primary?: boolean;
  unique?: boolean;
  notNull?: boolean;
  /** if true, values are masked when read() returns rows */
  masked?: boolean;
};

type TableSchema = {
  name: string;
  columns: Column[];
  /** Optional TTL configuration */
  ttl?: { column: string };
};

Buat / Hapus / Ubah / Deskripsikan

ts
// Create
await db.schema.create({
  name: "users",
  columns: [
    { name: "id", type: "uuid", primary: true },
    { name: "email", type: "string", unique: true },
    { name: "name", type: "text" },
    { name: "age", type: "number" },
  ],
});

// List all schemas
const all = db.schema.all();

// Describe one
const usersSchema = await db.schema.describe("users");

// Alter: add a column
await db.schema.alterAddColumn("users", { name: "city", type: "string" });

// Alter: drop a column
await db.schema.alterDropColumn("users", "city");

// Drop
await db.schema.drop("users");

Operasi alter menyimpan skema segera melalui catatan checkpoint internal.


Tables (CRUD)

Each table exposes a focused set of methods through db.table(name).

ts
type Table<T = any> = {
  insert(row: T): Promise<void>;
  upsert(row: T): Promise<void>; // insert-or-replace by id
  delete(where: Partial<T>): Promise<number>; // deletes by primary key or equality match
  find(q?: Partial<T> | ((row: T) => boolean)): Promise<T[]>;
};

Deteksi kunci primer

  • Jika skema memiliki kolom dengan primary: true, kolom tersebut akan digunakan sebagai kunci.
  • Jika tidak, AingDB menebak kolom id/_id jika ada.
  • Jika tidak ada kunci yang diberikan, sebuah UUID akan dibuat dan disisipkan.

Insert / Upsert / Find / Delete

ts
const users = db.table<{
  id: string;
  email: string;
  name: string;
  age?: number;
}>("users");

// Insert (key auto-detected)
await users.insert({ id: "u1", email: "[email protected]", name: "Alice", age: 31 });
await users.insert({ id: "u2", email: "[email protected]", name: "Bob", age: 22 });

// Upsert (replaces same primary key)
await users.upsert({ id: "u2", email: "[email protected]", name: "Bobby", age: 23 });

// Find by equality object
const all = await users.find(); // all rows
const adults = await users.find({ age: 23 }); // exact match

// Find by predicate (use JS power like regex/range)
const older = await users.find((r) => (r.age ?? 0) >= 30);
const namedA = await users.find((r) => /^a/i.test(r.name));

// Delete by key/object
await users.delete({ id: "u1" }); // returns number deleted

Tip: Menggunakan fungsi predikat memberi Anda penyaringan yang fleksibel (regex, rentang, logika komposit) bahkan jika Anda tidak menggunakan SQL.


Maintenance & SQL

Compact (VACUUM)

AingDB bersifat append‑only; compact() menulis ulang file minimal dengan keadaan saat ini.

ts
await db.compact();

Di Windows, kompaksi melakukan penggantian nama yang aman. Jika Anda melihat EPERM saat akses berat, pastikan DB tidak dibuka di tempat lain, atau coba lagi dengan penundaan singkat.

Analyze (stats)

ts
await db.analyze(); // placeholder hook (no-op in the minimal engine)

Minimal SQL Convenience

Kenyamanan SQL Minimal

db.query(sql) mendukung subset pragmatis yang berguna untuk skrip cepat:

  • INSERT INTO t (a,b) VALUES (...), (...);
  • SELECT * FROM t [WHERE a='x' AND b=123];
  • UPDATE t SET a=..., b=... [WHERE a='x' AND ...];
  • DELETE FROM t [WHERE a='x' AND ...];
ts
// INSERT
await db.query(`
  INSERT INTO users (id,email,name,age)
  VALUES ('u3','[email protected]','Carol',29), ('u4','[email protected]','Dan',19);
`);

// SELECT
const rows = await db.query(`SELECT * FROM users WHERE age = 29;`);
// rows is an array of objects

// UPDATE
await db.query(`UPDATE users SET name='Daniel' WHERE id='u4';`);

// DELETE
await db.query(`DELETE FROM users WHERE id='u3';`);

Parser SQL menerima kondisi kesetaraan sederhana dengan AND. Untuk kueri yang lebih kompleks, gunakan API tabel dengan fungsi predikat, atau tambahkan lapisan query Anda sendiri.


End‑to‑End Example

ts
import { AingDB } from "@triyatna/aingdb";

async function main() {
  const db = await AingDB.open({
    path: "./demo.adb",
    encryption: {
      enabled: true,
      algorithm: "aes-256-gcm",
      kdf: "scrypt",
      passphrase: "change-me",
    },
    // audit is disabled by default; enable only when needed
    // audit: { enabled: true }
  });

  // 1) Schema
  await db.schema.create({
    name: "users",
    columns: [
      { name: "id", type: "uuid", primary: true },
      { name: "email", type: "string", unique: true },
      { name: "name", type: "text" },
      { name: "age", type: "number" },
    ],
  });

  // 2) Table CRUD
  const users = db.table<{
    id: string;
    email: string;
    name: string;
    age?: number;
  }>("users");
  await users.insert({ id: "u1", email: "[email protected]", name: "Alice", age: 31 });
  await users.insert({ id: "u2", email: "[email protected]", name: "Bob", age: 22 });
  await users.upsert({ id: "u2", email: "[email protected]", name: "Bobby", age: 23 });

  const all = await users.find();
  console.log("All users:", all);

  const twentySomethings = await users.find(
    (r) => (r.age ?? 0) >= 20 && (r.age ?? 0) < 30
  );
  console.log("20s:", twentySomethings);

  // 3) SQL convenience
  await db.query(
    `INSERT INTO users (id,email,name,age) VALUES ('u3','[email protected]','Carol',29);`
  );
  const sel = await db.query(`SELECT * FROM users WHERE age = 29;`);
  console.log("SQL select age=29:", sel);

  // 4) Maintenance
  await db.compact();
  await db.close();
}

main().catch((e) => {
  console.error(e);
  process.exit(1);
});

Notes & Best Practices

  • Enkripsi: Selalu gunakan passphrase yang kuat. Mengganti passphrase memerlukan re‑enkripsi (tidak dibahas di sini).
  • Audit: Dinonaktifkan secara default demi performa. Aktifkan hanya saat Anda membutuhkan jejak yang tidak dapat diubah.
  • Kompaksi: Lakukan secara berkala atau setelah perubahan besar untuk mengembalikan ruang.
  • Cadangan: Ambil snapshot dengan menyalin file .adb ketika proses sedang tidak aktif, atau gunakan alat cadangan tingkat OS Anda.
  • Indeks & Pencarian: Mesin minimal menyediakan hook sederhana untuk registri FTS/RTREE/Vector, yang defaultnya tidak melakukan apa‑apa. Anda dapat memasang implementasi sendiri jika diperlukan.

API Surface Summary

ts
class AingDB {
  static open(opts: AingOptions): Promise<AingDB>;
  close(): Promise<void>;

  schema: {
    create(s: TableSchema): Promise<void>;
    all(): TableSchema[];
    drop(name: string): Promise<void>;
    alterAddColumn(table: string, col: Column): Promise<void>;
    alterDropColumn(table: string, colName: string): Promise<void>;
    describe(table: string): Promise<TableSchema | null>;
  };

  table<T = any>(
    name: string
  ): {
    insert(row: T): Promise<void>;
    upsert(row: T): Promise<void>;
    delete(where: Partial<T>): Promise<number>;
    find(q?: Partial<T> | ((r: T) => boolean)): Promise<T[]>;
  };

  analyze(table?: string): Promise<void>;
  compact(): Promise<void>;

  // Minimal SQL helpers
  query(sql: string): Promise<any>;
  explain(sql: string): Promise<any>; // debug/placeholder
  merge(sql: string): Promise<any>; // placeholder
}

Dirilis di bawah Lisensi MIT.