This guide explains the repository structure after the package migration. It
is the starting point for deciding where a change belongs. For detailed
pipeline behavior and hard correctness contracts, use
ARCHITECTURE.md.
PolyStella publishes five packages. The canonical Astro package and its compatibility package form one fixed version group. Core, adapters, and providers are versioned independently. Arrows mean "depends on."
flowchart TD
compat["@cloudflare/polystella<br/>compatibility only"] --> astro["@cloudflare/polystella-astro<br/>canonical Astro package"]
astro --> adapters["@cloudflare/polystella-adapters<br/>portable formats"]
astro --> providers["@cloudflare/polystella-providers<br/>portable transports"]
astro --> core["@cloudflare/polystella-core<br/>translation protocol"]
adapters --> core
providers --> core
Dependencies point toward reusable code. Core never imports adapters, providers, or Astro. Adapters and providers do not import each other. The compatibility package contains no implementation and points only to the canonical Astro package.
Published dependencies on independently versioned packages use compatible caret ranges. The compatibility package pins the exact Astro version because it forwards that package's API and CLI unchanged.
| Directory | Published package | Responsibility |
|---|---|---|
packages/core/ |
@cloudflare/polystella-core |
Translation contracts, prompts, batching, retries, and response parsing. |
packages/adapters/ |
@cloudflare/polystella-adapters |
Portable parsing, extraction, grouping, and translation application. |
packages/providers/ |
@cloudflare/polystella-providers |
Workers AI and Anthropic implementations of the core translator contract. |
packages/astro/ |
@cloudflare/polystella-astro |
Canonical Astro integration, host policy, storage, routing, runtime APIs, and CLI. |
packages/polystella/ |
@cloudflare/polystella |
Temporary compatibility forwarding to @cloudflare/polystella-astro. |
The three reusable packages form an in-process translation pipeline:
flowchart LR
source[Source bytes or record] --> parse[Adapter parses and extracts segments]
parse --> orchestrate[Core groups, batches, prompts, and validates]
orchestrate --> transport[Provider calls the selected model]
transport --> orchestrate
orchestrate --> apply[Adapter applies translations]
apply --> output[Translated output]
The adapter owns source syntax. Core owns the translation protocol. The provider owns transport-specific I/O. None of these layers needs Astro.
@cloudflare/polystella-core is the lowest internal layer.
It owns:
Segment,Glossary,Logger, andTranslatorcontracts.- Prompt construction and provider-response parsing.
- Token estimation, grouping validation, and batch packing.
- Translation execution, retries, cancellation, and permanent provider errors.
Start at packages/core/src/index.ts. The main
implementations are translator.ts, prompt.ts, batch.ts,
translate-batch.ts, and translate-segments.ts.
Core does not know about file formats, R2, the filesystem, Astro, or any specific AI transport.
@cloudflare/polystella-adapters owns portable format
handling:
- The
FileAdaptercontract. - Markdown, MDX, JSON, YAML, and TOML adapters.
- Segment extraction, grouping, and translation application.
- Structured key paths, MDX rules, and placeholder handling.
Start at packages/adapters/src/index.ts
and packages/adapters/src/adapters/.
Adapters depend on core for Segment and related contracts.
Astro-specific defaults, configuration, staging, and URL policy do not belong
here. Those wrappers live under
packages/astro/src/parsing/.
@cloudflare/polystella-providers implements the
core Translator contract for external model APIs:
- Workers AI over HTTP.
- Workers AI through a binding described with package-owned structural types.
- Anthropic over HTTP.
- Transport error normalization and permanent/retriable classification.
Start at packages/providers/src/index.ts,
workers-ai.ts, anthropic.ts, and http-error.ts. Providers depend only on
core internally.
Provider configuration belongs to the Astro package. The mapping from Astro
options to provider factories is
packages/astro/src/translation/provider.ts.
@cloudflare/polystella-astro is the canonical product
package. It composes the reusable packages and owns host-specific behavior:
- Astro hooks, option validation, and virtual modules.
- Source walking, translation-pass orchestration, and local staging.
- R2 keys, reads, writes, metadata, local indexes, reports, and pruning.
- Overrides, AI markers, and URL rewriting policy.
- Content collections, custom-loader support, runtime lookup, and middleware.
- Route shims, UI strings, catalog-only mode, React hooks, recipes, and CLI.
The primary entry points are:
| Area | Start here |
|---|---|
| Integration hooks | packages/astro/src/index.ts |
| Configuration | packages/astro/src/config/options.ts |
| Translation pass | packages/astro/src/translation/run.ts |
| Storage and cache | packages/astro/src/storage/ |
| Format policy | packages/astro/src/parsing/ |
| Content collections | packages/astro/src/content/ |
| Runtime APIs | packages/astro/src/runtime/ |
| Routing | packages/astro/src/routing/ |
| UI strings | packages/astro/src/i18n/ |
| Catalog-only mode | packages/astro/src/catalog/ |
| CLI dispatch | packages/astro/src/cli.ts |
Its public export map is declared in
packages/astro/package.json. The standalone
polystella executable is emitted from src/cli.ts. The ./client export is
types-only and comes from packages/astro/client.d.ts.
@cloudflare/polystella is not an implementation
layer. It exists so projects using the old package name can migrate without an
immediate import rewrite.
- Every source entry re-exports the matching
@cloudflare/polystella-astroentry. client.d.tsreferences the canonical client declarations.- Its CLI launches the canonical package's CLI.
- It must not gain independent behavior or restore low-level exports moved to core, adapters, or providers.
When adding an Astro public export, update the canonical manifest, add the
matching forwarding file and export in packages/polystella/, update the
public export reference, and run pnpm check:packages.
The integration and standalone CLI share
runTranslationPass. Astro-specific
setup remains in the integration entry.
sequenceDiagram
participant Astro
participant Integration as astro/src/index.ts
participant Run as translation/run.ts
participant Cache as local index and R2
participant Content as Astro content layer
Astro->>Integration: astro:config:setup
Integration->>Run: runTranslationPass()
Run->>Cache: override, local index, R2, or provider
Run-->>Integration: staged files and run metadata
Integration->>Integration: publish custom-loader bridge
Astro->>Content: content sync
Content->>Content: regular siblings read staged files
Content->>Integration: custom-loader siblings use bridge
Astro->>Integration: astro:build:done
Integration->>Integration: emit build report
Translation must run during astro:config:setup because Astro syncs content
before build:start. See ARCHITECTURE.md#hook-timing.
| Change | Package |
|---|---|
| Change prompts, batching, retry behavior, or translator contracts | Core |
| Parse or reconstruct a portable content format | Adapters |
| Add or change an external AI transport | Providers |
| Change Astro options, files, R2, routing, middleware, content, or CLI | Astro |
| Mirror a canonical Astro export under the old package name | Compatibility package |
If a change requires Node, Astro, filesystem, or R2 APIs, it does not belong in core, adapters, or providers. If a provider implementation starts importing Astro config types, move that mapping back to the Astro package instead.
The boundaries are executable, not only documented:
| Contract | Enforcement |
|---|---|
| Package dependency graph, exports, tarball contents, CLIs, and compatibility parity | scripts/check-packages.mjs |
| Reusable packages avoid Node and host imports | tests/boundaries/reusable-packages.test.ts |
Reusable packages execute under Workerd without nodejs_compat |
tests/workerd/ and scripts/check-workerd-portability.mjs |
| End-to-end extraction behavior remains stable | scripts/check-monorepo-baseline.mjs |
| Public export documentation matches manifests | docs/scripts/check-exports.ts |
| All packages version together | .changeset/config.json |
The root package.json builds in dependency order: core,
adapters, providers, canonical Astro, then compatibility forwarding.
Before opening a package-affecting change:
- Put the change in the lowest package that can own it without importing a higher layer.
- Export it only if downstream consumers need it; package manifests are the public API source of truth.
- Mirror new Astro exports in the compatibility package while that package is supported.
- Add or update the smallest boundary or behavior test that protects the contract.
- Add a Changesets entry and run
pnpm test,pnpm typecheck, andpnpm check:packages.
Use these stable sections for implementation details: