AstroBaaS

Build on it

API stability

Generated from STABILITY.md in the AstroBaaS repository. The repository is the source of truth; this page is a copy of it.

This document defines what AstroBaaS promises to theme and plugin authors, and what it doesn’t — so you can build on it without being surprised by an upgrade.

TL;DR

  • The public API is everything exported from astrobaas/core, astrobaas/plugins, and astrobaas/client. Build against those import paths only.
  • AstroBaaS is pre-1.0 (@alpha). The public API may still change, but every breaking change to it is called out in CHANGELOG.md under a Breaking heading.
  • Anything under src/lib/*, src/pages/*, src/components/* is internal and can change in any release with no notice. Don’t import it directly.

The public surface

Covered by the stability policy below.

astrobaas/plugins (bundled plugins)

ExportKindNotes
BUNDLED_PLUGINSconstThe plugins that ship with AstroBaaS, in load order. Add your own entry here when you vendor a plugin into a fork; a plugin loaded from ASTROBAAS_PLUGINS does not need it.

astrobaas/core (server + shared)

ExportKindNotes
Post, Category, User, MediaFile, Theme, ThemeConfig, ThemeSettings, Setting, ContentChange, PluginRecord, CustomEntity, ContactMessage, Subscriber, ApiKey, Webhook, WebhookDelivery, AuditEvent, PostRevision, TwoFactor, Role, PostStatus, EntityType, DatabaseSchematypesDomain models.
Product, ProductImage, Brand, ProductCategory, Order, OrderItem, OrderStatus, Customer, StockStatus, StockStatusInfo, ProductDownload, DownloadEntitlement, DownloadLogEntrytypesCommerce models. Commerce is off by default, but the types are always exported: a storefront types its catalogue against them whether or not this install serves one. Product.price_from_cents / price_to_cents are read-only, derived on every read (cheapest / dearest enabled variant; price_cents for a simple product) and are the key ?sort=price_* orders by.
StoragetypeThe storage contract the app depends on.
InvoiceDocument, InvoiceRecord, CreditMemoRecord, InvoiceRecordLine, InvoiceVatRow, InvoiceParty, InvoiceIssuerState, InvoiceLineTaxTreatmenttypesInvoice and credit-memo RECORDS on an order (Order.invoice_records): frozen, non-fiscal.
InvoiceIssuer, InvoiceIssueResult, InvoiceIssueContext, InvoiceIssuersFilterContext, LateIssueOutcometypesWhat an INVOICE_ISSUERS plugin implements and returns. See PLUGIN_DEVELOPMENT.md.
Plugin, FilterFn, ActionFn, PluginSettings, OrderLineExtrastypesPlugin shapes. OrderLineExtras is what an order_line filter may add to a line.
TaxIdVerdict, TaxIdVerdictStatustypesThe value a TAX_ID_VERDICT filter receives and returns. See “Tax-id verdicts” in PLUGIN_DEVELOPMENT.md.
TaxRatetypeOne row of the VAT rate table, the value a TAX_RATES filter receives and returns. See “Tax rates from a plugin” in PLUGIN_DEVELOPMENT.md.
PluginManager, pluginManagerclass/valueHook registry + the shared instance.
PLUGIN_HOOKSconstThe canonical hook-name catalog. Always reference these constants, never raw strings.
definePlugin, defineTheme, ThemeDefinitionfn/typeAuthor helpers (type inference). ThemeDefinition is a union: a root theme carries a complete settings, a theme with extends carries a partial one.
registerContentType, getContentTypes, getContentType, schemaForContentType, ContentTypeDefinition, ContentTypeFieldfn/registry/typesCustom content types.
THEME_SLOTS, ThemeSlotName, ThemeSlotProps, ThemeComponents, HeaderProps, FooterProps, PostCardProps, PostCardData, PostArticleProps, PageArticleProps, HomeProps, SidebarProps, BreadcrumbsProps, BreadcrumbItem, TableOfContentsProps, TocItem, ResolvedNavItem, isThemeSlot, overriddenSlotsconst/types/fnTheme template-override contract. Adding a NEW slot is additive: themes that don’t know about it inherit the default, so it is not a breaking change.
validateManifest, renderHeadTags, manifestContentTypes, apiRangeSatisfied, MANIFEST_API_VERSION, MANIFEST_LIMITS, PluginManifest, ManifestCapabilities, ManifestHeadTag, ManifestWebhook, ManifestValidationResultfn/const/typesDeclarative plugin manifests. MANIFEST_API_VERSION is its own semver line: a manifest declares the major it targets via astrobaasApi, and a mismatched major is refused rather than half-honoured. Adding a capability is additive; removing or changing one is a MAJOR bump.
sanitizeHtmlfnAllow-list HTML sanitizer; run on any author HTML before set:html.
validate, slugify, Schema, FieldRule, ValidateOk, ValidateErrfn/typesInput validation. validate returns the ValidateOk | ValidateErr union — narrow on ok rather than reading .data blind.
ApiResponseBuilder, ApiResponse, ApiSuccessResponse, ApiErrorResponseclass/typesThe house response shape for custom endpoints. Preserve the service layer’s status at the route boundary — 400 and 409 mean different things to a client.
fireEvent, webhookMatches, webhookBody, WEBHOOK_EVENTSfn/constOutbound webhook dispatch + helpers.
signWebhook, newWebhookSecretfnHMAC signing for webhook payloads (also for verifying receivers).
emitPluginWebhookEvent, allPluginWebhookEventsfnA plugin’s DECLARED webhook events (webhookEvents): emit one through the core delivery (refused for an undeclared event, another plugin’s, or a core namespace such as order.*), and list what active plugins declared. A compiled plugin uses host.webhooks.emit, which is the first bound to its own ids.
requestClient, RequestClientfn/typeWho a request is for — { ip, ua, forwarded } — from Astro.locals. X-Client-IP / X-Client-UA count only on an API-key request; ip is the same identity the rate limiter counts. Plugin routes get it as ctx.client.
sendEmail, getEmailTransport, setEmailTransport, consoleTransport, webhookTransport, emailChannelActive, emailChannelLabel, EmailMessage, EmailTransport, EmailAttachmentfn/typePluggable email transport; messages may carry attachments. emailChannelActive() is false while mail only goes to the server console, so a feature whose point is delivery can say so.
recordAudit, AUDIT, AuditDetailsfn/const/typeSecurity audit log — record sensitive actions from plugins/custom routes.
reportErrorfnStructured error reporting from a plugin or custom route. Never throws, so it is safe on an error path.
plainText, countWords, sentences, decodeEntities, truncateWordsfnText extraction from stored HTML. Exported so a plugin computing a read time or an excerpt uses the same stripper as the byline and the editor’s analysis panel — five private copies once existed and two disagreed about whether a tag boundary is a word boundary. The bundled reading-time plugin is the worked example.
organizationNode, webSiteNode, articleNode, webPageNode, collectionPageNode, productNode, siteIdentityNodes, structuredData, SdContext, ArticleInput, ProductInputfn/typesschema.org builders. Pure functions, no I/O — a decoupled storefront that renders its own product pages emits the same nodes the built-in front end does.
createPluginStore, PluginStore, PluginListOptions, LocalDBfn/type/valueNamespaced plugin storage (list(collection, { uncapped: true }) opts out of the 5,000-record cap) for use OUTSIDE a route handler (filters, actions, scheduled work); inside one prefer ctx.store, already bound to the owning plugin. LocalDB is promised only as the backend argument to createPluginStore — its own wider surface is internal and may change.
getPluginSettings, getPluginSettingfnA plugin’s own settings — the record an operator edits on its admin screen. Scoped to a plugin id, so a plugin cannot read its neighbour’s. Read where you use the value, not once at activate(): an operator who changes a setting does not restart the site.
PluginCatalog, CatalogProduct, CatalogBrand, CatalogIterateOptionstypesThe shapes of host.catalog, the read-only product and brand reads a compiled external plugin is handed. Types only: the values arrive through the factory’s host, never through an import. See “The contract” in PLUGIN_DEVELOPMENT.md.
PluginCoupons, IssueCouponInput, IssuedCoupon, CouponStatustypesThe shapes of host.coupons — issue, status, revoke — through which a compiled plugin hands out discount codes the checkout honours. Types only, like host.catalog. See “Issuing coupons” in PLUGIN_DEVELOPMENT.md.
PaymentProvider, PaymentOutcome, PaymentStatus, WebhookVerificationError, ManualMethodDeftypes/classThe payment-gateway contract, so a gateway can be shipped as a plugin without reading src/lib/payments/*. A provider’s own verification error must merely END in VerificationError for the platform to answer 401 rather than 500.
api, apiFetch, configureApiClient, ApiResult, ApiFetchOptionsfn/typesCSRF-aware browser fetch helper, for client-side scripts a theme or a plugin admin page ships. Server-side callers want astrobaas/client instead.

astrobaas/client (frontend SDK)

ExportKindNotes
createClient, AstroBaasClientfn/classTyped REST client over bearer auth.
AstroBaasErrorclassThrown on non-2xx / {success:false}; carries status, code, details.
verifyWebhookSignaturefnReceiver-side HMAC verifier (WebCrypto, constant-time).
toSelectable, selectableVariants, variantAxes, axisStates, optionStates, reconcileSelection, matchVariant, selectionFor, initialSelection, missingAxes, selectionLabel, blockedText, droppedChoices, swatchAxes, swatchOf, swatchColor, swatchImage, isLightColor, SelectableVariant, VariantAxis, OptionBlock, OptionState, VariantSelection, VariantLike, Swatch, SwatchAttributeLikefn/typesVariant picker logic, pure and dependency-free: options in the merchant’s order, each value judged unavailable or sold-out against the choices made before it, and the variant a full choice names. A value the shopper picked is never swapped for another: a choice an earlier change invalidates is dropped and reported by droppedChoices. buyable comes from the CMS’s stock_status.purchasable, never a second stock rule. swatchAxes decides which options are drawn as colour or image swatches (marked display: 'swatch', or every value has one) and re-validates each swatch. The storefront starter’s VariantSelector is the worked example.
ClientOptions, OrderTimelineItem, ListPostsOptions, ListProductsOptions, ProductSort, ListOrdersOptions, Page, PageMeta, CreatePostInput, UpdatePostInput, RelatedPostSummary, BasketLine, CheckoutAddress, CheckoutInput, CreateKeyInput, CreatedKey, ApiKeyInfo, RegisterWebhookInput, RegisteredWebhook, WebhookInfo, WebhookDeliveryInfo, AuditEventInfo, LocaleConfig, ContentApi, CurrentPrincipal, ApiKeyPrincipaltypesSDK input/output shapes.

Exported, but not part of the promise

A module can export something because the application itself imports it across files. That is not a promise to anyone outside. These names are reachable and are not covered:

ExportWhereWhy it is not promised
ensurePluginsBootstrapped, reloadPlugins, pluginBootstrapReport, PluginBootstrapReportastrobaas/pluginsThe bundled-plugin loading mechanism. The middleware and a dozen routes call these; a plugin must not, because when they run and what they rebuild is exactly the internal detail this document reserves the right to change.
redeliversrc/lib/webhooks.tsRe-sending a logged delivery is an operator action with its own authenticated route (POST /api/webhooks/deliveries/{id}/redeliver) and client method (baas.webhooks.redeliver(id)). Go through those, so the role check runs.

tests/stability.test.mjs asserts this list is exhaustive: every export of the three public barrels is either documented above or named here, so the document cannot drift behind the code again.

Policy

While pre-1.0:

  • Additive changes (new exports, new optional fields, new hooks) can land in any minor release.
  • Breaking changes to the public surface are allowed but will be:
    1. listed under Breaking in the CHANGELOG, and
    2. accompanied by the reason and a migration note.
  • Hook names in PLUGIN_HOOKS will not be silently renamed or removed — a removed hook is a documented breaking change.

At 1.0 and after the public surface follows semver: breaking changes only in major versions.

What is explicitly not covered

  • Internal modules (src/lib/*, src/middleware.ts, route files, components).
  • The on-disk db.json format (it’s seeded/migrated by the app; don’t write it directly — go through Storage).
  • The admin UI markup/DOM structure.
  • The bundled-plugin loading mechanism’s internals (use the documented BUNDLED_PLUGINS registry).

Hook contract

Each hook in PLUGIN_HOOKS documents its kind (filter/action) and payload in PLUGIN_DEVELOPMENT.md. Filters must return the same type they receive. post_content filter output is re-sanitized at the render boundary, so a content filter cannot introduce XSS.