npm package: @qrvey/app-state
Description: ![install size](https://packagephobia.now.sh/badge?p=%40qrvey%2Fapp-state) ![coverage](https://img.shields.io/badge/unit_test_coverage-100%25-brightgreen)
Latest version: 1.0.1-888

# @qrvey/app-state

![install size](https://packagephobia.now.sh/badge?p=%40qrvey%2Fapp-state)
![coverage](https://img.shields.io/badge/unit_test_coverage-100%25-brightgreen)

**@qrvey/app-state** is a utility library for checking the current state of a Qrvey application. It determines whether an application is locked (i.e., fully installed and ready) based on the caller's identity and the application's lifecycle state.

## Installation

```bash
npm install @qrvey/app-state
```

## API Documentation

### `isLocked(params, identity)`

Checks whether an application is locked based on the provided identity or its state.

-   **Parameters**
    -   `params` (`object`) – The parameters for checking the application lock status.
        -   `params.appId` (`string`) – The ID of the application to check.
        -   `params.userId` (`string`) – The ID of the user requesting the application lock status.
    -   `identity` (`Identity`) – A token string or an `IdentityAsObject`.
-   **Returns** `Promise<boolean>` – Resolves to `true` when any of the following conditions are met:
    -   The identity's `token_client` is `SYSTEM_SERVICE`.
    -   The application's state is `FINISHED`.

### Types

#### `AppState` (enum)

| Value        | Description                              |
| ------------ | ---------------------------------------- |
| `INSTALLING` | The application is being installed.      |
| `UPDATING`   | The application is being updated.        |
| `FINISHED`   | The application is fully installed.      |

#### `AppStateResponse`

| Property | Type       | Default              | Description                         |
| -------- | ---------- | -------------------- | ----------------------------------- |
| `state`  | `AppState` | `FINISHED`  | The current state of the application. |

#### `TokenClient` (enum)

| Value            | Description                          |
| ---------------- | ------------------------------------ |
| `SYSTEM_SERVICE` | A system-level service token.        |
| `USER`           | A regular user token.                |

#### `Identity`

A union type that accepts either:

-   A **string** (raw token) – decoded internally via the Token Service.
-   An **`IdentityAsObject`** – an object with a `token_client` property (defaults to `USER`) and any additional properties.

## Middleware

The package provides framework-specific middleware that checks if an application is locked before processing a request. When locked, the middleware short-circuits with a `423` response.

### `appStateMiddleware.express.isAppLocked`

Express middleware. Expects `req.user.id`, `req.params.appId` (or `appid`), and optionally `req.headers.authorization`.

```ts
import { appStateMiddleware } from '@qrvey/app-state';

router.use('/apps/:appId/*', appStateMiddleware.express.isAppLocked);
```

### `appStateMiddleware.fastify.isAppLocked`

Fastify pre-handler hook with the same behavior.

```ts
import { appStateMiddleware } from '@qrvey/app-state';

fastify.addHook('preHandler', appStateMiddleware.fastify.isAppLocked);
```

### Middleware behavior

```
Request
  │
  ├─ No identity & no Authorization header → 423 (deny by default)
  │
  ├─ Identity is an API key (no `exp` claim)
  │    └─ Tags identity with `qv_token_client: SYSTEM_SERVICE`
  │
  ├─ ApplicationValidator.isLocked() → true  → 423
  │
  └─ ApplicationValidator.isLocked() → false → next() / done()
```

| Condition                              | Response |
| -------------------------------------- | -------- |
| App is locked                          | `423`    |
| No identity and no auth header         | `423`    |
| App is not locked                      | `next()` |
| Caller is `SYSTEM_SERVICE`             | `next()` |

### Errors

-   **Express**: forwards errors to `next(err)` for the framework error handler.
-   **Fastify**: forwards errors to `done(err)`, wrapping non-Error values.

## Example Usage

```ts
import { isLocked } from '@qrvey/app-state';

// Using a raw token string
const locked = await isLocked({ appId: 'app-123', userId: 'user-456' }, 'my-jwt-token');
console.log(locked); // true or false

const locked = await isLocked({ appId: 'app-123', userId: 'user-456' }, {
    token_client: 'USER',
});
console.log(locked); // true when app state is FINISHED
```