@leendev/onramp-vue
v0.0.2
Published
Vue 3 wrapper for Leen OnRamp
Maintainers
Readme
Leen OnRamp for Vue
OnRamp is Leen's embeddable connection onboarding component. By integrating OnRamp into your workflow, you enable your users to effortlessly create new connections with all of the vendors supported by Leen.
npm i @leendev/onramp-vueRequires Vue 3.4 or newer. For the full documentation, refer to Leen's docs on creating connections with OnRamp here.
Usage
<script setup lang="ts">
import { ref } from 'vue';
import { LeenOnRamp, type LeenOnRampResponse } from '@leendev/onramp-vue';
const props = defineProps<{ connectionInviteToken: string }>();
const showOnRamp = ref(false);
const response = ref<LeenOnRampResponse | undefined>(undefined);
</script>
<template>
<button @click="showOnRamp = true">Open Leen OnRamp</button>
<LeenOnRamp
v-if="showOnRamp"
v-model:show="showOnRamp"
:token="connectionInviteToken"
@response="response = $event"
/>
</template>v-model:show is two-way: OnRamp sets it to false when the user closes the
modal, which unmounts the component and destroys the underlying instance.
Global registration
If you would rather register the component once instead of importing it per call site:
import { createApp } from 'vue';
import { LeenOnRampPlugin } from '@leendev/onramp-vue';
createApp(App).use(LeenOnRampPlugin).mount('#app');Props
Only token is required. Defaults below are the OnRamp bundle's own defaults, so
omitting a prop and passing its default value are equivalent.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| token | string | — | Required. Connection invite token from the Create Connection Invite Token API. Scoped to one vendor and one connection. |
| show | boolean | true | Read side of v-model:show. Setting it to false destroys the instance. |
| bundleVersion | string | "latest" | Which bundle to load: "dev" for static.leen.dev/dev, otherwise a version pinned under static.leen.dev/prod/<version>. |
| containerId | string | auto-generated | Id of the element OnRamp mounts into. Defaults to a unique leenOnRampComponent-N, so several instances can coexist. |
| requireIdentifier | boolean | false | Require the user to supply a connection identifier. |
| modalSize | 'sm' \| 'md' \| 'lg' \| 'xl' | 'md' | Modal width. |
| primaryColor | string | '#B5FF56' | Accent color for primary buttons. Any CSS color. |
| logoUrl | string | Leen's logo | Logo shown in the modal header. |
| docsUrlOverrides | Record<string, string> | Leen's docs | Per-vendor documentation links, keyed by vendor. See below. |
| fontFamily | string | 'Inter' | Font family name. Pair with fontUrl. |
| fontUrl | string | Inter from Google Fonts | Stylesheet URL for the font. Required if fontFamily is not already loaded by your page. |
| fontSizes | OnRampFontSizeOptions | see below | Per-element font sizes. |
| darkMode | boolean | false | Switch to the dark theme. |
| darkModeColor | DarkModeColorOptions | Leen's dark palette | Dark theme color overrides. Only applies when darkMode is true. |
| textOverrides | TextOverrides | — | Override button copy. |
| config | LeenConfig | — | Passed through to the bundle. Use region here for non-US Leen regions. |
Object-shaped props
interface OnRampFontSizeOptions {
headerSize?: string; // default '1.25rem'
bodySize?: string; // default '1rem'
buttonSize?: string; // default '0.875rem'
}
// Only used when darkMode is true.
interface DarkModeColorOptions {
primary?: string; // hex
secondary?: string; // hex
border?: string; // hex
}
interface TextOverrides {
button?: {
create?: string; // copy for the create-connection button
update?: string; // copy for the update-connection button
};
}
// Keyed by vendor, as returned by Leen's vendor enum.
type DocsUrlMapping = Record<string, string>;
interface LeenConfig {
region?: string; // e.g. 'eu-c1'. Defaults to Leen's US region.
containerId?: string; // set by the component; do not pass it here
[key: string]: unknown;
}Any CSS unit works for the font sizes (px, rem, em, …).
Events
| Event | Payload | Description |
| --- | --- | --- |
| response | LeenOnRampResponse \| undefined | Emitted when a connection is created or updated. |
| update:show | boolean | Backs v-model:show. Emitted with false when the user closes the modal. |
interface LeenOnRampResponse {
data: ConnectionResponse;
httpStatus: number;
message: string;
}
interface ConnectionResponse {
id: string;
vendor: string;
refresh_interval_secs: number;
timeout_secs: number;
organization_id: string;
is_active: boolean;
oauth2_authorize_url?: string;
identifier: string;
}Connection errors
OnRamp dispatches a leenConnectionError event on window when connection
creation fails. The wrapper does not surface this as a Vue event, so listen for it
directly:
import { onMounted, onBeforeUnmount } from 'vue';
const onConnectionError = (event: Event) => console.error(event);
onMounted(() => window.addEventListener('leenConnectionError', onConnectionError));
onBeforeUnmount(() =>
window.removeEventListener('leenConnectionError', onConnectionError),
);Fully-configured example
<script setup lang="ts">
import { ref } from 'vue';
import { LeenOnRamp, type LeenOnRampResponse } from '@leendev/onramp-vue';
const showOnRamp = ref(false);
const response = ref<LeenOnRampResponse | undefined>(undefined);
const docsUrlOverrides = {
SNYK: 'https://link.to/docs/snyk',
CROWDSTRIKE: 'https://link.to/docs/crowdstrike',
};
</script>
<template>
<LeenOnRamp
v-if="showOnRamp"
v-model:show="showOnRamp"
:token="connectionInviteToken"
:require-identifier="true"
modal-size="lg"
primary-color="#000000"
logo-url="https://link.to/logo"
:docs-url-overrides="docsUrlOverrides"
font-family="Roboto"
font-url="https://fonts.googleapis.com/css2?family=Roboto&display=swap"
:font-sizes="{ headerSize: '24px', bodySize: '18px', buttonSize: '12px' }"
:dark-mode="true"
:dark-mode-color="{ primary: '#2A004E', secondary: '#500073' }"
:text-overrides="{ button: { create: 'Connect', update: 'Reconnect' } }"
:config="{ region: 'eu-c1' }"
@response="response = $event"
/>
</template>Nuxt / SSR
The OnRamp bundle is browser-only. The component guards every window and
document access and only loads the bundle on mount, so importing it during SSR
is safe. Wrap it in <ClientOnly> to skip server rendering the placeholder:
<ClientOnly>
<LeenOnRamp :token="token" v-model:show="showOnRamp" />
</ClientOnly>Development
bun install
bun run dev # dev harness on http://localhost:3003
bun run build # ES + UMD bundles and .d.ts into dist/
bun run typecheck
bun run test # unit tests (vitest, stubbed bundle)
bun run test:e2e # end-to-end (playwright, real bundle + dev API)Invite token
The dev harness and the e2e suite both need a connection invite token. Tokens are short-lived — roughly 30-70 minutes — so expect to re-mint often:
export LEEN_DEV_API_KEY=... # dev environment API key
export LEEN_DEV_ORG_ID=... # dev organization id
bun run mint-token # writes .env.local, defaults to vendor TENABLEThe e2e specs assert against the TENABLE vendor form, so keep the default vendor
unless you also update them. You can pass another vendor as an argument
(bun run mint-token CROWDSTRIKE) for manual poking in the harness.
You can also paste a token straight into the harness input, or write .env.local
by hand:
VITE_LEEN_INVITE_TOKEN=eyJhbGciOi...The e2e suite checks the token's exp before running and skips with a clear
reason when it is missing or expired, rather than failing every assertion on a
timeout. So N skipped means "no usable token", not "broken wrapper".
CI
.github/workflows/ci.yml runs typecheck, unit tests and build on every push and
pull request, plus the e2e suite in a separate job.
Invite tokens expire in under an hour, so the token itself cannot be a repository
secret — CI mints a fresh one per run via bun run mint-token. That needs two
secrets:
| Secret | Value |
| --- | --- |
| LEEN_DEV_API_KEY | An API key for the dev environment. Prefer a dedicated CI key you can rotate independently. |
| LEEN_DEV_ORG_ID | The dev organization id tokens are minted against. |
gh secret set LEEN_DEV_API_KEY --repo leeninc/onRamp-wrapper-vue
gh secret set LEEN_DEV_ORG_ID --repo leeninc/onRamp-wrapper-vueTwo behaviours worth knowing:
- Locally, an absent or expired token skips the e2e suite. In CI it fails, because a skip there would report green having verified nothing.
- The e2e job is skipped for pull requests from forks, which don't receive
secrets. The
verifyjob still runs.
Testing notes
OnRamp renders its UI into a shadow root hosted inside the wrapper's container
div, so the container's own innerHTML is always just <div></div>. Playwright
locators pierce open shadow roots, so getByRole / getByText work normally;
hand-rolled querySelector checks need to go through .shadowRoot.
Support
Leen ships OnRamp wrappers for React, Angular, and Vue. If you need support for another JS runtime, reach out to [email protected].
