npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@be-enlighten/enspace-sdk-vue

v0.11.2

Published

Vue 3 composables/plugin and Nuxt 4 module for the Enspace SDK.

Downloads

1,204

Readme

@be-enlighten/enspace-sdk-vue

Adapter Vue 3 e Nuxt 4 para o @be-enlighten/enspace-sdk-core.

| Entrypoint | O que entrega | |---|---| | @be-enlighten/enspace-sdk-vue | Plugin, setup com Keycloak e composables base (useEnspace, useEnspaceScoped, useAuth). | | .../query | Data layer sobre Pinia Colada: cache, dedup, mutations com invalidação, workspace ativo e formulários. | | .../realtime | Composables de eventos ao vivo (canais, eventos de usuário, notificações). | | .../ai | Composables para widgets de chat de AI (sessão com streaming, seleções, upload). | | .../nuxt | Módulo Nuxt 4 com auto-import. |

Instalação

pnpm add @be-enlighten/enspace-sdk-vue

pnpm add pinia @pinia/colada   # camada /query
pnpm add pusher-js             # camada /realtime
pnpm add ai @ai-sdk/vue        # camada /ai

vue@^3.5 é o único peer obrigatório. Todos os outros são opcionais e só necessários para a camada correspondente: pinia + @pinia/colada para a /query, pusher-js para a /realtime, ai + @ai-sdk/vue para a /ai, nuxt + @nuxt/kit para o módulo /nuxt.

Setup — Vue 3

Com api-key ou bearer o caminho é síncrono:

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { PiniaColada } from '@pinia/colada'
import { EnspacePlugin } from '@be-enlighten/enspace-sdk-vue'
import App from './App.vue'

const app = createApp(App)

// Pinia e Pinia Colada ANTES do EnspacePlugin — a /query depende deles.
app.use(createPinia())
app.use(PiniaColada)
app.use(EnspacePlugin, {
  baseUrl: import.meta.env.VITE_ENSPACE_BASE_URL,
  auth: { type: 'api-key', key: import.meta.env.VITE_ENSPACE_API_KEY },
})

app.mount('#app')

Com Keycloak, use setupEnspace antes do mount:

import { setupEnspace } from '@be-enlighten/enspace-sdk-vue'

const { client, keycloak } = await setupEnspace(app, {
  baseUrl: import.meta.env.VITE_ENSPACE_BASE_URL,
  keycloak: {
    url: import.meta.env.VITE_KEYCLOAK_URL,
    realm: import.meta.env.VITE_KEYCLOAK_REALM,
    clientId: import.meta.env.VITE_KEYCLOAK_CLIENT_ID,
  },
})

app.mount('#app')

O init do keycloak-js é assíncrono e o app.use() do Vue descarta o retorno do install, então app.use(EnspacePlugin, { keycloak }) lança um erro apontando para setupEnspace. Se o init do Keycloak falhar (servidor fora, silent check sem retorno), a app monta em modo degradado não autenticado: os provides existem, useAuth() funciona e login() redireciona para o SSO.

Para auth sem Keycloak também existe setupEnspaceSync(app, options), que rejeita a opção keycloak.

Adicione o helper de silent check ao public/ da app:

cp node_modules/@be-enlighten/enspace-sdk-vue/dist/auth/silent-check-sso.html public/

Opções do plugin

Além de todos os campos de EnspaceConfig do core (baseUrl, auth, workspace, language, retry, timeoutMs, hooks, realtime):

| Opção | Descrição | |---|---| | keycloak | Config do SSO por redirect. Mutuamente exclusiva com auth. | | keycloak.onSessionExpired | Política quando a sessão SSO morre de verdade (refresh rejeitado pelo IdP): 'login' (default — redireciona para o Keycloak), 'emit' (só sinaliza em useAuth().sessionExpired) ou 'none'. | | workspacePersistence | Persistência do workspace ativo: { persist, storageKey, storage }. |

O SDK também faz refresh proativo quando a aba volta a ficar visível (visibilitychange): após suspensão/background, o token é reavaliado antes da próxima request, em vez de descobrir a sessão morta só no primeiro 401.

app.use(EnspacePlugin, {
  baseUrl,
  auth,
  workspacePersistence: { persist: true, storageKey: 'app:ws', storage: sessionStorage },
})

Setup — Nuxt 4

export default defineNuxtConfig({
  modules: ['@be-enlighten/enspace-sdk-vue/nuxt'],
  enspace: {
    baseUrl: process.env.NUXT_PUBLIC_ENSPACE_BASE_URL!,
    auth: {
      type: 'keycloak',
      url: process.env.NUXT_PUBLIC_KEYCLOAK_URL!,
      realm: process.env.NUXT_PUBLIC_KEYCLOAK_REALM!,
      clientId: process.env.NUXT_PUBLIC_KEYCLOAK_CLIENT_ID!,
    },
    // Opcional — opções browser-only do keycloak-js (serializáveis):
    keycloakBrowser: {
      checkLoginIframe: false, // session-iframe legacy quebra com cookies 3rd-party
      onSessionExpired: 'login', // 'login' (default) | 'emit' | 'none'
    },
  },
})

O módulo instala @pinia/nuxt e @pinia/colada-nuxt, registra o plugin client e auto-importa os composables do adapter mais enspaceKeys, toFieldErrors, enspaceChannels e realtimeEvents do core. Config inválida falha no build, não no primeiro request.

Duas restrições do módulo:

  • hooks não é aceito. A config é publicada em runtimeConfig.public e funções não sobrevivem à serialização. Para observabilidade HTTP, use setupEnspace ou construa o client com createEnspace.
  • api-key e bearer expõem a credencial ao browser. A config vai para runtimeConfig.public, portanto para o payload HTML e o bundle client. Use apenas credenciais de escopo limitado, nunca com privilégio de admin.

SSR

O plugin do módulo roda em modo client: os provides do SDK não existem durante o render no servidor. useEnspace(), useEnspaceScoped(), useAuth() e toda a /query dependem do provide e não funcionam em SSR. Em páginas com SSR ligado, envolva os componentes que os usam em <ClientOnly> ou desligue o SSR da página (ssr: false).

Composables base

useEnspace()

Retorna o EnspaceClient injetado.

<script setup lang="ts">
import { useEnspace } from '@be-enlighten/enspace-sdk-vue'

const client = useEnspace()
const profile = await client.account.getProfile()
</script>

useEnspaceScoped({ workspace })

WorkspaceScope reativo, sem mutar o client. Aceita workspace como string, Ref ou getter.

import { ref } from 'vue'
import { useEnspaceScoped } from '@be-enlighten/enspace-sdk-vue'

const activeWorkspace = ref<string | undefined>('ws_abc')
const { client, scope } = useEnspaceScoped({ workspace: activeWorkspace })

// scope.value muda automaticamente quando activeWorkspace muda
await scope.value?.members.list()

useAuth()

Disponível quando o setup recebeu a opção keycloak.

import { useAuth } from '@be-enlighten/enspace-sdk-vue'

const {
  isAuthenticated, // ComputedRef<boolean>
  token, // ComputedRef<string | undefined>
  user, // ComputedRef<{ id, username, email? } | undefined>
  roles, // ComputedRef<string[]>
  sessionExpired, // ComputedRef<boolean> — sessão SSO morreu de verdade
  login, // (options?) => Promise<void>
  logout, // (options?) => Promise<void>
  updateToken, // (minValidity?: number) => Promise<string | undefined>
  hasRoles, // (roles: string[]) => boolean
} = useAuth()

await login({ redirectUri: `${location.origin}/dashboard` })
if (hasRoles(['admin'])) { /* ... */ }

A strategy injeta Authorization: Bearer <token> em cada request e faz refresh automático quando necessário. Se a sessão SSO morrer no IdP (refresh rejeitado), a política onSessionExpired decide a reação (default: redirect para o login) e sessionExpired sinaliza o estado para a UI.

Data layer (/query)

Importe de @be-enlighten/enspace-sdk-vue/query. O EnspaceClient continua sendo um objeto JS puro, sem cache nem reatividade: a /query é a camada reativa sobre ele.

useWorkspace() — workspace ativo

Estado compartilhado e instanciado uma vez: lista cacheada, activeReference persistido e dedup embutido.

import { useWorkspace } from '@be-enlighten/enspace-sdk-vue/query'

const { workspaces, activeReference, activeWorkspace, scope, setActive, ensureActive } = useWorkspace()

onMounted(() => ensureActive()) // N chamadas concorrentes = 1 fetch

Composables de resource

import { useCreateTask, useDeleteTask, useTasksList, useUpdateTask } from '@be-enlighten/enspace-sdk-vue/query'

// Listagem: cache, dedup e refetch automático quando os params mudam.
// Desabilitada sozinha enquanto não há workspace ativo, sem guard manual.
const { data: tasks, isLoading, refresh } = useTasksList({ _limit: 50, status: 'pending' })

// Mutations invalidam as queries afetadas e aguardam o refetch,
// então a UI nunca pisca dado velho.
const { mutateAsync: createTask, asyncStatus } = useCreateTask()
await createTask({ name: 'Revisar proposta' })

A cobertura acompanha os resources do client: account, apiKeys, workspaces, members, memberGroups, invites, roles, dictionaries, modelViews, uploads, types (+ items/fields), workflows (+ .executions/.versions/.nodes/.logs), ai (chats, inference, agents, models, documents, review), tasks, financial (user/admin/workspace), plans (admin/workspace) e communications (notificações, tipos, comments e threads).

useEnspaceQuery / useEnspaceMutation

Para qualquer chamada sem composable dedicado, combine o foundation com enspaceKeys do core:

import { enspaceKeys } from '@be-enlighten/enspace-sdk-core'
import { useEnspaceQuery, useWorkspace } from '@be-enlighten/enspace-sdk-vue/query'

const { activeReference } = useWorkspace()

const { data: groups } = useEnspaceQuery({
  key: () => enspaceKeys.workspace(activeReference.value!).memberGroups.list({ _limit: 50 }),
  query: ({ client }) =>
    client.workspaces.workspace(activeReference.value!).memberGroups.list({ _limit: 50 }),
  enabled: () => activeReference.value !== undefined,
})
import { useEnspaceMutation } from '@be-enlighten/enspace-sdk-vue/query'

const { mutateAsync } = useEnspaceMutation({
  mutation: (id: number, { client }) =>
    client.workspaces.workspace(wsId).roles.delete(id),
  invalidate: () => enspaceKeys.workspace(wsId).roles.root, // prefixo: invalida lists + items
  // awaitInvalidation: false → invalida em background
})

useEnspaceForm

Validação client com o schema da própria API, re-exportado pelo pacote de schemas, mais erros do servidor mapeados para campos automaticamente:

import { TaskCreateRequest } from '@be-enlighten/enspace-sdk-schemas'
import { useEnspaceForm } from '@be-enlighten/enspace-sdk-vue/query'

const { state, saving, fieldErrors, formErrors, fieldError, submit, reset } = useEnspaceForm({
  schema: TaskCreateRequest.pick({ name: true, priority: true, due_date: true }),
  initial: { name: '', priority: 'normal', due_date: '' },
  submit: data => createTask(data), // ValidationError da API vira fieldErrors
})

// Template: v-model="state.name", :error="fieldError('name')", :loading="saving"
  • O erro de um campo limpa sozinho quando o campo é editado.
  • Campos com z.coerce/z.preprocess (input unknown): passe o terceiro genérico com a shape do estado, useEnspaceForm<typeof schema, Task, FormState>({ ... }).
  • Com Nuxt UI, mapeie fieldErrors para a prop :errors do UForm ({ name, message }[]).

Cache keys

enspaceKeys vem do core e é hierárquica: invalidar um prefixo invalida tudo abaixo dele.

import { enspaceKeys } from '@be-enlighten/enspace-sdk-core'

enspaceKeys.tasks.lists() // todas as listagens de tasks
enspaceKeys.workspace('ws_1').tasks.list({ _limit: 50 })
enspaceKeys.workspace('ws_1').tasks.root // lists + count + items
enspaceKeys.workspaces.byId('ws_1') // tudo do workspace, em cascata

list()list({})list({ campo: undefined }), e workspace(id).rootworkspaces.byId(id).

Realtime (/realtime)

Importe de @be-enlighten/enspace-sdk-vue/realtime. Requer realtime: { appKey, host, port } no setup do plugin (ou enspace.realtime no nuxt.config.ts) e pusher-js instalado. Sem config de realtime os composables são no-op com um warn único: nada quebra.

Todos são SSR-safe e fazem cleanup no unmount.

useRealtimeNotifications()

Notificações ao vivo do usuário autenticado. Invalida o cache de notificações da /query a cada evento. Chame uma vez, no layout raiz.

import { useRealtimeNotifications } from '@be-enlighten/enspace-sdk-vue/realtime'

const toast = useToast()

useRealtimeNotifications({
  onNotification: n => toast.add({ title: n.title, description: n.body ?? undefined }),
  onRead: ({ references }) => console.debug('lidas', references),
  onDismissed: ({ reference }) => console.debug('dispensada', reference),
})

Opções: enabled (default true), invalidate (default true), throttleMs (default 1000, janela só da invalidação de cache), onNotification, onRead, onDismissed. Retorna { enabled, state, isBound, lastEventAt }. Callbacks nunca são throttleados, então toasts saem na hora.

useRealtimeChannel()

Canal privado de entidade, com nome reativo.

import { enspaceChannels, enspaceKeys, realtimeEvents } from '@be-enlighten/enspace-sdk-core'
import { useWorkspace } from '@be-enlighten/enspace-sdk-vue/query'
import { useRealtimeChannel } from '@be-enlighten/enspace-sdk-vue/realtime'
import { useQueryCache } from '@pinia/colada'

const { activeReference } = useWorkspace()
const queryCache = useQueryCache()

const { channelName, isSubscribed, lastEventAt } = useRealtimeChannel({
  channel: () => activeReference.value ? enspaceChannels.tasksList(activeReference.value) : undefined,
  events: realtimeEvents.tasks,
  onEvent: () => void queryCache.invalidateQueries({
    key: enspaceKeys.workspace(activeReference.value!).tasks.root,
  }),
})

Opções: channel (obrigatório; null/undefined deixa o composable em espera), events (obrigatório, estático por instância), onEvent, enabled (default true), throttleMs (default 3000, leading + trailing; 0 desativa). O subscribe acontece quando channel resolve e enabled é true; a troca de nome ou enabled: false derruba a assinatura.

useRealtimeUserEvents()

Eventos do canal do usuário autenticado, sem nome de canal.

import { realtimeEvents } from '@be-enlighten/enspace-sdk-core'
import { useRealtimeUserEvents } from '@be-enlighten/enspace-sdk-vue/realtime'

const { isBound, lastEventAt } = useRealtimeUserEvents({
  events: [realtimeEvents.user.workspaceJoined, realtimeEvents.user.workspaceRemoved],
  onEvent: (event, payload) => console.log(event, payload),
})

useRealtimeClient()

Estado reativo da conexão, para indicadores de UI. Não conecta por conta própria.

const { realtime, enabled, state } = useRealtimeClient()
// state.value → { connection, connected, signedIn }

AI (/ai)

Importe de @be-enlighten/enspace-sdk-vue/ai. Composables para construir widgets de chat sobre o resource ai do core:

  • Sessão com streaming: useAiChatSession + EnspaceChatTransport.
  • Erros: AiChatErrorHandler mapeia códigos como insufficient_balance para mensagens localizadas.
  • Seleções por turno: useAiModelSelection, useAiModeSelection, useAiReasoningSelection, useAiAgentsSelection.
  • Anexos: useAiChatUpload.
  • Utilitários de UI: auto-scroll, smooth-stream e agrupamento do histórico por data.

Pressupõe a /query instalada. Os peers ai e @ai-sdk/vue são opcionais.

Tipos

Re-exports type-only do core:

import type { AuthConfig, EnspaceClient, EnspaceConfig, RequestOptions, RetryConfig } from '@be-enlighten/enspace-sdk-vue'

Para tipos de domínio (User, Workspace, Member, ...), importe de @be-enlighten/enspace-sdk-schemas ou do core.

Licença

MIT.