@intelex-ia/sdk 0.1.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.
@@ -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":"AA+EA,4DAA4D;AAC5D,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACf;IAA5B,YAA4B,IAAY;QACtC,KAAK,CACH,uCAAuC,IAAI,8BAA8B;YACvE,+DAA+D,CAClE,CAAC;QAJwB,SAAI,GAAJ,IAAI,CAAQ;QAKtC,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,IAAqB;IAC5C,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAChE,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI;QACrC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,QAAQ,EAAE,IAAI,EAAE,8EAA8E;QAC9F,MAAM,EAAE,QAAQ;KACjB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CACxB,IAA+B;IAE/B,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,IAAI,aAAa,CAAC;IAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,IAAI,WAAW,CAAC;IAE5C,uCAAuC;IACvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAE9C,+BAA+B;IAC/B,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM;YAAE,SAAS,CAAC,gCAAgC;QACvD,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,SAAS,CAAC,2BAA2B;QACvD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,qDAAqD;IACrD,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO;YAAE,SAAS;QAEvB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAClC,SAAS;QACX,CAAC;QAED,yCAAyC;QACzC,QAAQ,UAAU,EAAE,CAAC;YACnB,KAAK,aAAa;gBAChB,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;gBAClC,MAAM;YACR,KAAK,YAAY;gBACf,iDAAiD;gBACjD,MAAM;YACR,KAAK,OAAO;gBACV,MAAM,IAAI,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,aAAa;IACb,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,IAAI,QAAQ,KAAK,MAAM;QAAE,OAAO,GAAG,CAAC;IAEpC,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC;QAChD,gEAAgE;QAChE,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACzD,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC;QACnC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -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/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":"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"}
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;AAKD,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,8BAA8B,CAAC;AAatD,MAAM,IAAI,GAAG,CAAC,CAAU,EAAgC,EAAE,CACxD,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAE3D,MAAM,KAAK,GAAG,CAAC,CAAU,EAAiB,EAAE,CAC1C,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAEtD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAwC,EACxC,IAA2B;IAE3B,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAClC,MAAM,GAAG,GAA8B,EAAE,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACjB,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;;YACrD,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,oEAAoE;IACpE,oEAAoE;IACpE,IAAI,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,MAAM,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC;YACzB,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjC,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC1B,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC;QACpB,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,cAAc;YAAE,SAAS;QACxD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,MAAM,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC;QACzB,MAAM,OAAO,GACX,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;YACtB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;iBACpD,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,IAAI,CAAC;YACf,CAAC,CAAC,IAAI,CAAC;QACX,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACnF,CAAC;IAED,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC,CAAC,kBAAkB;QACnE,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,KAAK;KACnB,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,SAAS,cAAc,CAAC,CAAkB,EAAE,OAAgB;IAC1D,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO;QACpB,CAAC,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE;QAC7B,CAAC,CAAC,wBAAwB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,IAAI,WAAW,EAAE,CAAC;IAC5G,OAAO,GAAG,IAAI,MAAM,OAAO,EAAE,CAAC;AAChC,CAAC;AAUD;;;;;;;;;GASG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,OAAO,EACP,OAAO,EACP,SAAS,GAAG,UAAU,GACP;IACf,MAAM,CAAC,GAAG,eAAe,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAChD,OAAO,CACL,eAAK,SAAS,EAAE,SAAS,aACvB,qCAAa,CAAC,CAAC,KAAK,IAAU,EAC7B,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,6CAAgB,CAAC,CAAC,OAAO,IAAQ,CAAC,CAAC,CAAC,IAAI,IACjD,CACP,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,OAAO,EACP,OAAO,EACP,SAAS,GAAG,eAAe,GACZ;IACf,MAAM,CAAC,GAAG,eAAe,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAChD,OAAO,CACL,mBAAS,SAAS,EAAE,SAAS,gBAAa,SAAS,aACjD,aAAG,SAAS,EAAE,GAAG,SAAS,OAAO,6BAAe,CAAC,CAAC,KAAK,IAAK,EAC5D,YAAG,SAAS,EAAE,GAAG,SAAS,QAAQ,YAAG,cAAc,CAAC,CAAC,EAAE,OAAO,CAAC,GAAK,IAC5D,CACX,CAAC;AACJ,CAAC"}