0.1.2Updated 10 days ago
# @draco/connect

A minimal, framework-agnostic TypeScript utility for connecting to a **Draco** backend from Deno or any TypeScript runtime that supports URL imports.
It handles authentication cookies, permission announcement, user lookups, and event logging with barebones functionality.

Further functionality and automatic routing will be provided at a later date by:
- @draco/connect-fresh
- @draco/connect-oak
- @draco/connect-hono

---

## Installation

```ts
// Import directly via Viapak
import { Draco } from "https://viapak.xyz/@draco/connect";
```

---

## Configuration

Before using any Draco functions, configure it once on application startup:

```ts
await Draco.Configure({
  host: "https://draco.galaxi.online",
  api_key: "your-api-key",
  permissions: Permissions,
  cookie_name: "draco_uuid"
});
```

**Environment variable fallbacks:**

You can leave out any of `host`, `api_key` or `cookie_name` in the confuguration as long as you define the following in your environment.

| Parameter     | Environment Fallback  |
|---------------|-----------------------|
| `host`        | `DRACO_HOST`          |
| `api_key`     | `DRACO_API_KEY`       |
| `cookie_name` | `DRACO_API_KEY`       |

---

## Using the Available Functions

### 1. **`AnnouncePermissions`**

Synchronize your app’s declared permissions with Draco.
Called automatically in `Configure()`, but can be triggered manually.

---

### 2. **`GetCookie`**

Retrieve the Draco cookie UUID from a `Request` object.

```ts
const cookie_uuid = Draco.GetCookie(request);
```

---

### 3. **`CookieRedirect`**

Initialize a new cookie if missing and redirect back to the same URL.
✅ Use this at the start of a request flow when a Draco UUID is mandatory.

```ts
if (!Draco.GetCookie(request)) return Draco.CookieRedirect(request);
```

---

### 4. **`LoginRedirect`**

Kick off a login flow with Draco.

```ts
return await Draco.LoginRedirect(cookie_uuid, "https://my-app.example.com/destination-after-login");
```

---

### 5. **`LogoutRedirect`**

Clear the cookie and return a redirect `Response`.

```ts
return Draco.LogoutRedirect();
```

---

### 6. **`GetUser`**

Fetch the current user profile. Uses a built-in memory cache unless `force` is true.

```ts
const user = await Draco.GetUser(request);
// or
const user = await Draco.GetUser(cookie_uuid);
```

💡 **When to use:** At the start of any request needing authenticated user data.

---

### 7. **`LogEvent`**

Record a user activity/event in Draco.

```ts
await Draco.LogEvent(request, {
  type: "user_added",
  information: JSON.stringify({ id: 1234 })
});
```

💡 **When to use:** Audit logs or privileged user actions.

---

## Permissions

### Optimal Structure

Use a flat object mapping human-readable constants to permission keys:

```ts
enum Permissions {
  VIEW_DASHBOARD = "view_dashboard",
  EDIT_USERS = "edit_users",
};
```

### Announcing Permissions

Happens in `Configure()` automatically, but you can call manually:

```ts
await Draco.AnnouncePermissions();
```

### Example

**Route protection:**

```ts
if (!user.can(Draco.Permissions.VIEW_DASHBOARD)) {
  return new Response("Forbidden", { status: 403 });
}
```

**Feature flags:**

```tsx
<div>
  { user.can(Draco.Permissions.EDIT_USERS) &&
    <EditButton user={user} />
  }
</div>
```

---

## Suggested Patterns

**1. Request Middleware (Oak/Hono/Fresh adapters)**

```ts
export async function requireUser(ctx) {
  const draco_uuid = await Draco.GetCookie(ctx.req.raw);
  const user = await Draco.GetUser(draco_uuid);
  if (!user) return await Draco.LoginRedirect(draco_uuid, ctx.req.url);
  ctx.state.user = user;
}
```

**2. Event Logging Wrapper**

```ts
function logAction(request: Request, type: string, info: Record<string, any>) {
  return Draco.LogEvent(request, { type, information: info });
}
```

**3. Force Cookie Initialization**

```ts
if (!Draco.GetCookie(req)) return Draco.CookieRedirect(req);
```