@intelex-ia/sdk 0.1.0 → 0.2.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
@@ -2,76 +2,158 @@
2
2
 
3
3
  SDK pra consumir a **Content API IntelexIA** em sites headless (Next.js, Astro etc.). Multiplataforma Fase 2b.
4
4
 
5
- > **O que você ganha:** `getArticles` / `getArticleBySlug` tipados, um `<ArticleJsonLd>` pra injetar o schema JSON-LD, e um `articleToMetadata` pra plugar no `generateMetadata` do Next.js App Router. Zero dependências (usa `fetch` global).
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
6
 
7
- ## Instalação
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:
8
10
 
9
11
  ```bash
10
- pnpm add @intelex-ia/sdk
11
- # ou
12
- npm install @intelex-ia/sdk
12
+ npx @intelex-ia/cli init
13
13
  ```
14
14
 
15
- > **Enquanto não publicamos no npm**, instale via tarball/git URL (peça pro time IntelexIA).
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
+ ```
16
23
 
17
24
  ## Configuração
18
25
 
19
- 1. No painel da IntelexIA, **cadastre o site como "Headless (código)"** (vai pedir só domain + opcional revalidate URL/secret).
26
+ 1. No painel da IntelexIA, **cadastre o site como "Headless (código)"** (vai pedir domain + URL e secret de revalidate).
20
27
  2. Abra o site na lista → seção **"Tokens de API (Content API)"** → **Gerar token**. Você recebe `itx_…` (mostrado uma única vez).
21
- 3. Coloque o token em `.env.local`:
28
+ 3. Coloque em `.env.local`:
22
29
 
23
30
  ```bash
24
- INTELEXIA_TOKEN=itx_seu_token_aqui
31
+ INTELEXIA_API_TOKEN=itx_seu_token_aqui
25
32
  INTELEXIA_API_URL=https://api.innovarticles.intelexia.com.br # opcional, é o default
33
+ INTELEXIA_REVALIDATE_SECRET=cole-o-mesmo-segredo-do-painel
26
34
  ```
27
35
 
28
- ## Uso (Next.js App Router)
36
+ ## Três modos de integração
29
37
 
30
- ### Cliente — `lib/intelexia.ts`
38
+ Cada cliente tem uma situação diferente. Escolha o modo que faz sentido:
31
39
 
32
- ```ts
33
- import { createClient } from "@intelex-ia/sdk";
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 |
34
45
 
35
- export const intelexia = createClient({
36
- token: process.env.INTELEXIA_TOKEN!,
37
- // ISR: revalida a cada 60s (ou use webhook revalidate pra invalidar
38
- // sob demanda — ver mais abaixo).
39
- init: { next: { revalidate: 60, tags: ["intelexia-articles"] } },
40
- });
41
- ```
46
+ ---
42
47
 
43
- ### Listagem — `app/blog/page.tsx` (Server Component)
48
+ ### Modo 1: Additive (cliente existente — caso mais comum)
44
49
 
45
50
  ```tsx
51
+ // app/blog/page.tsx
46
52
  import { intelexia } from "@/lib/intelexia";
53
+ import { mergePosts } from "@intelex-ia/sdk";
54
+ import { posts as localPosts } from "@/lib/posts"; // seu array atual
47
55
  import Link from "next/link";
48
56
 
49
57
  export default async function BlogIndex() {
50
- const { items } = await intelexia.getArticles({ limit: 20 });
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
+
51
78
  return (
52
- <main>
53
- <h1>Blog</h1>
54
- <ul>
55
- {items.map((a) => (
56
- <li key={a.id}>
57
- <Link href={`/blog/${a.slug}`}>{a.title}</Link>
58
- {a.publishedAt && (
59
- <time>{new Date(a.publishedAt).toLocaleDateString("pt-BR")}</time>
60
- )}
61
- </li>
62
- ))}
63
- </ul>
64
- </main>
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>
65
88
  );
66
89
  }
67
90
  ```
68
91
 
69
- ### Post — `app/blog/[slug]/page.tsx` (com SEO completo)
92
+ Pro post individual, tenta o painel primeiro, cai pro local se 404:
70
93
 
71
94
  ```tsx
95
+ // app/blog/[slug]/page.tsx
72
96
  import { intelexia } from "@/lib/intelexia";
97
+ import { IntelexiaApiError } from "@intelex-ia/sdk";
73
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";
74
155
  import { IntelexiaApiError } from "@intelex-ia/sdk";
156
+ import { ArticleJsonLd, articleToMetadata } from "@intelex-ia/sdk/react";
75
157
  import { notFound } from "next/navigation";
76
158
 
77
159
  async function fetchArticle(slug: string) {
@@ -83,50 +165,52 @@ async function fetchArticle(slug: string) {
83
165
  }
84
166
  }
85
167
 
86
- export async function generateMetadata({
87
- params,
88
- }: {
89
- params: Promise<{ slug: string }>;
90
- }) {
168
+ export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
91
169
  const { slug } = await params;
92
170
  const { article } = await fetchArticle(slug);
93
- return articleToMetadata(article); // → title, description, OG, Twitter, keywords
171
+ return articleToMetadata(article);
94
172
  }
95
173
 
96
- export default async function PostPage({
97
- params,
98
- }: {
99
- params: Promise<{ slug: string }>;
100
- }) {
174
+ export default async function PostPage({ params }: { params: Promise<{ slug: string }> }) {
101
175
  const { slug } = await params;
102
176
  const { article } = await fetchArticle(slug);
103
177
  return (
104
178
  <article>
105
- {/* Schema JSON-LD (Article/Breadcrumb/FAQ/HowTo/Org/Person) */}
106
179
  <ArticleJsonLd article={article} />
107
-
108
180
  <h1>{article.title}</h1>
109
- {article.featuredImageUrl && (
110
- <img src={article.featuredImageUrl} alt={article.title ?? ""} />
111
- )}
112
- <div
113
- dangerouslySetInnerHTML={{ __html: article.contentHtml ?? "" }}
114
- />
181
+ <div dangerouslySetInnerHTML={{ __html: article.contentHtml ?? "" }} />
115
182
  </article>
116
183
  );
117
184
  }
118
185
  ```
119
186
 
120
- ### Webhook de revalidate — `app/api/revalidate/route.ts`
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`
121
206
 
122
- Quando um artigo publica no painel, o worker IntelexIA faz um `POST` no seu endpoint de revalidate (configurado no cadastro do site). Implemente assim:
207
+ Quando um artigo publica/edita no painel, nosso worker faz `POST` no seu endpoint. Implemente assim:
123
208
 
124
209
  ```ts
125
210
  import { NextResponse } from "next/server";
126
211
  import { revalidatePath, revalidateTag } from "next/cache";
127
212
 
128
213
  export async function POST(req: Request) {
129
- // Autentica com o secret cadastrado no painel (header X ou body)
130
214
  const headerSecret = req.headers.get("x-revalidate-secret");
131
215
  const body = (await req.json().catch(() => ({}))) as {
132
216
  secret?: string;
@@ -138,7 +222,6 @@ export async function POST(req: Request) {
138
222
  return NextResponse.json({ ok: false, error: "unauthorized" }, { status: 401 });
139
223
  }
140
224
 
141
- // Revalida a página do post (se veio slug) e o índice do blog.
142
225
  if (body.slug) revalidatePath(`/blog/${body.slug}`);
143
226
  revalidatePath("/blog");
144
227
  revalidateTag("intelexia-articles");
@@ -147,13 +230,7 @@ export async function POST(req: Request) {
147
230
  }
148
231
  ```
149
232
 
150
- E no `.env.local`:
151
-
152
- ```bash
153
- INTELEXIA_REVALIDATE_SECRET=cole-o-mesmo-segredo-do-painel
154
- ```
155
-
156
- > O worker envia o secret tanto em `x-revalidate-secret` quanto no body (`{ secret, slug, domain }`), tem timeout de 15s e é **fire-and-forget** (se o seu endpoint falhar, o artigo continua publicado e a ISR atualiza pelo `revalidate: 60`).
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`).
157
234
 
158
235
  ## API
159
236
 
@@ -174,6 +251,18 @@ Resposta: `{ ok, mode, lifecycle, count, items: ArticleListItem[] }`.
174
251
 
175
252
  Resposta: `{ ok, mode, lifecycle, article: Article }`. **404 → joga `IntelexiaApiError`** (use `try/catch` + `notFound()`).
176
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
+
177
266
  ### `mode: "full" | "plain"`
178
267
 
179
268
  - **`full`** (sites `active`/`paused`): inclui schema JSON-LD, metaTitle/Description, keyword(s), interlinking interno no `contentHtml`.
@@ -189,7 +278,7 @@ A decisão é **server-side e não-negociável** — o cliente não escolhe o mo
189
278
  ## Erros
190
279
 
191
280
  ```ts
192
- import { IntelexiaApiError } from "@intelex-ia/sdk";
281
+ import { IntelexiaApiError, MergeConflictError } from "@intelex-ia/sdk";
193
282
 
194
283
  try {
195
284
  await intelexia.getArticleBySlug("post-inexistente");
@@ -198,10 +287,24 @@ try {
198
287
  console.error(err.status, err.message); // 404, "Artigo não encontrado."
199
288
  }
200
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
+ }
201
298
  ```
202
299
 
203
300
  ## Compatibilidade
204
301
 
205
- - Node 18+ (fetch nativo), Edge Runtime, Browser.
206
- - React 18+ (peer dep opcional — só pros helpers `/react`).
207
- - TypeScript: tipos publicados no `dist`.
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`.
package/dist/index.d.ts CHANGED
@@ -6,4 +6,5 @@
6
6
  */
7
7
  export { createClient, IntelexiaApiError, type ClientConfig, type ArticlesListResponse, type ArticleResponse, type IntelexiaClient, } from "./client.js";
8
8
  export type { Lifecycle, Mode, Article, ArticleListItem, CustomMetaTag, ErrorResponse, } from "./types.js";
9
+ export { mergePosts, MergeConflictError, type UnifiedPost, type MergePostsOptions, } from "./merge.js";
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,SAAS,EACT,IAAI,EACJ,OAAO,EACP,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,SAAS,EACT,IAAI,EACJ,OAAO,EACP,eAAe,EACf,aAAa,EACb,aAAa,GACd,MAAM,YAAY,CAAC;AAIpB,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,iBAAiB,GACvB,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -5,4 +5,7 @@
5
5
  * `@intelex-ia/sdk/react`.
6
6
  */
7
7
  export { createClient, IntelexiaApiError, } from "./client.js";
8
+ // Merge helpers (0.2.0+) — pro caso real "cliente já tem posts antigos
9
+ // e quer adicionar os do painel sem perder os antigos".
10
+ export { mergePosts, MergeConflictError, } from "./merge.js";
8
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,YAAY,EACZ,iBAAiB,GAKlB,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EACL,YAAY,EACZ,iBAAiB,GAKlB,MAAM,aAAa,CAAC;AAWrB,uEAAuE;AACvE,wDAAwD;AACxD,OAAO,EACL,UAAU,EACV,kBAAkB,GAGnB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * `mergePosts()` — junta posts locais (formato do cliente) com artigos do
3
+ * painel IntelexIA num feed unificado. Resolve o caso real "cliente já tem
4
+ * 50 artigos no site e quer ADICIONAR os da IA sem perder os antigos".
5
+ *
6
+ * Função pura, zero deps, zero React. Roda em qualquer ambiente JS.
7
+ *
8
+ * @example modo "additive" (clientes existentes — caso mais comum)
9
+ * import { listArticles } from "@/lib/intelexia"
10
+ * import { mergePosts } from "@intelex-ia/sdk"
11
+ *
12
+ * const { items: remote } = await client.getArticles({ limit: 50 })
13
+ * const merged = mergePosts({
14
+ * local: localPosts,
15
+ * remote,
16
+ * mapLocal: (p) => ({
17
+ * slug: p.slug,
18
+ * title: p.title,
19
+ * excerpt: p.excerpt,
20
+ * publishedAt: p.date,
21
+ * category: p.category,
22
+ * }),
23
+ * })
24
+ * // merged: UnifiedPost[] — ordenado por data desc, slug duplicado: remoto vence
25
+ */
26
+ import type { ArticleListItem } from "./types.js";
27
+ /**
28
+ * Shape unificado pra renderizar lista de posts independente da origem.
29
+ * Cliente que precisa de mais campos pode estender via TypeScript generics
30
+ * — por enquanto fixamos o mínimo viável pra UI (slug + meta de card).
31
+ */
32
+ export interface UnifiedPost {
33
+ /** Slug pra URL. Único no feed (dedup por slug). */
34
+ slug: string;
35
+ /** Título do post. */
36
+ title: string;
37
+ /** Resumo curto (excerpt/metaDescription). Pode ser `null` se ausente. */
38
+ excerpt: string | null;
39
+ /** Data ISO 8601 (`2026-05-26T10:00:00Z` ou `2026-05-26`). */
40
+ publishedAt: string;
41
+ /** URL absoluta da imagem destacada. `null` se não tiver. */
42
+ featuredImageUrl: string | null;
43
+ /** Categoria principal (string display). `null` se não classificado. */
44
+ category: string | null;
45
+ /** De onde veio: `"local"` (cliente) ou `"remote"` (painel IntelexIA). */
46
+ source: "local" | "remote";
47
+ }
48
+ /** Opções de `mergePosts()`. `local` é genérico — cliente passa qualquer shape. */
49
+ export interface MergePostsOptions<TLocal> {
50
+ /** Array de posts locais (formato livre do cliente). */
51
+ local: readonly TLocal[];
52
+ /** Array de artigos vindos da Content API (via `client.getArticles()`). */
53
+ remote: readonly ArticleListItem[];
54
+ /**
55
+ * Mapper que converte um post local pro `UnifiedPost`. Obrigatório porque
56
+ * cada cliente tem um shape diferente (markdown frontmatter, CMS custom,
57
+ * array hardcoded, etc.). Retorne `null` pra ignorar o post (filtro).
58
+ */
59
+ mapLocal: (post: TLocal) => Omit<UnifiedPost, "source"> | null;
60
+ /**
61
+ * Estratégia quando um slug existe nos DOIS lados:
62
+ * - `"remote-wins"` (default): painel substitui o local. Faz sentido pra
63
+ * migração gradual (cliente publica versão nova IA do post antigo).
64
+ * - `"local-wins"`: ignora o remoto, mantém o local.
65
+ * - `"throw"`: lança `MergeConflictError` (forçar resolução explícita).
66
+ */
67
+ dedupe?: "remote-wins" | "local-wins" | "throw";
68
+ /**
69
+ * Ordenação do feed final:
70
+ * - `"date-desc"` (default): mais recente primeiro.
71
+ * - `"date-asc"`: mais antigo primeiro.
72
+ * - `"none"`: mantém ordem (remotos primeiro, locais depois — útil pra
73
+ * "destaques curados" no topo).
74
+ */
75
+ sortBy?: "date-desc" | "date-asc" | "none";
76
+ }
77
+ /** Lançado quando `dedupe: "throw"` e há slug duplicado. */
78
+ export declare class MergeConflictError extends Error {
79
+ readonly slug: string;
80
+ constructor(slug: string);
81
+ }
82
+ /**
83
+ * Junta posts locais + remotos num único feed. Veja `MergePostsOptions`.
84
+ */
85
+ export declare function mergePosts<TLocal>(opts: MergePostsOptions<TLocal>): UnifiedPost[];
86
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,wEAAwE;IACxE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,0EAA0E;IAC1E,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC5B;AAED,mFAAmF;AACnF,MAAM,WAAW,iBAAiB,CAAC,MAAM;IACvC,wDAAwD;IACxD,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,2EAA2E;IAC3E,MAAM,EAAE,SAAS,eAAe,EAAE,CAAC;IACnC;;;;OAIG;IACH,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC;IAC/D;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,aAAa,GAAG,YAAY,GAAG,OAAO,CAAC;IAChD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;CAC5C;AAED,4DAA4D;AAC5D,qBAAa,kBAAmB,SAAQ,KAAK;aACf,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;CAOzC;AAmBD;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAC/B,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAC9B,WAAW,EAAE,CAoDf"}
package/dist/merge.js ADDED
@@ -0,0 +1,84 @@
1
+ /** Lançado quando `dedupe: "throw"` e há slug duplicado. */
2
+ export class MergeConflictError extends Error {
3
+ slug;
4
+ constructor(slug) {
5
+ super(`[@intelex-ia/sdk] mergePosts: slug "${slug}" existe em local E remote. ` +
6
+ `Configure dedupe: "remote-wins" ou "local-wins" pra resolver.`);
7
+ this.slug = slug;
8
+ this.name = "MergeConflictError";
9
+ }
10
+ }
11
+ /**
12
+ * Normaliza `ArticleListItem` (Content API) pro `UnifiedPost`. Filtra itens
13
+ * sem slug/title/publishedAt (não dá pra renderizar card).
14
+ */
15
+ function remoteToUnified(item) {
16
+ if (!item.slug || !item.title || !item.publishedAt)
17
+ return null;
18
+ return {
19
+ slug: item.slug,
20
+ title: item.title,
21
+ excerpt: item.metaDescription ?? null,
22
+ publishedAt: item.publishedAt,
23
+ featuredImageUrl: item.featuredImageUrl,
24
+ category: null, // Content API não devolve categoria na listagem (Fase 2b inc1 — pode evoluir)
25
+ source: "remote",
26
+ };
27
+ }
28
+ /**
29
+ * Junta posts locais + remotos num único feed. Veja `MergePostsOptions`.
30
+ */
31
+ export function mergePosts(opts) {
32
+ const dedupeMode = opts.dedupe ?? "remote-wins";
33
+ const sortMode = opts.sortBy ?? "date-desc";
34
+ // Indexa por slug pra dedup eficiente.
35
+ const bySlug = new Map();
36
+ // 1) Adiciona LOCAIS primeiro.
37
+ for (const localPost of opts.local) {
38
+ const mapped = opts.mapLocal(localPost);
39
+ if (!mapped)
40
+ continue; // mapper retornou null = filtro
41
+ if (!mapped.slug)
42
+ continue; // post sem slug é ilegível
43
+ bySlug.set(mapped.slug, { ...mapped, source: "local" });
44
+ }
45
+ // 2) Adiciona REMOTOS aplicando estratégia de dedup.
46
+ for (const remoteItem of opts.remote) {
47
+ const unified = remoteToUnified(remoteItem);
48
+ if (!unified)
49
+ continue;
50
+ const existing = bySlug.get(unified.slug);
51
+ if (!existing) {
52
+ bySlug.set(unified.slug, unified);
53
+ continue;
54
+ }
55
+ // Conflito: existe local com mesmo slug.
56
+ switch (dedupeMode) {
57
+ case "remote-wins":
58
+ bySlug.set(unified.slug, unified);
59
+ break;
60
+ case "local-wins":
61
+ // mantém o que já tá lá (local), descarta remoto
62
+ break;
63
+ case "throw":
64
+ throw new MergeConflictError(unified.slug);
65
+ }
66
+ }
67
+ // 3) Ordena.
68
+ const all = Array.from(bySlug.values());
69
+ if (sortMode === "none")
70
+ return all;
71
+ return all.sort((a, b) => {
72
+ const dateA = new Date(a.publishedAt).getTime();
73
+ const dateB = new Date(b.publishedAt).getTime();
74
+ // Datas inválidas vão pro final (NaN comparisons sempre false).
75
+ if (Number.isNaN(dateA) && Number.isNaN(dateB))
76
+ return 0;
77
+ if (Number.isNaN(dateA))
78
+ return 1;
79
+ if (Number.isNaN(dateB))
80
+ return -1;
81
+ return sortMode === "date-desc" ? dateB - dateA : dateA - dateB;
82
+ });
83
+ }
84
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=merge.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.test.d.ts","sourceRoot":"","sources":["../src/merge.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,224 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { mergePosts, MergeConflictError } from "./merge.js";
3
+ const localFixture = [
4
+ {
5
+ slug: "post-antigo-2024",
6
+ title: "Post antigo de 2024",
7
+ excerpt: "Resumo do antigo",
8
+ date: "2024-01-15",
9
+ category: "Geral",
10
+ },
11
+ {
12
+ slug: "post-antigo-2025",
13
+ title: "Post antigo de 2025",
14
+ excerpt: "Resumo de 2025",
15
+ date: "2025-06-10",
16
+ category: "Geral",
17
+ },
18
+ ];
19
+ const remoteFixture = [
20
+ {
21
+ id: "uuid-1",
22
+ slug: "novo-post-ia",
23
+ title: "Novo post gerado por IA",
24
+ featuredImageUrl: "https://cdn.ex.com/1.jpg",
25
+ words: 1200,
26
+ publishedAt: "2026-05-20T10:00:00Z",
27
+ metaDescription: "Resumo SEO do post novo",
28
+ },
29
+ {
30
+ id: "uuid-2",
31
+ slug: "outro-novo-ia",
32
+ title: "Outro post novo",
33
+ featuredImageUrl: null,
34
+ words: 800,
35
+ publishedAt: "2026-04-15T10:00:00Z",
36
+ metaDescription: "Outro resumo",
37
+ },
38
+ ];
39
+ const mapLocal = (p) => ({
40
+ slug: p.slug,
41
+ title: p.title,
42
+ excerpt: p.excerpt,
43
+ publishedAt: p.date,
44
+ category: p.category,
45
+ featuredImageUrl: null,
46
+ });
47
+ // ── Tests ─────────────────────────────────────────────────────────────────
48
+ describe("mergePosts", () => {
49
+ it("merge feliz — sem conflito, ordenado date-desc (default)", () => {
50
+ const result = mergePosts({
51
+ local: localFixture,
52
+ remote: remoteFixture,
53
+ mapLocal,
54
+ });
55
+ expect(result).toHaveLength(4);
56
+ // Mais recente primeiro
57
+ expect(result.map((p) => p.slug)).toEqual([
58
+ "novo-post-ia", // 2026-05-20 (remoto)
59
+ "outro-novo-ia", // 2026-04-15 (remoto)
60
+ "post-antigo-2025", // 2025-06-10 (local)
61
+ "post-antigo-2024", // 2024-01-15 (local)
62
+ ]);
63
+ // Source flag preservado
64
+ expect(result[0]?.source).toBe("remote");
65
+ expect(result[3]?.source).toBe("local");
66
+ });
67
+ it("ordena date-asc quando solicitado", () => {
68
+ const result = mergePosts({
69
+ local: localFixture,
70
+ remote: remoteFixture,
71
+ mapLocal,
72
+ sortBy: "date-asc",
73
+ });
74
+ expect(result.map((p) => p.slug)).toEqual([
75
+ "post-antigo-2024",
76
+ "post-antigo-2025",
77
+ "outro-novo-ia",
78
+ "novo-post-ia",
79
+ ]);
80
+ });
81
+ it("sortBy 'none' mantém ordem (locais primeiro, depois remotos)", () => {
82
+ const result = mergePosts({
83
+ local: localFixture,
84
+ remote: remoteFixture,
85
+ mapLocal,
86
+ sortBy: "none",
87
+ });
88
+ expect(result.map((p) => p.slug)).toEqual([
89
+ "post-antigo-2024",
90
+ "post-antigo-2025",
91
+ "novo-post-ia",
92
+ "outro-novo-ia",
93
+ ]);
94
+ });
95
+ it("dedupe 'remote-wins' (default) — remoto substitui local com mesmo slug", () => {
96
+ const localCom = [...localFixture, { ...localFixture[0], slug: "novo-post-ia", title: "Versão local antiga" }];
97
+ const result = mergePosts({
98
+ local: localCom,
99
+ remote: remoteFixture,
100
+ mapLocal,
101
+ });
102
+ expect(result).toHaveLength(4);
103
+ const post = result.find((p) => p.slug === "novo-post-ia");
104
+ expect(post.title).toBe("Novo post gerado por IA");
105
+ expect(post.source).toBe("remote");
106
+ });
107
+ it("dedupe 'local-wins' — mantém o local, ignora o remoto", () => {
108
+ const localCom = [
109
+ ...localFixture,
110
+ { ...localFixture[0], slug: "novo-post-ia", title: "Versão local sagrada" },
111
+ ];
112
+ const result = mergePosts({
113
+ local: localCom,
114
+ remote: remoteFixture,
115
+ mapLocal,
116
+ dedupe: "local-wins",
117
+ });
118
+ expect(result).toHaveLength(4);
119
+ const post = result.find((p) => p.slug === "novo-post-ia");
120
+ expect(post.title).toBe("Versão local sagrada");
121
+ expect(post.source).toBe("local");
122
+ });
123
+ it("dedupe 'throw' — lança MergeConflictError com slug do conflito", () => {
124
+ const localCom = [
125
+ ...localFixture,
126
+ { ...localFixture[0], slug: "novo-post-ia", title: "Conflito" },
127
+ ];
128
+ expect(() => mergePosts({
129
+ local: localCom,
130
+ remote: remoteFixture,
131
+ mapLocal,
132
+ dedupe: "throw",
133
+ })).toThrowError(MergeConflictError);
134
+ });
135
+ it("mapLocal retornando null filtra o post (cliente quer esconder rascunhos)", () => {
136
+ const result = mergePosts({
137
+ local: localFixture,
138
+ remote: remoteFixture,
139
+ mapLocal: (p) => (p.slug === "post-antigo-2024" ? null : mapLocal(p)),
140
+ });
141
+ expect(result.find((p) => p.slug === "post-antigo-2024")).toBeUndefined();
142
+ expect(result).toHaveLength(3);
143
+ });
144
+ it("filtra remotos sem slug/title/publishedAt (defesa contra API inconsistente)", () => {
145
+ const remoteCom = [
146
+ ...remoteFixture,
147
+ // sem slug
148
+ { id: "x", slug: null, title: "x", featuredImageUrl: null, words: 0, publishedAt: "2026-01-01" },
149
+ // sem title
150
+ { id: "y", slug: "y", title: null, featuredImageUrl: null, words: 0, publishedAt: "2026-01-01" },
151
+ // sem publishedAt
152
+ { id: "z", slug: "z", title: "z", featuredImageUrl: null, words: 0, publishedAt: null },
153
+ ];
154
+ const result = mergePosts({ local: [], remote: remoteCom, mapLocal });
155
+ expect(result).toHaveLength(2); // só os 2 válidos do fixture
156
+ });
157
+ it("lista vazia em ambos os lados retorna []", () => {
158
+ const result = mergePosts({ local: [], remote: [], mapLocal });
159
+ expect(result).toEqual([]);
160
+ });
161
+ it("só locais (cliente sem painel ainda) retorna locais ordenados", () => {
162
+ const result = mergePosts({ local: localFixture, remote: [], mapLocal });
163
+ expect(result).toHaveLength(2);
164
+ expect(result[0].source).toBe("local");
165
+ });
166
+ it("só remotos (cliente sem posts antigos) retorna remotos ordenados", () => {
167
+ const result = mergePosts({ local: [], remote: remoteFixture, mapLocal });
168
+ expect(result).toHaveLength(2);
169
+ expect(result[0].source).toBe("remote");
170
+ });
171
+ it("ArticleListItem em modo 'plain' (sem metaDescription) vira excerpt null", () => {
172
+ const remotePlain = [
173
+ {
174
+ id: "uuid-3",
175
+ slug: "post-cancelled",
176
+ title: "Post de site cancelado",
177
+ featuredImageUrl: null,
178
+ words: 500,
179
+ publishedAt: "2026-05-01T10:00:00Z",
180
+ // sem metaTitle/metaDescription (plain mode)
181
+ },
182
+ ];
183
+ const result = mergePosts({ local: [], remote: remotePlain, mapLocal });
184
+ expect(result).toHaveLength(1);
185
+ expect(result[0].excerpt).toBeNull();
186
+ });
187
+ it("data inválida vai pro final do feed sem quebrar", () => {
188
+ const remoteInvalido = [
189
+ {
190
+ id: "good",
191
+ slug: "good",
192
+ title: "Good",
193
+ featuredImageUrl: null,
194
+ words: 100,
195
+ publishedAt: "2026-05-20T10:00:00Z",
196
+ },
197
+ {
198
+ id: "bad",
199
+ slug: "bad",
200
+ title: "Bad",
201
+ featuredImageUrl: null,
202
+ words: 100,
203
+ publishedAt: "not-a-date",
204
+ },
205
+ ];
206
+ const result = mergePosts({ local: [], remote: remoteInvalido, mapLocal });
207
+ expect(result).toHaveLength(2);
208
+ expect(result[0].slug).toBe("good"); // válido primeiro
209
+ expect(result[1].slug).toBe("bad"); // inválido vai pro final
210
+ });
211
+ });
212
+ describe("UnifiedPost type", () => {
213
+ it("source flag permite filtrar por origem na UI", () => {
214
+ const result = mergePosts({
215
+ local: localFixture,
216
+ remote: remoteFixture,
217
+ mapLocal,
218
+ });
219
+ const onlyRemote = result.filter((p) => p.source === "remote");
220
+ expect(onlyRemote).toHaveLength(2);
221
+ onlyRemote.forEach((p) => expect(p.source).toBe("remote"));
222
+ });
223
+ });
224
+ //# sourceMappingURL=merge.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.test.js","sourceRoot":"","sources":["../src/merge.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intelex-ia/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "SDK pra consumir a Content API da IntelexIA em sites headless (Next.js, Astro, etc.). Multiplataforma Fase 2b.",
5
5
  "license": "MIT",
6
6
  "author": "intelex-ia",