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
Treatinkentry, with the designer UI lazy-loaded as a separate chunk on firstdesigner.open(). The loader stays small (≤ 15 KB gzipped), isasync/defersafe, never callsdocument.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/serverentry (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 callfetchdirectly. - 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.initvalidates the key prefix: only publishable keys are allowed; a secret key throwskey_scope_violationimmediately. 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/serveris 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.