@esposter/azure
v4.0.0
Published
The Azure wire conventions Esposter's clients and its mocks both speak — OData filter clauses, entity key casing, and service limits.
Maintainers
Readme
@esposter/azure
The parts of Azure's wire contract that both a real client and a fake one have to agree on: the OData filter clause vocabulary shared by Table Storage and Search, the entity key casing convention, and the service limits. No SDK client is constructed here and no credential is read — this package is pure data and pure functions, so it runs anywhere.
Table of Contents
🚀 Getting Started
pnpm add @esposter/azure📖 Documentation
Clauses
A Clause<T> is a filter condition expressed against a property of T, and it is what the OData string is
built from rather than the string being assembled by hand. BinaryOperator covers the comparisons Table
Storage understands; SearchOperator covers the collection predicates only Azure Search does; UnaryOperator
is how clauses are combined.
import type { Clause } from "@esposter/azure";
import { BinaryOperator } from "@esposter/azure";
const clause: Clause<{ createdAt: Date }> = { key: "createdAt", operator: BinaryOperator.Ge, value: new Date(0) };Values
serializeValue renders a SerializableValue the way the target service expects — Table Storage wants a
datetime'<iso>' literal for a Date, Search wants the bare ISO string, and isTableFilter selects between
them. Strings go through escapeValue, which doubles embedded single quotes so a value can never close its own
literal and append filter syntax of its own.
import { escapeValue, serializeValue } from "@esposter/azure";Filters
serializeClauses turns a clause array into a Table Storage filter string and serializeSearchClauses into a
Search one; the two differ only in how a Date renders, which is why they share a single core. Clauses on the
same key are grouped — a range pair is joined with and, anything else with or — so a caller never assembles
the boolean structure by hand. deserializeClause is the inverse, and is what lets a fake client evaluate a
filter it was handed rather than pattern-matching the string.
import { BinaryOperator, CompositeKeyPropertyNames, getTableNullClause, serializeClauses } from "@esposter/azure";
const roomId = "00000000-0000-0000-0000-000000000000";
const filter = serializeClauses([
{ key: CompositeKeyPropertyNames.partitionKey, operator: BinaryOperator.Eq, value: roomId },
getTableNullClause("deletedAt"),
]);Null is where the two services diverge and the helpers carry the difference: Table Storage cannot compare
against null at all, so getTableNullClause expresses "is null" as a negated NaN comparison, while Search
supports it directly through getSearchNullClause / getSearchNonNullClause. getPartitionKeyFilter is the
one-clause case worth naming, since a read, a count and a purge of the same entity must all start from it.
Keys
CompositeKey is the partitionKey/rowKey pair every Table entity carries, and CompositeKeyPropertyNames
is how those names are referred to without restating the strings. The wire spells those two capitalized and
JavaScript does not, so serializeKey and deserializeKey convert at the boundary and nothing in between has
to know.
import { CompositeKey, CompositeKeyPropertyNames, serializeKey } from "@esposter/azure";Limits
AZURE_MAX_BATCH_SIZE, AZURE_MAX_PAGE_SIZE and AZURE_MAX_QUEUE_VISIBILITY_TIMEOUT_MS are the service's own
ceilings, named so a caller paginating, batching or enqueueing does not hard-code them.
Commands
Run from packages/azure/:
pnpm build # compile to dist/
pnpm test # vitest watch mode (coverage is run from the repo root)
pnpm lint:fix # auto-fix lint
pnpm typecheck # type check⚖️ License
This project is licensed under the Apache-2.0 license.
