Skip to main content

Architecture

How @treatink/sdk is structured, and the boundaries the design guarantees.

1. Where the SDK sits in the platform

The SDK is the engine's only supported browser client. The dependency direction is fixed:

Treatink API engine ← source of truth

│ HTTP (publishable key) │ HTTP (secret key, server only)
│ │
@treatink/sdk ───────────────────────┘
▲ (browser) (@treatink/sdk/server)

consumers: partner storefronts, treatink.com D2C, the Shopify app

The SDK owns the personalization moment — the designer modal plus the client for publishable-key operations. The partner owns everything else: product pages, cart, checkout, payment. Order submission is the partner's server responsibility; the SDK only pre-wires it.

2. Package shape

One repository, two published forms:

  • Browser bundle — an ESM build exposing the Treatink entry, with the designer UI lazy-loaded as a separate chunk on first designer.open(). The loader stays small (≤ 15 KB gzipped), is async/defer safe, never calls document.write, and ships an SRI hash per release.
  • npm package@treatink/sdk: ESM + TypeScript types. The browser entry has no secret-key code path. A separate @treatink/sdk/server entry (Node ≥ 18) performs the one secret-key operation: order submission.

3. The one architectural seam: Transport

Everything backend-facing goes through a single Transport interface with two implementations — HttpTransport (live) and FixtureTransport (bundled backend simulation). Nothing above the transport knows which backend is live. Switching mode: 'live' | 'fixtures' swaps the implementation and nothing else.

  • API namespaces (products, templates, artwork, …) call the transport; they never call fetch directly.
  • The transport is the only place URLs, headers, auth, and retry logic live.
  • Fixtures reproduce the documented wire contract exactly — IDs, statuses, error envelope — so flipping to live requires no consumer code change.

4. The catalog adapter

All catalog parsing sits behind a single adapter: wire shape in, stable internal model out. Consumers and the designer only ever see the internal model, so backend catalog schema changes are isolated to one file and never surface as breaking SDK changes.

5. The cutout engine is pure

The compositing engine (geometry, transforms, text placement, export) has zero DOM dependencies; canvas is injected at the edges. That makes it fully testable in isolation and runnable in Node — the same module can drive future server-side print rendering. One engine on both sides is what backs the WYSIWYG guarantee: what the shopper saves is what gets printed.

6. Security boundaries

  • Treatink.init validates the key prefix: only publishable keys are allowed; a secret key throws key_scope_violation immediately. The browser bundle contains no code that attaches a secret key to any request — enforced by construction and by a build-time check.
  • Keys go only in Authorization: Bearer, never in URLs, never logged.
  • No third-party requests of any kind.
  • @treatink/sdk/server is the only place a secret key is used, and it is a separate entry point never imported by the browser build.

See Security & privacy for the full checklist.

7. Rendering model (light DOM)

The modal renders in the light DOM with tk--prefixed classes and one injected stylesheet with a scoped reset. Host customization is init theme params (→ CSS variables) plus ordinary host CSS targeting the documented tk- classes — see Theming.

8. Compatibility targets

Evergreen Chrome/Edge/Firefox (last two majors) and Safari ≥ 16 including iOS. No IE. Main-thread canvas compositing at 900×1200 is within budget; on memory-constrained iOS Safari the SDK caps in-browser composite dimensions rather than crashing.