# JS SDK

Cea mai rapidă integrare: două linii de HTML, fără build, fără CMS. SDK-ul
randează blogul într-un container pe pagina ta existentă și gestionează
navigarea client-side.

```html
<div data-baas-blog
     data-key="pk_YOUR_KEY"
     data-base-path="/blog"
     data-api="https://api.dailysmith.com"></div>
<script src="https://api.dailysmith.com/v1/sdk/blog.js" async></script>
```

Pune asta pe pagina servită la calea de bază a blogului tău (`/blog` implicit).
Header-ul, footer-ul și stilurile tale rămân neschimbate — blogul se randează în
interiorul containerului și moștenește tipografia site-ului tău.

:::callout{severity=info title="Folosești un asistent AI de cod?"}
Toată această pagină există și ca playbook scris pentru agenți. Ia promptul din
[Instalare cu AI](/ro/docs/install-ai), lipește-l în asistentul tău, iar el
conectează fragmentul, configurează shell-ul pentru toate URL-urile blogului și
verifică livrarea pentru tine.
:::

## Atribute

| Atribut | Obligatoriu | Sens |
|---|---|---|
| `data-key` | da | Cheia publică a site-ului tău (`pk_…`, afișată pe pagina site-ului) |
| `data-api` | da | Originea API-ului de unde se încarcă conținutul — setează-l mereu; fragmentul de mai sus îl fixează la `https://api.dailysmith.com` |
| `data-base-path` | nu | Calea pe care e montat blogul; implicit `/blog`. Trebuie să corespundă cu calea de bază a blogului configurată în dashboard **și** cu calea URL sub care e servită această pagină. |
| `data-baas-head` | nu | Setează la `"false"` ca să ții SDK-ul complet în afara `<head>`-ului tău — vezi „Dacă site-ul tău își gestionează propriul head” mai jos. |
| `data-baas-theme` | nu | `"light"` sau `"dark"` fixează blogul pe o singură schemă de culoare, indiferent ce face pagina din jur; `"inherit"` retrage paleta blogului, ca propriul tău CSS să furnizeze fiecare token. Nesetat, blogul urmează `data-theme` de pe site-ul tău, apoi preferința de sistem a cititorului. Vezi [Teme](/ro/docs/theming). |

SDK-ul folosește **primul** element `[data-baas-blog]` de pe pagină; un singur
container de blog per pagină. Expune și `window.baasBlog`, cu `init(el?)` și
`destroy()`, pentru aplicații cu rutare client-side — vezi mai jos.

## Servește shell-ul pentru fiecare URL de blog

:::callout{severity=warning title="Cerința pe care oamenii o ratează cel mai des"}
SDK-ul împinge URL-uri reale (`/blog/my-post-slug`) pe măsură ce cititorii
navighează. Serverul tău trebuie să returneze pagina cu fragmentul pentru
**calea de bază și tot ce e sub ea** — altfel un refresh sau un link distribuit
către un articol lovește 404-ul tău înainte ca SDK-ul să apuce să se încarce.
SDK-ul citește URL-ul curent la inițializare, deci odată ce shell-ul e servit,
se randează articolul corect.
:::

Cum faci asta pe configurațiile obișnuite:

**Netlify** — `_redirects`:

```
/blog/*   /blog/index.html   200
```

**Vercel** — `vercel.json`:

```json
{ "rewrites": [{ "source": "/blog/:path*", "destination": "/blog" }] }
```

**nginx**:

```nginx
location /blog {
    try_files $uri /blog/index.html;
}
```

**Framework-uri** — folosește o rută catch-all ca pagină de blog și pune
fragmentul în ea: `app/blog/[[...slug]]/page.tsx` (Next.js),
`src/routes/blog/[...rest]/+page.svelte` (SvelteKit),
`src/pages/blog/[...slug].astro` (Astro).

### Compromisul: acum orice URL de blog răspunde cu 200

Rewrite-ul de shell înseamnă că `/blog/orice-cale` returnează HTTP 200 cu
shell-ul — statusul real se află la un hop distanță, în API. Iată ce se
întâmplă în această zonă de soft-404:

- Când API-ul returnează **404 pentru un articol lipsă**, SDK-ul randează o
  stare distinctă de „nu a fost găsit”, setează `<meta name="robots" content="noindex">`,
  golește canonical-ul și JSON-LD-ul, și declanșează un eveniment **anulabil**
  `baas:notfound` — apelează `preventDefault()` pe el ca să randezi propriul tău
  UI de 404 în loc.
- Eșecurile de rețea și erorile de server sunt separate: SDK-ul declanșează
  `baas:error` cu `{status}` (`0` înseamnă că fetch-ul însuși a eșuat) și
  afișează un mesaj de „temporar indisponibil”.
- Dacă ai nevoie ca URL-urile necunoscute să răspundă cu un **HTTP 404 real**
  pentru crawlere, exact asta oferă căile de [edge proxy](/ro/docs/install-proxy)
  și [prerender static](/ro/docs/install-prerender) — statusul API-ului trece
  direct.

## Aplicații cu rutare client-side (React, Vue, SvelteKit, …)

Scriptul se leagă (bind) când se execută, iar re-executarea lui (sau apelarea
`window.baasBlog.init()`) se re-leagă idempotent: instanța anterioară e
distrusă mai întâi, deci nu se acumulează niciodată listeneri. `destroy()`
dezleagă listenerul de `popstate`, golește containerul și restaurează fiecare
nod din `<head>` pe care SDK-ul l-a creat sau suprascris.

Într-o aplicație care schimbă rutele client-side, conectează-l la ciclul de
viață al rutei de blog:

```jsx
// Exemplu React — aceeași structură funcționează în hook-ul de mount al oricărui framework
useEffect(() => {
  if (window.baasBlog) {
    window.baasBlog.init();            // SDK deja încărcat: re-leagă la acest container
  } else {
    const s = document.createElement('script');
    s.src = 'https://api.dailysmith.com/v1/sdk/blog.js';
    s.async = true;
    document.body.appendChild(s);      // se execută și se leagă la load
  }
  return () => window.baasBlog && window.baasBlog.destroy();
}, []);

return <div data-baas-blog data-key="pk_YOUR_KEY"
            data-base-path="/blog" data-api="https://api.dailysmith.com" />;
```

Alternativă: **navigare hard (full page load) către blog.** Dacă linkurile
tale către blog sunt tag-uri `<a>` simple care declanșează un page load
complet (scoate ruta de blog din interceptarea de linkuri a routerului tău),
fragmentul static funcționează așa cum e.

Încă o observație despre router: SDK-ul navighează cu `history.pushState`,
lucru pe care routerele client-side nu îl pot observa. Declanșează
`baas:navigate` după fiecare push, ca analytics-ul, breadcrumbs-urile sau
managerii de scroll să se poată conecta la el (vezi Evenimente).

## Dacă site-ul tău își gestionează propriul head

Sub calea de bază, SDK-ul deține `document.title`, meta-tagurile de descriere
și `og:*`, link-ul canonical și JSON-LD-ul. Acesta e contractul — nu le mai
gestiona și din routerul tău propriu cât timp un URL de blog e activ, altfel
cele două sisteme se vor lupta între ele. SDK-ul marchează fiecare nod pe care
îl creează cu un atribut `data-baas`, reține valoarea originală a oricărui
nod-gazdă pe care îl suprascrie și pune totul înapoi la `destroy()`.

Dacă site-ul tău are propriul strat de SEO, renunță în schimb: setează
`data-baas-head="false"` pe container și aplică tu metadatele, din evenimentul
`baas:page`, care poartă tot ce are nevoie head-ul.

## Evenimente

Toate evenimentele sunt `CustomEvent`-uri care urcă (bubble) din container,
deci poți asculta pe `document`:

| Eveniment | Detaliu | Note |
|---|---|---|
| `baas:page` | `{path, kind, title, description, canonical, json_ld, locale, og_locale, hero_image_url}` | După fiecare randare reușită (`kind` e `index` sau `post`) |
| `baas:notfound` | `{path}` | Articol lipsă (API 404). Anulabil — `preventDefault()` și randează propriul tău 404 |
| `baas:error` | `{status, path}` | Fetch eșuat; `status: 0` înseamnă eroare de rețea. Anulabil |
| `baas:navigate` | `{path}` | După `history.pushState` — routerele nu îl pot vedea, acesta e hook-ul tău |

```js
document.addEventListener('baas:notfound', (e) => {
  e.preventDefault();
  renderMyOwn404();
});
document.addEventListener('baas:navigate', (e) => {
  analytics.pageview(e.detail.path);
});
```

## Content-Security-Policy

Un site cu CSP strict are nevoie de originea API-ului în patru directive:

```
script-src  'self' https://api.dailysmith.com          # scriptul SDK-ului propriu-zis
connect-src 'self' https://api.dailysmith.com          # fetch-urile de conținut (fragment)
img-src     'self' data: https://api.dailysmith.com    # imaginile hero ale articolelor
style-src   'self' https://api.dailysmith.com 'unsafe-inline'
```

`style-src` are nevoie de originea API-ului pentru foaia de stil a
conținutului bogat (încărcată ca `<link>`), iar `'unsafe-inline'` doar dacă
blogul tău are o [paletă de temă](/ro/docs/theming) — tema per-blog e injectată
ca un singur element `<style>` inline, mic.

:::callout{severity=warning title="img-src e cea care se uită"}
Imaginile hero sunt servite de pe originea API-ului, așa că valoarea
implicită strictă obișnuită `img-src 'self' data:` elimină fiecare hero —
fără vreo eroare vizibilă și doar pe articolele care au unul. Indexul și
articolele fără hero vor arăta perfect în regulă.
:::

## Ce face SDK-ul

- Preia fragmente HTML randate server-side și le injectează în container
  (`aria-busy` e setat cât timp se încarcă).
- Actualizează `document.title`, meta descrierea, tagurile Open Graph,
  link-ul canonical și JSON-LD-ul per pagină — marcate cu `data-baas`,
  restaurate la `destroy()`.
- Interceptează linkurile din interiorul blogului pentru navigare
  instantanee prin History API; back/forward funcționează cum te aștepți;
  `baas:navigate` se declanșează pentru routerul sau analytics-ul tău.
- Încarcă o singură dată foaia de stil a componentelor de conținut bogat, ca
  un `<link>` cacheable (`#baas-md2-css` → `https://api.dailysmith.com/v1/sdk/md2.css`) și
  injectează [paleta de temă](/ro/docs/theming) a blogului tău ca un `<style>`
  inline mic (`#baas-theme-css`), înlocuindu-l când se schimbă.

## Site-uri statice și hosturi prerandate

Două lucruri contează când site-ul tău e prerandat sau complet static:

- Regula de rewrite de la „Servește shell-ul” trebuie **emisă de build-ul
  tău** (un fișier `_redirects` commit-uit, un `vercel.json` în repo) — o
  regulă adăugată manual dintr-un dashboard dispare la următoarea schimbare a
  pipeline-ului de deploy.
- Până rulează JavaScript-ul, fiecare URL de articol servește `<head>`-ul
  shell-ului prerandat — titlul și canonical-ul shell-ului, nu ale
  articolului. Crawlerele care execută JS primesc valorile corecte; pentru
  HTML garantat per URL, rulează [CLI-ul de prerender](/ro/docs/install-prerender)
  la build — output-ul lui dublează și rolul de shell.

## Verifică că funcționează

::::steps

:::step[Deschide indexul blogului]
Vizitează `/blog` pe site-ul tău. Lista de articole ar trebui să se randeze
în interiorul chrome-ului paginii tale. În tab-ul network din devtools vei
vedea o cerere către `https://api.dailysmith.com/v1/render/pk_…/blog?format=fragment` —
acesta e și verificarea care funcționează pentru instalările SPA, unde
script tag-ul e injectat la runtime și nu apare niciodată în HTML-ul servit.
:::

:::step[Intră într-un articol, apoi dă refresh]
URL-ul ar trebui să se schimbe în `/blog/<slug>`, titlul documentului ar
trebui să se actualizeze — iar un hard refresh pe acel URL trebuie să
randeze același articol. Dacă refresh-ul dă 404, revizitează „Servește
shell-ul pentru fiecare URL de blog” mai sus.
:::

:::step[Verifică head-ul și starea de 404]
După ce deschizi un articol, head-ul documentului ar trebui să poarte
titlul, meta descrierea, URL-ul canonical și un tag `#baas-jsonld` de
script. Apoi vizitează `/blog/definitely-not-a-post`: ar trebui să vezi
starea de „nu a fost găsit”, iar head-ul ar trebui să poarte
`<meta name="robots" content="noindex">`.
:::

::::

## SEO: combină-l cu livrare server-side

SDK-ul de unul singur randează după ce se încarcă JavaScript-ul. Crawlerele
moderne execută JS, dar pentru SEO garantat la nivel de crawler, servește
același conținut și static:

- [Edge proxy](/ro/docs/install-proxy) — o regulă de rewrite servește pagini SSR
  complete de pe domeniul tău; SDK-ul apoi face upgrade la navigare
  client-side. (Regula de proxy satisface și cerința de shell de mai sus,
  iar URL-urile necunoscute returnează 404-uri reale.)
- [Prerender static](/ro/docs/install-prerender) — generează fișiere HTML la
  deploy; paginile prerandate dublează și rolul de shell pentru fiecare URL
  de articol.
