@intelex-ia/sdk 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,310 +1,310 @@
1
- # `@intelex-ia/sdk`
2
-
3
- SDK pra consumir a **Content API IntelexIA** em sites headless (Next.js, Astro etc.). Multiplataforma Fase 2b.
4
-
5
- > **O que você ganha:** `getArticles` / `getArticleBySlug` tipados, `mergePosts()` pra unir conteúdo antigo + novo do painel, `<ArticleJsonLd>` pra injetar schema JSON-LD, e `articleToMetadata` pra plugar no `generateMetadata` do Next.js App Router. Zero dependências (usa `fetch` global).
6
-
7
- ## Instalação rápida (recomendado)
8
-
9
- Se você está começando agora, use o CLI — ele detecta a estrutura do seu projeto e configura tudo automaticamente:
10
-
11
- ```bash
12
- npx @intelex-ia/cli init
13
- ```
14
-
15
- Cria os arquivos no lugar certo (App Router ou Pages Router, com ou sem `src/`), configura `.env.local`, gera um `INTELEXIA.md` completo na raiz e instala o SDK pra você.
16
-
17
- ## Instalação manual
18
-
19
- ```bash
20
- npm install @intelex-ia/sdk
21
- # ou pnpm add @intelex-ia/sdk / yarn add @intelex-ia/sdk
22
- ```
23
-
24
- ## Configuração
25
-
26
- 1. No painel da IntelexIA, **cadastre o site como "Headless (código)"** (vai pedir domain + URL e secret de revalidate).
27
- 2. Abra o site na lista → seção **"Tokens de API (Content API)"** → **Gerar token**. Você recebe `itx_…` (mostrado uma única vez).
28
- 3. Coloque em `.env.local`:
29
-
30
- ```bash
31
- INTELEXIA_API_TOKEN=itx_seu_token_aqui
32
- INTELEXIA_API_URL=https://api.innovarticles.intelexia.com.br # opcional, é o default
33
- INTELEXIA_REVALIDATE_SECRET=cole-o-mesmo-segredo-do-painel
34
- ```
35
-
36
- ## Três modos de integração
37
-
38
- Cada cliente tem uma situação diferente. Escolha o modo que faz sentido:
39
-
40
- | Modo | Quando usar | O que muda no `/blog` |
41
- |---|---|---|
42
- | **🔀 Additive (recomendado)** | Cliente já tem posts antigos e quer **adicionar** os do painel sem perder os antigos | `mergePosts()` une os dois feeds, dedupa por slug, ordena por data |
43
- | **🔁 Replace** | Cliente migrou tudo pro painel — só os novos contam | Substitui `getAllPosts()` por `client.getArticles()` |
44
- | **🆕 Isolated** | Cliente quer manter `/blog` antigo intacto e criar rota nova só pra IA | Cria `/blog/ia` separado, blog antigo zero alterado |
45
-
46
- ---
47
-
48
- ### Modo 1: Additive (cliente existente — caso mais comum)
49
-
50
- ```tsx
51
- // app/blog/page.tsx
52
- import { intelexia } from "@/lib/intelexia";
53
- import { mergePosts } from "@intelex-ia/sdk";
54
- import { posts as localPosts } from "@/lib/posts"; // seu array atual
55
- import Link from "next/link";
56
-
57
- export default async function BlogIndex() {
58
- // 1) Busca os artigos do painel
59
- const { items: remote } = await intelexia.getArticles({ limit: 50 });
60
-
61
- // 2) Mistura com os seus posts antigos
62
- const all = mergePosts({
63
- local: localPosts,
64
- remote,
65
- mapLocal: (p) => ({
66
- slug: p.slug,
67
- title: p.title,
68
- excerpt: p.excerpt,
69
- publishedAt: p.date,
70
- featuredImageUrl: null,
71
- category: p.category,
72
- }),
73
- // Opcional — defaults sensatos:
74
- // dedupe: "remote-wins" (painel substitui local se slug colide)
75
- // sortBy: "date-desc" (mais recente primeiro)
76
- });
77
-
78
- return (
79
- <ul>
80
- {all.map((p) => (
81
- <li key={p.slug}>
82
- <Link href={`/blog/${p.slug}`}>{p.title}</Link>
83
- <time>{new Date(p.publishedAt).toLocaleDateString("pt-BR")}</time>
84
- {p.source === "remote" && <span className="badge-ia">✨ IA</span>}
85
- </li>
86
- ))}
87
- </ul>
88
- );
89
- }
90
- ```
91
-
92
- Pro post individual, tenta o painel primeiro, cai pro local se 404:
93
-
94
- ```tsx
95
- // app/blog/[slug]/page.tsx
96
- import { intelexia } from "@/lib/intelexia";
97
- import { IntelexiaApiError } from "@intelex-ia/sdk";
98
- import { ArticleJsonLd, articleToMetadata } from "@intelex-ia/sdk/react";
99
- import { getPostBySlug } from "@/lib/posts";
100
- import { notFound } from "next/navigation";
101
-
102
- export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
103
- const { slug } = await params;
104
-
105
- // 1) Tenta painel
106
- try {
107
- const { article } = await intelexia.getArticleBySlug(slug);
108
- return (
109
- <article>
110
- <ArticleJsonLd article={article} />
111
- <h1>{article.title}</h1>
112
- <div dangerouslySetInnerHTML={{ __html: article.contentHtml ?? "" }} />
113
- </article>
114
- );
115
- } catch (err) {
116
- if (!(err instanceof IntelexiaApiError) || err.status !== 404) throw err;
117
- }
118
-
119
- // 2) Fallback: post antigo local
120
- const local = getPostBySlug(slug);
121
- if (!local) notFound();
122
- return (
123
- <article>
124
- <h1>{local.title}</h1>
125
- {/* renderiza o local do jeito que você já fazia */}
126
- </article>
127
- );
128
- }
129
- ```
130
-
131
- ### Modo 2: Replace (migração completa)
132
-
133
- ```tsx
134
- // app/blog/page.tsx
135
- import { intelexia } from "@/lib/intelexia";
136
-
137
- export default async function BlogIndex() {
138
- const { items } = await intelexia.getArticles({ limit: 20 });
139
- return (
140
- <ul>
141
- {items.map((a) => (
142
- <li key={a.id}>
143
- <Link href={`/blog/${a.slug}`}>{a.title}</Link>
144
- </li>
145
- ))}
146
- </ul>
147
- );
148
- }
149
- ```
150
-
151
- Pro post individual, sem fallback:
152
-
153
- ```tsx
154
- import { intelexia } from "@/lib/intelexia";
155
- import { IntelexiaApiError } from "@intelex-ia/sdk";
156
- import { ArticleJsonLd, articleToMetadata } from "@intelex-ia/sdk/react";
157
- import { notFound } from "next/navigation";
158
-
159
- async function fetchArticle(slug: string) {
160
- try {
161
- return await intelexia.getArticleBySlug(slug);
162
- } catch (err) {
163
- if (err instanceof IntelexiaApiError && err.status === 404) notFound();
164
- throw err;
165
- }
166
- }
167
-
168
- export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
169
- const { slug } = await params;
170
- const { article } = await fetchArticle(slug);
171
- return articleToMetadata(article);
172
- }
173
-
174
- export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
175
- const { slug } = await params;
176
- const { article } = await fetchArticle(slug);
177
- return (
178
- <article>
179
- <ArticleJsonLd article={article} />
180
- <h1>{article.title}</h1>
181
- <div dangerouslySetInnerHTML={{ __html: article.contentHtml ?? "" }} />
182
- </article>
183
- );
184
- }
185
- ```
186
-
187
- ### Modo 3: Isolated (rota nova, blog antigo intacto)
188
-
189
- Cria `app/blog/ia/page.tsx` e `app/blog/ia/[slug]/page.tsx` com o mesmo código do modo Replace, mas em rota separada (`/blog/ia` em vez de `/blog`). Zero alteração no blog existente.
190
-
191
- ---
192
-
193
- ## Cliente — `lib/intelexia.ts`
194
-
195
- ```ts
196
- import { createClient } from "@intelex-ia/sdk";
197
-
198
- export const intelexia = createClient({
199
- token: process.env.INTELEXIA_API_TOKEN!,
200
- // ISR: revalida a cada 60s; o webhook revalidate invalida sob demanda
201
- init: { next: { revalidate: 60, tags: ["intelexia-articles"] } },
202
- });
203
- ```
204
-
205
- ## Webhook de revalidate — `app/api/revalidate/route.ts`
206
-
207
- Quando um artigo publica/edita no painel, nosso worker faz `POST` no seu endpoint. Implemente assim:
208
-
209
- ```ts
210
- import { NextResponse } from "next/server";
211
- import { revalidatePath, revalidateTag } from "next/cache";
212
-
213
- export async function POST(req: Request) {
214
- const headerSecret = req.headers.get("x-revalidate-secret");
215
- const body = (await req.json().catch(() => ({}))) as {
216
- secret?: string;
217
- slug?: string | null;
218
- domain?: string;
219
- };
220
- const got = headerSecret ?? body.secret ?? "";
221
- if (!process.env.INTELEXIA_REVALIDATE_SECRET || got !== process.env.INTELEXIA_REVALIDATE_SECRET) {
222
- return NextResponse.json({ ok: false, error: "unauthorized" }, { status: 401 });
223
- }
224
-
225
- if (body.slug) revalidatePath(`/blog/${body.slug}`);
226
- revalidatePath("/blog");
227
- revalidateTag("intelexia-articles");
228
-
229
- return NextResponse.json({ ok: true, revalidated: { slug: body.slug ?? null } });
230
- }
231
- ```
232
-
233
- > O worker envia o secret tanto no header `x-revalidate-secret` quanto no body, tem timeout de 15s e é **fire-and-forget** (se seu endpoint falhar, o artigo continua publicado e a ISR atualiza pelo `revalidate: 60`).
234
-
235
- ## API
236
-
237
- ### `createClient(config)`
238
-
239
- | Opção | Tipo | Default |
240
- | -------- | -------------------- | ------------------------------------------------- |
241
- | `token` | `string` (obrig.) | — |
242
- | `apiUrl` | `string` | `https://api.innovarticles.intelexia.com.br` |
243
- | `fetch` | `typeof fetch` | `globalThis.fetch` |
244
- | `init` | `RequestInit` | `undefined` (passe `{ next: { revalidate: N } }`) |
245
-
246
- ### `client.getArticles({ limit?, offset?, init? })`
247
-
248
- Resposta: `{ ok, mode, lifecycle, count, items: ArticleListItem[] }`.
249
-
250
- ### `client.getArticleBySlug(slug, { init? })`
251
-
252
- Resposta: `{ ok, mode, lifecycle, article: Article }`. **404 → joga `IntelexiaApiError`** (use `try/catch` + `notFound()`).
253
-
254
- ### `mergePosts({ local, remote, mapLocal, dedupe?, sortBy? })` 🆕 0.2.0
255
-
256
- | Opção | Tipo | Default |
257
- | ---------- | ------------------------------------------------- | ---------------- |
258
- | `local` | `readonly TLocal[]` (qualquer shape) | — |
259
- | `remote` | `readonly ArticleListItem[]` | — |
260
- | `mapLocal` | `(post: TLocal) => Omit<UnifiedPost,"source"> \| null` | — |
261
- | `dedupe` | `"remote-wins" \| "local-wins" \| "throw"` | `"remote-wins"` |
262
- | `sortBy` | `"date-desc" \| "date-asc" \| "none"` | `"date-desc"` |
263
-
264
- Retorna `UnifiedPost[]` com `source: "local" | "remote"`. `mapLocal` retornando `null` filtra o post (útil pra esconder rascunhos). Slugs duplicados resolvidos pela estratégia `dedupe`. Veja exemplo no Modo Additive acima.
265
-
266
- ### `mode: "full" | "plain"`
267
-
268
- - **`full`** (sites `active`/`paused`): inclui schema JSON-LD, metaTitle/Description, keyword(s), interlinking interno no `contentHtml`.
269
- - **`plain`** (site `cancelled`): só conteúdo cru — schema/meta/keywords vêm `null`, interlinking removido. O `ArticleJsonLd` e o `articleToMetadata` lidam graciosamente (omitem o que não tem).
270
-
271
- A decisão é **server-side e não-negociável** — o cliente não escolhe o modo.
272
-
273
- ### Helpers `/react`
274
-
275
- - `<ArticleJsonLd article={article} />` — renderiza `<script type="application/ld+json">` com o `schemaJson`. No-op se ausente.
276
- - `articleToMetadata(article)` → objeto compatível com o `Metadata` do Next.js App Router (title, description, keywords, openGraph, twitter, customMetaTags como `other`).
277
-
278
- ## Erros
279
-
280
- ```ts
281
- import { IntelexiaApiError, MergeConflictError } from "@intelex-ia/sdk";
282
-
283
- try {
284
- await intelexia.getArticleBySlug("post-inexistente");
285
- } catch (err) {
286
- if (err instanceof IntelexiaApiError) {
287
- console.error(err.status, err.message); // 404, "Artigo não encontrado."
288
- }
289
- }
290
-
291
- try {
292
- mergePosts({ local, remote, mapLocal, dedupe: "throw" });
293
- } catch (err) {
294
- if (err instanceof MergeConflictError) {
295
- console.error(`Conflito de slug: ${err.slug}`);
296
- }
297
- }
298
- ```
299
-
300
- ## Compatibilidade
301
-
302
- - **Node** 18+ (fetch nativo), Edge Runtime, Browser.
303
- - **React** 18+ (peer dep opcional — só pros helpers `/react`).
304
- - **TypeScript:** tipos publicados no `dist`.
305
- - **Frameworks:** Next.js (App Router e Pages Router), Astro, Remix, SvelteKit, Vite, qualquer JS.
306
-
307
- ## Changelog
308
-
309
- - **0.2.0** — adiciona `mergePosts()` + tipo `UnifiedPost` + `MergeConflictError` (3 modos de integração).
310
- - **0.1.0** — release inicial: `createClient`, `getArticles`, `getArticleBySlug`, `<ArticleJsonLd>`, `articleToMetadata`.
1
+ # `@intelex-ia/sdk`
2
+
3
+ SDK pra consumir a **Content API IntelexIA** em sites headless (Next.js, Astro etc.). Multiplataforma Fase 2b.
4
+
5
+ > **O que você ganha:** `getArticles` / `getArticleBySlug` tipados, `mergePosts()` pra unir conteúdo antigo + novo do painel, `<ArticleJsonLd>` pra injetar schema JSON-LD, e `articleToMetadata` pra plugar no `generateMetadata` do Next.js App Router. Zero dependências (usa `fetch` global).
6
+
7
+ ## Instalação rápida (recomendado)
8
+
9
+ Se você está começando agora, use o CLI — ele detecta a estrutura do seu projeto e configura tudo automaticamente:
10
+
11
+ ```bash
12
+ npx @intelex-ia/cli init
13
+ ```
14
+
15
+ Cria os arquivos no lugar certo (App Router ou Pages Router, com ou sem `src/`), configura `.env.local`, gera um `INTELEXIA.md` completo na raiz e instala o SDK pra você.
16
+
17
+ ## Instalação manual
18
+
19
+ ```bash
20
+ npm install @intelex-ia/sdk
21
+ # ou pnpm add @intelex-ia/sdk / yarn add @intelex-ia/sdk
22
+ ```
23
+
24
+ ## Configuração
25
+
26
+ 1. No painel da IntelexIA, **cadastre o site como "Headless (código)"** (vai pedir domain + URL e secret de revalidate).
27
+ 2. Abra o site na lista → seção **"Tokens de API (Content API)"** → **Gerar token**. Você recebe `itx_…` (mostrado uma única vez).
28
+ 3. Coloque em `.env.local`:
29
+
30
+ ```bash
31
+ INTELEXIA_API_TOKEN=itx_seu_token_aqui
32
+ INTELEXIA_API_URL=https://api.innovarticles.intelexia.com.br # opcional, é o default
33
+ INTELEXIA_REVALIDATE_SECRET=cole-o-mesmo-segredo-do-painel
34
+ ```
35
+
36
+ ## Três modos de integração
37
+
38
+ Cada cliente tem uma situação diferente. Escolha o modo que faz sentido:
39
+
40
+ | Modo | Quando usar | O que muda no `/blog` |
41
+ |---|---|---|
42
+ | **🔀 Additive (recomendado)** | Cliente já tem posts antigos e quer **adicionar** os do painel sem perder os antigos | `mergePosts()` une os dois feeds, dedupa por slug, ordena por data |
43
+ | **🔁 Replace** | Cliente migrou tudo pro painel — só os novos contam | Substitui `getAllPosts()` por `client.getArticles()` |
44
+ | **🆕 Isolated** | Cliente quer manter `/blog` antigo intacto e criar rota nova só pra IA | Cria `/blog/ia` separado, blog antigo zero alterado |
45
+
46
+ ---
47
+
48
+ ### Modo 1: Additive (cliente existente — caso mais comum)
49
+
50
+ ```tsx
51
+ // app/blog/page.tsx
52
+ import { intelexia } from "@/lib/intelexia";
53
+ import { mergePosts } from "@intelex-ia/sdk";
54
+ import { posts as localPosts } from "@/lib/posts"; // seu array atual
55
+ import Link from "next/link";
56
+
57
+ export default async function BlogIndex() {
58
+ // 1) Busca os artigos do painel
59
+ const { items: remote } = await intelexia.getArticles({ limit: 50 });
60
+
61
+ // 2) Mistura com os seus posts antigos
62
+ const all = mergePosts({
63
+ local: localPosts,
64
+ remote,
65
+ mapLocal: (p) => ({
66
+ slug: p.slug,
67
+ title: p.title,
68
+ excerpt: p.excerpt,
69
+ publishedAt: p.date,
70
+ featuredImageUrl: null,
71
+ category: p.category,
72
+ }),
73
+ // Opcional — defaults sensatos:
74
+ // dedupe: "remote-wins" (painel substitui local se slug colide)
75
+ // sortBy: "date-desc" (mais recente primeiro)
76
+ });
77
+
78
+ return (
79
+ <ul>
80
+ {all.map((p) => (
81
+ <li key={p.slug}>
82
+ <Link href={`/blog/${p.slug}`}>{p.title}</Link>
83
+ <time>{new Date(p.publishedAt).toLocaleDateString("pt-BR")}</time>
84
+ {p.source === "remote" && <span className="badge-ia">✨ IA</span>}
85
+ </li>
86
+ ))}
87
+ </ul>
88
+ );
89
+ }
90
+ ```
91
+
92
+ Pro post individual, tenta o painel primeiro, cai pro local se 404:
93
+
94
+ ```tsx
95
+ // app/blog/[slug]/page.tsx
96
+ import { intelexia } from "@/lib/intelexia";
97
+ import { IntelexiaApiError } from "@intelex-ia/sdk";
98
+ import { ArticleJsonLd, articleToMetadata } from "@intelex-ia/sdk/react";
99
+ import { getPostBySlug } from "@/lib/posts";
100
+ import { notFound } from "next/navigation";
101
+
102
+ export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
103
+ const { slug } = await params;
104
+
105
+ // 1) Tenta painel
106
+ try {
107
+ const { article } = await intelexia.getArticleBySlug(slug);
108
+ return (
109
+ <article>
110
+ <ArticleJsonLd article={article} />
111
+ <h1>{article.title}</h1>
112
+ <div dangerouslySetInnerHTML={{ __html: article.contentHtml ?? "" }} />
113
+ </article>
114
+ );
115
+ } catch (err) {
116
+ if (!(err instanceof IntelexiaApiError) || err.status !== 404) throw err;
117
+ }
118
+
119
+ // 2) Fallback: post antigo local
120
+ const local = getPostBySlug(slug);
121
+ if (!local) notFound();
122
+ return (
123
+ <article>
124
+ <h1>{local.title}</h1>
125
+ {/* renderiza o local do jeito que você já fazia */}
126
+ </article>
127
+ );
128
+ }
129
+ ```
130
+
131
+ ### Modo 2: Replace (migração completa)
132
+
133
+ ```tsx
134
+ // app/blog/page.tsx
135
+ import { intelexia } from "@/lib/intelexia";
136
+
137
+ export default async function BlogIndex() {
138
+ const { items } = await intelexia.getArticles({ limit: 20 });
139
+ return (
140
+ <ul>
141
+ {items.map((a) => (
142
+ <li key={a.id}>
143
+ <Link href={`/blog/${a.slug}`}>{a.title}</Link>
144
+ </li>
145
+ ))}
146
+ </ul>
147
+ );
148
+ }
149
+ ```
150
+
151
+ Pro post individual, sem fallback:
152
+
153
+ ```tsx
154
+ import { intelexia } from "@/lib/intelexia";
155
+ import { IntelexiaApiError } from "@intelex-ia/sdk";
156
+ import { ArticleJsonLd, articleToMetadata } from "@intelex-ia/sdk/react";
157
+ import { notFound } from "next/navigation";
158
+
159
+ async function fetchArticle(slug: string) {
160
+ try {
161
+ return await intelexia.getArticleBySlug(slug);
162
+ } catch (err) {
163
+ if (err instanceof IntelexiaApiError && err.status === 404) notFound();
164
+ throw err;
165
+ }
166
+ }
167
+
168
+ export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
169
+ const { slug } = await params;
170
+ const { article } = await fetchArticle(slug);
171
+ return articleToMetadata(article);
172
+ }
173
+
174
+ export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
175
+ const { slug } = await params;
176
+ const { article } = await fetchArticle(slug);
177
+ return (
178
+ <article>
179
+ <ArticleJsonLd article={article} />
180
+ <h1>{article.title}</h1>
181
+ <div dangerouslySetInnerHTML={{ __html: article.contentHtml ?? "" }} />
182
+ </article>
183
+ );
184
+ }
185
+ ```
186
+
187
+ ### Modo 3: Isolated (rota nova, blog antigo intacto)
188
+
189
+ Cria `app/blog/ia/page.tsx` e `app/blog/ia/[slug]/page.tsx` com o mesmo código do modo Replace, mas em rota separada (`/blog/ia` em vez de `/blog`). Zero alteração no blog existente.
190
+
191
+ ---
192
+
193
+ ## Cliente — `lib/intelexia.ts`
194
+
195
+ ```ts
196
+ import { createClient } from "@intelex-ia/sdk";
197
+
198
+ export const intelexia = createClient({
199
+ token: process.env.INTELEXIA_API_TOKEN!,
200
+ // ISR: revalida a cada 60s; o webhook revalidate invalida sob demanda
201
+ init: { next: { revalidate: 60, tags: ["intelexia-articles"] } },
202
+ });
203
+ ```
204
+
205
+ ## Webhook de revalidate — `app/api/revalidate/route.ts`
206
+
207
+ Quando um artigo publica/edita no painel, nosso worker faz `POST` no seu endpoint. Implemente assim:
208
+
209
+ ```ts
210
+ import { NextResponse } from "next/server";
211
+ import { revalidatePath, revalidateTag } from "next/cache";
212
+
213
+ export async function POST(req: Request) {
214
+ const headerSecret = req.headers.get("x-revalidate-secret");
215
+ const body = (await req.json().catch(() => ({}))) as {
216
+ secret?: string;
217
+ slug?: string | null;
218
+ domain?: string;
219
+ };
220
+ const got = headerSecret ?? body.secret ?? "";
221
+ if (!process.env.INTELEXIA_REVALIDATE_SECRET || got !== process.env.INTELEXIA_REVALIDATE_SECRET) {
222
+ return NextResponse.json({ ok: false, error: "unauthorized" }, { status: 401 });
223
+ }
224
+
225
+ if (body.slug) revalidatePath(`/blog/${body.slug}`);
226
+ revalidatePath("/blog");
227
+ revalidateTag("intelexia-articles");
228
+
229
+ return NextResponse.json({ ok: true, revalidated: { slug: body.slug ?? null } });
230
+ }
231
+ ```
232
+
233
+ > O worker envia o secret tanto no header `x-revalidate-secret` quanto no body, tem timeout de 15s e é **fire-and-forget** (se seu endpoint falhar, o artigo continua publicado e a ISR atualiza pelo `revalidate: 60`).
234
+
235
+ ## API
236
+
237
+ ### `createClient(config)`
238
+
239
+ | Opção | Tipo | Default |
240
+ | -------- | -------------------- | ------------------------------------------------- |
241
+ | `token` | `string` (obrig.) | — |
242
+ | `apiUrl` | `string` | `https://api.innovarticles.intelexia.com.br` |
243
+ | `fetch` | `typeof fetch` | `globalThis.fetch` |
244
+ | `init` | `RequestInit` | `undefined` (passe `{ next: { revalidate: N } }`) |
245
+
246
+ ### `client.getArticles({ limit?, offset?, init? })`
247
+
248
+ Resposta: `{ ok, mode, lifecycle, count, items: ArticleListItem[] }`.
249
+
250
+ ### `client.getArticleBySlug(slug, { init? })`
251
+
252
+ Resposta: `{ ok, mode, lifecycle, article: Article }`. **404 → joga `IntelexiaApiError`** (use `try/catch` + `notFound()`).
253
+
254
+ ### `mergePosts({ local, remote, mapLocal, dedupe?, sortBy? })` 🆕 0.2.0
255
+
256
+ | Opção | Tipo | Default |
257
+ | ---------- | ------------------------------------------------- | ---------------- |
258
+ | `local` | `readonly TLocal[]` (qualquer shape) | — |
259
+ | `remote` | `readonly ArticleListItem[]` | — |
260
+ | `mapLocal` | `(post: TLocal) => Omit<UnifiedPost,"source"> \| null` | — |
261
+ | `dedupe` | `"remote-wins" \| "local-wins" \| "throw"` | `"remote-wins"` |
262
+ | `sortBy` | `"date-desc" \| "date-asc" \| "none"` | `"date-desc"` |
263
+
264
+ Retorna `UnifiedPost[]` com `source: "local" | "remote"`. `mapLocal` retornando `null` filtra o post (útil pra esconder rascunhos). Slugs duplicados resolvidos pela estratégia `dedupe`. Veja exemplo no Modo Additive acima.
265
+
266
+ ### `mode: "full" | "plain"`
267
+
268
+ - **`full`** (sites `active`/`paused`): inclui schema JSON-LD, metaTitle/Description, keyword(s), interlinking interno no `contentHtml`.
269
+ - **`plain`** (site `cancelled`): só conteúdo cru — schema/meta/keywords vêm `null`, interlinking removido. O `ArticleJsonLd` e o `articleToMetadata` lidam graciosamente (omitem o que não tem).
270
+
271
+ A decisão é **server-side e não-negociável** — o cliente não escolhe o modo.
272
+
273
+ ### Helpers `/react`
274
+
275
+ - `<ArticleJsonLd article={article} />` — renderiza `<script type="application/ld+json">` com o `schemaJson`. No-op se ausente.
276
+ - `articleToMetadata(article)` → objeto compatível com o `Metadata` do Next.js App Router (title, description, keywords, openGraph, twitter, customMetaTags como `other`).
277
+
278
+ ## Erros
279
+
280
+ ```ts
281
+ import { IntelexiaApiError, MergeConflictError } from "@intelex-ia/sdk";
282
+
283
+ try {
284
+ await intelexia.getArticleBySlug("post-inexistente");
285
+ } catch (err) {
286
+ if (err instanceof IntelexiaApiError) {
287
+ console.error(err.status, err.message); // 404, "Artigo não encontrado."
288
+ }
289
+ }
290
+
291
+ try {
292
+ mergePosts({ local, remote, mapLocal, dedupe: "throw" });
293
+ } catch (err) {
294
+ if (err instanceof MergeConflictError) {
295
+ console.error(`Conflito de slug: ${err.slug}`);
296
+ }
297
+ }
298
+ ```
299
+
300
+ ## Compatibilidade
301
+
302
+ - **Node** 18+ (fetch nativo), Edge Runtime, Browser.
303
+ - **React** 18+ (peer dep opcional — só pros helpers `/react`).
304
+ - **TypeScript:** tipos publicados no `dist`.
305
+ - **Frameworks:** Next.js (App Router e Pages Router), Astro, Remix, SvelteKit, Vite, qualquer JS.
306
+
307
+ ## Changelog
308
+
309
+ - **0.2.0** — adiciona `mergePosts()` + tipo `UnifiedPost` + `MergeConflictError` (3 modos de integração).
310
+ - **0.1.0** — release inicial: `createClient`, `getArticles`, `getArticleBySlug`, `<ArticleJsonLd>`, `articleToMetadata`.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=autoria.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoria.test.d.ts","sourceRoot":"","sources":["../src/autoria.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { autoriaDoArtigo, SELO_IA } from "./react.js";
3
+ /**
4
+ * A autoria do artigo tem duas regras duras, e as duas vêm de fora da
5
+ * engenharia:
6
+ *
7
+ * 1. `author` só vale quando é `Organization`. Texto produzido com apoio de IA
8
+ * e assinado por uma PESSOA é o que os Termos do Google proíbem desde
9
+ * 30/07/2026. O profissional aparece como REVISOR.
10
+ *
11
+ * 2. A autoria NUNCA some. Sem cadastro, assina a equipe da empresa — e o selo
12
+ * de apoio de IA sai do mesmo jeito. Artigo no ar sem dizer quem assina foi
13
+ * exatamente o defeito medido no siteIA em 21/10/2026.
14
+ */
15
+ describe("autoriaDoArtigo", () => {
16
+ const comOrganization = {
17
+ schemaJson: {
18
+ "@graph": [
19
+ {
20
+ "@type": "BlogPosting",
21
+ author: { "@type": "Organization", name: "Equipe Editorial Parceria" },
22
+ contributor: {
23
+ "@type": "Person",
24
+ name: "Mauro Roger Pinto",
25
+ jobTitle: "Contador responsável técnico",
26
+ identifier: "CRC-SC 020357/O-4",
27
+ },
28
+ dateModified: "2026-10-21",
29
+ publisher: { "@type": "Organization", name: "Parceria Assessoria Contábil" },
30
+ },
31
+ ],
32
+ },
33
+ };
34
+ it("usa a Organization como autor e o contributor como revisor", () => {
35
+ const a = autoriaDoArtigo(comOrganization);
36
+ expect(a.autor).toBe("Equipe Editorial Parceria");
37
+ expect(a.revisor).toBe("Mauro Roger Pinto, Contador responsável técnico, CRC-SC 020357/O-4");
38
+ expect(a.revisadoEm).toBe("2026-10-21");
39
+ expect(a.temCadastro).toBe(true);
40
+ });
41
+ it("IGNORA author do tipo Person — e cai para a equipe da empresa", () => {
42
+ const a = autoriaDoArtigo({
43
+ schemaJson: {
44
+ "@type": "BlogPosting",
45
+ author: { "@type": "Person", name: "Fulano de Tal" },
46
+ publisher: { "@type": "Organization", name: "Contabilidade Acme" },
47
+ },
48
+ });
49
+ expect(a.autor).not.toContain("Fulano");
50
+ expect(a.autor).toBe("Equipe Editorial Contabilidade Acme");
51
+ expect(a.temCadastro).toBe(false);
52
+ });
53
+ it("sem autoria no schema, assina a equipe tirada do publisher", () => {
54
+ const a = autoriaDoArtigo({
55
+ schemaJson: { "@type": "BlogPosting", publisher: { "@type": "Organization", name: "Acme" } },
56
+ });
57
+ expect(a.autor).toBe("Equipe Editorial Acme");
58
+ expect(a.revisor).toBeNull();
59
+ expect(a.temCadastro).toBe(false);
60
+ });
61
+ it("a prop `empresa` vence o publisher do schema", () => {
62
+ const a = autoriaDoArtigo({ schemaJson: { publisher: { "@type": "Organization", name: "Nome Velho" } } }, { empresa: "Nome Novo" });
63
+ expect(a.autor).toBe("Equipe Editorial Nome Novo");
64
+ });
65
+ it("sem schema nenhum ainda assina — nunca devolve vazio", () => {
66
+ for (const vazio of [null, undefined, "", 0, [], "texto solto"]) {
67
+ const a = autoriaDoArtigo({ schemaJson: vazio });
68
+ expect(a.autor.length).toBeGreaterThan(0);
69
+ expect(a.temCadastro).toBe(false);
70
+ }
71
+ });
72
+ it("revisor sem cargo nem registro não gera pontuação solta", () => {
73
+ const a = autoriaDoArtigo({
74
+ schemaJson: {
75
+ author: { "@type": "Organization", name: "Equipe" },
76
+ contributor: { "@type": "Person", name: "Ana Souza" },
77
+ },
78
+ });
79
+ expect(a.revisor).toBe("Ana Souza");
80
+ });
81
+ it("o selo é o texto único do grupo", () => {
82
+ expect(SELO_IA).toBe("Apoio de IA · Revisão humana");
83
+ });
84
+ });
85
+ //# sourceMappingURL=autoria.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoria.test.js","sourceRoot":"","sources":["../src/autoria.test.ts"],"names":[],"mappings":"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"}
package/dist/react.d.ts CHANGED
@@ -88,4 +88,62 @@ export interface ArticleMetadata {
88
88
  */
89
89
  export declare function articleToMetadata(article: ArticleMetadataInput): ArticleMetadata;
90
90
  export type { Article, CustomMetaTag } from "./types.js";
91
+ /**
92
+ * Selo de transparência. Fecha a linha do revisor na ASSINATURA, no fim do
93
+ * artigo — nunca na barra do topo.
94
+ */
95
+ export declare const SELO_IA = "Apoio de IA \u00B7 Revis\u00E3o humana";
96
+ export interface AutoriaDoArtigo {
97
+ /** Quem assina. SEMPRE preenchido: cai para a equipe da empresa. */
98
+ autor: string;
99
+ /** "Nome, cargo, registro" do revisor, ou `null` quando não há cadastro. */
100
+ revisor: string | null;
101
+ /** ISO da revisão, quando o schema traz `dateModified`. */
102
+ revisadoEm: string | null;
103
+ /** `false` quando a autoria veio da queda, e não do schema. */
104
+ temCadastro: boolean;
105
+ }
106
+ /**
107
+ * Lê a autoria do `schemaJson` do artigo.
108
+ *
109
+ * ⚠️ `author` SÓ vale quando é `Organization`. Texto produzido com apoio de IA
110
+ * e assinado por uma PESSOA é o que os Termos do Google proíbem desde
111
+ * 30/07/2026 — então, quando o schema traz uma Person em `author`, ela é
112
+ * ignorada aqui. O profissional aparece como REVISOR, via `contributor`.
113
+ *
114
+ * NUNCA devolve vazio: sem autoria no schema, assina a equipe da empresa
115
+ * (`empresa`, ou o `publisher.name` do próprio schema). Artigo no ar sem dizer
116
+ * quem assina e sem declarar o apoio de IA não é uma saída aceitável — foi o
117
+ * que esta função existe para impedir.
118
+ */
119
+ export declare function autoriaDoArtigo(article: {
120
+ schemaJson?: unknown | null;
121
+ }, opts?: {
122
+ empresa?: string;
123
+ }): AutoriaDoArtigo;
124
+ export interface AuthorBarProps {
125
+ article: {
126
+ schemaJson?: unknown | null;
127
+ };
128
+ /** Nome do negócio, para a queda. Sem ele, usa o `publisher` do schema. */
129
+ empresa?: string;
130
+ /** Classe do contêiner. O estilo é do SITE — o SDK não traz CSS. */
131
+ className?: string;
132
+ }
133
+ /**
134
+ * Barra de autoria, logo abaixo do `<h1>`.
135
+ *
136
+ * SEMPRE renderiza (ver `autoriaDoArtigo`). Não traz CSS: o SDK não pode impor
137
+ * visual a sites que têm cada um o seu. As classes (`ia-autor` por padrão) são
138
+ * ganchos para o site vestir.
139
+ *
140
+ * O selo de IA NÃO vem aqui — ele fecha a linha do revisor em
141
+ * `<AuthorSignature>`, no fim do artigo.
142
+ */
143
+ export declare function AuthorBar({ article, empresa, className, }: AuthorBarProps): ReactElement;
144
+ /**
145
+ * Assinatura, no FIM do artigo. É aqui que o selo de IA fecha a linha do
146
+ * revisor — e aparece tanto com profissional cadastrado quanto sem.
147
+ */
148
+ export declare function AuthorSignature({ article, empresa, className, }: AuthorBarProps): ReactElement;
91
149
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAW,aAAa,EAAE,MAAM,YAAY,CAAC;AAEzD,iEAAiE;AACjE,UAAU,kBAAkB;IAC1B,UAAU,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,GACR,EAAE;IACD,OAAO,EAAE,kBAAkB,CAAC;CAC7B,GAAG,YAAY,GAAG,IAAI,CAYtB;AAED,mEAAmE;AACnE,UAAU,oBAAoB;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;CACzC;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE;QACV,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC5B,CAAC;IACF,OAAO,CAAC,EAAE;QACR,IAAI,EAAE,qBAAqB,CAAC;QAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,oBAAoB,GAC5B,eAAe,CAiCjB;AAGD,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
package/dist/react.js CHANGED
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /**
3
3
  * Renderiza um `<script type="application/ld+json">` com o schema JSON-LD do
4
4
  * artigo. Pode ser colocado em qualquer lugar do `<body>` — o Google lê tanto
@@ -81,4 +81,100 @@ export function articleToMetadata(article) {
81
81
  ...(Object.keys(other).length > 0 ? { other } : {}),
82
82
  };
83
83
  }
84
+ // ─────────────────────────────────────────────────────────────────────────────
85
+ // AUTORIA
86
+ // ─────────────────────────────────────────────────────────────────────────────
87
+ /**
88
+ * Selo de transparência. Fecha a linha do revisor na ASSINATURA, no fim do
89
+ * artigo — nunca na barra do topo.
90
+ */
91
+ export const SELO_IA = "Apoio de IA · Revisão humana";
92
+ const ehNo = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
93
+ const texto = (v) => typeof v === "string" && v.trim() ? v.trim() : null;
94
+ /**
95
+ * Lê a autoria do `schemaJson` do artigo.
96
+ *
97
+ * ⚠️ `author` SÓ vale quando é `Organization`. Texto produzido com apoio de IA
98
+ * e assinado por uma PESSOA é o que os Termos do Google proíbem desde
99
+ * 30/07/2026 — então, quando o schema traz uma Person em `author`, ela é
100
+ * ignorada aqui. O profissional aparece como REVISOR, via `contributor`.
101
+ *
102
+ * NUNCA devolve vazio: sem autoria no schema, assina a equipe da empresa
103
+ * (`empresa`, ou o `publisher.name` do próprio schema). Artigo no ar sem dizer
104
+ * quem assina e sem declarar o apoio de IA não é uma saída aceitável — foi o
105
+ * que esta função existe para impedir.
106
+ */
107
+ export function autoriaDoArtigo(article, opts) {
108
+ const schema = article.schemaJson;
109
+ const nos = [];
110
+ if (ehNo(schema)) {
111
+ const grafo = schema["@graph"];
112
+ if (Array.isArray(grafo))
113
+ nos.push(...grafo.filter(ehNo));
114
+ else
115
+ nos.push(schema);
116
+ }
117
+ // Nome da empresa para a queda: o que veio por prop vence; senão, o
118
+ // `publisher` do schema, que é justamente a organização do cliente.
119
+ let empresa = texto(opts?.empresa);
120
+ if (!empresa) {
121
+ for (const no of nos) {
122
+ const pub = no.publisher;
123
+ if (ehNo(pub) && texto(pub.name)) {
124
+ empresa = texto(pub.name);
125
+ break;
126
+ }
127
+ }
128
+ }
129
+ for (const no of nos) {
130
+ const a = no.author;
131
+ if (!ehNo(a) || a["@type"] !== "Organization")
132
+ continue;
133
+ const autor = texto(a.name);
134
+ if (!autor)
135
+ continue;
136
+ const c = no.contributor;
137
+ const revisor = ehNo(c) && texto(c.name)
138
+ ? [texto(c.name), texto(c.jobTitle), texto(c.identifier)]
139
+ .filter(Boolean)
140
+ .join(", ")
141
+ : null;
142
+ return { autor, revisor, revisadoEm: texto(no.dateModified), temCadastro: true };
143
+ }
144
+ return {
145
+ autor: empresa ? `Equipe Editorial ${empresa}` : "Equipe Editorial",
146
+ revisor: null,
147
+ revisadoEm: null,
148
+ temCadastro: false,
149
+ };
150
+ }
151
+ /** Texto da linha de revisão, igual nos dois blocos. */
152
+ function linhaDeRevisao(a, empresa) {
153
+ const quem = a.revisor
154
+ ? `Revisado por ${a.revisor}`
155
+ : `Revisado pela equipe ${empresa?.trim() || a.autor.replace(/^Equipe Editorial\s*/, "") || "editorial"}`;
156
+ return `${quem} · ${SELO_IA}`;
157
+ }
158
+ /**
159
+ * Barra de autoria, logo abaixo do `<h1>`.
160
+ *
161
+ * SEMPRE renderiza (ver `autoriaDoArtigo`). Não traz CSS: o SDK não pode impor
162
+ * visual a sites que têm cada um o seu. As classes (`ia-autor` por padrão) são
163
+ * ganchos para o site vestir.
164
+ *
165
+ * O selo de IA NÃO vem aqui — ele fecha a linha do revisor em
166
+ * `<AuthorSignature>`, no fim do artigo.
167
+ */
168
+ export function AuthorBar({ article, empresa, className = "ia-autor", }) {
169
+ const a = autoriaDoArtigo(article, { empresa });
170
+ return (_jsxs("div", { className: className, children: [_jsxs("strong", { children: ["por ", a.autor] }), a.revisor ? _jsxs("span", { children: ["Revis\u00E3o: ", a.revisor] }) : null] }));
171
+ }
172
+ /**
173
+ * Assinatura, no FIM do artigo. É aqui que o selo de IA fecha a linha do
174
+ * revisor — e aparece tanto com profissional cadastrado quanto sem.
175
+ */
176
+ export function AuthorSignature({ article, empresa, className = "ia-assinatura", }) {
177
+ const a = autoriaDoArtigo(article, { empresa });
178
+ return (_jsxs("section", { className: className, "aria-label": "Autoria", children: [_jsxs("p", { className: `${className}-nome`, children: ["Escrito por ", a.autor] }), _jsx("p", { className: `${className}-cargo`, children: linhaDeRevisao(a, empresa) })] }));
179
+ }
84
180
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":";AAgBA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,aAAa,CAAC,EAC5B,OAAO,GAGR;IACC,IAAI,CAAC,OAAO,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,CACL,iBACE,IAAI,EAAC,qBAAqB;QAC1B,mEAAmE;QACnE,2CAA2C;QAC3C,uBAAuB,EAAE;YACvB,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC;SAC3C,GACD,CACH,CAAC;AACJ,CAAC;AAoCD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAA6B;IAE7B,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC;IAC9D,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe,IAAI,SAAS,CAAC;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,IAAI,SAAS,CAAC;IACpD,MAAM,QAAQ,GAAG;QACf,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7C,GAAG,CAAC,OAAO,CAAC,eAAe,IAAI,EAAE,CAAC;KACnC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAElB,MAAM,KAAK,GAA2B,EAAE,CAAC;IACzC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,cAAc,IAAI,EAAE,EAAE,CAAC;QAC/C,IAAI,GAAG,EAAE,OAAO,IAAI,IAAI;YAAE,SAAS;QACnC,IAAI,GAAG,CAAC,QAAQ;YAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;aAC/C,IAAI,GAAG,CAAC,IAAI;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;IACnD,CAAC;IAED,OAAO;QACL,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,SAAS,EAAE;YACT,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC/C;QACD,OAAO,EAAE;YACP,IAAI,EAAE,qBAAqB;YAC3B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtC;QACD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
- {
2
- "name": "@intelex-ia/sdk",
3
- "version": "0.2.0",
4
- "description": "SDK pra consumir a Content API da IntelexIA em sites headless (Next.js, Astro, etc.). Multiplataforma Fase 2b.",
5
- "license": "MIT",
6
- "author": "intelex-ia",
7
- "homepage": "https://github.com/intelex-ia/combo_automatico/tree/main/packages/sdk",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+https://github.com/intelex-ia/combo_automatico.git",
11
- "directory": "packages/sdk"
12
- },
13
- "bugs": {
14
- "url": "https://github.com/intelex-ia/combo_automatico/issues"
15
- },
16
- "keywords": [
17
- "intelexia",
18
- "content-api",
19
- "headless",
20
- "cms",
21
- "next",
22
- "nextjs",
23
- "react",
24
- "sdk"
25
- ],
26
- "type": "module",
27
- "main": "./dist/index.js",
28
- "module": "./dist/index.js",
29
- "types": "./dist/index.d.ts",
30
- "exports": {
31
- ".": {
32
- "types": "./dist/index.d.ts",
33
- "default": "./dist/index.js"
34
- },
35
- "./react": {
36
- "types": "./dist/react.d.ts",
37
- "default": "./dist/react.js"
38
- }
39
- },
40
- "files": [
41
- "dist",
42
- "README.md"
43
- ],
44
- "scripts": {
45
- "build": "tsc",
46
- "typecheck": "tsc --noEmit"
47
- },
48
- "peerDependencies": {
49
- "react": ">=18"
50
- },
51
- "peerDependenciesMeta": {
52
- "react": {
53
- "optional": true
54
- }
55
- },
56
- "devDependencies": {
57
- "@types/react": "^18.3.12",
58
- "typescript": "^5.6.3"
59
- },
60
- "publishConfig": {
61
- "access": "public",
62
- "provenance": true
63
- }
64
- }
1
+ {
2
+ "name": "@intelex-ia/sdk",
3
+ "version": "0.3.0",
4
+ "description": "SDK pra consumir a Content API da IntelexIA em sites headless (Next.js, Astro, etc.). Multiplataforma Fase 2b.",
5
+ "license": "MIT",
6
+ "author": "intelex-ia",
7
+ "homepage": "https://github.com/intelex-ia/combo_automatico/tree/main/packages/sdk",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/intelex-ia/combo_automatico.git",
11
+ "directory": "packages/sdk"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/intelex-ia/combo_automatico/issues"
15
+ },
16
+ "keywords": [
17
+ "intelexia",
18
+ "content-api",
19
+ "headless",
20
+ "cms",
21
+ "next",
22
+ "nextjs",
23
+ "react",
24
+ "sdk"
25
+ ],
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "./react": {
36
+ "types": "./dist/react.d.ts",
37
+ "default": "./dist/react.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md"
43
+ ],
44
+ "scripts": {
45
+ "build": "tsc",
46
+ "typecheck": "tsc --noEmit"
47
+ },
48
+ "peerDependencies": {
49
+ "react": ">=18"
50
+ },
51
+ "peerDependenciesMeta": {
52
+ "react": {
53
+ "optional": true
54
+ }
55
+ },
56
+ "devDependencies": {
57
+ "@types/react": "^18.3.12",
58
+ "typescript": "^5.6.3"
59
+ },
60
+ "publishConfig": {
61
+ "access": "public",
62
+ "provenance": true
63
+ }
64
+ }