P
PUGUH

TypeScript SDK

Paket NPM @arsaka/puguh-sdk menyediakan client TypeScript bertipe penuh untuk mengintegrasikan layanan infrastruktur ARSAKA PUGUH ke aplikasi frontend dan Node.js Anda. SDK ini dilengkapi 6 modul yang mencakup autentikasi, organisasi, IAM, audit/control, aplikasi, dan webhooks.

Instalasi

bash
npm install @arsaka/puguh-sdk
# or
bun add @arsaka/puguh-sdk

Mulai Cepat

typescript
import { AuthClient, OrganizationClient, IAMClient } from '@arsaka/puguh-sdk';

const auth = new AuthClient({ baseUrl: 'https://api-puguh.arsaka.io' });

// Login
const { access_token } = await auth.login({
  email: 'user@example.com',
  password: 'password',
});

// Use authenticated clients
const orgs = new OrganizationClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
});

const orgList = await orgs.list();
// orgList = { items: OrganizationWithMembership[], total: number }
console.log(orgList.items[0].organization.name);

Modul

SDK diorganisasi dalam 6 modul client, masing-masing memetakan ke layanan inti PUGUH. Import hanya client yang Anda butuhkan — setiap modul mendukung tree-shaking.

1. AuthClient

Menangani autentikasi pengguna, siklus hidup token, magic link, dan multi-factor authentication.

typescript
import { AuthClient } from '@arsaka/puguh-sdk';

const auth = new AuthClient({ baseUrl: 'https://api-puguh.arsaka.io' });

// Register a new user
await auth.register({ email: 'new@example.com', password: 'securePass123' });

// Login and receive tokens
const { access_token, refresh_token } = await auth.login({
  email: 'new@example.com',
  password: 'securePass123',
});

// Refresh an expired access token
const { access_token: newToken } = await auth.refreshToken({ refresh_token });

// Magic link authentication
await auth.requestMagicLink({ email: 'user@example.com' });
const session = await auth.verifyMagicLink({ token: 'magic-link-token' });

// Multi-factor authentication
const { secret, qr_code_url } = await auth.setupMFA({ type: 'totp' });
await auth.confirmMFA({ code: '123456' });

Metode: login, register, refreshToken, requestMagicLink, verifyMagicLink, setupMFA, confirmMFA

2. OrganizationClient

Kelola organisasi, keanggotaan, dan undangan. PUGUH menggunakan model organisasi bergaya GitHub di mana setiap pengguna memiliki workspace personal ditambah organisasi tim.

typescript
import { OrganizationClient } from '@arsaka/puguh-sdk';

const orgs = new OrganizationClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
});

// List organizations the user belongs to
// Returns { items: OrganizationWithMembership[], total: number }
const orgList = await orgs.list();
console.log(orgList.items, orgList.total);

// Create a new team organization
const org = await orgs.create({ name: 'Acme Corp', slug: 'acme-corp' });

// Get organization details
const details = await orgs.get(org.organization.organizationId);

// Update organization settings
await orgs.update(org.organization.organizationId, { name: 'Acme Corporation' });

// Member management
const members = await orgs.listMembers(org.organization.organizationId);
await orgs.inviteMember(org.organization.organizationId, {
  email: 'colleague@example.com', role: 'member',
});
await orgs.removeMember(org.organization.organizationId, memberId);

// Delete organization
await orgs.delete(org.organization.organizationId);

Metode: list, get, create, update, delete, listMembers, inviteMember, removeMember

3. IAMClient

Identity and Access Management — kelola pengguna, role, dan penugasan role di organisasi Anda.

typescript
import { IAMClient } from '@arsaka/puguh-sdk';

const iam = new IAMClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// List users in the organization
// Returns { items: IamUser[], total, page, pageSize, hasNext, hasPrev }
const users = await iam.listUsers();
console.log(users.items, users.total);

// Get a specific user
const user = await iam.getUser(userId);

// Get user stats
const stats = await iam.getUserStats();
console.log(stats.total, stats.active);

// Role management
const roles = await iam.listRoles();
const customRole = await iam.createRole({
  name: 'billing_admin',
  description: 'Can manage billing',
  permissions: ['billing.invoices.read', 'billing.invoices.create'],
});
await iam.assignRole(userId, customRole.roleId);

// Permission matrix (roles x permissions)
const matrix = await iam.getPermissionMatrix();
console.log(matrix.roles, matrix.permissions); // permissions is string[]

Metode: listUsers, getUserStats, getUser, getUserRoles, listRoles, getRole, getRolePermissions, createRole, updateRole, deleteRole, assignRole, revokeRole, listServiceAccounts, listPermissions, getPermissionMatrix

4. ControlClient

Akses audit trail, event log, dead-letter queue, dan metrik platform. Semua path berada di bawah /control.

typescript
import { ControlClient } from '@arsaka/puguh-sdk';

const control = new ControlClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// Audit logs — who did what, when
// Returns { items: AuditRecord[], total, page, limit, pages }
const audit = await control.listAudit({
  resourceType: 'user',
  limit: 50,
});

// Single audit record
const record = await control.getAudit(auditId);

// Event stream
// Returns { items: SystemEvent[], total, page, limit, pages }
const events = await control.listEvents({ limit: 50 });

// Dead-letter queue — failed event deliveries
// DLQ items have totalAttempts (not retryCount)
const dlq = await control.listDLQ();

// Metrics trends (7d, 30d, or 90d)
const trends = await control.getMetricsTrends('30d');

Metode: listAudit, getAudit, listEvents, getEvent, listDLQ, getDLQ, retryDLQ, getMetricsTrends

5. ApplicationClient

Kelola aplikasi dalam organisasi. Setiap organisasi dapat memiliki beberapa aplikasi dengan daftar anggota masing-masing.

typescript
import { ApplicationClient } from '@arsaka/puguh-sdk';

const apps = new ApplicationClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// List all applications
// Returns { applications: Application[], total, limit, offset }
const appList = await apps.list();

// Create a new application
const app = await apps.create({ name: 'MANTRA Dashboard', slug: 'mantra' });

// Get application details
const appDetails = await apps.get(app.id);

// Update application
await apps.update(app.id, { name: 'MANTRA Production' });

// Manage application members
const appMembers = await apps.listMembers(app.id);

// Delete application
await apps.delete(app.id);

Metode: list, get, create, update, delete, listMembers

6. WebhookClient

Konfigurasi webhook endpoint dan kelola pengiriman event. Gunakan webhooks untuk menerima notifikasi real-time saat event terjadi di organisasi Anda.

typescript
import { WebhookClient } from '@arsaka/puguh-sdk';

const webhooks = new WebhookClient({
  baseUrl: 'https://api-puguh.arsaka.io',
  accessToken: access_token,
  organizationId: 'org-uuid',
});

// List registered webhooks
// Returns { items: Webhook[], total, page, limit, pages }
const list = await webhooks.list();

// Create a new webhook (secret only returned on create)
const webhook = await webhooks.create({
  url: 'https://example.com/webhooks/puguh',
  eventTypes: ['user.registered', 'organization.created'],
  description: 'My webhook endpoint',
});
console.log(webhook.secret); // Save this — not returned again

// Get a webhook (no secret in response)
const wh = await webhooks.get(webhook.id);

// Update webhook
await webhooks.update(webhook.id, {
  description: 'Updated description',
  isActive: false,
});

// List recent deliveries for a webhook
const deliveries = await webhooks.listDeliveries(webhook.id);

// Test webhook (sends ping)
const testResult = await webhooks.test(webhook.id);
console.log(testResult.deliveryId, testResult.status);

// Delete webhook
await webhooks.delete(webhook.id);

Metode: create, list, get, update, delete, listDeliveries, test

Tipe TypeScript

SDK mengekspor definisi tipe lengkap untuk semua entitas API. Gunakan untuk mengetik kode aplikasi Anda:

typescript
import type {
  OrganizationListResponse,
  OrganizationWithMembership,
  IamUser,
  AuditRecord,
  LoginResponse,
  PuguhError,
} from '@arsaka/puguh-sdk';

// Type your handlers
function handleOrg(item: OrganizationWithMembership): void {
  console.log(item.organization.name, item.role);
}

// Organization list is paginated
function showOrgs(response: OrganizationListResponse): void {
  console.log(response.items);       // OrganizationWithMembership[]
  console.log(response.total);       // number
}

Penanganan Error

Semua metode client melempar PuguhError pada response non-2xx. Error menyertakan kode status HTTP, pesan yang mudah dibaca, dan detail opsional.

typescript
import { PuguhError } from '@arsaka/puguh-sdk';

try {
  const orgs = await client.list();
} catch (error) {
  if (error instanceof PuguhError) {
    console.error(error.statusCode);  // HTTP status code (e.g. 401, 403, 404)
    console.error(error.message);     // Human-readable error message
    console.error(error.errorCode);   // Error code string (e.g. 'UNAUTHORIZED')
    console.error(error.details);     // Optional additional error details
  }
}

Opsi Konfigurasi

Semua konstruktor client menerima objek konfigurasi yang sama:

OpsiTipeWajibDeskripsi
baseUrl string Ya URL API PUGUH (contoh https://api-puguh.arsaka.io)
accessToken string Tidak* JWT access token dari AuthClient.login(). Wajib untuk semua client kecuali AuthClient.
apiKey string Tidak API key untuk autentikasi server-to-server (alternatif dari accessToken)
organizationId string Tidak UUID organisasi default untuk operasi berskala
applicationId string Tidak UUID aplikasi default untuk operasi berskala
timeout number Tidak Timeout request dalam milidetik (default: 30000)

Catatan

AuthClient tidak memerlukan accessToken karena digunakan untuk mendapatkan token. Semua client lain memerlukan accessToken atau apiKey untuk autentikasi.

Environment Variables

Setup .env yang direkomendasikan untuk penggunaan server-side:

bash
PUGUH_BASE_URL=https://api-puguh.arsaka.io
PUGUH_API_KEY=pk_live_your_api_key
PUGUH_ORGANIZATION_ID=your-org-uuid
PUGUH_APPLICATION_ID=your-app-uuid
typescript
import { OrganizationClient } from '@arsaka/puguh-sdk';

const client = new OrganizationClient({
  baseUrl: process.env.PUGUH_BASE_URL!,
  apiKey: process.env.PUGUH_API_KEY!,
  organizationId: process.env.PUGUH_ORGANIZATION_ID!,
});

Praktik Terbaik

1. Jangan Pernah Hardcode Kredensial

typescript
// Bad
const auth = new AuthClient({ baseUrl: 'https://api-puguh.arsaka.io' });
const { access_token } = await auth.login({
  email: 'admin@arsaka.dev',
  password: 'hardcoded-password',  // Never do this
});

// Good — use environment variables
const { access_token } = await auth.login({
  email: process.env.PUGUH_EMAIL!,
  password: process.env.PUGUH_PASSWORD!,
});

2. Gunakan Ulang Instance Client

Buat instance client sekali dan gunakan ulang. Setiap instance mempertahankan konfigurasi dan status autentikasinya.

typescript
// Create once at application startup
const orgs = new OrganizationClient({
  baseUrl: process.env.PUGUH_BASE_URL!,
  accessToken: access_token,
});

// Reuse across your application
export { orgs };

3. Tangani Refresh Token

Access token kedaluwarsa. Gunakan refreshToken untuk mendapatkan access token baru tanpa meminta pengguna login ulang.

typescript
import { AuthClient, PuguhError } from '@arsaka/puguh-sdk';

async function withTokenRefresh<T>(
  fn: () => Promise<T>,
  auth: AuthClient,
  refreshToken: string,
): Promise<T> {
  try {
    return await fn();
  } catch (error) {
    if (error instanceof PuguhError && error.statusCode === 401) {
      const { access_token } = await auth.refreshToken({
        refresh_token: refreshToken,
      });
      // Update client token and retry
      client.accessToken = access_token;
      return await fn();
    }
    throw error;
  }
}

4. Gunakan Penanganan Error yang Tepat

typescript
import { PuguhError } from '@arsaka/puguh-sdk';

try {
  const org = await orgs.get(orgId);
} catch (error) {
  if (error instanceof PuguhError) {
    switch (error.statusCode) {
      case 401:
        // Token expired — refresh and retry
        break;
      case 403:
        // Insufficient permissions
        break;
      case 404:
        // Organization not found
        break;
      case 429:
        // Rate limited — back off and retry
        break;
      default:
        // Unexpected error
        throw error;
    }
  }
}

Terkait