@baselayer-sdk/autocomplete

中文文档
0.10.0发布于 2026/10/29Apache-2.0TypeScriptESM + CJS构建来源已验证近 30 天下载 4231

Baselayer business autocomplete for the browser: a framework-free client, React hooks and a styled typeahead, and a server-side session mint helper

原始使用文档
文档目录8 节
  1. Install
  2. How it fits together
  3. 1. Add the mint endpoint to your backend
  4. 2. Drop in the component
  5. 3. Send the pick with your search
  6. Documentation
  7. Development
  8. License

@baselayer-sdk/autocomplete

Autocomplete for your own product, backed by Baselayer's registry, that helps the person typing pick the right business. Every suggestion is a canonical entity already linked to the entities around it: a business arrives with its registered states, officers, agents and addresses, and a business_token that pins your Baselayer search to exactly that business.

It finds a business three ways: by its name, by a person who holds a role on it, or by an address it is filed at. Whichever way, the pick is a business.

Pre-release. Until 1.0, a breaking change bumps the minor version. 1.0 freezes the API.

The typeahead for "harbor concrete pum"

  • Framework-free core (@baselayer-sdk/autocomplete): sessions, refresh, backoff, and every refusal the API can answer, handled for you.
  • React (@baselayer-sdk/autocomplete/react): headless hooks, and styled components, BusinessAutocomplete, PersonAutocomplete and AddressAutocomplete, ready out of the box and restyled end to end.
  • Server helper (@baselayer-sdk/autocomplete/server): the one endpoint your backend adds, so your API key never reaches a browser.

Install

npm install @baselayer-sdk/autocomplete

React 18 or 19 is an optional peer; the core and the server helper need neither React nor a DOM.

How it fits together

The browser, your backend and Baselayer, and the five hops between them

  1. When the field gets focus, the SDK asks your endpoint for a session. Your own cookie authenticates the call.
  2. Your backend mints it with your API key, forwarding the page's Origin, and returns Baselayer's answer unchanged.
  3. Every keystroke goes straight to Baselayer with the session in a header: no cookies, no key.
  4. Picking a row hands your form its business_token.
  5. Your backend sends the token with POST /searches, so the search resolves to exactly the business that was picked.

Your API key stays on your backend. The browser holds only a short-lived session that is bound to your page's origin and carries its own request budget.

Request by request, from focus to submit:

Every request between the browser, your backend and Baselayer

Both diagrams are the site's own, rendered to files by pnpm diagrams; a test fails when a file falls behind its component.

1. Add the mint endpoint to your backend

Next.js (App Router), in app/api/ac-session/route.ts:

import { createMintHandler } from "@baselayer-sdk/autocomplete/server";

export const POST = createMintHandler({
  apiKey: process.env.BASELAYER_API_KEY!,
  allowedOrigins: ["https://app.example.com"],
});

Express:

import { mintForOrigin } from "@baselayer-sdk/autocomplete/server";

app.post("/api/ac-session", requireLogin, async (req, res) => {
  const origin = req.get("Origin");
  if (!origin || !ALLOWED.has(origin)) {
    return res.status(403).end();
  }
  const out = await mintForOrigin({
    apiKey: process.env.BASELAYER_API_KEY!,
    origin,
  });
  res.status(out.status).set(out.headers).json(out.body);
});

Any other backend: see the mint endpoint contract. Put the route behind your own authentication, like the rest of your API: every session your endpoint mints counts against your organization's pool.

2. Drop in the component

import { useState } from "react";
import { BusinessAutocomplete } from "@baselayer-sdk/autocomplete/react";
import "@baselayer-sdk/autocomplete/react/styles.css";

export function LegalNameField() {
  const [name, setName] = useState("");
  const [businessToken, setBusinessToken] = useState<string>();
  return (
    <BusinessAutocomplete
      id="legal-name"
      label="Legal entity name"
      baseUrl="https://api.baselayer.com"
      mintUrl="/api/ac-session"
      value={name}
      onChange={value => {
        setName(value);
        setBusinessToken(undefined); // an edit is no longer the pick
      }}
      onPick={(_, pick) => setBusinessToken(pick.businessToken)}
    />
  );
}

id seeds the combobox's ids: the input is ${id}-input, the label ${id}-label and the menu ${id}-menu. The field is controlled: value is yours and onChange reports typing. A pick calls onChange with the row's canonical name, then onPick(suggestion, pick), so clearing the token in onChange, as above, keeps the pick. A blur never picks.

Exactly one of these connects it:

  • mintUrl and baseUrl: your endpoint, POSTed with your own cookie (defaultMint(mintUrl), credentials: "same-origin").
  • mint and baseUrl: your own MintFunction, for any other call.
  • client, without baseUrl: a client from createAutocompleteClient, shared across fields; the component never resets a client it was given.

The first mint or mintUrl is kept for the component's life; a new baseUrl re-creates its client. The rest is optional:

  • mintOn ("keystroke"): when the session is minted, one of MINT_TIMINGS (type MintTiming). keystroke mints on the first keystroke, overlapping the rest of the typing, so a click that types nothing spends no mint; focus mints as the field takes focus, so the first answer pays only for its suggestions; request mints with the first request, and that answer waits.
  • enabled (true): off, the field is a plain input; nothing is minted and nothing is asked.
  • limit (5, or the session's most when that is fewer): rows per keystroke, an integer from 1 to 20 and no more than the session's most; anything else is refused before it is sent, and no menu opens.
  • minChars (3): trimmed characters before a keystroke is sent.
  • debounceMs (250): the pause after a keystroke before it is sent; a newer keystroke cancels it.
  • filters: narrowing filters sent with every query. While the text is shorter than the session's stem nothing is asked, and the menu asks the person to keep typing (messages.filtersNeedMore, which takes the characters still missing if you want to say a number); a client you pass follows its own onShortStem (Filters).
  • onUnavailable({ unavailable }): called when the deployment stops being able to mint (503 code 481), and again when it can; render your plain input meanwhile.
  • list: what each row lists under it, a line per item, in the order given: the business's officers and agents (people) and its addresses. None by default, and each row is the one option it has always been (A business row's lines).
  • enabledLines (["business"]) and onPickEntity: which lines can be picked. Name person or address too, and an officer or an address picked goes to onPickEntity as an EntityPick, which is then required (What can be picked).
  • showSelection (true): after an officer or an address is picked, a line under the field names it (The line under the field).
  • iconSegments and icons: the segments that carry an icon (none by default; ["name", "address"] draws one before the name and the address) and your own icons, by entity or entity and role, or false for none (Icons).
  • include: the related entities the autocomplete service expands for each row, by default what layout places and list lists. Rows you draw yourself with renderRow name what they read: include={["people"]}.
  • label: the text of the default <label>; without it none is drawn.
  • name, inputRef, onFocus and onBlur: the input's, passed through.
  • look, layout, menuWidth, menuFollowsInputWidth, open, classNames, renderLabel, renderInput, renderRow and unstyled: see Styling; layout is the field each place of a row shows (A row's places and fields). messages: see Error states.

Your backend sends the token as its POST /searches body:

{ "business_token": "…" }

The token stands in for name and address: send it on its own, since the search refuses a body that carries it beside either one.

The search then resolves to exactly the business that was picked. A token lives 15 minutes (BUSINESS_TOKEN_TTL_SECONDS, 900) from when the row was served. The pick that onPick hands you carries it as businessToken, with pickedAt and expiresAt (epoch ms, pickedAt plus those 15 minutes). expiresAt is advisory, and a little late, since the clock started before the pick; the API is what refuses a stale token (422 code 3042). If the search refuses it (expired, or the business is gone), drop the token and search by the name and address as typed: refusedThePin(status, code) tells you when that is the right move.

The pick also carries matchedOn: what the row matched on besides its name, when the visitor narrowed by an officer, an address or a state (see What matched); [] for a pick the name alone reached.

PersonAutocomplete and AddressAutocomplete find the business through a person or an address. Each person or address in the menu comes with how many businesses it leads to and a line for each of the first, and a pick is one of those businesses: onPick hands you a BusinessPick with the same businessToken, the businessName for your name field, and through, the person or address it was reached by:

<PersonAutocomplete
  id="officer-name"
  label="Officer's name"
  baseUrl="https://api.baselayer.com"
  mintUrl="/api/ac-session"
  value={typed}
  onChange={setTyped}
  onPick={pick => {
    setBusinessName(pick.businessName);
    setBusinessToken(pick.businessToken);
  }}
/>

A business row can list what it leads to the same way, a line for each officer, agent and address under it, and let an officer be picked too:

<BusinessAutocomplete
  …
  list={["people", "addresses"]}
  enabledLines={["business", "person"]}
  onPick={(_, pick) => setBusinessToken(pick.businessToken)}
  onPickEntity={officer => setOfficer(officer)}
/>

The business is picked from its row as it always was. An officer picked hands onPickEntity { type: "person", token: null, label } (a person a row lists has no token), puts the business's name in the field, and is named on a line under it. See Businesses, people and addresses.

A session searches what its scope allows, which your mint endpoint can narrow (see Narrowing a session), and the SDK never asks for anything outside it.

Documentation

  • Mint endpoint contract: the three rules, and examples for Next.js, Express and curl
  • Headless use: the hooks, and the core without React
  • Businesses, people and addresses: the three routes, what a session may search, and search
  • Styling: CSS variables, class names, render props, unstyled
  • Error states: what the SDK does with every answer
  • Security: what the session is and what never leaves your backend
  • The site: the overview, the generated API reference and the demo, and how to publish them
  • Live demo: try it with your own API key, and run the search

Development

pnpm install
pnpm test          # vitest: core, server and site in node, react in jsdom
pnpm typecheck
pnpm lint
pnpm build         # dist/ with ESM, CJS and type declarations
pnpm site          # the site on http://localhost:3000
pnpm demo          # the same, opened on the demo, forwarding to production
DEMO_API=sample pnpm demo   # the demo on made-up data, with no network

Releases are tags; see CONTRIBUTING.md.

License

Apache-2.0

版本与发布记录

@baselayer-sdk/autocomplete 共发布 12 个版本,首次发布于 2026/09/25;过去 12 个月发布了 12 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.10.0latest2026/10/299 天前
0.9.02026/10/1821 天前
0.8.02026/10/1523 天前
0.7.02026/10/1325 天前
0.6.02026/10/1226 天前
0.5.02026/10/0929 天前

其他发布通道:next → 0.1.0-rc.0。

关于 @baselayer-sdk/autocomplete 的常见问题

@baselayer-sdk/autocomplete 是什么?

Baselayer business autocomplete for the browser: a framework-free client, React hooks and a styled typeahead, and a server-side session mint helper

如何安装 @baselayer-sdk/autocomplete?

在项目目录运行 npm install @baselayer-sdk/autocomplete,或使用 pnpm add / yarn add / bun add。

@baselayer-sdk/autocomplete 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@baselayer-sdk/autocomplete 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@baselayer-sdk/autocomplete 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@baselayer-sdk/autocomplete 可以商用吗?

@baselayer-sdk/autocomplete 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@baselayer-sdk/autocomplete 的下载量有多少?

根据 npm 官方下载统计,@baselayer-sdk/autocomplete 在 2026/10/08 – 2026/11/06 这 30 天内被下载 4,231 次,日均约 141 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@baselayer-sdk/autocomplete 还在维护吗?

最新版本 0.10.0 发布于 2026/10/29(9 天前);至今共发布 12 个版本,其中过去 12 个月发布了 12 个;首次发布于 2026/09/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。