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

@adopt-tech/provisioning

v1.1.0

Published

Type-safe JS toolkit for creating and managing Adopt entities, configurations, and subscriptions

Readme

@adopt-tech/provisioning

npm npm downloads

Type-safe JS/TS toolkit for programmatic provisioning of Adopt CMP entities via the Hasura GraphQL Actions API.

Prerequisites

To use this SDK you need an API account on the Adopt platform. Contact the AdOpt team at [email protected] to request access.

Overview

adopt-provisioning lets you create and configure the full Adopt entity hierarchy in a single idempotent run:

Organization
└── Disclaimer
    ├── Options (GCM, consent TTL, languages)
    ├── Style (colors, theme, position)
    ├── Terms document
    ├── Privacy document
    ├── Cookie document (URL, upload, or auto-generated from scan)
    ├── Optout / DSAR page
    ├── Vendors
    ├── TCF
    └── Scan
Members (per organization)

Key features

  • Idempotent runs — re-running with the same state ID resumes from where it left off
  • Rollback on error — deletes every entity created in the run on failure
  • maxConcurrency: 1 — controls execution order of sibling provisioners
  • Fetcher API — read back any provisioned entity for verification
  • getInstallationSnippet(disclaimerId) — generates the <head> snippet for banner installation

Partner integration

For the complete end-to-end provisioning flow (login, org, member, disclaimer, style, docs, scan, cookie doc auto-generation) see examples/partner-integration.ts.

export [email protected]
export ADOPT_USER_PASSWORD=yourpassword

# Dry-run — no API calls, shows planned tree
npx ts-node examples/partner-integration.ts --dry-run

# Real provisioning
npx ts-node examples/partner-integration.ts

Edit ORG_NAME, ORG_PATH, DOMAIN, and ADMIN_EMAIL at the top of the file per tenant. Each run auto-generates a unique state ID — no risk of skipping provisioners from a previous run.

One-call provisioning (createFullDisclaimer)

The fastest way to provision a fully configured disclaimer. A single server-side call creates the org, disclaimer, style, documents, and optionally a DSAR portal — returning the IDs and install snippet immediately.

const client = new AdoptProvisioningClient({
  environment: 'staging',
  username: process.env.ADOPT_USER_EMAIL!,
  password: process.env.ADOPT_USER_PASSWORD!,
})

const [result] = await client.createFullDisclaimer({
  urls: ['https://yoursite.com'],
  immediateAvailable: true,   // banner loads without waiting for a scan
  skipBranding: true,         // return immediately, trigger scan in background

  org: {
    orgPathname: 'your-org',  // unique slug; auto-derived from hostname if omitted
    orgName: 'Your Company',
    // orgId: 'existing-uuid',  // use an existing org instead of creating one
  },

  style: {
    mainColor: '#00DD80',
    textColor: '#000000',
    backgroundColor: '#ffffff',
  },

  documents: {
    privacyUrl: 'https://yoursite.com/privacy',
    cookieUrl:  'https://yoursite.com/cookies',
    termsUrl:   'https://yoursite.com/terms',
    // omit any URL to auto-generate the document from a questionnaire
  },

  dsar: {
    createDsar:       true,
    // dpo: '[email protected]',  // defaults to authenticated user when omitted
    inviteDpoAsAdmin: true,     // invite the DPO to the org as admin if not already a member
  },
})

console.log(result.disclaimerId)  // UUID
console.log(result.dashboardUrl)  // https://app.goadopt.io/org/…/disclaimer/…
console.log(result.installCode)   // <meta …/> + <script …/>

When to use createFullDisclaimer vs the provisioner tree

  • Use createFullDisclaimer for simple, one-shot onboarding (e.g. partner integrations that spin up a banner per customer).
  • Use the provisioner tree (ResumableProvisioningExecutor) for complex scenarios that need resume, rollback, incremental updates, or fine-grained control over each entity.

Full runnable example: examples/create-full-disclaimer.ts

Installation

npm install @adopt-tech/provisioning

Authentication

Two modes are supported:

// Token mode — provide a pre-existing JWT (no auto-refresh)
const client = new AdoptProvisioningClient({
  environment: 'staging', // or 'production'
  token: process.env.ADOPT_USER_TOKEN,
})

// Credentials mode — auto-refreshes when JWT expires (recommended for long runs)
const client = new AdoptProvisioningClient({
  environment: 'staging',
  username: process.env.ADOPT_USER_EMAIL,
  password: process.env.ADOPT_USER_PASSWORD,
})

Environment variables

| Variable | Required | Description | |---|---|---| | ADOPT_USER_EMAIL | credentials mode | User email for auto-login | | ADOPT_USER_PASSWORD | credentials mode | User password for auto-login | | ADOPT_USER_TOKEN | token mode | Pre-existing Cognito JWT | | ADOPT_HASURA_URL | if no environment | Hasura GraphQL endpoint | | ADOPT_BACKEND_URL | if no environment | Backend URL for credentials login | | ADOPT_HASURA_ROLE | No | Hasura role (default: api) | | ADOPT_HUB_URL | No | Hub base URL (auto-set by environment) |

environment: 'staging' or 'production' sets all URLs automatically (hasuraUrl, backendUrl, hubUrl).

Quick start

import {
  AdoptProvisioningClient,
  ResumableProvisioningExecutor,
  OrganizationProvisioner,
  DisclaimerProvisioner,
  DisclaimerStyleProvisioner,
  TermsDocumentProvisioner,
  ScanProvisioner,
  CookieDocumentProvisioner,
} from '@adopt-tech/provisioning'

const client = new AdoptProvisioningClient({
  environment: 'staging',
  username: process.env.ADOPT_USER_EMAIL!,
  password: process.env.ADOPT_USER_PASSWORD!,
})

const org = new OrganizationProvisioner('my-org', {
  name: 'Acme Corp',
  pathName: 'acme-corp',
})

const disclaimer = new DisclaimerProvisioner('my-disclaimer', {
  name: 'Cookie Banner',
  domains: ['https://acme.com'],
})

disclaimer.addChild(
  new DisclaimerStyleProvisioner('my-style', {
    primaryColorLight: '#00DD80',
    positionController: 'left',
    disclaimerTheme: 'modern',
  }),
)

disclaimer.addChild(
  new TermsDocumentProvisioner('my-terms', {
    language: 'pt',
    organizationId: '', // resolved from parent org at runtime
    url: 'https://acme.com/terms.pdf',
  }),
)

// Scan runs before cookie doc so autoGenerate can resolve the scan ID
disclaimer.addChild(new ScanProvisioner('my-scan', { mode: 'fire-and-forget' }))
disclaimer.addChild(
  new CookieDocumentProvisioner('my-cookie', {
    language: 'pt',
    organizationId: '',
    autoGenerate: true,
    documentName: 'Cookie Policy',
  }),
)

org.addChild(disclaimer)

const executor = new ResumableProvisioningExecutor({
  client,
  config: { rollbackOnError: true, maxConcurrency: 1 },
})

const result = await executor.execute({ organizations: [org] })

Installation snippet

After provisioning, get the HTML snippet to inject on the tenant's website:

import { getInstallationSnippet } from '@adopt-tech/provisioning'

const { html } = getInstallationSnippet(disclaimerId)
// <meta name="adopt-website-id" content="..." />
// <script src="//tag.goadopt.io/injector.js?website_code=..." class="adopt-injector"></script>

Executor shortcuts

Instead of building full provisioner trees, update a single entity on an existing disclaimer or org:

const executor = new ResumableProvisioningExecutor({ client, config: {} })

// Update style on an existing disclaimer
await executor.executeForDisclaimer(disclaimerId, [
  new DisclaimerStyleProvisioner('style', { primaryColorLight: '#00DD80' }),
])

// Update options on an existing disclaimer
await executor.executeForDisclaimer(disclaimerId, [
  new DisclaimerOptionsProvisioner('opts', { googleConsentMode: true, consentTTLDays: 30 }),
])

// Provision a new member on an existing org
await executor.executeForOrganization(organizationId, [
  new MemberProvisioner('admin', { email: '[email protected]', role: MemberRole.Admin }),
])

Fetcher API

Read back any entity after provisioning:

const fetcher = createFetcher(client)
const discFetcher = fetcher.disclaimers({ id: disclaimerId })

// Style
const style = await discFetcher.getStyle()
// style.primary_color_light, style.disclaimer_css.styles.web.card (fonts)

// Options
const opts = await discFetcher.getOptions()
// opts.consent_ttl_in_days, opts.hide_disclaimer, opts.google_consent_mode

// Linked documents (privacy, cookies, terms master IDs)
const docs = await discFetcher.getDocuments()

// Document content URL (the URL the user configured)
const url = await fetcher.documentContentUrl(docs.document_master_privacy_id!)
// → 'https://example.com/privacy-policy'

// Hub public links
fetcher.documentUrl(docs.document_master_privacy_id!)  // hub.goadopt.io/document/{id}
fetcher.dsarPortalUrl(requestsPageMasterId)            // hub.goadopt.io/privacy-hub/{id}

// DSAR portal
const dsar = await discFetcher.getLinkedDsar()
// dsar.requestsPageMasterId, dsar.page (texts, requests, documents, style)

// Last linked DSAR master (even after unlink)
const masterId = await discFetcher.getLastLinkedDsarMasterId()

// Platform default texts for DSAR portal (pre-populate UI forms)
const [pt] = await fetcher.dsarDefaults('pt')
// pt.texts.title, pt.texts.requests.dataAccess, pt.texts.fields.name.name

DSAR portal management

// Create portal
await executor.executeForDisclaimer(disclaimerId, [
  new RequestsPageProvisioner('dsar', {
    name:      'Requests Portal',
    dpoUserId: process.env.DPO_USER_ID!,
    languages: { fallback: 'pt', languages: ['pt', 'en'] },
    requests:  [{ legislation: Legislation.LGPD, requests: Object.values(DsarRequestType) }],
    style:     { colors: { background: '#FFFFFF', button: '#00DD80' } }, // typed RequestsPageStyle
  }),
])

// Disable (soft — reversible)
await client.disableDsarPage(disclaimerId)

// Re-enable
await client.linkRequestPage(disclaimerId, requestsPageMasterId)

// Permanently delete
await client.unlinkRequestPage(disclaimerId)
await client.deleteRequestsPage(requestsPageMasterId)

Document management

// Link documents to disclaimer
await executor.executeForDisclaimer(disclaimerId, [
  new PrivacyDocumentProvisioner('privacy', { language: 'pt', url: 'https://example.com/privacy' }),
  new CookieDocumentProvisioner('cookies',  { language: 'pt', url: 'https://example.com/cookies' }),
  new TermsDocumentProvisioner('terms',     { language: 'pt', url: 'https://example.com/terms'   }),
])

// Update existing document (creates new version of same master)
await executor.executeForDisclaimer(disclaimerId, [
  new PrivacyDocumentProvisioner('privacy-v2', {
    language:         'pt',
    url:              'https://example.com/privacy-v2',
    masterDocumentId: existingMasterId,   // ← targets existing master
  }),
])

// Disable document toggle without deleting
await executor.executeForDisclaimer(disclaimerId, [
  new PrivacyDocumentProvisioner('privacy-off', { language: 'pt', noDoc: true }),
])

// Permanently delete document master
await client.deleteDocument(docMasterId)

Resume & rollback

// Fixed stateId enables resume on interrupted runs
const executor = new ResumableProvisioningExecutor({
  client,
  stateId: 'my-tenant-run',   // omit to auto-generate (fresh run every time)
  config: { rollbackOnError: true },
})

// Rollback a previous run by state ID
import { FreshProvisionerFactory } from '@adopt-tech/provisioning'
const { organizations } = await FreshProvisionerFactory.fromDefaultStorage('my-tenant-run')
await executor.rollback(organizations)

Note: Only reuse a stateId to resume an interrupted run. For a new tenant, omit it or use a unique ID — reusing a completed state will skip provisioners that ran before.

Examples

Full, runnable examples are available at goadopt/adopt-provisioning-examples.

Provisioning

# Full partner onboarding: org, member, disclaimer, style, docs, scan
npx ts-node examples/partner-integration.ts --dry-run   # preview tree
npx ts-node examples/partner-integration.ts             # run

# Minimal disclaimer (all required documents)
npx ts-node examples/create-functional-disclaimer.ts

Banner configuration

# Style — colors, position, fonts
npx ts-node examples/api-estilo.ts          # apply
MODE=read npx ts-node examples/api-estilo.ts  # read current

# Texts — title, body, button labels (per language + legislation)
npx ts-node examples/api-textos.ts

# Options — Google Consent Mode, hide after consent, TTL
npx ts-node examples/api-options.ts
MODE=read npx ts-node examples/api-options.ts

# Documents — privacy policy, cookie policy, terms of service
npx ts-node examples/api-documentos.ts           # create/link via URL
MODE=read   npx ts-node examples/api-documentos.ts  # read current + hub links
MODE=update DOC_MASTER_ID=uuid npx ts-node examples/api-documentos.ts
MODE=nodoc  npx ts-node examples/api-documentos.ts  # disable toggles

DSAR / Requests page (LGPD)

# Create/update portal with all 11 LGPD rights
npx ts-node examples/api-dsar.ts

# Read current portal config + public link
MODE=read npx ts-node examples/api-dsar.ts

# Platform default texts (pre-populate your UI forms)
MODE=defaults LANGUAGE=en npx ts-node examples/api-dsar.ts

# Full LGPD configuration — style, documents, all request types
npx ts-node examples/api-requisicoes.ts

# Disable / re-enable portal
MODE=unlink npx ts-node examples/api-dsar.ts
MODE=delete npx ts-node examples/api-dsar.ts

Metrics & scanning

# Consent metrics for current month
npx ts-node examples/api-metricas.ts
MONTH=3 YEAR=2025 npx ts-node examples/api-metricas.ts

# Scan + recategorize tags
npx ts-node examples/scan-and-recategorize.ts

# TCF setup
npx ts-node examples/tcf-setup.ts

License

MIT