@frank/crm (0.1.1)

Published 2026-09-26 21:54:01 +10:00 by steve

Installation

@frank:registry=
npm install @frank/crm@0.1.1
"@frank/crm": "0.1.1"

About this package

@frank/crm

One Frappe CRM site per customer, driven from a Node server. Ported from Blockwise src/lib/crm unchanged in behaviour: per-site vault credentials, command_id idempotency, the provision state machine with the real ready-probe, and the delivery worker with wait-not-fail semantics. Server-only; nothing here may be imported by a client component.

Public API (src/index.ts)

  • createWorkspaceCrm - mapping + credential + client + commands for a workspace
  • createCrmClient, createCrmCommands - transport and typed command surface
  • configFromEnv, CrmConfig, CrmNames, defaultCrmNames - configuration
  • loadCrmSiteCredential, upsertCrmSiteCredential, clearCrmSiteCredential
  • resolveCrmSite, requireCrmSite, isValidCrmSite, crmSiteForSlug
  • signSsoToken, crmPublicHostForSite, ssoLoginUrl - the CRM sign-in token
  • provisionCrmSiteForWorkspace, createVaultCrmSiteProbe, createCrmProvisionTenantProvisioner - the state machine
  • ensureLeadCrmDeliveryJob, queueLeadCrmDelivery, executeLeadCrmDeliveryJobById, crmCaptureCommandId, formatLeadCrmDelivery
  • CrmError and helpers, domain types (CrmLead, stages, tasks)

Config

configFromEnv(env, names) is the only env reader: base URL (the CRM backend, never an nginx frontend), timeout, vault key, delivery kill switch. Table, queue-kind, RPC and Frappe-namespace names are a CrmNames parameter; defaultCrmNames holds the Blockwise values. Queue helpers take the product's job-queue enqueue as a parameter; this package owns no queue. Tests run with npm run test: node --test, no network or database.

Usage

const crm = await createWorkspaceCrm({ supabase, serviceSupabase, workspaceId, config });
await crm.commands.captureEnquiry({
  commandId: `crm-capture:${lead.source}:${lead.submissionId}`, // replay-safe
  sourceProvider: lead.source,
  sourceSubmissionId: lead.submissionId,
});

CRM sign-in token

signSsoToken mints the short-lived HS256 JWT that signs a workspace member into Frappe's own CRM screens on their site (design step 3). The signing key is the site's vault credential apiSecret, the same api_secret blockwise_crm/sso.py decrypts on the site, so there is no second secret. Claims: iss blockwise.sale, aud the site name, sub the trimmed lower-cased email, workspace_id, role (owner, admin, member, operator), iat, exp (default 60s, maximum 120s, matching the verifier), jti (24 random url-safe characters, consumed once per site). Bad input throws CrmSsoInputError; it refuses the inputs the site is known to refuse (the verifier stays the authority).

const { token, expiresAt } = signSsoToken({
  credential, siteName, email, workspaceId, role,
});
// The browser posts the token to ssoLoginUrl(siteName), which is
// crmPublicHostForSite(siteName) under https: acme.crm.internal becomes
// crm-acme.blockwise.sale (first level; the public base domain is
// names.publicBaseDomain).

Dependencies

Dependencies

ID Version
@supabase/supabase-js ^2.49.8

Development dependencies

ID Version
@types/node ^22.0.0
Details
npm
2026-09-26 21:54:01 +10:00
121
latest
57 KiB
Assets (1)
crm-0.1.1.tgz 57 KiB
Versions (2) View all
0.1.1 2026-09-26
0.1.0 2026-09-26