@neetru/sdk 3.1.6 → 3.1.9
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/dist/auth.cjs +65 -22
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/auth.mjs +65 -22
- package/dist/auth.mjs.map +1 -1
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.d.cts +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.mjs.map +1 -1
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +1 -1
- package/dist/checkout.d.ts +1 -1
- package/dist/checkout.mjs.map +1 -1
- package/dist/db-react.d.cts +1 -1
- package/dist/db-react.d.ts +1 -1
- package/dist/db.cjs +55 -16
- package/dist/db.cjs.map +1 -1
- package/dist/db.d.cts +1 -1
- package/dist/db.d.ts +1 -1
- package/dist/db.mjs +55 -16
- package/dist/db.mjs.map +1 -1
- package/dist/entitlements.cjs.map +1 -1
- package/dist/entitlements.d.cts +1 -1
- package/dist/entitlements.d.ts +1 -1
- package/dist/entitlements.mjs.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +8 -8
- package/dist/errors.d.ts +8 -8
- package/dist/errors.mjs.map +1 -1
- package/dist/firestore-compat.cjs +4 -9
- package/dist/firestore-compat.cjs.map +1 -1
- package/dist/firestore-compat.d.cts +2 -2
- package/dist/firestore-compat.d.ts +2 -2
- package/dist/firestore-compat.mjs +4 -9
- package/dist/firestore-compat.mjs.map +1 -1
- package/dist/index.cjs +66 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +66 -23
- package/dist/index.mjs.map +1 -1
- package/dist/mocks.cjs.map +1 -1
- package/dist/mocks.d.cts +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/mocks.mjs.map +1 -1
- package/dist/notifications.cjs.map +1 -1
- package/dist/notifications.d.cts +1 -1
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.mjs.map +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.mjs.map +1 -1
- package/dist/support.cjs.map +1 -1
- package/dist/support.d.cts +1 -1
- package/dist/support.d.ts +1 -1
- package/dist/support.mjs.map +1 -1
- package/dist/telemetry.cjs +5 -1
- package/dist/telemetry.cjs.map +1 -1
- package/dist/telemetry.d.cts +1 -1
- package/dist/telemetry.d.ts +1 -1
- package/dist/telemetry.mjs +5 -1
- package/dist/telemetry.mjs.map +1 -1
- package/dist/{types-BXvGHppn.d.cts → types-DDiVzgCT.d.cts} +19 -2
- package/dist/{types-CQVak2a3.d.ts → types-HWDOUmlC.d.ts} +19 -2
- package/dist/usage.cjs +2 -2
- package/dist/usage.cjs.map +1 -1
- package/dist/usage.d.cts +1 -1
- package/dist/usage.d.ts +1 -1
- package/dist/usage.mjs +2 -2
- package/dist/usage.mjs.map +1 -1
- package/dist/webhooks.cjs.map +1 -1
- package/dist/webhooks.d.cts +1 -1
- package/dist/webhooks.d.ts +1 -1
- package/dist/webhooks.mjs.map +1 -1
- package/package.json +156 -156
package/dist/react.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/firestore-compat.ts","../src/react.ts"],"names":["result","allowed"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\r\n * @neetru/sdk/firestore-compat — camada de COMPATIBILIDADE (transicional).\r\n *\r\n * ⚠️ **STATUS: compat / transitional.** Este módulo espelha a superfície\r\n * modular do Firebase Firestore (`doc`, `collection`, `query`, `where`,\r\n * `getDocs`, `setDoc`, `onSnapshot`, `writeBatch`, `runTransaction`,\r\n * `serverTimestamp`, `Timestamp`, `FieldValue`, …) para que produtos que\r\n * migram do Firebase **não precisem reescrever as chamadas à mão**.\r\n *\r\n * Ele NÃO é a API nativa do SDK e NÃO compete com `client.db` (NeetruDb).\r\n * É um auxílio de migração: troque os imports de `firebase/firestore` por\r\n * `@neetru/sdk/firestore-compat`, valide, e depois migre incrementalmente\r\n * para a superfície nativa `client.db.collection(...)` quando fizer sentido.\r\n *\r\n * Esta implementação foi desenhada a partir dos shims reais que os produtos\r\n * `neetru-gestovendas` (`src/neetru/firestore.ts`) e `neetru-pdvagiliza`\r\n * (`src/lib/neetru/firestore-compat.ts`) escreveram à mão — espelha\r\n * exatamente a superfície que eles precisam para poder **deletar os shims**.\r\n *\r\n * ### Backing\r\n * Tudo flui por `client.db` (`NeetruDb`). As funções recebem ou um `NeetruDb`\r\n * (o `client.db`) ou o `NeetruClient` inteiro como primeiro argumento — ambos\r\n * são aceitos por `getFirestore(...)`/`collection(...)`/`doc(...)`.\r\n *\r\n * ### Diferenças deliberadas vs Firebase\r\n * - `serverTimestamp()` e `increment()`/`arrayUnion()`/`arrayRemove()` retornam\r\n * sentinels do SDK (`@neetru/sdk` `serverTimestamp`/`increment`) — resolvidos\r\n * SERVER-SIDE pelo Core (não pelo relógio spoofável do cliente).\r\n * - `Timestamp` é uma classe leve (sem proto de Firebase) com `toDate()`,\r\n * `toMillis()`, `seconds`, `nanoseconds` e `Timestamp.now()/fromDate()/fromMillis()`.\r\n * - `onSnapshot` usa o realtime nativo do `client.db` (`onSnapshot`/`doc().onSnapshot`)\r\n * quando o transporte oferece; caso contrário entrega o snapshot inicial e\r\n * reage às mudanças locais (offline-first do SDK).\r\n *\r\n * @module\r\n */\r\n\r\nimport type {\r\n NeetruDb,\r\n DbCollectionRef,\r\n DbQuery,\r\n DbWhereFilter,\r\n DbListResult,\r\n DbGetResult,\r\n} from './db/client-db';\r\nimport {\r\n serverTimestamp as sdkServerTimestamp,\r\n increment as sdkIncrement,\r\n type FieldSentinel,\r\n} from './db/field-value';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Resolução do handle de banco\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Aceita tanto o `NeetruDb` (`client.db`) quanto o `NeetruClient` inteiro.\r\n * Produtos legados chamam `getFirestore(client)` ou `getFirestore(client.db)`;\r\n * ambos resolvem para o `NeetruDb` subjacente.\r\n */\r\nexport type FirestoreLike = NeetruDb | { db: NeetruDb };\r\n\r\n/** Marker brand reconhecível em runtime — `getFirestore()` devolve isto. */\r\nexport interface Firestore {\r\n readonly __neetruFirestore: true;\r\n readonly db: NeetruDb;\r\n}\r\n\r\nfunction isClientWrapper(v: FirestoreLike | Firestore): v is { db: NeetruDb } {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n 'db' in v &&\r\n typeof (v as { db?: unknown }).db === 'object' &&\r\n (v as { db?: { collection?: unknown } }).db?.collection !== undefined\r\n );\r\n}\r\n\r\nfunction resolveDb(handle: FirestoreLike | Firestore): NeetruDb {\r\n if ((handle as Firestore).__neetruFirestore) return (handle as Firestore).db;\r\n if (isClientWrapper(handle)) return handle.db;\r\n // É o próprio NeetruDb (tem `.collection`).\r\n return handle as NeetruDb;\r\n}\r\n\r\n/**\r\n * Equivalente compat de `getFirestore(app)`. Aceita o `NeetruClient` ou o\r\n * `client.db` (`NeetruDb`) e devolve um handle `Firestore` opaco.\r\n *\r\n * @example\r\n * ```ts\r\n * import { getFirestore } from '@neetru/sdk/firestore-compat';\r\n * const db = getFirestore(client); // ou getFirestore(client.db)\r\n * ```\r\n */\r\nexport function getFirestore(handle: FirestoreLike): Firestore {\r\n return { __neetruFirestore: true, db: resolveDb(handle) };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Refs — Collection & Document\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Operadores `where` suportados (subset do Firestore + ops do SDK). */\r\nexport type WhereFilterOp =\r\n | '=='\r\n | '!='\r\n | '<'\r\n | '<='\r\n | '>'\r\n | '>='\r\n | 'in'\r\n | 'not-in'\r\n | 'array-contains'\r\n | 'array-contains-any';\r\n\r\nexport interface CollectionReference<T = Record<string, unknown>> {\r\n readonly type: 'collection';\r\n readonly __kind: 'collection';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly db: NeetruDb;\r\n /** Handle nativo do SDK — atalho para a superfície `client.db`. */\r\n readonly ref: DbCollectionRef<T>;\r\n}\r\n\r\nexport interface DocumentReference<T = Record<string, unknown>> {\r\n readonly type: 'document';\r\n readonly __kind: 'document';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n /** Marcador de tipo (phantom) — preserva `T` para inferência do caller. */\r\n readonly __doc?: T;\r\n}\r\n\r\n/** Aceita tanto `Firestore` quanto `NeetruDb`/`NeetruClient` direto. */\r\ntype DbArg = Firestore | FirestoreLike;\r\n\r\n/**\r\n * `collection(db, name)` — referência a uma coleção.\r\n *\r\n * Aceita `db` como `Firestore` (de `getFirestore`), `NeetruClient` ou `client.db`.\r\n */\r\nexport function collection<T = Record<string, unknown>>(\r\n db: DbArg,\r\n name: string,\r\n): CollectionReference<T> {\r\n const resolved = resolveDb(db);\r\n return {\r\n type: 'collection',\r\n __kind: 'collection',\r\n id: name,\r\n path: name,\r\n db: resolved,\r\n ref: resolved.collection<T>(name),\r\n };\r\n}\r\n\r\n/**\r\n * `doc(...)` — referência a um documento. Suporta as 3 assinaturas legadas:\r\n * doc(db, 'collection', 'id') → coleção + id\r\n * doc(collectionRef, 'id') → id dentro de uma CollectionReference\r\n * doc(db, 'collection/id') → path com barra (id auto-gerado se id ausente)\r\n *\r\n * Quando o `id` é omitido, gera um id v4 (espelha `doc(collectionRef)` do Firebase\r\n * que cria uma ref com id automático antes do `setDoc`).\r\n */\r\nexport function doc<T = Record<string, unknown>>(\r\n dbOrCollection: DbArg | CollectionReference<T>,\r\n ...path: string[]\r\n): DocumentReference<T> {\r\n // Forma doc(collectionRef, id?)\r\n if (\r\n typeof dbOrCollection === 'object' &&\r\n dbOrCollection !== null &&\r\n (dbOrCollection as CollectionReference<T>).__kind === 'collection'\r\n ) {\r\n const col = dbOrCollection as CollectionReference<T>;\r\n const id = path[0] ?? generateId();\r\n return makeDocRef<T>(col.db, col.id, id);\r\n }\r\n\r\n const resolved = resolveDb(dbOrCollection as DbArg);\r\n\r\n if (path.length >= 2) {\r\n return makeDocRef<T>(resolved, path[0], path.slice(1).join('/'));\r\n }\r\n if (path[0]?.includes('/')) {\r\n const parts = path[0].split('/');\r\n return makeDocRef<T>(resolved, parts[0], parts.slice(1).join('/') || generateId());\r\n }\r\n // doc(db, 'collection') — id auto-gerado\r\n return makeDocRef<T>(resolved, path[0] ?? '', generateId());\r\n}\r\n\r\nfunction makeDocRef<T>(db: NeetruDb, collectionName: string, id: string): DocumentReference<T> {\r\n return {\r\n type: 'document',\r\n __kind: 'document',\r\n id,\r\n collection: collectionName,\r\n path: `${collectionName}/${id}`,\r\n db,\r\n };\r\n}\r\n\r\n/**\r\n * Gera um id estilo Firestore (20 chars alfanuméricos) com aleatoriedade\r\n * CRIPTOGRÁFICA.\r\n *\r\n * Antes usava `Math.random()` — previsível e colisível (PRNG não-criptográfico,\r\n * estado recuperável). IDs de documento previsíveis permitem enumeração/forja.\r\n * Agora usa `globalThis.crypto.getRandomValues` (WebCrypto), disponível tanto em\r\n * Node ≥20 quanto em browsers — sem importar `node:crypto` (que quebraria o\r\n * bundle browser). Rejeita bytes ≥ 248 (4×62) para eliminar o viés de módulo\r\n * sobre o alfabeto de 62 caracteres.\r\n */\r\nfunction generateId(): string {\r\n const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 62\r\n const cryptoObj = (globalThis as { crypto?: Crypto }).crypto;\r\n if (!cryptoObj?.getRandomValues) {\r\n throw new Error(\r\n '[Neetru SDK / firestore-compat] WebCrypto indisponível: generateId() exige ' +\r\n 'globalThis.crypto.getRandomValues (Node ≥20 ou ambiente browser).',\r\n );\r\n }\r\n let out = '';\r\n while (out.length < 20) {\r\n const buf = new Uint8Array(20 - out.length);\r\n cryptoObj.getRandomValues(buf);\r\n for (const byte of buf) {\r\n if (byte < 248) {\r\n out += CHARS[byte % 62];\r\n if (out.length === 20) break;\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUERY — acumula constraints e gera DbQuery quando consumido\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type QueryConstraintType = 'where' | 'orderBy' | 'limit';\r\n\r\nexport interface QueryConstraint {\r\n readonly __constraint: QueryConstraintType;\r\n}\r\n\r\ninterface WhereConstraint extends QueryConstraint {\r\n readonly __constraint: 'where';\r\n readonly field: string;\r\n readonly op: WhereFilterOp;\r\n readonly value: unknown;\r\n}\r\n\r\ninterface OrderByConstraint extends QueryConstraint {\r\n readonly __constraint: 'orderBy';\r\n readonly field: string;\r\n readonly direction: 'asc' | 'desc';\r\n}\r\n\r\ninterface LimitConstraint extends QueryConstraint {\r\n readonly __constraint: 'limit';\r\n readonly value: number;\r\n}\r\n\r\nexport interface Query<T = Record<string, unknown>> {\r\n readonly type: 'query';\r\n readonly __kind: 'query';\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n readonly ref: DbCollectionRef<T>;\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * `query(collectionRef, ...constraints)` — combina `where`/`orderBy`/`limit`.\r\n * Aceita também uma `Query` como fonte (encadeamento de constraints).\r\n */\r\nexport function query<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n ...constraints: QueryConstraint[]\r\n): Query<T> {\r\n const base: Query<T> =\r\n source.__kind === 'query'\r\n ? source\r\n : {\r\n type: 'query',\r\n __kind: 'query',\r\n path: source.path,\r\n collection: source.id,\r\n db: source.db,\r\n ref: source.ref,\r\n constraints: [],\r\n };\r\n return { ...base, constraints: [...base.constraints, ...constraints] };\r\n}\r\n\r\nexport function where(field: string, op: WhereFilterOp, value: unknown): WhereConstraint {\r\n return { __constraint: 'where', field, op, value };\r\n}\r\n\r\nexport function orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): OrderByConstraint {\r\n return { __constraint: 'orderBy', field, direction };\r\n}\r\n\r\nexport function limit(value: number): LimitConstraint {\r\n return { __constraint: 'limit', value };\r\n}\r\n\r\n/**\r\n * Converte os constraints de uma `Query` compat para o `DbQuery` nativo do SDK.\r\n *\r\n * Exportado para que os hooks React (`@neetru/sdk/react`) possam reusar a mesma\r\n * tradução — fonte única de verdade da conversão de constraints.\r\n */\r\nexport function toDbQuery(constraints: QueryConstraint[]): DbQuery {\r\n const whereFilters: DbWhereFilter[] = [];\r\n let orderByClause: DbQuery['orderBy'];\r\n let limitValue: number | undefined;\r\n\r\n for (const c of constraints) {\r\n if (c.__constraint === 'where') {\r\n const w = c as WhereConstraint;\r\n whereFilters.push({ field: w.field, op: normalizeOp(w.op), value: w.value });\r\n } else if (c.__constraint === 'orderBy') {\r\n const o = c as OrderByConstraint;\r\n orderByClause = { field: o.field, direction: o.direction };\r\n } else if (c.__constraint === 'limit') {\r\n limitValue = (c as LimitConstraint).value;\r\n }\r\n }\r\n\r\n const out: DbQuery = {};\r\n if (whereFilters.length > 0) out.where = whereFilters;\r\n if (orderByClause) out.orderBy = orderByClause;\r\n if (limitValue !== undefined) out.limit = limitValue;\r\n return out;\r\n}\r\n\r\n/**\r\n * O `DbWhereFilter` do SDK não declara `array-contains-any`/`not-in` no union.\r\n * Passamos o op como-está; o Core valida server-side e retorna erro explícito\r\n * se não suportado.\r\n *\r\n * Bug corrigido: remapeava 'array-contains-any' → 'array-contains' silenciosamente,\r\n * o que tem semântica completamente diferente (checa literal do array, não interseção)\r\n * e nunca retornava resultados para queries reais.\r\n */\r\nfunction normalizeOp(op: WhereFilterOp): DbWhereFilter['op'] {\r\n // Todas as ops passam direto; o Core valida server-side.\r\n return op as DbWhereFilter['op'];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SNAPSHOTS — emulam a API antiga do Firebase\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Remove o campo `id` dos dados (o Firestore expõe id via `.id`, não em `data()`). */\r\nfunction stripId<T>(raw: Record<string, unknown>): T {\r\n if (raw && typeof raw === 'object' && 'id' in raw) {\r\n const { id: _omit, ...rest } = raw;\r\n return rest as T;\r\n }\r\n return raw as T;\r\n}\r\n\r\nexport class DocumentSnapshot<T = Record<string, unknown>> {\r\n constructor(\r\n public readonly id: string,\r\n private readonly _data: T | null,\r\n public readonly ref?: DocumentReference<T>,\r\n ) {}\r\n\r\n exists(): boolean {\r\n return this._data != null;\r\n }\r\n\r\n data(): T | undefined {\r\n return this._data ?? undefined;\r\n }\r\n\r\n /** Compat: `snap.get('field')` lê um campo top-level. */\r\n get(field: string): unknown {\r\n return this._data ? (this._data as Record<string, unknown>)[field] : undefined;\r\n }\r\n}\r\n\r\nexport interface QueryDocumentSnapshot<T = Record<string, unknown>> {\r\n readonly id: string;\r\n data(): T;\r\n exists(): boolean;\r\n get(field: string): unknown;\r\n readonly ref: DocumentReference<T>;\r\n}\r\n\r\nexport class QuerySnapshot<T = Record<string, unknown>> {\r\n public readonly docs: Array<QueryDocumentSnapshot<T>>;\r\n public readonly empty: boolean;\r\n public readonly size: number;\r\n\r\n constructor(\r\n rows: Array<{ id: string; data: Record<string, unknown> }>,\r\n db?: NeetruDb,\r\n collectionName?: string,\r\n ) {\r\n this.docs = rows.map(({ id, data }) => {\r\n const stripped = stripId<T>(data);\r\n const ref: DocumentReference<T> | undefined =\r\n db && collectionName ? makeDocRef<T>(db, collectionName, id) : undefined;\r\n return {\r\n id,\r\n data: () => stripped,\r\n exists: () => true,\r\n get: (field: string) => (stripped as Record<string, unknown>)?.[field],\r\n // ref sempre presente quando db+collection conhecidos (caso comum).\r\n ref: ref as DocumentReference<T>,\r\n };\r\n });\r\n this.empty = this.docs.length === 0;\r\n this.size = this.docs.length;\r\n }\r\n\r\n forEach(cb: (doc: QueryDocumentSnapshot<T>) => void): void {\r\n this.docs.forEach(cb);\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// READS\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Extrai os dados crus do primeiro doc de um `DbGetResult`. */\r\nfunction firstDocData(result: DbGetResult<Record<string, unknown>> | null): {\r\n data: Record<string, unknown> | null;\r\n id: string | null;\r\n} {\r\n const first = result?.docs[0];\r\n return { data: first ? (first.data as Record<string, unknown>) : null, id: first?.id ?? null };\r\n}\r\n\r\nexport async function getDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n): Promise<DocumentSnapshot<T>> {\r\n const result = (await ref.db.collection(ref.collection).get(ref.id)) as DbGetResult<\r\n Record<string, unknown>\r\n > | null;\r\n const { data } = firstDocData(result);\r\n return new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? (stripId<T>({ id: ref.id, ...data })) : null,\r\n ref,\r\n );\r\n}\r\n\r\n/** Mapeia um `DbListResult` para linhas `{ id, data }` (id garantido). */\r\nfunction listResultToRows(\r\n result: DbListResult<Record<string, unknown>>,\r\n): Array<{ id: string; data: Record<string, unknown> }> {\r\n return result.docs.map((d) => ({ id: d.id, data: { ...(d.data as Record<string, unknown>) } }));\r\n}\r\n\r\nexport async function getDocs<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n): Promise<QuerySnapshot<T>> {\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const collectionName = source.__kind === 'query' ? source.collection : source.id;\r\n const result = (await source.db\r\n .collection(collectionName)\r\n .list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n return new QuerySnapshot<T>(listResultToRows(result), source.db, collectionName);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITES\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SetOptions {\r\n merge?: boolean;\r\n mergeFields?: string[];\r\n}\r\n\r\nexport async function setDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options: SetOptions = {},\r\n): Promise<void> {\r\n const col = ref.db.collection(ref.collection);\r\n const payload = data as Record<string, unknown>;\r\n if (options.merge || (options.mergeFields && options.mergeFields.length > 0)) {\r\n // Bug corrigido: `update()` retorna 404 no Core quando o doc não existe.\r\n // Verificamos existência antes: se existir → update (merge); se não → set (criar).\r\n // Isso espelha a semântica do Firebase setDoc({merge:true}) (upsert real).\r\n const existing = await col.get(ref.id);\r\n const docExists = Array.isArray((existing as { docs?: unknown[] } | null)?.docs)\r\n ? ((existing as { docs: unknown[] }).docs.length > 0)\r\n : existing != null;\r\n if (docExists) {\r\n await col.update(ref.id, payload);\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n}\r\n\r\nexport async function addDoc<T = Record<string, unknown>>(\r\n source: CollectionReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<DocumentReference<T>> {\r\n const result = await source.db.collection(source.id).add(data as Record<string, unknown>);\r\n return makeDocRef<T>(source.db, source.id, result.id);\r\n}\r\n\r\nexport async function updateDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<void> {\r\n await ref.db.collection(ref.collection).update(ref.id, data as Record<string, unknown>);\r\n}\r\n\r\nexport async function deleteDoc(ref: DocumentReference): Promise<void> {\r\n await ref.db.collection(ref.collection).remove(ref.id);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITE BATCH\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\ninterface PendingBatchOp {\r\n collection: string;\r\n op:\r\n | { kind: 'set'; id: string; data: Record<string, unknown> }\r\n | { kind: 'update'; id: string; data: Record<string, unknown> }\r\n | { kind: 'remove'; id: string };\r\n}\r\n\r\nexport class WriteBatch {\r\n private ops: PendingBatchOp[] = [];\r\n\r\n constructor(private readonly db: NeetruDb) {}\r\n\r\n set<T>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n _options?: SetOptions,\r\n ): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'set', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'update', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n delete(ref: DocumentReference): this {\r\n this.ops.push({ collection: ref.collection, op: { kind: 'remove', id: ref.id } });\r\n return this;\r\n }\r\n\r\n async commit(): Promise<void> {\r\n // Agrupa por coleção — o `DbCollectionRef.batch` opera numa coleção por vez.\r\n const byCol = new Map<string, PendingBatchOp['op'][]>();\r\n for (const { collection: name, op } of this.ops) {\r\n const list = byCol.get(name) ?? [];\r\n list.push(op);\r\n byCol.set(name, list);\r\n }\r\n for (const [name, ops] of byCol.entries()) {\r\n // O union nativo `DbBatchOp` casa 1:1 com nossas variantes (set/update/remove).\r\n await this.db.collection(name).batch(ops as never);\r\n }\r\n this.ops = [];\r\n }\r\n}\r\n\r\nexport function writeBatch(db: DbArg): WriteBatch {\r\n return new WriteBatch(resolveDb(db));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// runTransaction — emulação otimista (read → write) sobre client.db\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Handle de transação compat. O SDK não expõe transações multi-doc atômicas\r\n * com lock server-side, então emulamos a forma do Firebase: `tx.get` lê do\r\n * `client.db`, e `tx.set/update/delete` enfileiram escritas que são aplicadas\r\n * no fim do callback.\r\n *\r\n * ⚠️ **LIMITAÇÃO CONHECIDA — sem isolamento server-side (lost-update silencioso):**\r\n *\r\n * Diferente do Firestore nativo (que usa serializable snapshot isolation com\r\n * contention detection), este shim executa reads e writes em rounds separados\r\n * sem garantia de atomicidade:\r\n *\r\n * 1. `tx.get(ref)` faz um read imediato via `client.db`.\r\n * 2. As escritas (`tx.set/update/delete`) são **enfileiradas** — não bloqueiam\r\n * outros writers.\r\n * 3. No fim do callback, `batch.commit()` aplica as escritas. Se outro processo\r\n * tiver modificado o mesmo documento entre o read e o commit, a escrita\r\n * será um **lost-update silencioso** (sem erro, sem retry automático).\r\n *\r\n * **Quando isso importa:** counters, saldos, estoques — qualquer dado\r\n * que precise de leitura-modificação-escrita atômica.\r\n *\r\n * **Alternativas seguras:**\r\n * - Use **Server Actions no Core** (`src/lib/actions/`) que rodam dentro\r\n * de transações Firestore Admin SDK reais (com locks server-side).\r\n * - Use `increment()` sentinel para contadores numéricos — server-side atomic.\r\n * - Use `arrayUnion()`/`arrayRemove()` para sets (também server-side atomic).\r\n *\r\n * Esta emulação é aceitável para migrações transitórias e para operações\r\n * onde a probabilidade de concorrência é baixa (ex: dados de um único usuário\r\n * sem write concorrente). **Não use para dados financeiros ou de inventário.**\r\n */\r\nexport interface Transaction {\r\n get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;\r\n set<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>, options?: SetOptions): Transaction;\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): Transaction;\r\n delete(ref: DocumentReference): Transaction;\r\n}\r\n\r\n/** Flag para emitir o aviso de limitação uma só vez por processo (não spammar). */\r\nlet _runTransactionWarnEmitted = false;\r\n\r\nexport async function runTransaction<R>(\r\n db: DbArg,\r\n updateFunction: (transaction: Transaction) => Promise<R>,\r\n): Promise<R> {\r\n // Aviso em desenvolvimento/staging: sem isolamento server-side.\r\n if (!_runTransactionWarnEmitted) {\r\n _runTransactionWarnEmitted = true;\r\n console.warn(\r\n '[Neetru SDK / firestore-compat] runTransaction(): esta emulação NÃO oferece' +\r\n ' isolamento server-side. Reads e writes são executados em rounds separados;' +\r\n ' um write concorrente entre o get() e o commit() resultará em lost-update' +\r\n ' silencioso. Para atomicidade real, use Server Actions no Core ou sentinels' +\r\n ' increment()/arrayUnion(). Veja a doc em src/firestore-compat.ts § runTransaction.',\r\n );\r\n }\r\n\r\n const resolved = resolveDb(db);\r\n const batch = new WriteBatch(resolved);\r\n\r\n const tx: Transaction = {\r\n async get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>> {\r\n return getDoc(ref);\r\n },\r\n set(ref, data, options) {\r\n batch.set(ref, data, options);\r\n return tx;\r\n },\r\n update(ref, data) {\r\n batch.update(ref, data);\r\n return tx;\r\n },\r\n delete(ref) {\r\n batch.delete(ref);\r\n return tx;\r\n },\r\n };\r\n\r\n const result = await updateFunction(tx);\r\n await batch.commit();\r\n return result;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// onSnapshot — realtime via client.db (com fallback inicial)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type Unsubscribe = () => void;\r\n\r\ntype DocOnNext<T> = (snap: DocumentSnapshot<T>) => void;\r\ntype QueryOnNext<T> = (snap: QuerySnapshot<T>) => void;\r\ntype OnError = (err: Error) => void;\r\n\r\nexport function onSnapshot<T>(\r\n ref: DocumentReference<T>,\r\n onNext: DocOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: Query<T> | CollectionReference<T>,\r\n onNext: QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: DocumentReference<T> | Query<T> | CollectionReference<T>,\r\n onNext: DocOnNext<T> | QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe {\r\n // Documento único\r\n if (source.__kind === 'document') {\r\n const ref = source as DocumentReference<T>;\r\n const docRef = ref.db.collection(ref.collection).doc(ref.id);\r\n try {\r\n return docRef.onSnapshot((snap) => {\r\n const data = snap?.docs[0]?.data as Record<string, unknown> | undefined;\r\n (onNext as DocOnNext<T>)(\r\n new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? stripId<T>({ id: ref.id, ...data }) : null,\r\n ref,\r\n ),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n }\r\n\r\n // Coleção / Query\r\n const collectionName = source.__kind === 'query' ? source.collection : (source as CollectionReference<T>).id;\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const col = source.db.collection(collectionName);\r\n try {\r\n return col.onSnapshot(dbQuery, (snap: DbListResult<Record<string, unknown>>) => {\r\n (onNext as QueryOnNext<T>)(\r\n new QuerySnapshot<T>(listResultToRows(snap), source.db, collectionName),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Timestamp — classe leve compat\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Reimplementação compat de `Timestamp` do Firestore. Sem proto do Firebase.\r\n * Cobre `seconds`, `nanoseconds`, `toDate()`, `toMillis()`, `isEqual()`,\r\n * `valueOf()`, `toJSON()` + estáticos `now()`, `fromDate()`, `fromMillis()`.\r\n */\r\nexport class Timestamp {\r\n constructor(\r\n public readonly seconds: number,\r\n public readonly nanoseconds: number,\r\n ) {}\r\n\r\n static now(): Timestamp {\r\n return Timestamp.fromMillis(Date.now());\r\n }\r\n\r\n static fromDate(date: Date): Timestamp {\r\n return Timestamp.fromMillis(date.getTime());\r\n }\r\n\r\n static fromMillis(millis: number): Timestamp {\r\n const seconds = Math.floor(millis / 1000);\r\n const nanoseconds = (millis - seconds * 1000) * 1e6;\r\n return new Timestamp(seconds, nanoseconds);\r\n }\r\n\r\n toDate(): Date {\r\n return new Date(this.toMillis());\r\n }\r\n\r\n toMillis(): number {\r\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\r\n }\r\n\r\n isEqual(other: Timestamp): boolean {\r\n return this.seconds === other.seconds && this.nanoseconds === other.nanoseconds;\r\n }\r\n\r\n valueOf(): string {\r\n // Estável para ordenação por string (espelha Firebase).\r\n return `${this.seconds.toString().padStart(12, '0')}.${this.nanoseconds\r\n .toString()\r\n .padStart(9, '0')}`;\r\n }\r\n\r\n toJSON(): { seconds: number; nanoseconds: number } {\r\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\r\n }\r\n\r\n toString(): string {\r\n return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// FieldValue + sentinels\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Sentinel de `arrayUnion`/`arrayRemove`. O SDK ainda não tem sentinel de array\r\n * server-side, então emitimos um marker namespaceado que o Core reconhece\r\n * (mesma família de `__neetru__`). Se o backing não suportar, o produto deve\r\n * preferir read-modify-write via Server Action. Documentado como compat.\r\n */\r\nexport interface ArrayUnionSentinel {\r\n readonly __neetru__: 'arrayUnion';\r\n readonly elements: unknown[];\r\n}\r\nexport interface ArrayRemoveSentinel {\r\n readonly __neetru__: 'arrayRemove';\r\n readonly elements: unknown[];\r\n}\r\n\r\n/**\r\n * Sentinel de campo a deletar (compat `deleteField()`).\r\n */\r\nexport interface DeleteFieldSentinel {\r\n readonly __neetru__: 'deleteField';\r\n}\r\n\r\n/**\r\n * `serverTimestamp()` — delega ao sentinel do SDK (resolvido SERVER-SIDE).\r\n * NÃO retorna mais ISO/Date do cliente (que era spoofável).\r\n */\r\nexport function serverTimestamp(): FieldSentinel {\r\n return sdkServerTimestamp();\r\n}\r\n\r\n/**\r\n * `increment(n)` — incremento atômico server-side (delega ao sentinel do SDK).\r\n */\r\nexport function increment(n: number): FieldSentinel {\r\n return sdkIncrement(n);\r\n}\r\n\r\n/** `arrayUnion(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayUnion(...elements: unknown[]): ArrayUnionSentinel {\r\n return { __neetru__: 'arrayUnion', elements };\r\n}\r\n\r\n/** `arrayRemove(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayRemove(...elements: unknown[]): ArrayRemoveSentinel {\r\n return { __neetru__: 'arrayRemove', elements };\r\n}\r\n\r\n/** `deleteField()` — sentinel compat para remover um campo. */\r\nexport function deleteField(): DeleteFieldSentinel {\r\n return { __neetru__: 'deleteField' };\r\n}\r\n\r\n/**\r\n * Namespace estático `FieldValue` — espelha `firebase/firestore`'s `FieldValue`.\r\n * Os métodos delegam às funções modulares acima.\r\n *\r\n * @example\r\n * ```ts\r\n * import { FieldValue } from '@neetru/sdk/firestore-compat';\r\n * await updateDoc(ref, {\r\n * tags: FieldValue.arrayUnion('novo'),\r\n * counter: FieldValue.increment(1),\r\n * updatedAt: FieldValue.serverTimestamp(),\r\n * });\r\n * ```\r\n */\r\nexport const FieldValue = {\r\n serverTimestamp,\r\n increment,\r\n arrayUnion,\r\n arrayRemove,\r\n delete: deleteField,\r\n} as const;\r\n","/**\r\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\r\n *\r\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\r\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\r\n *\r\n * Exports:\r\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\r\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\r\n * (suporte a `mode='readonly'` pro decisão CEO §5).\r\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\r\n *\r\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\r\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\r\n * puxam React mesmo via transitive.\r\n */\r\nimport * as React from 'react';\r\nimport type { NeetruClient, NeetruUser, AuthNamespace } from './types';\r\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\r\nimport type {\r\n CollectionReference,\r\n Query,\r\n DocumentReference,\r\n} from './firestore-compat';\r\nimport { toDbQuery } from './firestore-compat';\r\nimport type { DbListResult, DbGetResult } from './db/client-db';\r\n\r\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\r\n\r\nexport interface CheckoutLinkProps\r\n extends Omit<\r\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\r\n 'href' | 'onClick' | 'onError'\r\n > {\r\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\r\n client: NeetruClient;\r\n /** Slug do produto (ex: `neetru-pulse`). */\r\n productId: string;\r\n /** Plan id (ex: `pro_monthly`). */\r\n planId: string;\r\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\r\n callbackUrl: string;\r\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\r\n tenantType?: 'pf' | 'pj';\r\n tenantId?: string;\r\n /** Children — texto/JSX do CTA. */\r\n children: React.ReactNode;\r\n /** Callback quando intent foi criada (antes de redirect). */\r\n onIntentCreated?: (result: CheckoutStartResult) => void;\r\n /** Callback de erro. */\r\n onError?: (err: Error) => void;\r\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\r\n noAutoRedirect?: boolean;\r\n /** Override de href (default: `#`). */\r\n href?: string;\r\n}\r\n\r\n/**\r\n * Wrapper de `<a>` que dispara `client.checkout.start()` no click.\r\n *\r\n * Uso típico:\r\n * ```tsx\r\n * <CheckoutLink\r\n * client={client}\r\n * productId=\"meu-saas\"\r\n * planId=\"pro_monthly\"\r\n * callbackUrl=\"https://meu-saas.com/billing/success\"\r\n * >\r\n * Assinar Pro\r\n * </CheckoutLink>\r\n * ```\r\n *\r\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\r\n * de URL no hover (pode ser sobrescrito via prop spread).\r\n */\r\nexport function CheckoutLink({\r\n client,\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n children,\r\n onIntentCreated,\r\n onError,\r\n noAutoRedirect,\r\n ...rest\r\n}: CheckoutLinkProps): React.ReactElement {\r\n const [submitting, setSubmitting] = React.useState(false);\r\n\r\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\r\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\r\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } }).checkout;\r\n\r\n const handleClick = React.useCallback(\r\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\r\n event.preventDefault();\r\n if (submitting) return;\r\n if (!checkout || typeof checkout.start !== 'function') {\r\n onError?.(new Error('SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+.'));\r\n return;\r\n }\r\n setSubmitting(true);\r\n try {\r\n const result = await checkout.start({\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n autoRedirect: !noAutoRedirect,\r\n });\r\n onIntentCreated?.(result);\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n },\r\n [submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\r\n );\r\n\r\n return React.createElement(\r\n 'a',\r\n {\r\n ...rest,\r\n href: rest.href ?? '#',\r\n onClick: handleClick,\r\n 'aria-busy': submitting || undefined,\r\n 'data-neetru-checkout-link': 'true',\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\r\n\r\n/** Modo de gate quando entitlement não é permitido. */\r\nexport type EntitlementGateMode = 'block' | 'readonly';\r\n\r\ninterface EntitlementContextValue {\r\n /** True quando feature está em modo read-only (limit exceeded). */\r\n readonly: boolean;\r\n /** Reason code da última check. */\r\n reason?: string;\r\n /** Limite atual (free tier). */\r\n limit?: number;\r\n /** Uso atual. */\r\n currentUsage?: number;\r\n}\r\n\r\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\r\n readonly: false,\r\n});\r\n\r\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\r\n * desabilitar inputs/botões de escrita. */\r\nexport function useEntitlementContext(): EntitlementContextValue {\r\n return React.useContext(EntitlementContext);\r\n}\r\n\r\nexport interface EntitlementGateProps {\r\n /** Cliente SDK Neetru. */\r\n client: NeetruClient;\r\n /** Feature key a checar (ex: `export.csv`). */\r\n feature: string;\r\n /** Slug do produto. Default: lê do client.config se ausente. */\r\n productSlug?: string;\r\n /** Modo:\r\n * - `block` (default): renderiza fallback quando não permitido.\r\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\r\n mode?: EntitlementGateMode;\r\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\r\n fallback?: React.ReactNode;\r\n /** Loading UI enquanto check resolve (Suspense-like). */\r\n loading?: React.ReactNode;\r\n children: React.ReactNode;\r\n}\r\n\r\ninterface EntitlementCheckResult {\r\n allowed: boolean;\r\n reason?: string;\r\n limit?: number;\r\n currentUsage?: number;\r\n behavior?: 'readonly' | null;\r\n}\r\n\r\n/**\r\n * Wrapper que gateia children por entitlement check.\r\n *\r\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\r\n * - Children renderizam normalmente.\r\n * - Context injeta `{readonly: true}`.\r\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\r\n * escritas (botões, forms, inputs).\r\n *\r\n * Quando `mode='block'` + check retorna `allowed=false`:\r\n * - `fallback` é renderizado (ex: paywall, upsell modal).\r\n *\r\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\r\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\r\n */\r\nexport function EntitlementGate({\r\n client,\r\n feature,\r\n productSlug,\r\n mode = 'block',\r\n fallback,\r\n loading,\r\n children,\r\n}: EntitlementGateProps): React.ReactElement {\r\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\r\n status: 'loading',\r\n });\r\n\r\n // Resolve productSlug from prop or client.config\r\n const resolvedSlug =\r\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\r\n\r\n // tenantId fallback: client.config.tenantId (default no createNeetruClient).\r\n // usage.check valida ambos productId + tenantId — sem isso, lança\r\n // validation_failed e o Gate fica trancado em allowed:false.\r\n const resolvedTenantId = (client.config as { tenantId?: string }).tenantId;\r\n\r\n React.useEffect(() => {\r\n let cancelled = false;\r\n const run = async () => {\r\n try {\r\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\r\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\r\n let result: EntitlementCheckResult;\r\n const usage = (client as {\r\n usage?: {\r\n check: (\r\n resource: string,\r\n opts?: { productId?: string; tenantId?: string },\r\n ) => Promise<EntitlementCheckResult>;\r\n };\r\n }).usage;\r\n if (usage && typeof usage.check === 'function' && resolvedTenantId) {\r\n const res = await usage.check(feature, {\r\n productId: resolvedSlug,\r\n tenantId: resolvedTenantId,\r\n });\r\n result = {\r\n allowed: res.allowed === true,\r\n reason: res.reason,\r\n limit: res.limit,\r\n currentUsage: res.currentUsage,\r\n behavior: res.behavior,\r\n };\r\n } else {\r\n // Sem tenantId, cai pra entitlements.check (não exige tenant) em vez\r\n // de chamar usage.check e tomar validation_failed silencioso.\r\n const allowed = await client.entitlements.check(resolvedSlug, feature);\r\n result = { allowed };\r\n }\r\n if (!cancelled) setState({ status: 'ready', result });\r\n } catch (err) {\r\n // Fail open: erros de rede/timeout não bloqueiam o usuário com plano válido.\r\n // Bug corrigido: antes setava allowed:false (fail-closed) contradizendo o comentário.\r\n if (!cancelled) {\r\n setState({ status: 'ready', result: { allowed: true, reason: 'check_error_fallback' } });\r\n }\r\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed — fail-open', err);\r\n }\r\n };\r\n run();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [client, feature, resolvedSlug, resolvedTenantId]);\r\n\r\n if (state.status === 'loading') {\r\n return React.createElement(React.Fragment, null, loading ?? null);\r\n }\r\n\r\n const result = state.result;\r\n const allowed = result?.allowed === true;\r\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\r\n\r\n if (!allowed && mode === 'block' && !isReadonly) {\r\n return React.createElement(React.Fragment, null, fallback ?? null);\r\n }\r\n\r\n return React.createElement(\r\n EntitlementContext.Provider,\r\n {\r\n value: {\r\n readonly: isReadonly,\r\n reason: result?.reason,\r\n limit: result?.limit,\r\n currentUsage: result?.currentUsage,\r\n },\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ════════════════════════════════════════════════════════════════════════════\r\n// SDK-2 — Hooks de dados de 1ª classe (compat com hooks caseiros por-produto)\r\n//\r\n// ⚠️ STATUS: 1ª classe, mas desenhados para CASAR com a superfície que os\r\n// produtos (gestovendas / pdvagiliza) escreveram à mão em cima do\r\n// firestore-compat. Substituem os hooks `useCollection`/`useDoc`/`useUser`\r\n// que cada produto reescrevia (~200-400 LOC por produto).\r\n//\r\n// Estes hooks aceitam **refs do `@neetru/sdk/firestore-compat`**\r\n// (`CollectionReference` / `Query` / `DocumentReference`) e devolvem o shape\r\n// `{ data, isLoading, error, isStale, lastUpdated }` que os produtos já\r\n// consomem. Internamente usam o realtime nativo do `client.db`\r\n// (`onSnapshot`) quando disponível, com fallback de polling visibility-aware.\r\n//\r\n// Diferença vs `@neetru/sdk/db/react` (`useDocument`/`useCollection` de lá):\r\n// - Lá: API offline-first, ref = `DbCollectionRef` nativo, retorno\r\n// `{ docs, loading, fromCache, stale }`.\r\n// - Aqui: API firestore-compat, ref = ref do firestore-compat, retorno\r\n// `{ data, isLoading, error, isStale, lastUpdated }` (id já embutido em\r\n// cada item; data nunca duplica o id internamente).\r\n// ════════════════════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resultado de `useCollection` (subpath `@neetru/sdk/react`, API firestore-compat).\r\n *\r\n * Nome distinto de `UseCollectionResult` do subpath `@neetru/sdk/db/react`:\r\n * - `src/db/react.ts` → `{ docs, loading, fromCache, stale, hasPendingWrites }`\r\n * - `src/react.ts` → `{ data, isLoading, error, isStale, lastUpdated }` (este)\r\n *\r\n * Consumers que importam de ambos subpaths não terão clash de tipo.\r\n * Alias de retrocompat `UseCollectionResult` exportado abaixo.\r\n */\r\nexport interface FirestoreCompatCollectionResult<T> {\r\n /** Documentos (cada um com `id` embutido) ou `null` enquanto carrega/sem ref. */\r\n data: Array<T & { id: string }> | null;\r\n /** `true` enquanto o primeiro snapshot não chegou. */\r\n isLoading: boolean;\r\n /** Erro do último refresh (dados exibidos podem estar defasados). */\r\n error: Error | null;\r\n /** `true` quando um refresh falhou ou o snapshot veio do cache/offline. */\r\n isStale: boolean;\r\n /** epoch ms do último snapshot bem-sucedido, ou `null`. */\r\n lastUpdated: number | null;\r\n}\r\n\r\n/**\r\n * Alias de retrocompat — exportado para não quebrar produtos que importam\r\n * `UseCollectionResult` do subpath `@neetru/sdk/react`. Prefira\r\n * `FirestoreCompatCollectionResult` em código novo para evitar o clash de nome\r\n * com `UseCollectionResult` de `@neetru/sdk/db/react`.\r\n *\r\n * @deprecated Use `FirestoreCompatCollectionResult` em novo código.\r\n */\r\nexport type UseCollectionResult<T> = FirestoreCompatCollectionResult<T>;\r\n\r\n/** Resultado de `useDoc`. */\r\nexport interface UseDocResult<T> {\r\n data: (T & { id: string }) | null;\r\n isLoading: boolean;\r\n error: Error | null;\r\n isStale: boolean;\r\n lastUpdated: number | null;\r\n}\r\n\r\n/** Opções comuns dos hooks de dados. */\r\nexport interface UseDataOptions {\r\n /**\r\n * Intervalo de polling de fallback em ms (quando o transporte não oferece\r\n * realtime nativo). Default 5000. O polling pausa quando a aba está oculta.\r\n */\r\n intervalMs?: number;\r\n /**\r\n * Força polling mesmo quando o `onSnapshot` nativo está disponível. Útil para\r\n * transportes REST onde `onSnapshot` só entrega o snapshot inicial.\r\n * Default: `false` (usa realtime quando o transporte oferece).\r\n */\r\n poll?: boolean;\r\n}\r\n\r\nconst DEFAULT_INTERVAL_MS = 5000;\r\n\r\n/** `true` quando a aba está oculta (não dispara polling em background). */\r\nfunction isDocumentHidden(): boolean {\r\n return typeof document !== 'undefined' && document.hidden === true;\r\n}\r\n\r\n/**\r\n * Serializa uma fonte de coleção/query firestore-compat numa chave estável.\r\n * Garante que mudanças de VALOR (não de identidade) re-subscrevam, mas objetos\r\n * iguais não causem loop de efeito.\r\n */\r\nfunction serializeSource(\r\n source: CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n): string | null {\r\n if (!source) return null;\r\n if (source.__kind === 'collection') {\r\n return `collection:${source.id}`;\r\n }\r\n const dbQuery = toDbQuery(source.constraints);\r\n return `query:${source.collection}:${JSON.stringify(dbQuery)}`;\r\n}\r\n\r\n/** Normaliza linhas de um snapshot do client.db em `{...data, id}` (id embutido). */\r\nfunction rowsFromList<T>(snap: DbListResult<Record<string, unknown>>): Array<T & { id: string }> {\r\n return snap.docs.map((d) => {\r\n const data = d.data as Record<string, unknown>;\r\n // Garante que o id externo prevaleça (sem duplicar dentro do objeto data).\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id: d.id };\r\n });\r\n}\r\n\r\nfunction rowFromGet<T>(\r\n snap: DbGetResult<Record<string, unknown>> | null,\r\n id: string,\r\n): (T & { id: string }) | null {\r\n const first = snap?.docs[0];\r\n if (!first) return null;\r\n const data = first.data as Record<string, unknown>;\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id };\r\n}\r\n\r\n/**\r\n * Hook de tempo real para uma coleção (ou subconjunto filtrado via `query`).\r\n *\r\n * Aceita uma `CollectionReference` ou `Query` do `@neetru/sdk/firestore-compat`.\r\n * Subscreve ao `onSnapshot` nativo do `client.db`; quando o transporte é REST\r\n * (sem realtime nativo), faz polling visibility-aware no `intervalMs`.\r\n *\r\n * ```tsx\r\n * import { collection, query, where } from '@neetru/sdk/firestore-compat';\r\n * import { useCollection } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(\r\n * () => query(collection(client.db, 'orders'), where('status', '==', 'open')),\r\n * [],\r\n * );\r\n * const { data, isLoading, error, isStale } = useCollection<Order>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useCollection<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseCollectionResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const sourceKey = serializeSource(\r\n source as CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n );\r\n\r\n const [state, setState] = React.useState<UseCollectionResult<T>>({\r\n data: null,\r\n isLoading: sourceKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n // Mantém a fonte mais recente sem re-disparar o efeito por identidade.\r\n const sourceRef = React.useRef(source);\r\n sourceRef.current = source;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = sourceRef.current;\r\n\r\n if (!current || sourceKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const collectionName = current.__kind === 'query' ? current.collection : current.id;\r\n const dbQuery = current.__kind === 'query' ? toDbQuery(current.constraints) : undefined;\r\n const col = current.db.collection(collectionName);\r\n\r\n const applySnap = (snap: DbListResult<Record<string, unknown>>) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowsFromList<T>(snap),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n // Mantém o último data bom — não limpa em erro de refresh.\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n // Polling explícito (REST) ou realtime nativo.\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await col.list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = col.onSnapshot(dbQuery, applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [sourceKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n/**\r\n * Hook de tempo real para um documento.\r\n *\r\n * Aceita uma `DocumentReference` do `@neetru/sdk/firestore-compat`.\r\n *\r\n * ```tsx\r\n * import { doc } from '@neetru/sdk/firestore-compat';\r\n * import { useDoc } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(() => doc(client.db, 'profiles', uid), [uid]);\r\n * const { data, isLoading } = useDoc<Profile>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseDocResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const refKey = ref && ref.id ? `${ref.collection}/${ref.id}` : null;\r\n\r\n const [state, setState] = React.useState<UseDocResult<T>>({\r\n data: null,\r\n isLoading: refKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n const refRef = React.useRef(ref);\r\n refRef.current = ref;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = refRef.current;\r\n\r\n if (!current || refKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const docRef = current.db.collection(current.collection).doc(current.id);\r\n const id = current.id;\r\n\r\n const applySnap = (snap: DbGetResult<Record<string, unknown>> | null) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowFromGet<T>(snap, id),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap?.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await docRef.get()) as DbGetResult<Record<string, unknown>> | null;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = docRef.onSnapshot(applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [refKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n// ─── useUser ────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Fonte de auth aceita por `useUser`: o `NeetruClient` inteiro (resolve\r\n * `client.auth`) ou o `AuthNamespace` direto.\r\n */\r\nexport type UseUserInput = NeetruClient | { auth: AuthNamespace } | AuthNamespace;\r\n\r\n/** Resultado de `useUser`. */\r\nexport interface UseUserResult {\r\n /** Usuário autenticado ou `null`. */\r\n user: NeetruUser | null;\r\n /** `true` enquanto o estado de auth inicial não foi resolvido. */\r\n isLoading: boolean;\r\n /** Erro na resolução de auth (raro — listener nunca deveria lançar). */\r\n error: Error | null;\r\n}\r\n\r\nfunction resolveAuth(input: UseUserInput): AuthNamespace {\r\n const maybe = input as { auth?: AuthNamespace };\r\n if (maybe.auth && typeof maybe.auth.onAuthStateChanged === 'function') {\r\n return maybe.auth;\r\n }\r\n return input as AuthNamespace;\r\n}\r\n\r\n/**\r\n * Hook de estado de autenticação.\r\n *\r\n * Subscreve a `client.auth.onAuthStateChanged` e devolve `{ user, isLoading,\r\n * error }`. Inclui um **timeout de seed** (default 8s): se o estado de auth não\r\n * resolver nesse prazo, `isLoading` cai para `false` com `user: null` — evita\r\n * spinners infinitos quando o provedor de auth nunca dispara o callback.\r\n *\r\n * ```tsx\r\n * import { useUser } from '@neetru/sdk/react';\r\n * const { user, isLoading } = useUser(client);\r\n * if (isLoading) return <Spinner />;\r\n * if (!user) return <SignInButton />;\r\n * return <Avatar user={user} />;\r\n * ```\r\n *\r\n * Aceita o `NeetruClient`, `{ auth }` ou o `AuthNamespace` direto.\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useUser(\r\n input: UseUserInput,\r\n options?: { timeoutMs?: number },\r\n): UseUserResult {\r\n const timeoutMs = options?.timeoutMs ?? 8000;\r\n const auth = resolveAuth(input);\r\n\r\n const [state, setState] = React.useState<UseUserResult>(() => {\r\n // Seed síncrono do estado atual (se o namespace já tem user em cache).\r\n let seeded: NeetruUser | null = null;\r\n try {\r\n seeded = auth.getUser();\r\n } catch {\r\n seeded = null;\r\n }\r\n return { user: seeded, isLoading: seeded === null, error: null };\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n let settled = false;\r\n\r\n // Timeout de seed: se onAuthStateChanged nunca disparar, destrava o loading.\r\n const timer = setTimeout(() => {\r\n if (!mountedRef.current || settled) return;\r\n setState((prev) => (prev.isLoading ? { ...prev, isLoading: false } : prev));\r\n }, timeoutMs);\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = auth.onAuthStateChanged((user) => {\r\n if (!mountedRef.current) return;\r\n settled = true;\r\n setState({ user: user ?? null, isLoading: false, error: null });\r\n });\r\n } catch (e) {\r\n if (mountedRef.current) {\r\n setState({\r\n user: null,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n }\r\n }\r\n\r\n return () => {\r\n mountedRef.current = false;\r\n clearTimeout(timer);\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [auth, timeoutMs]);\r\n\r\n return state;\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/firestore-compat.ts","../src/react.ts"],"names":["result","allowed"],"mappings":"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","file":"react.mjs","sourcesContent":["/**\r\n * @neetru/sdk/firestore-compat — camada de COMPATIBILIDADE (transicional).\r\n *\r\n * ⚠️ **STATUS: compat / transitional.** Este módulo espelha a superfície\r\n * modular do Firebase Firestore (`doc`, `collection`, `query`, `where`,\r\n * `getDocs`, `setDoc`, `onSnapshot`, `writeBatch`, `runTransaction`,\r\n * `serverTimestamp`, `Timestamp`, `FieldValue`, …) para que produtos que\r\n * migram do Firebase **não precisem reescrever as chamadas à mão**.\r\n *\r\n * Ele NÃO é a API nativa do SDK e NÃO compete com `client.db` (NeetruDb).\r\n * É um auxílio de migração: troque os imports de `firebase/firestore` por\r\n * `@neetru/sdk/firestore-compat`, valide, e depois migre incrementalmente\r\n * para a superfície nativa `client.db.collection(...)` quando fizer sentido.\r\n *\r\n * Esta implementação foi desenhada a partir dos shims reais que os produtos\r\n * `neetru-gestovendas` (`src/neetru/firestore.ts`) e `neetru-pdvagiliza`\r\n * (`src/lib/neetru/firestore-compat.ts`) escreveram à mão — espelha\r\n * exatamente a superfície que eles precisam para poder **deletar os shims**.\r\n *\r\n * ### Backing\r\n * Tudo flui por `client.db` (`NeetruDb`). As funções recebem ou um `NeetruDb`\r\n * (o `client.db`) ou o `NeetruClient` inteiro como primeiro argumento — ambos\r\n * são aceitos por `getFirestore(...)`/`collection(...)`/`doc(...)`.\r\n *\r\n * ### Diferenças deliberadas vs Firebase\r\n * - `serverTimestamp()` e `increment()`/`arrayUnion()`/`arrayRemove()` retornam\r\n * sentinels do SDK (`@neetru/sdk` `serverTimestamp`/`increment`) — resolvidos\r\n * SERVER-SIDE pelo Core (não pelo relógio spoofável do cliente).\r\n * - `Timestamp` é uma classe leve (sem proto de Firebase) com `toDate()`,\r\n * `toMillis()`, `seconds`, `nanoseconds` e `Timestamp.now()/fromDate()/fromMillis()`.\r\n * - `onSnapshot` usa o realtime nativo do `client.db` (`onSnapshot`/`doc().onSnapshot`)\r\n * quando o transporte oferece; caso contrário entrega o snapshot inicial e\r\n * reage às mudanças locais (offline-first do SDK).\r\n *\r\n * @module\r\n */\r\n\r\nimport type {\r\n NeetruDb,\r\n DbCollectionRef,\r\n DbQuery,\r\n DbWhereFilter,\r\n DbListResult,\r\n DbGetResult,\r\n} from './db/client-db';\r\nimport {\r\n serverTimestamp as sdkServerTimestamp,\r\n increment as sdkIncrement,\r\n type FieldSentinel,\r\n} from './db/field-value';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Resolução do handle de banco\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Aceita tanto o `NeetruDb` (`client.db`) quanto o `NeetruClient` inteiro.\r\n * Produtos legados chamam `getFirestore(client)` ou `getFirestore(client.db)`;\r\n * ambos resolvem para o `NeetruDb` subjacente.\r\n */\r\nexport type FirestoreLike = NeetruDb | { db: NeetruDb };\r\n\r\n/** Marker brand reconhecível em runtime — `getFirestore()` devolve isto. */\r\nexport interface Firestore {\r\n readonly __neetruFirestore: true;\r\n readonly db: NeetruDb;\r\n}\r\n\r\nfunction isClientWrapper(v: FirestoreLike | Firestore): v is { db: NeetruDb } {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n 'db' in v &&\r\n typeof (v as { db?: unknown }).db === 'object' &&\r\n (v as { db?: { collection?: unknown } }).db?.collection !== undefined\r\n );\r\n}\r\n\r\nfunction resolveDb(handle: FirestoreLike | Firestore): NeetruDb {\r\n if ((handle as Firestore).__neetruFirestore) return (handle as Firestore).db;\r\n if (isClientWrapper(handle)) return handle.db;\r\n // É o próprio NeetruDb (tem `.collection`).\r\n return handle as NeetruDb;\r\n}\r\n\r\n/**\r\n * Equivalente compat de `getFirestore(app)`. Aceita o `NeetruClient` ou o\r\n * `client.db` (`NeetruDb`) e devolve um handle `Firestore` opaco.\r\n *\r\n * @example\r\n * ```ts\r\n * import { getFirestore } from '@neetru/sdk/firestore-compat';\r\n * const db = getFirestore(client); // ou getFirestore(client.db)\r\n * ```\r\n */\r\nexport function getFirestore(handle: FirestoreLike): Firestore {\r\n return { __neetruFirestore: true, db: resolveDb(handle) };\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Refs — Collection & Document\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Operadores `where` suportados (subset do Firestore + ops do SDK). */\r\nexport type WhereFilterOp =\r\n | '=='\r\n | '!='\r\n | '<'\r\n | '<='\r\n | '>'\r\n | '>='\r\n | 'in'\r\n | 'not-in'\r\n | 'array-contains'\r\n | 'array-contains-any';\r\n\r\nexport interface CollectionReference<T = Record<string, unknown>> {\r\n readonly type: 'collection';\r\n readonly __kind: 'collection';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly db: NeetruDb;\r\n /** Handle nativo do SDK — atalho para a superfície `client.db`. */\r\n readonly ref: DbCollectionRef<T>;\r\n}\r\n\r\nexport interface DocumentReference<T = Record<string, unknown>> {\r\n readonly type: 'document';\r\n readonly __kind: 'document';\r\n readonly id: string;\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n /** Marcador de tipo (phantom) — preserva `T` para inferência do caller. */\r\n readonly __doc?: T;\r\n}\r\n\r\n/** Aceita tanto `Firestore` quanto `NeetruDb`/`NeetruClient` direto. */\r\ntype DbArg = Firestore | FirestoreLike;\r\n\r\n/**\r\n * `collection(db, name)` — referência a uma coleção.\r\n *\r\n * Aceita `db` como `Firestore` (de `getFirestore`), `NeetruClient` ou `client.db`.\r\n */\r\nexport function collection<T = Record<string, unknown>>(\r\n db: DbArg,\r\n name: string,\r\n): CollectionReference<T> {\r\n const resolved = resolveDb(db);\r\n return {\r\n type: 'collection',\r\n __kind: 'collection',\r\n id: name,\r\n path: name,\r\n db: resolved,\r\n ref: resolved.collection<T>(name),\r\n };\r\n}\r\n\r\n/**\r\n * `doc(...)` — referência a um documento. Suporta as 3 assinaturas legadas:\r\n * doc(db, 'collection', 'id') → coleção + id\r\n * doc(collectionRef, 'id') → id dentro de uma CollectionReference\r\n * doc(db, 'collection/id') → path com barra (id auto-gerado se id ausente)\r\n *\r\n * Quando o `id` é omitido, gera um id v4 (espelha `doc(collectionRef)` do Firebase\r\n * que cria uma ref com id automático antes do `setDoc`).\r\n */\r\nexport function doc<T = Record<string, unknown>>(\r\n dbOrCollection: DbArg | CollectionReference<T>,\r\n ...path: string[]\r\n): DocumentReference<T> {\r\n // Forma doc(collectionRef, id?)\r\n if (\r\n typeof dbOrCollection === 'object' &&\r\n dbOrCollection !== null &&\r\n (dbOrCollection as CollectionReference<T>).__kind === 'collection'\r\n ) {\r\n const col = dbOrCollection as CollectionReference<T>;\r\n const id = path[0] ?? generateId();\r\n return makeDocRef<T>(col.db, col.id, id);\r\n }\r\n\r\n const resolved = resolveDb(dbOrCollection as DbArg);\r\n\r\n if (path.length >= 2) {\r\n return makeDocRef<T>(resolved, path[0], path.slice(1).join('/'));\r\n }\r\n if (path[0]?.includes('/')) {\r\n const parts = path[0].split('/');\r\n return makeDocRef<T>(resolved, parts[0], parts.slice(1).join('/') || generateId());\r\n }\r\n // doc(db, 'collection') — id auto-gerado\r\n return makeDocRef<T>(resolved, path[0] ?? '', generateId());\r\n}\r\n\r\nfunction makeDocRef<T>(db: NeetruDb, collectionName: string, id: string): DocumentReference<T> {\r\n return {\r\n type: 'document',\r\n __kind: 'document',\r\n id,\r\n collection: collectionName,\r\n path: `${collectionName}/${id}`,\r\n db,\r\n };\r\n}\r\n\r\n/**\r\n * Gera um id estilo Firestore (20 chars alfanuméricos) com aleatoriedade\r\n * CRIPTOGRÁFICA.\r\n *\r\n * Antes usava `Math.random()` — previsível e colisível (PRNG não-criptográfico,\r\n * estado recuperável). IDs de documento previsíveis permitem enumeração/forja.\r\n * Agora usa `globalThis.crypto.getRandomValues` (WebCrypto), disponível tanto em\r\n * Node ≥20 quanto em browsers — sem importar `node:crypto` (que quebraria o\r\n * bundle browser). Rejeita bytes ≥ 248 (4×62) para eliminar o viés de módulo\r\n * sobre o alfabeto de 62 caracteres.\r\n */\r\nfunction generateId(): string {\r\n const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; // 62\r\n const cryptoObj = (globalThis as { crypto?: Crypto }).crypto;\r\n if (!cryptoObj?.getRandomValues) {\r\n throw new Error(\r\n '[Neetru SDK / firestore-compat] WebCrypto indisponível: generateId() exige ' +\r\n 'globalThis.crypto.getRandomValues (Node ≥20 ou ambiente browser).',\r\n );\r\n }\r\n let out = '';\r\n while (out.length < 20) {\r\n const buf = new Uint8Array(20 - out.length);\r\n cryptoObj.getRandomValues(buf);\r\n for (const byte of buf) {\r\n if (byte < 248) {\r\n out += CHARS[byte % 62];\r\n if (out.length === 20) break;\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUERY — acumula constraints e gera DbQuery quando consumido\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type QueryConstraintType = 'where' | 'orderBy' | 'limit';\r\n\r\nexport interface QueryConstraint {\r\n readonly __constraint: QueryConstraintType;\r\n}\r\n\r\ninterface WhereConstraint extends QueryConstraint {\r\n readonly __constraint: 'where';\r\n readonly field: string;\r\n readonly op: WhereFilterOp;\r\n readonly value: unknown;\r\n}\r\n\r\ninterface OrderByConstraint extends QueryConstraint {\r\n readonly __constraint: 'orderBy';\r\n readonly field: string;\r\n readonly direction: 'asc' | 'desc';\r\n}\r\n\r\ninterface LimitConstraint extends QueryConstraint {\r\n readonly __constraint: 'limit';\r\n readonly value: number;\r\n}\r\n\r\nexport interface Query<T = Record<string, unknown>> {\r\n readonly type: 'query';\r\n readonly __kind: 'query';\r\n readonly path: string;\r\n readonly collection: string;\r\n readonly db: NeetruDb;\r\n readonly ref: DbCollectionRef<T>;\r\n readonly constraints: QueryConstraint[];\r\n}\r\n\r\n/**\r\n * `query(collectionRef, ...constraints)` — combina `where`/`orderBy`/`limit`.\r\n * Aceita também uma `Query` como fonte (encadeamento de constraints).\r\n */\r\nexport function query<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n ...constraints: QueryConstraint[]\r\n): Query<T> {\r\n const base: Query<T> =\r\n source.__kind === 'query'\r\n ? source\r\n : {\r\n type: 'query',\r\n __kind: 'query',\r\n path: source.path,\r\n collection: source.id,\r\n db: source.db,\r\n ref: source.ref,\r\n constraints: [],\r\n };\r\n return { ...base, constraints: [...base.constraints, ...constraints] };\r\n}\r\n\r\nexport function where(field: string, op: WhereFilterOp, value: unknown): WhereConstraint {\r\n return { __constraint: 'where', field, op, value };\r\n}\r\n\r\nexport function orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): OrderByConstraint {\r\n return { __constraint: 'orderBy', field, direction };\r\n}\r\n\r\nexport function limit(value: number): LimitConstraint {\r\n return { __constraint: 'limit', value };\r\n}\r\n\r\n/**\r\n * Converte os constraints de uma `Query` compat para o `DbQuery` nativo do SDK.\r\n *\r\n * Exportado para que os hooks React (`@neetru/sdk/react`) possam reusar a mesma\r\n * tradução — fonte única de verdade da conversão de constraints.\r\n */\r\nexport function toDbQuery(constraints: QueryConstraint[]): DbQuery {\r\n const whereFilters: DbWhereFilter[] = [];\r\n let orderByClause: DbQuery['orderBy'];\r\n let limitValue: number | undefined;\r\n\r\n for (const c of constraints) {\r\n if (c.__constraint === 'where') {\r\n const w = c as WhereConstraint;\r\n whereFilters.push({ field: w.field, op: normalizeOp(w.op), value: w.value });\r\n } else if (c.__constraint === 'orderBy') {\r\n const o = c as OrderByConstraint;\r\n orderByClause = { field: o.field, direction: o.direction };\r\n } else if (c.__constraint === 'limit') {\r\n limitValue = (c as LimitConstraint).value;\r\n }\r\n }\r\n\r\n const out: DbQuery = {};\r\n if (whereFilters.length > 0) out.where = whereFilters;\r\n if (orderByClause) out.orderBy = orderByClause;\r\n if (limitValue !== undefined) out.limit = limitValue;\r\n return out;\r\n}\r\n\r\n/**\r\n * O `DbWhereFilter` do SDK não declara `array-contains-any`/`not-in` no union.\r\n * Passamos o op como-está; o Core valida server-side e retorna erro explícito\r\n * se não suportado.\r\n *\r\n * Bug corrigido: remapeava 'array-contains-any' → 'array-contains' silenciosamente,\r\n * o que tem semântica completamente diferente (checa literal do array, não interseção)\r\n * e nunca retornava resultados para queries reais.\r\n */\r\nfunction normalizeOp(op: WhereFilterOp): DbWhereFilter['op'] {\r\n // Todas as ops passam direto; o Core valida server-side.\r\n return op as DbWhereFilter['op'];\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// SNAPSHOTS — emulam a API antiga do Firebase\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Remove o campo `id` dos dados (o Firestore expõe id via `.id`, não em `data()`). */\r\nfunction stripId<T>(raw: Record<string, unknown>): T {\r\n if (raw && typeof raw === 'object' && 'id' in raw) {\r\n const { id: _omit, ...rest } = raw;\r\n return rest as T;\r\n }\r\n return raw as T;\r\n}\r\n\r\nexport class DocumentSnapshot<T = Record<string, unknown>> {\r\n constructor(\r\n public readonly id: string,\r\n private readonly _data: T | null,\r\n public readonly ref?: DocumentReference<T>,\r\n ) {}\r\n\r\n exists(): boolean {\r\n return this._data != null;\r\n }\r\n\r\n data(): T | undefined {\r\n return this._data ?? undefined;\r\n }\r\n\r\n /** Compat: `snap.get('field')` lê um campo top-level. */\r\n get(field: string): unknown {\r\n return this._data ? (this._data as Record<string, unknown>)[field] : undefined;\r\n }\r\n}\r\n\r\nexport interface QueryDocumentSnapshot<T = Record<string, unknown>> {\r\n readonly id: string;\r\n data(): T;\r\n exists(): boolean;\r\n get(field: string): unknown;\r\n readonly ref: DocumentReference<T>;\r\n}\r\n\r\nexport class QuerySnapshot<T = Record<string, unknown>> {\r\n public readonly docs: Array<QueryDocumentSnapshot<T>>;\r\n public readonly empty: boolean;\r\n public readonly size: number;\r\n\r\n constructor(\r\n rows: Array<{ id: string; data: Record<string, unknown> }>,\r\n db?: NeetruDb,\r\n collectionName?: string,\r\n ) {\r\n this.docs = rows.map(({ id, data }) => {\r\n const stripped = stripId<T>(data);\r\n const ref: DocumentReference<T> | undefined =\r\n db && collectionName ? makeDocRef<T>(db, collectionName, id) : undefined;\r\n return {\r\n id,\r\n data: () => stripped,\r\n exists: () => true,\r\n get: (field: string) => (stripped as Record<string, unknown>)?.[field],\r\n // ref sempre presente quando db+collection conhecidos (caso comum).\r\n ref: ref as DocumentReference<T>,\r\n };\r\n });\r\n this.empty = this.docs.length === 0;\r\n this.size = this.docs.length;\r\n }\r\n\r\n forEach(cb: (doc: QueryDocumentSnapshot<T>) => void): void {\r\n this.docs.forEach(cb);\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// READS\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/** Extrai os dados crus do primeiro doc de um `DbGetResult`. */\r\nfunction firstDocData(result: DbGetResult<Record<string, unknown>> | null): {\r\n data: Record<string, unknown> | null;\r\n id: string | null;\r\n} {\r\n const first = result?.docs[0];\r\n return { data: first ? (first.data as Record<string, unknown>) : null, id: first?.id ?? null };\r\n}\r\n\r\nexport async function getDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n): Promise<DocumentSnapshot<T>> {\r\n const result = (await ref.db.collection(ref.collection).get(ref.id)) as DbGetResult<\r\n Record<string, unknown>\r\n > | null;\r\n const { data } = firstDocData(result);\r\n return new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? (stripId<T>({ id: ref.id, ...data })) : null,\r\n ref,\r\n );\r\n}\r\n\r\n/** Mapeia um `DbListResult` para linhas `{ id, data }` (id garantido). */\r\nfunction listResultToRows(\r\n result: DbListResult<Record<string, unknown>>,\r\n): Array<{ id: string; data: Record<string, unknown> }> {\r\n return result.docs.map((d) => ({ id: d.id, data: { ...(d.data as Record<string, unknown>) } }));\r\n}\r\n\r\nexport async function getDocs<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T>,\r\n): Promise<QuerySnapshot<T>> {\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const collectionName = source.__kind === 'query' ? source.collection : source.id;\r\n const result = (await source.db\r\n .collection(collectionName)\r\n .list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n return new QuerySnapshot<T>(listResultToRows(result), source.db, collectionName);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITES\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport interface SetOptions {\r\n merge?: boolean;\r\n mergeFields?: string[];\r\n}\r\n\r\nexport async function setDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options: SetOptions = {},\r\n): Promise<void> {\r\n const col = ref.db.collection(ref.collection);\r\n const payload = data as Record<string, unknown>;\r\n if (options.merge || (options.mergeFields && options.mergeFields.length > 0)) {\r\n // bug_0273e4dd: ANTES o check de existência usava o cache LOCAL (col.get é\r\n // offline-first). Doc que existia só no SERVIDOR dava docExists=false → caía\r\n // em col.set() (replace) e APAGAVA todos os campos ausentes do payload →\r\n // perda de dados silenciosa. Agora a decisão é DO SERVIDOR via merge-upsert:\r\n // update({upsert:true}) cria-se-ausente OU mescla-se-presente, sem nunca\r\n // apagar campos não enviados — exatamente o setDoc({merge:true}) do Firebase.\r\n // Contrato Core acordado (deploy0632): PATCH com upsert=true não 404a em\r\n // ausente e faz ref.set(data,{merge:true}).\r\n await col.update(ref.id, payload, { upsert: true });\r\n } else {\r\n await col.set(ref.id, payload);\r\n }\r\n}\r\n\r\nexport async function addDoc<T = Record<string, unknown>>(\r\n source: CollectionReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<DocumentReference<T>> {\r\n const result = await source.db.collection(source.id).add(data as Record<string, unknown>);\r\n return makeDocRef<T>(source.db, source.id, result.id);\r\n}\r\n\r\nexport async function updateDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n): Promise<void> {\r\n await ref.db.collection(ref.collection).update(ref.id, data as Record<string, unknown>);\r\n}\r\n\r\nexport async function deleteDoc(ref: DocumentReference): Promise<void> {\r\n await ref.db.collection(ref.collection).remove(ref.id);\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// WRITE BATCH\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\ninterface PendingBatchOp {\r\n collection: string;\r\n op:\r\n | { kind: 'set'; id: string; data: Record<string, unknown> }\r\n | { kind: 'update'; id: string; data: Record<string, unknown>; upsert?: boolean }\r\n | { kind: 'remove'; id: string };\r\n}\r\n\r\nexport class WriteBatch {\r\n private ops: PendingBatchOp[] = [];\r\n\r\n constructor(private readonly db: NeetruDb) {}\r\n\r\n set<T>(\r\n ref: DocumentReference<T>,\r\n data: Partial<T> | Record<string, unknown>,\r\n options?: SetOptions,\r\n ): this {\r\n // bug_0273e4dd: SetOptions.merge era DESCARTADO → batch.set(ref,data,{merge:true})\r\n // virava replace e APAGAVA campos ausentes (perda de dados). Agora merge vira\r\n // update com upsert (merge-server-side, cria-se-ausente), igual ao setDoc.\r\n const merge = !!(options?.merge || (options?.mergeFields && options.mergeFields.length > 0));\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: merge\r\n ? { kind: 'update', id: ref.id, data: data as Record<string, unknown>, upsert: true }\r\n : { kind: 'set', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): this {\r\n this.ops.push({\r\n collection: ref.collection,\r\n op: { kind: 'update', id: ref.id, data: data as Record<string, unknown> },\r\n });\r\n return this;\r\n }\r\n\r\n delete(ref: DocumentReference): this {\r\n this.ops.push({ collection: ref.collection, op: { kind: 'remove', id: ref.id } });\r\n return this;\r\n }\r\n\r\n async commit(): Promise<void> {\r\n // Agrupa por coleção — o `DbCollectionRef.batch` opera numa coleção por vez.\r\n const byCol = new Map<string, PendingBatchOp['op'][]>();\r\n for (const { collection: name, op } of this.ops) {\r\n const list = byCol.get(name) ?? [];\r\n list.push(op);\r\n byCol.set(name, list);\r\n }\r\n for (const [name, ops] of byCol.entries()) {\r\n // O union nativo `DbBatchOp` casa 1:1 com nossas variantes (set/update/remove).\r\n await this.db.collection(name).batch(ops as never);\r\n }\r\n this.ops = [];\r\n }\r\n}\r\n\r\nexport function writeBatch(db: DbArg): WriteBatch {\r\n return new WriteBatch(resolveDb(db));\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// runTransaction — emulação otimista (read → write) sobre client.db\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Handle de transação compat. O SDK não expõe transações multi-doc atômicas\r\n * com lock server-side, então emulamos a forma do Firebase: `tx.get` lê do\r\n * `client.db`, e `tx.set/update/delete` enfileiram escritas que são aplicadas\r\n * no fim do callback.\r\n *\r\n * ⚠️ **LIMITAÇÃO CONHECIDA — sem isolamento server-side (lost-update silencioso):**\r\n *\r\n * Diferente do Firestore nativo (que usa serializable snapshot isolation com\r\n * contention detection), este shim executa reads e writes em rounds separados\r\n * sem garantia de atomicidade:\r\n *\r\n * 1. `tx.get(ref)` faz um read imediato via `client.db`.\r\n * 2. As escritas (`tx.set/update/delete`) são **enfileiradas** — não bloqueiam\r\n * outros writers.\r\n * 3. No fim do callback, `batch.commit()` aplica as escritas. Se outro processo\r\n * tiver modificado o mesmo documento entre o read e o commit, a escrita\r\n * será um **lost-update silencioso** (sem erro, sem retry automático).\r\n *\r\n * **Quando isso importa:** counters, saldos, estoques — qualquer dado\r\n * que precise de leitura-modificação-escrita atômica.\r\n *\r\n * **Alternativas seguras:**\r\n * - Use **Server Actions no Core** (`src/lib/actions/`) que rodam dentro\r\n * de transações Firestore Admin SDK reais (com locks server-side).\r\n * - Use `increment()` sentinel para contadores numéricos — server-side atomic.\r\n * - Use `arrayUnion()`/`arrayRemove()` para sets (também server-side atomic).\r\n *\r\n * Esta emulação é aceitável para migrações transitórias e para operações\r\n * onde a probabilidade de concorrência é baixa (ex: dados de um único usuário\r\n * sem write concorrente). **Não use para dados financeiros ou de inventário.**\r\n */\r\nexport interface Transaction {\r\n get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>>;\r\n set<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>, options?: SetOptions): Transaction;\r\n update<T>(ref: DocumentReference<T>, data: Partial<T> | Record<string, unknown>): Transaction;\r\n delete(ref: DocumentReference): Transaction;\r\n}\r\n\r\n/** Flag para emitir o aviso de limitação uma só vez por processo (não spammar). */\r\nlet _runTransactionWarnEmitted = false;\r\n\r\nexport async function runTransaction<R>(\r\n db: DbArg,\r\n updateFunction: (transaction: Transaction) => Promise<R>,\r\n): Promise<R> {\r\n // Aviso em desenvolvimento/staging: sem isolamento server-side.\r\n if (!_runTransactionWarnEmitted) {\r\n _runTransactionWarnEmitted = true;\r\n console.warn(\r\n '[Neetru SDK / firestore-compat] runTransaction(): esta emulação NÃO oferece' +\r\n ' isolamento server-side. Reads e writes são executados em rounds separados;' +\r\n ' um write concorrente entre o get() e o commit() resultará em lost-update' +\r\n ' silencioso. Para atomicidade real, use Server Actions no Core ou sentinels' +\r\n ' increment()/arrayUnion(). Veja a doc em src/firestore-compat.ts § runTransaction.',\r\n );\r\n }\r\n\r\n const resolved = resolveDb(db);\r\n const batch = new WriteBatch(resolved);\r\n\r\n const tx: Transaction = {\r\n async get<T>(ref: DocumentReference<T>): Promise<DocumentSnapshot<T>> {\r\n return getDoc(ref);\r\n },\r\n set(ref, data, options) {\r\n batch.set(ref, data, options);\r\n return tx;\r\n },\r\n update(ref, data) {\r\n batch.update(ref, data);\r\n return tx;\r\n },\r\n delete(ref) {\r\n batch.delete(ref);\r\n return tx;\r\n },\r\n };\r\n\r\n const result = await updateFunction(tx);\r\n await batch.commit();\r\n return result;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// onSnapshot — realtime via client.db (com fallback inicial)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\nexport type Unsubscribe = () => void;\r\n\r\ntype DocOnNext<T> = (snap: DocumentSnapshot<T>) => void;\r\ntype QueryOnNext<T> = (snap: QuerySnapshot<T>) => void;\r\ntype OnError = (err: Error) => void;\r\n\r\nexport function onSnapshot<T>(\r\n ref: DocumentReference<T>,\r\n onNext: DocOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: Query<T> | CollectionReference<T>,\r\n onNext: QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe;\r\nexport function onSnapshot<T>(\r\n source: DocumentReference<T> | Query<T> | CollectionReference<T>,\r\n onNext: DocOnNext<T> | QueryOnNext<T>,\r\n onError?: OnError,\r\n): Unsubscribe {\r\n // Documento único\r\n if (source.__kind === 'document') {\r\n const ref = source as DocumentReference<T>;\r\n const docRef = ref.db.collection(ref.collection).doc(ref.id);\r\n try {\r\n return docRef.onSnapshot((snap) => {\r\n const data = snap?.docs[0]?.data as Record<string, unknown> | undefined;\r\n (onNext as DocOnNext<T>)(\r\n new DocumentSnapshot<T>(\r\n ref.id,\r\n data ? stripId<T>({ id: ref.id, ...data }) : null,\r\n ref,\r\n ),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n }\r\n\r\n // Coleção / Query\r\n const collectionName = source.__kind === 'query' ? source.collection : (source as CollectionReference<T>).id;\r\n const dbQuery = source.__kind === 'query' ? toDbQuery(source.constraints) : undefined;\r\n const col = source.db.collection(collectionName);\r\n try {\r\n return col.onSnapshot(dbQuery, (snap: DbListResult<Record<string, unknown>>) => {\r\n (onNext as QueryOnNext<T>)(\r\n new QuerySnapshot<T>(listResultToRows(snap), source.db, collectionName),\r\n );\r\n });\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n return () => {};\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Timestamp — classe leve compat\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Reimplementação compat de `Timestamp` do Firestore. Sem proto do Firebase.\r\n * Cobre `seconds`, `nanoseconds`, `toDate()`, `toMillis()`, `isEqual()`,\r\n * `valueOf()`, `toJSON()` + estáticos `now()`, `fromDate()`, `fromMillis()`.\r\n */\r\nexport class Timestamp {\r\n constructor(\r\n public readonly seconds: number,\r\n public readonly nanoseconds: number,\r\n ) {}\r\n\r\n static now(): Timestamp {\r\n return Timestamp.fromMillis(Date.now());\r\n }\r\n\r\n static fromDate(date: Date): Timestamp {\r\n return Timestamp.fromMillis(date.getTime());\r\n }\r\n\r\n static fromMillis(millis: number): Timestamp {\r\n const seconds = Math.floor(millis / 1000);\r\n const nanoseconds = (millis - seconds * 1000) * 1e6;\r\n return new Timestamp(seconds, nanoseconds);\r\n }\r\n\r\n toDate(): Date {\r\n return new Date(this.toMillis());\r\n }\r\n\r\n toMillis(): number {\r\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\r\n }\r\n\r\n isEqual(other: Timestamp): boolean {\r\n return this.seconds === other.seconds && this.nanoseconds === other.nanoseconds;\r\n }\r\n\r\n valueOf(): string {\r\n // Estável para ordenação por string (espelha Firebase).\r\n return `${this.seconds.toString().padStart(12, '0')}.${this.nanoseconds\r\n .toString()\r\n .padStart(9, '0')}`;\r\n }\r\n\r\n toJSON(): { seconds: number; nanoseconds: number } {\r\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\r\n }\r\n\r\n toString(): string {\r\n return `Timestamp(seconds=${this.seconds}, nanoseconds=${this.nanoseconds})`;\r\n }\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// FieldValue + sentinels\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Sentinel de `arrayUnion`/`arrayRemove`. O SDK ainda não tem sentinel de array\r\n * server-side, então emitimos um marker namespaceado que o Core reconhece\r\n * (mesma família de `__neetru__`). Se o backing não suportar, o produto deve\r\n * preferir read-modify-write via Server Action. Documentado como compat.\r\n */\r\nexport interface ArrayUnionSentinel {\r\n readonly __neetru__: 'arrayUnion';\r\n readonly elements: unknown[];\r\n}\r\nexport interface ArrayRemoveSentinel {\r\n readonly __neetru__: 'arrayRemove';\r\n readonly elements: unknown[];\r\n}\r\n\r\n/**\r\n * Sentinel de campo a deletar (compat `deleteField()`).\r\n */\r\nexport interface DeleteFieldSentinel {\r\n readonly __neetru__: 'deleteField';\r\n}\r\n\r\n/**\r\n * `serverTimestamp()` — delega ao sentinel do SDK (resolvido SERVER-SIDE).\r\n * NÃO retorna mais ISO/Date do cliente (que era spoofável).\r\n */\r\nexport function serverTimestamp(): FieldSentinel {\r\n return sdkServerTimestamp();\r\n}\r\n\r\n/**\r\n * `increment(n)` — incremento atômico server-side (delega ao sentinel do SDK).\r\n */\r\nexport function increment(n: number): FieldSentinel {\r\n return sdkIncrement(n);\r\n}\r\n\r\n/** `arrayUnion(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayUnion(...elements: unknown[]): ArrayUnionSentinel {\r\n return { __neetru__: 'arrayUnion', elements };\r\n}\r\n\r\n/** `arrayRemove(...elements)` — sentinel compat (resolvido pelo Core). */\r\nexport function arrayRemove(...elements: unknown[]): ArrayRemoveSentinel {\r\n return { __neetru__: 'arrayRemove', elements };\r\n}\r\n\r\n/** `deleteField()` — sentinel compat para remover um campo. */\r\nexport function deleteField(): DeleteFieldSentinel {\r\n return { __neetru__: 'deleteField' };\r\n}\r\n\r\n/**\r\n * Namespace estático `FieldValue` — espelha `firebase/firestore`'s `FieldValue`.\r\n * Os métodos delegam às funções modulares acima.\r\n *\r\n * @example\r\n * ```ts\r\n * import { FieldValue } from '@neetru/sdk/firestore-compat';\r\n * await updateDoc(ref, {\r\n * tags: FieldValue.arrayUnion('novo'),\r\n * counter: FieldValue.increment(1),\r\n * updatedAt: FieldValue.serverTimestamp(),\r\n * });\r\n * ```\r\n */\r\nexport const FieldValue = {\r\n serverTimestamp,\r\n increment,\r\n arrayUnion,\r\n arrayRemove,\r\n delete: deleteField,\r\n} as const;\r\n","/**\r\n * SDK React helpers (v1.1) — subpath import `@neetru/sdk/react`.\r\n *\r\n * Helpers leves pra React 18+/19. Não puxa `react` como dep direta — caller\r\n * fornece via peer (alinhado com `@stripe/react-stripe-js` / `firebase`).\r\n *\r\n * Exports:\r\n * - `<CheckoutLink>` — wrapper de `<a>` que chama `client.checkout.start()` no click.\r\n * - `<EntitlementGate>` — wrapper que gateia children por entitlement check\r\n * (suporte a `mode='readonly'` pro decisão CEO §5).\r\n * - `useEntitlementContext()` — hook pra ler estado de readonly do contexto.\r\n *\r\n * **Bundle**: este file SÓ é carregado por quem importa `@neetru/sdk/react`.\r\n * Tree-shake garante que SDK consumers Node-only (CLI, edge handlers) não\r\n * puxam React mesmo via transitive.\r\n */\r\nimport * as React from 'react';\r\nimport type { NeetruClient, NeetruUser, AuthNamespace } from './types';\r\nimport type { CheckoutStartInput, CheckoutStartResult } from './checkout';\r\nimport type {\r\n CollectionReference,\r\n Query,\r\n DocumentReference,\r\n} from './firestore-compat';\r\nimport { toDbQuery } from './firestore-compat';\r\nimport type { DbListResult, DbGetResult } from './db/client-db';\r\n\r\n// ─── <CheckoutLink> ─────────────────────────────────────────────────────────\r\n\r\nexport interface CheckoutLinkProps\r\n extends Omit<\r\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\r\n 'href' | 'onClick' | 'onError'\r\n > {\r\n /** Cliente SDK Neetru. Passado explicitamente pra evitar context provider. */\r\n client: NeetruClient;\r\n /** Slug do produto (ex: `neetru-pulse`). */\r\n productId: string;\r\n /** Plan id (ex: `pro_monthly`). */\r\n planId: string;\r\n /** Pra onde o produto SaaS quer voltar pós-checkout. */\r\n callbackUrl: string;\r\n /** Override pra checkout em nome de uma org. Default: PF do uid logado. */\r\n tenantType?: 'pf' | 'pj';\r\n tenantId?: string;\r\n /** Children — texto/JSX do CTA. */\r\n children: React.ReactNode;\r\n /** Callback quando intent foi criada (antes de redirect). */\r\n onIntentCreated?: (result: CheckoutStartResult) => void;\r\n /** Callback de erro. */\r\n onError?: (err: Error) => void;\r\n /** Quando true, NÃO redireciona automaticamente — caller controla. */\r\n noAutoRedirect?: boolean;\r\n /** Override de href (default: `#`). */\r\n href?: string;\r\n}\r\n\r\n/**\r\n * Wrapper de `<a>` que dispara `client.checkout.start()` no click.\r\n *\r\n * Uso típico:\r\n * ```tsx\r\n * <CheckoutLink\r\n * client={client}\r\n * productId=\"meu-saas\"\r\n * planId=\"pro_monthly\"\r\n * callbackUrl=\"https://meu-saas.com/billing/success\"\r\n * >\r\n * Assinar Pro\r\n * </CheckoutLink>\r\n * ```\r\n *\r\n * `href` é stub (`#`) — preventDefault no click. Mantido pra a11y / preview\r\n * de URL no hover (pode ser sobrescrito via prop spread).\r\n */\r\nexport function CheckoutLink({\r\n client,\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n children,\r\n onIntentCreated,\r\n onError,\r\n noAutoRedirect,\r\n ...rest\r\n}: CheckoutLinkProps): React.ReactElement {\r\n const [submitting, setSubmitting] = React.useState(false);\r\n\r\n // Lazy access — `client.checkout` foi adicionado em SDK 1.1, fall back gracioso\r\n // se caller passar um client antigo (mesmo que types resolvam, runtime check).\r\n const checkout = (client as { checkout?: { start: (input: CheckoutStartInput) => Promise<CheckoutStartResult> } }).checkout;\r\n\r\n const handleClick = React.useCallback(\r\n async (event: React.MouseEvent<HTMLAnchorElement>) => {\r\n event.preventDefault();\r\n if (submitting) return;\r\n if (!checkout || typeof checkout.start !== 'function') {\r\n onError?.(new Error('SDK client missing `checkout` namespace. Upgrade @neetru/sdk to 1.1+.'));\r\n return;\r\n }\r\n setSubmitting(true);\r\n try {\r\n const result = await checkout.start({\r\n productId,\r\n planId,\r\n callbackUrl,\r\n tenantType,\r\n tenantId,\r\n autoRedirect: !noAutoRedirect,\r\n });\r\n onIntentCreated?.(result);\r\n } catch (err) {\r\n onError?.(err instanceof Error ? err : new Error(String(err)));\r\n } finally {\r\n setSubmitting(false);\r\n }\r\n },\r\n [submitting, checkout, productId, planId, callbackUrl, tenantType, tenantId, noAutoRedirect, onIntentCreated, onError],\r\n );\r\n\r\n return React.createElement(\r\n 'a',\r\n {\r\n ...rest,\r\n href: rest.href ?? '#',\r\n onClick: handleClick,\r\n 'aria-busy': submitting || undefined,\r\n 'data-neetru-checkout-link': 'true',\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ─── <EntitlementGate> + useEntitlementContext ────────────────────────────\r\n\r\n/** Modo de gate quando entitlement não é permitido. */\r\nexport type EntitlementGateMode = 'block' | 'readonly';\r\n\r\ninterface EntitlementContextValue {\r\n /** True quando feature está em modo read-only (limit exceeded). */\r\n readonly: boolean;\r\n /** Reason code da última check. */\r\n reason?: string;\r\n /** Limite atual (free tier). */\r\n limit?: number;\r\n /** Uso atual. */\r\n currentUsage?: number;\r\n}\r\n\r\nconst EntitlementContext = React.createContext<EntitlementContextValue>({\r\n readonly: false,\r\n});\r\n\r\n/** Hook pra ler estado de readonly. Componentes filhos consomem isso pra\r\n * desabilitar inputs/botões de escrita. */\r\nexport function useEntitlementContext(): EntitlementContextValue {\r\n return React.useContext(EntitlementContext);\r\n}\r\n\r\nexport interface EntitlementGateProps {\r\n /** Cliente SDK Neetru. */\r\n client: NeetruClient;\r\n /** Feature key a checar (ex: `export.csv`). */\r\n feature: string;\r\n /** Slug do produto. Default: lê do client.config se ausente. */\r\n productSlug?: string;\r\n /** Modo:\r\n * - `block` (default): renderiza fallback quando não permitido.\r\n * - `readonly`: sempre renderiza children mas injeta `readonly: true` no contexto. */\r\n mode?: EntitlementGateMode;\r\n /** Fallback UI quando `mode='block'` e check retorna `allowed=false`. */\r\n fallback?: React.ReactNode;\r\n /** Loading UI enquanto check resolve (Suspense-like). */\r\n loading?: React.ReactNode;\r\n children: React.ReactNode;\r\n}\r\n\r\ninterface EntitlementCheckResult {\r\n allowed: boolean;\r\n reason?: string;\r\n limit?: number;\r\n currentUsage?: number;\r\n behavior?: 'readonly' | null;\r\n}\r\n\r\n/**\r\n * Wrapper que gateia children por entitlement check.\r\n *\r\n * Quando `mode='readonly'` + check retorna `behavior='readonly'`:\r\n * - Children renderizam normalmente.\r\n * - Context injeta `{readonly: true}`.\r\n * - Componentes filhos chamam `useEntitlementContext()` pra desabilitar\r\n * escritas (botões, forms, inputs).\r\n *\r\n * Quando `mode='block'` + check retorna `allowed=false`:\r\n * - `fallback` é renderizado (ex: paywall, upsell modal).\r\n *\r\n * Decisão CEO §5 (overage): plataforma usa `readonly` sempre — produto SaaS\r\n * deve preferir `mode='readonly'` pra preservar engajamento (não hard-block).\r\n */\r\nexport function EntitlementGate({\r\n client,\r\n feature,\r\n productSlug,\r\n mode = 'block',\r\n fallback,\r\n loading,\r\n children,\r\n}: EntitlementGateProps): React.ReactElement {\r\n const [state, setState] = React.useState<{ status: 'loading' | 'ready'; result?: EntitlementCheckResult }>({\r\n status: 'loading',\r\n });\r\n\r\n // Resolve productSlug from prop or client.config\r\n const resolvedSlug =\r\n productSlug ?? (client.config as { productId?: string }).productId ?? '';\r\n\r\n // tenantId fallback: client.config.tenantId (default no createNeetruClient).\r\n // usage.check valida ambos productId + tenantId — sem isso, lança\r\n // validation_failed e o Gate fica trancado em allowed:false.\r\n const resolvedTenantId = (client.config as { tenantId?: string }).tenantId;\r\n\r\n React.useEffect(() => {\r\n let cancelled = false;\r\n const run = async () => {\r\n try {\r\n // Try usage.check first (returns full {allowed, reason, behavior, ...})\r\n // Falls back to entitlements.check (just boolean) if usage.check unavailable.\r\n let result: EntitlementCheckResult;\r\n const usage = (client as {\r\n usage?: {\r\n check: (\r\n resource: string,\r\n opts?: { productId?: string; tenantId?: string },\r\n ) => Promise<EntitlementCheckResult>;\r\n };\r\n }).usage;\r\n if (usage && typeof usage.check === 'function' && resolvedTenantId) {\r\n const res = await usage.check(feature, {\r\n productId: resolvedSlug,\r\n tenantId: resolvedTenantId,\r\n });\r\n result = {\r\n allowed: res.allowed === true,\r\n reason: res.reason,\r\n limit: res.limit,\r\n currentUsage: res.currentUsage,\r\n behavior: res.behavior,\r\n };\r\n } else {\r\n // Sem tenantId, cai pra entitlements.check (não exige tenant) em vez\r\n // de chamar usage.check e tomar validation_failed silencioso.\r\n const allowed = await client.entitlements.check(resolvedSlug, feature);\r\n result = { allowed };\r\n }\r\n if (!cancelled) setState({ status: 'ready', result });\r\n } catch (err) {\r\n // Fail open: erros de rede/timeout não bloqueiam o usuário com plano válido.\r\n // Bug corrigido: antes setava allowed:false (fail-closed) contradizendo o comentário.\r\n if (!cancelled) {\r\n setState({ status: 'ready', result: { allowed: true, reason: 'check_error_fallback' } });\r\n }\r\n if (typeof console !== 'undefined') console.warn('[EntitlementGate] check failed — fail-open', err);\r\n }\r\n };\r\n run();\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [client, feature, resolvedSlug, resolvedTenantId]);\r\n\r\n if (state.status === 'loading') {\r\n return React.createElement(React.Fragment, null, loading ?? null);\r\n }\r\n\r\n const result = state.result;\r\n const allowed = result?.allowed === true;\r\n const isReadonly = !allowed && (mode === 'readonly' || result?.behavior === 'readonly');\r\n\r\n if (!allowed && mode === 'block' && !isReadonly) {\r\n return React.createElement(React.Fragment, null, fallback ?? null);\r\n }\r\n\r\n return React.createElement(\r\n EntitlementContext.Provider,\r\n {\r\n value: {\r\n readonly: isReadonly,\r\n reason: result?.reason,\r\n limit: result?.limit,\r\n currentUsage: result?.currentUsage,\r\n },\r\n },\r\n children,\r\n );\r\n}\r\n\r\n// ════════════════════════════════════════════════════════════════════════════\r\n// SDK-2 — Hooks de dados de 1ª classe (compat com hooks caseiros por-produto)\r\n//\r\n// ⚠️ STATUS: 1ª classe, mas desenhados para CASAR com a superfície que os\r\n// produtos (gestovendas / pdvagiliza) escreveram à mão em cima do\r\n// firestore-compat. Substituem os hooks `useCollection`/`useDoc`/`useUser`\r\n// que cada produto reescrevia (~200-400 LOC por produto).\r\n//\r\n// Estes hooks aceitam **refs do `@neetru/sdk/firestore-compat`**\r\n// (`CollectionReference` / `Query` / `DocumentReference`) e devolvem o shape\r\n// `{ data, isLoading, error, isStale, lastUpdated }` que os produtos já\r\n// consomem. Internamente usam o realtime nativo do `client.db`\r\n// (`onSnapshot`) quando disponível, com fallback de polling visibility-aware.\r\n//\r\n// Diferença vs `@neetru/sdk/db/react` (`useDocument`/`useCollection` de lá):\r\n// - Lá: API offline-first, ref = `DbCollectionRef` nativo, retorno\r\n// `{ docs, loading, fromCache, stale }`.\r\n// - Aqui: API firestore-compat, ref = ref do firestore-compat, retorno\r\n// `{ data, isLoading, error, isStale, lastUpdated }` (id já embutido em\r\n// cada item; data nunca duplica o id internamente).\r\n// ════════════════════════════════════════════════════════════════════════════\r\n\r\n/**\r\n * Resultado de `useCollection` (subpath `@neetru/sdk/react`, API firestore-compat).\r\n *\r\n * Nome distinto de `UseCollectionResult` do subpath `@neetru/sdk/db/react`:\r\n * - `src/db/react.ts` → `{ docs, loading, fromCache, stale, hasPendingWrites }`\r\n * - `src/react.ts` → `{ data, isLoading, error, isStale, lastUpdated }` (este)\r\n *\r\n * Consumers que importam de ambos subpaths não terão clash de tipo.\r\n * Alias de retrocompat `UseCollectionResult` exportado abaixo.\r\n */\r\nexport interface FirestoreCompatCollectionResult<T> {\r\n /** Documentos (cada um com `id` embutido) ou `null` enquanto carrega/sem ref. */\r\n data: Array<T & { id: string }> | null;\r\n /** `true` enquanto o primeiro snapshot não chegou. */\r\n isLoading: boolean;\r\n /** Erro do último refresh (dados exibidos podem estar defasados). */\r\n error: Error | null;\r\n /** `true` quando um refresh falhou ou o snapshot veio do cache/offline. */\r\n isStale: boolean;\r\n /** epoch ms do último snapshot bem-sucedido, ou `null`. */\r\n lastUpdated: number | null;\r\n}\r\n\r\n/**\r\n * Alias de retrocompat — exportado para não quebrar produtos que importam\r\n * `UseCollectionResult` do subpath `@neetru/sdk/react`. Prefira\r\n * `FirestoreCompatCollectionResult` em código novo para evitar o clash de nome\r\n * com `UseCollectionResult` de `@neetru/sdk/db/react`.\r\n *\r\n * @deprecated Use `FirestoreCompatCollectionResult` em novo código.\r\n */\r\nexport type UseCollectionResult<T> = FirestoreCompatCollectionResult<T>;\r\n\r\n/** Resultado de `useDoc`. */\r\nexport interface UseDocResult<T> {\r\n data: (T & { id: string }) | null;\r\n isLoading: boolean;\r\n error: Error | null;\r\n isStale: boolean;\r\n lastUpdated: number | null;\r\n}\r\n\r\n/** Opções comuns dos hooks de dados. */\r\nexport interface UseDataOptions {\r\n /**\r\n * Intervalo de polling de fallback em ms (quando o transporte não oferece\r\n * realtime nativo). Default 5000. O polling pausa quando a aba está oculta.\r\n */\r\n intervalMs?: number;\r\n /**\r\n * Força polling mesmo quando o `onSnapshot` nativo está disponível. Útil para\r\n * transportes REST onde `onSnapshot` só entrega o snapshot inicial.\r\n * Default: `false` (usa realtime quando o transporte oferece).\r\n */\r\n poll?: boolean;\r\n}\r\n\r\nconst DEFAULT_INTERVAL_MS = 5000;\r\n\r\n/** `true` quando a aba está oculta (não dispara polling em background). */\r\nfunction isDocumentHidden(): boolean {\r\n return typeof document !== 'undefined' && document.hidden === true;\r\n}\r\n\r\n/**\r\n * Serializa uma fonte de coleção/query firestore-compat numa chave estável.\r\n * Garante que mudanças de VALOR (não de identidade) re-subscrevam, mas objetos\r\n * iguais não causem loop de efeito.\r\n */\r\nfunction serializeSource(\r\n source: CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n): string | null {\r\n if (!source) return null;\r\n if (source.__kind === 'collection') {\r\n return `collection:${source.id}`;\r\n }\r\n const dbQuery = toDbQuery(source.constraints);\r\n return `query:${source.collection}:${JSON.stringify(dbQuery)}`;\r\n}\r\n\r\n/** Normaliza linhas de um snapshot do client.db em `{...data, id}` (id embutido). */\r\nfunction rowsFromList<T>(snap: DbListResult<Record<string, unknown>>): Array<T & { id: string }> {\r\n return snap.docs.map((d) => {\r\n const data = d.data as Record<string, unknown>;\r\n // Garante que o id externo prevaleça (sem duplicar dentro do objeto data).\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id: d.id };\r\n });\r\n}\r\n\r\nfunction rowFromGet<T>(\r\n snap: DbGetResult<Record<string, unknown>> | null,\r\n id: string,\r\n): (T & { id: string }) | null {\r\n const first = snap?.docs[0];\r\n if (!first) return null;\r\n const data = first.data as Record<string, unknown>;\r\n const { id: _omit, ...rest } = data;\r\n return { ...(rest as T), id };\r\n}\r\n\r\n/**\r\n * Hook de tempo real para uma coleção (ou subconjunto filtrado via `query`).\r\n *\r\n * Aceita uma `CollectionReference` ou `Query` do `@neetru/sdk/firestore-compat`.\r\n * Subscreve ao `onSnapshot` nativo do `client.db`; quando o transporte é REST\r\n * (sem realtime nativo), faz polling visibility-aware no `intervalMs`.\r\n *\r\n * ```tsx\r\n * import { collection, query, where } from '@neetru/sdk/firestore-compat';\r\n * import { useCollection } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(\r\n * () => query(collection(client.db, 'orders'), where('status', '==', 'open')),\r\n * [],\r\n * );\r\n * const { data, isLoading, error, isStale } = useCollection<Order>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useCollection<T = Record<string, unknown>>(\r\n source: CollectionReference<T> | Query<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseCollectionResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const sourceKey = serializeSource(\r\n source as CollectionReference<unknown> | Query<unknown> | null | undefined,\r\n );\r\n\r\n const [state, setState] = React.useState<UseCollectionResult<T>>({\r\n data: null,\r\n isLoading: sourceKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n // Mantém a fonte mais recente sem re-disparar o efeito por identidade.\r\n const sourceRef = React.useRef(source);\r\n sourceRef.current = source;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = sourceRef.current;\r\n\r\n if (!current || sourceKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const collectionName = current.__kind === 'query' ? current.collection : current.id;\r\n const dbQuery = current.__kind === 'query' ? toDbQuery(current.constraints) : undefined;\r\n const col = current.db.collection(collectionName);\r\n\r\n const applySnap = (snap: DbListResult<Record<string, unknown>>) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowsFromList<T>(snap),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n // Mantém o último data bom — não limpa em erro de refresh.\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n // Polling explícito (REST) ou realtime nativo.\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await col.list(dbQuery)) as DbListResult<Record<string, unknown>>;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = col.onSnapshot(dbQuery, applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [sourceKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n/**\r\n * Hook de tempo real para um documento.\r\n *\r\n * Aceita uma `DocumentReference` do `@neetru/sdk/firestore-compat`.\r\n *\r\n * ```tsx\r\n * import { doc } from '@neetru/sdk/firestore-compat';\r\n * import { useDoc } from '@neetru/sdk/react';\r\n *\r\n * const ref = useMemo(() => doc(client.db, 'profiles', uid), [uid]);\r\n * const { data, isLoading } = useDoc<Profile>(ref);\r\n * ```\r\n *\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useDoc<T = Record<string, unknown>>(\r\n ref: DocumentReference<T> | null | undefined,\r\n options?: UseDataOptions,\r\n): UseDocResult<T> {\r\n const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS;\r\n const poll = options?.poll ?? false;\r\n const refKey = ref && ref.id ? `${ref.collection}/${ref.id}` : null;\r\n\r\n const [state, setState] = React.useState<UseDocResult<T>>({\r\n data: null,\r\n isLoading: refKey !== null,\r\n error: null,\r\n isStale: false,\r\n lastUpdated: null,\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n const refRef = React.useRef(ref);\r\n refRef.current = ref;\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n const current = refRef.current;\r\n\r\n if (!current || refKey === null) {\r\n setState({ data: null, isLoading: false, error: null, isStale: false, lastUpdated: null });\r\n return () => {\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n setState((prev) => ({ ...prev, isLoading: prev.data === null }));\r\n\r\n const docRef = current.db.collection(current.collection).doc(current.id);\r\n const id = current.id;\r\n\r\n const applySnap = (snap: DbGetResult<Record<string, unknown>> | null) => {\r\n if (!mountedRef.current) return;\r\n setState({\r\n data: rowFromGet<T>(snap, id),\r\n isLoading: false,\r\n error: null,\r\n isStale: snap?.stale === true,\r\n lastUpdated: Date.now(),\r\n });\r\n };\r\n\r\n const applyError = (e: unknown) => {\r\n if (!mountedRef.current) return;\r\n setState((prev) => ({\r\n ...prev,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n isStale: true,\r\n }));\r\n };\r\n\r\n if (poll) {\r\n let cancelled = false;\r\n const fetchOnce = async () => {\r\n if (cancelled || isDocumentHidden()) return;\r\n try {\r\n const snap = (await docRef.get()) as DbGetResult<Record<string, unknown>> | null;\r\n if (!cancelled) applySnap(snap);\r\n } catch (e) {\r\n if (!cancelled) applyError(e);\r\n }\r\n };\r\n void fetchOnce();\r\n const timer = setInterval(fetchOnce, intervalMs);\r\n return () => {\r\n cancelled = true;\r\n clearInterval(timer);\r\n mountedRef.current = false;\r\n };\r\n }\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = docRef.onSnapshot(applySnap);\r\n } catch (e) {\r\n applyError(e);\r\n }\r\n return () => {\r\n mountedRef.current = false;\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [refKey, intervalMs, poll]);\r\n\r\n return state;\r\n}\r\n\r\n// ─── useUser ────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Fonte de auth aceita por `useUser`: o `NeetruClient` inteiro (resolve\r\n * `client.auth`) ou o `AuthNamespace` direto.\r\n */\r\nexport type UseUserInput = NeetruClient | { auth: AuthNamespace } | AuthNamespace;\r\n\r\n/** Resultado de `useUser`. */\r\nexport interface UseUserResult {\r\n /** Usuário autenticado ou `null`. */\r\n user: NeetruUser | null;\r\n /** `true` enquanto o estado de auth inicial não foi resolvido. */\r\n isLoading: boolean;\r\n /** Erro na resolução de auth (raro — listener nunca deveria lançar). */\r\n error: Error | null;\r\n}\r\n\r\nfunction resolveAuth(input: UseUserInput): AuthNamespace {\r\n const maybe = input as { auth?: AuthNamespace };\r\n if (maybe.auth && typeof maybe.auth.onAuthStateChanged === 'function') {\r\n return maybe.auth;\r\n }\r\n return input as AuthNamespace;\r\n}\r\n\r\n/**\r\n * Hook de estado de autenticação.\r\n *\r\n * Subscreve a `client.auth.onAuthStateChanged` e devolve `{ user, isLoading,\r\n * error }`. Inclui um **timeout de seed** (default 8s): se o estado de auth não\r\n * resolver nesse prazo, `isLoading` cai para `false` com `user: null` — evita\r\n * spinners infinitos quando o provedor de auth nunca dispara o callback.\r\n *\r\n * ```tsx\r\n * import { useUser } from '@neetru/sdk/react';\r\n * const { user, isLoading } = useUser(client);\r\n * if (isLoading) return <Spinner />;\r\n * if (!user) return <SignInButton />;\r\n * return <Avatar user={user} />;\r\n * ```\r\n *\r\n * Aceita o `NeetruClient`, `{ auth }` ou o `AuthNamespace` direto.\r\n * Seguro em React 18/19 StrictMode.\r\n */\r\nexport function useUser(\r\n input: UseUserInput,\r\n options?: { timeoutMs?: number },\r\n): UseUserResult {\r\n const timeoutMs = options?.timeoutMs ?? 8000;\r\n const auth = resolveAuth(input);\r\n\r\n const [state, setState] = React.useState<UseUserResult>(() => {\r\n // Seed síncrono do estado atual (se o namespace já tem user em cache).\r\n let seeded: NeetruUser | null = null;\r\n try {\r\n seeded = auth.getUser();\r\n } catch {\r\n seeded = null;\r\n }\r\n return { user: seeded, isLoading: seeded === null, error: null };\r\n });\r\n\r\n const mountedRef = React.useRef(true);\r\n\r\n React.useEffect(() => {\r\n mountedRef.current = true;\r\n let settled = false;\r\n\r\n // Timeout de seed: se onAuthStateChanged nunca disparar, destrava o loading.\r\n const timer = setTimeout(() => {\r\n if (!mountedRef.current || settled) return;\r\n setState((prev) => (prev.isLoading ? { ...prev, isLoading: false } : prev));\r\n }, timeoutMs);\r\n\r\n let unsub: (() => void) | undefined;\r\n try {\r\n unsub = auth.onAuthStateChanged((user) => {\r\n if (!mountedRef.current) return;\r\n settled = true;\r\n setState({ user: user ?? null, isLoading: false, error: null });\r\n });\r\n } catch (e) {\r\n if (mountedRef.current) {\r\n setState({\r\n user: null,\r\n isLoading: false,\r\n error: e instanceof Error ? e : new Error(String(e)),\r\n });\r\n }\r\n }\r\n\r\n return () => {\r\n mountedRef.current = false;\r\n clearTimeout(timer);\r\n unsub?.();\r\n };\r\n // eslint-disable-next-line react-hooks/exhaustive-deps\r\n }, [auth, timeoutMs]);\r\n\r\n return state;\r\n}\r\n"]}
|
package/dist/support.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/idempotency.ts","../src/http.ts","../src/validators.ts","../src/support.ts"],"names":["err","message"],"mappings":"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","file":"support.cjs","sourcesContent":["/**\r\n * Erros tipados do SDK.\r\n *\r\n * Todo erro lançado pelo SDK estende `NeetruError` — caller pode discriminar\r\n * por `.code` (string estável) sem parsing de message.\r\n */\r\n\r\n/** Códigos de erro estáveis do SDK. Adicionar aqui requer minor bump. */\r\nexport type NeetruErrorCode =\r\n | 'invalid_config'\r\n | 'missing_api_key'\r\n | 'unauthorized'\r\n | 'forbidden'\r\n | 'not_found'\r\n | 'rate_limited'\r\n | 'validation_failed'\r\n | 'network_error'\r\n | 'invalid_response'\r\n | 'server_error'\r\n | 'token_expired'\r\n | 'token_invalid_signature'\r\n | 'token_invalid_audience'\r\n | 'token_invalid_issuer'\r\n /** 3.0: runtime não expõe API necessária (ex: WebCrypto ausente). */\r\n | 'runtime_unsupported'\r\n /**\r\n * 3.1.3 (bug_c1c96a00): o `state` retornado pelo IdP no callback OIDC não bate com o gerado em\r\n * `signIn` (CSRF). Também usado quando não há transação OIDC pendente.\r\n */\r\n | 'oidc_state_mismatch'\r\n /** HTTP 409 — colisão de idempotência ou conflito de recurso. */\r\n | 'conflict'\r\n | 'unknown';\r\n\r\n/**\r\n * Erro tipado padrão do SDK. Sempre lançado em vez de Error genérico.\r\n *\r\n * @example\r\n * ```ts\r\n * try { await client.catalog.list(); }\r\n * catch (e) {\r\n * if (e instanceof NeetruError && e.code === 'rate_limited') retry();\r\n * }\r\n * ```\r\n */\r\nexport class NeetruError extends Error {\r\n public readonly code: NeetruErrorCode | string;\r\n public readonly status?: number;\r\n public readonly requestId?: string;\r\n\r\n constructor(\r\n code: NeetruErrorCode | string,\r\n message: string,\r\n status?: number,\r\n requestId?: string,\r\n ) {\r\n super(message);\r\n this.name = 'NeetruError';\r\n this.code = code;\r\n this.status = status;\r\n this.requestId = requestId;\r\n // Preserva o prototype chain ao herdar de Error (downlevel quirk de TS).\r\n Object.setPrototypeOf(this, NeetruError.prototype);\r\n }\r\n}\r\n","/**\n * Idempotency-Key helper — gera UUID v4 cripto-seguro para identificar\n * mutations não-idempotentes (`usage.report`, `support.createTicket`,\n * `notifications.send`, `webhooks.test`).\n *\n * Estratégia 3.0:\n * - SDK gera UUID v4 por chamada via `randomUUID()` (WebCrypto.subtle não\n * necessário aqui — `crypto.randomUUID()` está em Node 14.17+ e em todos\n * os browsers/Edge runtimes modernos).\n * - Header `Idempotency-Key: <uuid>` é injetado pelo `httpRequest` quando\n * `opts.idempotencyKey === true` OU caller passa explicit `opts.idempotencyKeyValue`.\n * - Core dedup em coleção `usage_idempotency_keys/{key}` TTL 24h (vide\n * `docs/CORE_CHANGES_REQUIRED.md` §2). Se Core ainda não dedupa, o header\n * é benign (ignorado).\n *\n * Defesa em camadas: combinado com `retries: 0` em POSTs (vide http.ts), o\n * risco de double-write em transientes vai pra zero.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Gera UUID v4 criptograficamente seguro. Universal: Node 14.17+, browsers\n * modernos, Edge runtimes (Workers/Vercel Edge/Cloudflare).\n *\n * Fallback puro-JS removido em 3.0 — `crypto.randomUUID()` é ubíquo desde\n * 2022. Se runtime não tem, lança `NeetruError('runtime_unsupported')`\n * (consumer deve polyfillar).\n *\n * **L-01 fix (Opus review 2026-05-27):** antes lançava `Error` cru, que\n * não era pego por `catch (err) { if (err instanceof NeetruError) ... }`\n * dos consumers. Agora throw canonical.\n *\n * @throws NeetruError('runtime_unsupported') se `crypto.randomUUID` não disponível.\n */\nexport function generateIdempotencyKey(): string {\n // WebCrypto (browser/Edge) e Node 14.17+ expõem `crypto.randomUUID()`.\n // Node anterior precisa de `node:crypto` mas SDK 3.0 exige Node ≥20 (vide engines).\n const cryptoObj: { randomUUID?: () => string } | undefined =\n typeof globalThis !== 'undefined' && 'crypto' in globalThis\n ? (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n : undefined;\n if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {\n return cryptoObj.randomUUID();\n }\n throw new NeetruError(\n 'runtime_unsupported',\n 'crypto.randomUUID not available in this runtime. SDK 3.0 requires Node ≥20 or modern browser/Edge.',\n );\n}\n\n/** Regex UUID v4 — validação para uso interno. */\nexport const UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n","/**\r\n * HTTP transport interno do SDK.\r\n *\r\n * Responsabilidades:\r\n * - Construir URL absoluta a partir de `baseUrl` + path\r\n * - Injetar Bearer token quando `requireAuth=true`\r\n * - Mapear status HTTP → `NeetruError` com `code` estável\r\n * - Parse defensivo de JSON (não lança em body vazio em 204)\r\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\r\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\r\n * presente.\r\n * - **Captura `X-Neetru-Version`** das respostas e emite `console.warn` quando\r\n * o Core está abaixo da versão mínima para a major do SDK.\r\n *\r\n * Política de retries (mudança breaking 3.0):\r\n * - GET/HEAD: default 2 retries (3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: default **0 retries** — mutations podem duplicar\r\n * side effects (ex: `usage.report` incrementa contador). Caller pode\r\n * reativar passando `retries: N` explicitamente ou usar `Idempotency-Key`\r\n * header (gerado automaticamente se `opts.idempotencyKey=true`).\r\n *\r\n * Idempotency-Key (3.0):\r\n * - `opts.idempotencyKey === true` → SDK gera UUID v4 e injeta header.\r\n * - `opts.idempotencyKey === 'uuid-string'` → usa o valor passado.\r\n * - Caller mantém retentos client-side se quiser; Core dedup serverside\r\n * (vide `docs/CORE_CHANGES_REQUIRED.md` §2).\r\n */\r\nimport { NeetruError, type NeetruErrorCode } from './errors';\r\nimport { generateIdempotencyKey } from './idempotency';\r\nimport type { ResolvedConfig } from './types';\r\n\r\n// ─── Compat Matrix SDK↔Core ──────────────────────────────────────────────────\r\n\r\n/**\r\n * Matriz de compatibilidade SDK major → versão mínima do Core.\r\n *\r\n * Formato do Core: CalVer `YYYY.MM.patch+revision` (ex: `2026.05.1+neetru-core-00096-79t`).\r\n * A comparação é feita apenas nas partes `YYYY.MM.patch` (prefixo antes de `+`).\r\n *\r\n * Quando o Core reporta `X-Neetru-Version` abaixo do mínimo para a SDK major\r\n * corrente, o SDK emite `console.warn` uma única vez por sessão de processo.\r\n *\r\n * Atualize esta tabela a cada major de SDK.\r\n */\r\nconst COMPAT_MATRIX: Record<number, string> = {\r\n // SDK major 3.x requer Core ≥ 2026.05.1 (suporte a X-Neetru-Version header,\r\n // runTransaction write-after-read, verifyWebhookRequest, webhooks 3.0+)\r\n 3: '2026.05.1',\r\n};\r\n\r\n/** CalVer sem sufixo `+revision`. Ex: `2026.05.1+abc` → `2026.05.1`. */\r\nfunction stripRevision(v: string): string {\r\n return v.split('+')[0] ?? v;\r\n}\r\n\r\n/**\r\n * Compara dois CalVer (`YYYY.MM.patch`). Retorna negativo se `a < b`,\r\n * positivo se `a > b`, 0 se iguais. Partes não numéricas tratadas como 0.\r\n */\r\nfunction compareCalVer(a: string, b: string): number {\r\n const partsA = stripRevision(a).split('.').map(Number);\r\n const partsB = stripRevision(b).split('.').map(Number);\r\n const len = Math.max(partsA.length, partsB.length);\r\n for (let i = 0; i < len; i++) {\r\n const pa = partsA[i] ?? 0;\r\n const pb = partsB[i] ?? 0;\r\n if (pa !== pb) return pa - pb;\r\n }\r\n return 0;\r\n}\r\n\r\n/** Lê a SDK major a partir da string `VERSION` exportada de `./index`. Fallback 3. */\r\nfunction getSdkMajor(): number {\r\n try {\r\n // Importação estática não disponível aqui (evita ciclo). Usa variável injetada\r\n // em tempo de build pela tsup via `define`. Fallback para 3 (current major).\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const ver = (globalThis as any).__NEETRU_SDK_VERSION__ as string | undefined;\r\n if (ver) return parseInt(ver.split('.')[0] ?? '3', 10);\r\n } catch {\r\n // ignore\r\n }\r\n return 3;\r\n}\r\n\r\n/** Aviso emitido no máximo uma vez por major detectada pra não spammar logs. */\r\nconst _warnedCoreTooOld = new Set<string>();\r\n\r\n/**\r\n * Verifica se a versão do Core (`X-Neetru-Version`) é compatível com a SDK major.\r\n * Emite `console.warn` quando incompatível (uma vez por processo por versão detectada).\r\n */\r\nexport function checkCoreVersionCompat(coreVersion: string): void {\r\n if (!coreVersion) return;\r\n const sdkMajor = getSdkMajor();\r\n const minCore = COMPAT_MATRIX[sdkMajor];\r\n if (!minCore) return; // sem restrição para esta major\r\n const stripped = stripRevision(coreVersion);\r\n if (compareCalVer(stripped, minCore) < 0) {\r\n const key = `${sdkMajor}:${stripped}`;\r\n if (!_warnedCoreTooOld.has(key)) {\r\n _warnedCoreTooOld.add(key);\r\n console.warn(\r\n `[Neetru SDK] Atenção: Core versão \"${coreVersion}\" é anterior ao mínimo recomendado` +\r\n ` \"${minCore}\" para SDK major ${sdkMajor}. Alguns recursos podem não funcionar` +\r\n ` corretamente. Atualize o Core para a versão mais recente.`,\r\n );\r\n }\r\n }\r\n}\r\n\r\n/** Opções da request HTTP. */\r\nexport interface HttpRequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\r\n path: string;\r\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\r\n query?: Record<string, string | number | boolean | undefined>;\r\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\r\n body?: unknown;\r\n /**\r\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\r\n * se config.apiKey ausente. Default false.\r\n */\r\n requireAuth?: boolean;\r\n /** Cabeçalhos extras. */\r\n headers?: Record<string, string>;\r\n /**\r\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\r\n * `network_error`).\r\n *\r\n * Default (3.0):\r\n * - GET/HEAD: 2 (= 3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: 0 — mutations podem duplicar side effects.\r\n *\r\n * Caller pode reativar passando explicitamente: `retries: 2`.\r\n */\r\n retries?: number;\r\n /**\r\n * Injeta `Idempotency-Key` header pra dedup serverside (3.0):\r\n * - `true` → SDK gera UUID v4 cripto-seguro automaticamente.\r\n * - string → usa o valor passado (deve ser UUID v4).\r\n * - `undefined`/`false` → sem header.\r\n */\r\n idempotencyKey?: boolean | string;\r\n}\r\n\r\nconst DEFAULT_RETRIES_READ = 2;\r\nconst DEFAULT_RETRIES_WRITE = 0;\r\nconst READ_METHODS = new Set(['GET', 'HEAD']);\r\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\r\n 'rate_limited',\r\n 'server_error',\r\n 'network_error',\r\n]);\r\n\r\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\r\nfunction backoffMs(attempt: number): number {\r\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\r\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\r\n return Math.max(50, Math.round(base + jitter));\r\n}\r\n\r\n/**\r\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\r\n * inválido. RFC 9110 §10.2.3.\r\n *\r\n * Bug fix: sem teto, um servidor hostil/mal-configurado poderia retornar\r\n * `Retry-After: 86400` (24h) ou uma HTTP-date anos no futuro, pendurando o\r\n * processo indefinidamente. Aplica teto de MAX_RETRY_AFTER_MS em ambos os caminhos.\r\n */\r\nconst MAX_RETRY_AFTER_MS = 60_000; // 60s — teto razoável para retry automático\r\n\r\nfunction parseRetryAfter(value: string | null): number | null {\r\n if (!value) return null;\r\n const secs = Number(value);\r\n if (Number.isFinite(secs) && secs >= 0) return Math.min(Math.round(secs * 1000), MAX_RETRY_AFTER_MS);\r\n const dateMs = Date.parse(value);\r\n if (Number.isFinite(dateMs)) {\r\n const delta = dateMs - Date.now();\r\n if (delta > 0) return Math.min(delta, MAX_RETRY_AFTER_MS);\r\n }\r\n return null;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/** Mapeamento status → code estável do NeetruError. */\r\nfunction statusToCode(status: number): NeetruErrorCode {\r\n if (status === 401) return 'unauthorized';\r\n if (status === 403) return 'forbidden';\r\n if (status === 404) return 'not_found';\r\n if (status === 409) return 'conflict'; // Bug fix: 409 vira 'conflict' (não 'unknown')\r\n if (status === 422 || status === 400) return 'validation_failed';\r\n if (status === 429) return 'rate_limited';\r\n if (status >= 500) return 'server_error';\r\n return 'unknown';\r\n}\r\n\r\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\r\n // Trim trailing slash em base e leading em path pra evitar `//`.\r\n const base = baseUrl.replace(/\\/+$/, '');\r\n const p = path.startsWith('/') ? path : `/${path}`;\r\n const url = new URL(`${base}${p}`);\r\n if (query) {\r\n for (const [k, v] of Object.entries(query)) {\r\n if (v === undefined) continue;\r\n url.searchParams.set(k, String(v));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n\r\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\r\nasync function safeJson(res: Response): Promise<unknown> {\r\n const text = await res.text();\r\n if (!text) return undefined;\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\r\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\r\n * automático em códigos transientes (rate_limited/server_error/network_error)\r\n * conforme `opts.retries` (default 2 = 3 tentativas).\r\n */\r\nexport async function httpRequest<T>(\r\n config: ResolvedConfig,\r\n opts: HttpRequestOptions,\r\n): Promise<T> {\r\n const method = opts.method ?? 'GET';\r\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\r\n // Default por método: reads tentam 2x, writes 0x. Caller override sempre vence.\r\n const defaultRetries = READ_METHODS.has(method)\r\n ? DEFAULT_RETRIES_READ\r\n : DEFAULT_RETRIES_WRITE;\r\n const maxRetries = opts.retries ?? defaultRetries;\r\n\r\n const headers: Record<string, string> = {\r\n accept: 'application/json',\r\n ...opts.headers,\r\n };\r\n\r\n if (opts.requireAuth) {\r\n if (!config.apiKey) {\r\n throw new NeetruError(\r\n 'missing_api_key',\r\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\r\n );\r\n }\r\n headers.authorization = `Bearer ${config.apiKey}`;\r\n }\r\n\r\n // Idempotency-Key: gerado por chamada se solicitado. UUID v4 cripto-seguro.\r\n if (opts.idempotencyKey) {\r\n const key =\r\n typeof opts.idempotencyKey === 'string'\r\n ? opts.idempotencyKey\r\n : generateIdempotencyKey();\r\n headers['idempotency-key'] = key;\r\n }\r\n\r\n // Body só é serializado uma vez — reusado entre tentativas.\r\n const bodyString =\r\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\r\n ? JSON.stringify(opts.body)\r\n : undefined;\r\n if (bodyString !== undefined) {\r\n headers['content-type'] = 'application/json';\r\n }\r\n\r\n let lastError: NeetruError | null = null;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n const init: RequestInit = { method, headers };\r\n if (bodyString !== undefined) init.body = bodyString;\r\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\r\n // requer Node 18+ ou browsers recentes.\r\n init.signal = AbortSignal.timeout(30_000);\r\n\r\n let res: Response;\r\n try {\r\n res = await config.fetch(url, init);\r\n } catch (err) {\r\n const message =\r\n err instanceof DOMException && err.name === 'TimeoutError'\r\n ? 'Network error: timeout after 30s'\r\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\r\n lastError = new NeetruError('network_error', message);\r\n if (attempt < maxRetries) {\r\n await sleep(backoffMs(attempt));\r\n continue;\r\n }\r\n throw lastError;\r\n }\r\n\r\n const requestId =\r\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\r\n\r\n if (res.ok) {\r\n // Captura X-Neetru-Version e verifica compat com a SDK major.\r\n const coreVersion = res.headers.get('x-neetru-version');\r\n if (coreVersion) checkCoreVersionCompat(coreVersion);\r\n const parsed = await safeJson(res);\r\n return parsed as T;\r\n }\r\n\r\n const body = (await safeJson(res)) as\r\n | { error?: { code?: string; message?: string } | string }\r\n | undefined;\r\n let code: string = statusToCode(res.status);\r\n let message = `HTTP ${res.status}`;\r\n if (body && typeof body === 'object' && 'error' in body) {\r\n const errField = body.error;\r\n if (typeof errField === 'string') {\r\n message = errField;\r\n } else if (errField && typeof errField === 'object') {\r\n if (typeof errField.code === 'string') code = errField.code;\r\n if (typeof errField.message === 'string') message = errField.message;\r\n }\r\n }\r\n const err = new NeetruError(code, message, res.status, requestId);\r\n lastError = err;\r\n\r\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\r\n if (isRetryable && attempt < maxRetries) {\r\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\r\n const delay = retryAfter ?? backoffMs(attempt);\r\n await sleep(delay);\r\n continue;\r\n }\r\n throw err;\r\n }\r\n\r\n // Unreachable — loop sempre retorna ou lança. Safety net.\r\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\r\n}\r\n","/**\n * Validators centralizados pra inputs do SDK que precisam casar com\n * regex do Core.\n *\n * **M-03 fix (Opus review 2026-05-27):** o Core rejeita productId que\n * não bate `/^[a-z0-9][a-z0-9_-]{0,62}$/` com 400 `validation_failed`.\n * O SDK não pré-validava — consumer recebia erro só em runtime de\n * produção, depois do round-trip de rede. Agora SDK throws localmente\n * antes do fetch.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Regex canonical de IDs no Neetru Core (productId, tenantId, etc).\n * Espelha `SDK_ID_RE` em `neetru_core/src/lib/schemas/sdk.ts`.\n */\nexport const SDK_ID_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;\n\n/**\n * Throws `NeetruError('validation_failed')` se `value` não bater no\n * formato canonical.\n *\n * @example\n * ```ts\n * assertValidSdkId('productId', 'gestovendas'); // ok\n * assertValidSdkId('productId', 'GestoVendas'); // throws\n * ```\n */\nexport function assertValidSdkId(field: string, value: string): void {\n if (typeof value !== 'string' || !SDK_ID_RE.test(value)) {\n throw new NeetruError(\n 'validation_failed',\n `${field} must match ${SDK_ID_RE.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`,\n );\n }\n}\n","/**\n * Support namespace — criar e listar tickets via SDK (3.0).\n *\n * **Breaking 3.0**:\n * - Endpoint mudou de `/api/v1/products/{slug}/tickets` (que **NUNCA existiu\n * no Core** — vaporware testado) para `/api/sdk/v1/support/tickets`\n * (canonical SDK path).\n * - `productId` agora é obrigatório no body/query (default resolve do\n * `config.productId`; throw `validation_failed` se ambos ausentes).\n * - `createTicket()` envia `Idempotency-Key` header (anti dup-create\n * em retry transient — Core dedup TTL 24h conforme CORE_CHANGES_REQUIRED §1).\n *\n * **Pré-requisito Core**: endpoint `/api/sdk/v1/support/tickets` (POST + GET)\n * precisa existir no Core antes do SDK 3.0 publish. Vide\n * `docs/CORE_CHANGES_REQUIRED.md` §1 para contrato esperado.\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport { assertValidSdkId } from './validators';\nimport type {\n CreateTicketInput,\n ResolvedConfig,\n SupportNamespace,\n SupportSeverity,\n SupportStatus,\n SupportTicket,\n} from './types';\n\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\n\ninterface RawTicket {\n id?: string;\n productId?: string;\n subject?: string;\n message?: string;\n severity?: string;\n status?: string;\n createdAt?: string;\n updatedAt?: string;\n productSlug?: string;\n}\n\nfunction toTicket(raw: unknown): SupportTicket {\n if (!raw || typeof raw !== 'object') {\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\n }\n const r = raw as RawTicket;\n if (typeof r.id !== 'string') {\n throw new NeetruError('invalid_response', 'Ticket missing id');\n }\n return {\n id: r.id,\n subject: typeof r.subject === 'string' ? r.subject : '',\n message: typeof r.message === 'string' ? r.message : '',\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\n ? (r.severity as SupportSeverity)\n : 'normal',\n status: VALID_STATUSES.includes(r.status as SupportStatus)\n ? (r.status as SupportStatus)\n : 'open',\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\n productSlug:\n typeof r.productId === 'string'\n ? r.productId\n : typeof r.productSlug === 'string'\n ? r.productSlug\n : undefined,\n };\n}\n\nfunction resolveProductId(\n config: ResolvedConfig,\n ...candidates: Array<string | undefined>\n): string {\n for (const c of candidates) {\n if (typeof c === 'string' && c.length > 0) {\n assertValidSdkId('productSlug', c); // M-03: pré-valida client-side\n return c;\n }\n }\n if (config.productId) {\n assertValidSdkId('productId', config.productId);\n return config.productId;\n }\n throw new NeetruError(\n 'validation_failed',\n 'productId required (pass to input.productSlug or set on createNeetruClient)',\n );\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. `productSlug` no input\n * (ou `config.productId` default) vai como `productId` no body do Core.\n *\n * Endpoint canonical: `POST /api/sdk/v1/support/tickets`.\n */\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\n if (!input || typeof input !== 'object') {\n throw new NeetruError('validation_failed', 'input is required');\n }\n if (!input.subject || typeof input.subject !== 'string') {\n throw new NeetruError('validation_failed', 'subject is required');\n }\n if (input.subject.length > 200) {\n throw new NeetruError('validation_failed', 'subject max 200 chars');\n }\n if (!input.message || typeof input.message !== 'string') {\n throw new NeetruError('validation_failed', 'message is required');\n }\n if (input.message.length > 10_000) {\n throw new NeetruError('validation_failed', 'message max 10000 chars');\n }\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\n throw new NeetruError(\n 'validation_failed',\n `severity must be one of ${VALID_SEVERITIES.join(', ')}`,\n );\n }\n\n const productId = resolveProductId(config, input.productSlug);\n // M-03: validação acontece dentro de resolveProductId; aqui só usa.\n\n const body: Record<string, unknown> = {\n productId,\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n // L-05 fix: propaga metadata quando consumer enviar.\n if (input.metadata && typeof input.metadata === 'object') {\n body.metadata = input.metadata;\n }\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: '/api/sdk/v1/support/tickets',\n body,\n requireAuth: true,\n idempotencyKey: true,\n });\n\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\n const candidate =\n raw && typeof raw === 'object' && 'ticket' in raw\n ? (raw as { ticket?: RawTicket }).ticket\n : raw;\n return toTicket(candidate);\n },\n\n /**\n * Lista tickets do customer no produto atual. `productSlug` no contexto.\n *\n * Endpoint canonical: `GET /api/sdk/v1/support/tickets?productId=...`.\n */\n async listMyTickets(options?: { productSlug?: string }): Promise<SupportTicket[]> {\n const productId = resolveProductId(config, options?.productSlug);\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/sdk/v1/support/tickets',\n query: { productId },\n requireAuth: true,\n });\n const list = Array.isArray(raw)\n ? raw\n : raw && typeof raw === 'object' && 'tickets' in raw\n ? ((raw as { tickets?: unknown[] }).tickets ?? [])\n : [];\n return list.map(toTicket);\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/idempotency.ts","../src/http.ts","../src/validators.ts","../src/support.ts"],"names":["err","message"],"mappings":"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","file":"support.cjs","sourcesContent":["/**\n * Erros tipados do SDK.\n *\n * Todo erro lançado pelo SDK estende `NeetruError` — caller pode discriminar\n * por `.code` (string estável) sem parsing de message.\n */\n\n/** Códigos de erro estáveis do SDK. Adicionar aqui requer minor bump. */\nexport type NeetruErrorCode =\n | 'invalid_config'\n | 'missing_api_key'\n | 'unauthorized'\n | 'forbidden'\n | 'not_found'\n | 'rate_limited'\n | 'validation_failed'\n | 'network_error'\n | 'invalid_response'\n | 'server_error'\n | 'token_expired'\n | 'token_invalid_signature'\n | 'token_invalid_audience'\n | 'token_invalid_issuer'\n /** 3.0: runtime não expõe API necessária (ex: WebCrypto ausente). */\n | 'runtime_unsupported'\n /**\n * 3.1.3 (bug_c1c96a00): o `state` retornado pelo IdP no callback OIDC não bate com o gerado em\n * `signIn` (CSRF). Também usado quando não há transação OIDC pendente.\n */\n | 'oidc_state_mismatch'\n /** HTTP 409 — colisão de idempotência ou conflito de recurso. */\n | 'conflict'\n | 'unknown';\n\n/**\n * Erro tipado padrão do SDK. Sempre lançado em vez de Error genérico.\n *\n * @example\n * ```ts\n * try { await client.catalog.list(); }\n * catch (e) {\n * if (e instanceof NeetruError && e.code === 'rate_limited') retry();\n * }\n * ```\n */\nexport class NeetruError extends Error {\n public readonly code: NeetruErrorCode | string;\n public readonly status?: number;\n public readonly requestId?: string;\n\n constructor(\n code: NeetruErrorCode | string,\n message: string,\n status?: number,\n requestId?: string,\n ) {\n super(message);\n this.name = 'NeetruError';\n this.code = code;\n this.status = status;\n this.requestId = requestId;\n // Preserva o prototype chain ao herdar de Error (downlevel quirk de TS).\n Object.setPrototypeOf(this, NeetruError.prototype);\n }\n}\n","/**\n * Idempotency-Key helper — gera UUID v4 cripto-seguro para identificar\n * mutations não-idempotentes (`usage.report`, `support.createTicket`,\n * `notifications.send`, `webhooks.test`).\n *\n * Estratégia 3.0:\n * - SDK gera UUID v4 por chamada via `randomUUID()` (WebCrypto.subtle não\n * necessário aqui — `crypto.randomUUID()` está em Node 14.17+ e em todos\n * os browsers/Edge runtimes modernos).\n * - Header `Idempotency-Key: <uuid>` é injetado pelo `httpRequest` quando\n * `opts.idempotencyKey === true` OU caller passa explicit `opts.idempotencyKeyValue`.\n * - Core dedup em coleção `usage_idempotency_keys/{key}` TTL 24h (vide\n * `docs/CORE_CHANGES_REQUIRED.md` §2). Se Core ainda não dedupa, o header\n * é benign (ignorado).\n *\n * Defesa em camadas: combinado com `retries: 0` em POSTs (vide http.ts), o\n * risco de double-write em transientes vai pra zero.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Gera UUID v4 criptograficamente seguro. Universal: Node 14.17+, browsers\n * modernos, Edge runtimes (Workers/Vercel Edge/Cloudflare).\n *\n * Fallback puro-JS removido em 3.0 — `crypto.randomUUID()` é ubíquo desde\n * 2022. Se runtime não tem, lança `NeetruError('runtime_unsupported')`\n * (consumer deve polyfillar).\n *\n * **L-01 fix (Opus review 2026-05-27):** antes lançava `Error` cru, que\n * não era pego por `catch (err) { if (err instanceof NeetruError) ... }`\n * dos consumers. Agora throw canonical.\n *\n * @throws NeetruError('runtime_unsupported') se `crypto.randomUUID` não disponível.\n */\nexport function generateIdempotencyKey(): string {\n // WebCrypto (browser/Edge) e Node 14.17+ expõem `crypto.randomUUID()`.\n // Node anterior precisa de `node:crypto` mas SDK 3.0 exige Node ≥20 (vide engines).\n const cryptoObj: { randomUUID?: () => string } | undefined =\n typeof globalThis !== 'undefined' && 'crypto' in globalThis\n ? (globalThis as { crypto?: { randomUUID?: () => string } }).crypto\n : undefined;\n if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {\n return cryptoObj.randomUUID();\n }\n throw new NeetruError(\n 'runtime_unsupported',\n 'crypto.randomUUID not available in this runtime. SDK 3.0 requires Node ≥20 or modern browser/Edge.',\n );\n}\n\n/** Regex UUID v4 — validação para uso interno. */\nexport const UUID_V4_RE =\n /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n","/**\r\n * HTTP transport interno do SDK.\r\n *\r\n * Responsabilidades:\r\n * - Construir URL absoluta a partir de `baseUrl` + path\r\n * - Injetar Bearer token quando `requireAuth=true`\r\n * - Mapear status HTTP → `NeetruError` com `code` estável\r\n * - Parse defensivo de JSON (não lança em body vazio em 204)\r\n * - Retry/backoff exponencial em `rate_limited` (429), `server_error` (5xx)\r\n * e `network_error` (timeout/falha de rede). Honra `Retry-After` quando\r\n * presente.\r\n * - **Captura `X-Neetru-Version`** das respostas e emite `console.warn` quando\r\n * o Core está abaixo da versão mínima para a major do SDK.\r\n *\r\n * Política de retries (mudança breaking 3.0):\r\n * - GET/HEAD: default 2 retries (3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: default **0 retries** — mutations podem duplicar\r\n * side effects (ex: `usage.report` incrementa contador). Caller pode\r\n * reativar passando `retries: N` explicitamente ou usar `Idempotency-Key`\r\n * header (gerado automaticamente se `opts.idempotencyKey=true`).\r\n *\r\n * Idempotency-Key (3.0):\r\n * - `opts.idempotencyKey === true` → SDK gera UUID v4 e injeta header.\r\n * - `opts.idempotencyKey === 'uuid-string'` → usa o valor passado.\r\n * - Caller mantém retentos client-side se quiser; Core dedup serverside\r\n * (vide `docs/CORE_CHANGES_REQUIRED.md` §2).\r\n */\r\nimport { NeetruError, type NeetruErrorCode } from './errors';\r\nimport { generateIdempotencyKey } from './idempotency';\r\nimport type { ResolvedConfig } from './types';\r\n\r\n// ─── Compat Matrix SDK↔Core ──────────────────────────────────────────────────\r\n\r\n/**\r\n * Matriz de compatibilidade SDK major → versão mínima do Core.\r\n *\r\n * Formato do Core: CalVer `YYYY.MM.patch+revision` (ex: `2026.05.1+neetru-core-00096-79t`).\r\n * A comparação é feita apenas nas partes `YYYY.MM.patch` (prefixo antes de `+`).\r\n *\r\n * Quando o Core reporta `X-Neetru-Version` abaixo do mínimo para a SDK major\r\n * corrente, o SDK emite `console.warn` uma única vez por sessão de processo.\r\n *\r\n * Atualize esta tabela a cada major de SDK.\r\n */\r\nconst COMPAT_MATRIX: Record<number, string> = {\r\n // SDK major 3.x requer Core ≥ 2026.05.1 (suporte a X-Neetru-Version header,\r\n // runTransaction write-after-read, verifyWebhookRequest, webhooks 3.0+)\r\n 3: '2026.05.1',\r\n};\r\n\r\n/** CalVer sem sufixo `+revision`. Ex: `2026.05.1+abc` → `2026.05.1`. */\r\nfunction stripRevision(v: string): string {\r\n return v.split('+')[0] ?? v;\r\n}\r\n\r\n/**\r\n * Compara dois CalVer (`YYYY.MM.patch`). Retorna negativo se `a < b`,\r\n * positivo se `a > b`, 0 se iguais. Partes não numéricas tratadas como 0.\r\n */\r\nfunction compareCalVer(a: string, b: string): number {\r\n const partsA = stripRevision(a).split('.').map(Number);\r\n const partsB = stripRevision(b).split('.').map(Number);\r\n const len = Math.max(partsA.length, partsB.length);\r\n for (let i = 0; i < len; i++) {\r\n const pa = partsA[i] ?? 0;\r\n const pb = partsB[i] ?? 0;\r\n if (pa !== pb) return pa - pb;\r\n }\r\n return 0;\r\n}\r\n\r\n/** Lê a SDK major a partir da string `VERSION` exportada de `./index`. Fallback 3. */\r\nfunction getSdkMajor(): number {\r\n try {\r\n // Importação estática não disponível aqui (evita ciclo). Usa variável injetada\r\n // em tempo de build pela tsup via `define`. Fallback para 3 (current major).\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const ver = (globalThis as any).__NEETRU_SDK_VERSION__ as string | undefined;\r\n if (ver) return parseInt(ver.split('.')[0] ?? '3', 10);\r\n } catch {\r\n // ignore\r\n }\r\n return 3;\r\n}\r\n\r\n/** Aviso emitido no máximo uma vez por major detectada pra não spammar logs. */\r\nconst _warnedCoreTooOld = new Set<string>();\r\n\r\n/**\r\n * Verifica se a versão do Core (`X-Neetru-Version`) é compatível com a SDK major.\r\n * Emite `console.warn` quando incompatível (uma vez por processo por versão detectada).\r\n */\r\nexport function checkCoreVersionCompat(coreVersion: string): void {\r\n if (!coreVersion) return;\r\n const sdkMajor = getSdkMajor();\r\n const minCore = COMPAT_MATRIX[sdkMajor];\r\n if (!minCore) return; // sem restrição para esta major\r\n const stripped = stripRevision(coreVersion);\r\n if (compareCalVer(stripped, minCore) < 0) {\r\n const key = `${sdkMajor}:${stripped}`;\r\n if (!_warnedCoreTooOld.has(key)) {\r\n _warnedCoreTooOld.add(key);\r\n console.warn(\r\n `[Neetru SDK] Atenção: Core versão \"${coreVersion}\" é anterior ao mínimo recomendado` +\r\n ` \"${minCore}\" para SDK major ${sdkMajor}. Alguns recursos podem não funcionar` +\r\n ` corretamente. Atualize o Core para a versão mais recente.`,\r\n );\r\n }\r\n }\r\n}\r\n\r\n/** Opções da request HTTP. */\r\nexport interface HttpRequestOptions {\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n /** Path relativo (ex: `/api/v1/cli/catalog`). Concatenado a `baseUrl`. */\r\n path: string;\r\n /** Query string params (chave → valor primitivo). Valores `undefined` ignorados. */\r\n query?: Record<string, string | number | boolean | undefined>;\r\n /** Body JSON-serializável. Ignorado em GET/DELETE. */\r\n body?: unknown;\r\n /**\r\n * Se true, injeta `Authorization: Bearer <apiKey>`. Lança `missing_api_key`\r\n * se config.apiKey ausente. Default false.\r\n */\r\n requireAuth?: boolean;\r\n /** Cabeçalhos extras. */\r\n headers?: Record<string, string>;\r\n /**\r\n * Número de retries em códigos transientes (`rate_limited`, `server_error`,\r\n * `network_error`).\r\n *\r\n * Default (3.0):\r\n * - GET/HEAD: 2 (= 3 tentativas total) — idempotente, seguro.\r\n * - POST/PUT/PATCH/DELETE: 0 — mutations podem duplicar side effects.\r\n *\r\n * Caller pode reativar passando explicitamente: `retries: 2`.\r\n */\r\n retries?: number;\r\n /**\r\n * Injeta `Idempotency-Key` header pra dedup serverside (3.0):\r\n * - `true` → SDK gera UUID v4 cripto-seguro automaticamente.\r\n * - string → usa o valor passado (deve ser UUID v4).\r\n * - `undefined`/`false` → sem header.\r\n */\r\n idempotencyKey?: boolean | string;\r\n}\r\n\r\nconst DEFAULT_RETRIES_READ = 2;\r\nconst DEFAULT_RETRIES_WRITE = 0;\r\nconst READ_METHODS = new Set(['GET', 'HEAD']);\r\nconst RETRYABLE_CODES = new Set<NeetruErrorCode>([\r\n 'rate_limited',\r\n 'server_error',\r\n 'network_error',\r\n]);\r\n\r\n/** Backoff exponencial com jitter ±20%. attempt: 0=primeira, 1=primeiro retry, ... */\r\nfunction backoffMs(attempt: number): number {\r\n const base = 200 * Math.pow(4, attempt); // 200ms, 800ms, 3.2s, ...\r\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\r\n return Math.max(50, Math.round(base + jitter));\r\n}\r\n\r\n/**\r\n * Honra `Retry-After` header (segundos ou HTTP-date). Retorna ms ou null se\r\n * inválido. RFC 9110 §10.2.3.\r\n *\r\n * Bug fix: sem teto, um servidor hostil/mal-configurado poderia retornar\r\n * `Retry-After: 86400` (24h) ou uma HTTP-date anos no futuro, pendurando o\r\n * processo indefinidamente. Aplica teto de MAX_RETRY_AFTER_MS em ambos os caminhos.\r\n */\r\nconst MAX_RETRY_AFTER_MS = 60_000; // 60s — teto razoável para retry automático\r\n\r\nfunction parseRetryAfter(value: string | null): number | null {\r\n if (!value) return null;\r\n const secs = Number(value);\r\n if (Number.isFinite(secs) && secs >= 0) return Math.min(Math.round(secs * 1000), MAX_RETRY_AFTER_MS);\r\n const dateMs = Date.parse(value);\r\n if (Number.isFinite(dateMs)) {\r\n const delta = dateMs - Date.now();\r\n if (delta > 0) return Math.min(delta, MAX_RETRY_AFTER_MS);\r\n }\r\n return null;\r\n}\r\n\r\nfunction sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n}\r\n\r\n/** Mapeamento status → code estável do NeetruError. */\r\nfunction statusToCode(status: number): NeetruErrorCode {\r\n if (status === 401) return 'unauthorized';\r\n if (status === 403) return 'forbidden';\r\n if (status === 404) return 'not_found';\r\n if (status === 409) return 'conflict'; // Bug fix: 409 vira 'conflict' (não 'unknown')\r\n if (status === 422 || status === 400) return 'validation_failed';\r\n if (status === 429) return 'rate_limited';\r\n if (status >= 500) return 'server_error';\r\n return 'unknown';\r\n}\r\n\r\nfunction buildUrl(baseUrl: string, path: string, query?: HttpRequestOptions['query']): string {\r\n // Trim trailing slash em base e leading em path pra evitar `//`.\r\n const base = baseUrl.replace(/\\/+$/, '');\r\n const p = path.startsWith('/') ? path : `/${path}`;\r\n const url = new URL(`${base}${p}`);\r\n if (query) {\r\n for (const [k, v] of Object.entries(query)) {\r\n if (v === undefined) continue;\r\n url.searchParams.set(k, String(v));\r\n }\r\n }\r\n return url.toString();\r\n}\r\n\r\n/** Parse JSON defensivo — retorna `undefined` em body vazio. */\r\nasync function safeJson(res: Response): Promise<unknown> {\r\n const text = await res.text();\r\n if (!text) return undefined;\r\n try {\r\n return JSON.parse(text);\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\n/**\r\n * Executa request HTTP. Em sucesso retorna body parseado; em erro lança\r\n * `NeetruError` com `code` derivado do status. Aplica retry/backoff\r\n * automático em códigos transientes (rate_limited/server_error/network_error)\r\n * conforme `opts.retries` (default 2 = 3 tentativas).\r\n */\r\nexport async function httpRequest<T>(\r\n config: ResolvedConfig,\r\n opts: HttpRequestOptions,\r\n): Promise<T> {\r\n const method = opts.method ?? 'GET';\r\n const url = buildUrl(config.baseUrl, opts.path, opts.query);\r\n // Default por método: reads tentam 2x, writes 0x. Caller override sempre vence.\r\n const defaultRetries = READ_METHODS.has(method)\r\n ? DEFAULT_RETRIES_READ\r\n : DEFAULT_RETRIES_WRITE;\r\n const maxRetries = opts.retries ?? defaultRetries;\r\n\r\n const headers: Record<string, string> = {\r\n accept: 'application/json',\r\n ...opts.headers,\r\n };\r\n\r\n if (opts.requireAuth) {\r\n if (!config.apiKey) {\r\n throw new NeetruError(\r\n 'missing_api_key',\r\n 'This operation requires an apiKey. Pass it to createNeetruClient({ apiKey }) or set NEETRU_API_KEY env var.',\r\n );\r\n }\r\n headers.authorization = `Bearer ${config.apiKey}`;\r\n }\r\n\r\n // Idempotency-Key: gerado por chamada se solicitado. UUID v4 cripto-seguro.\r\n if (opts.idempotencyKey) {\r\n const key =\r\n typeof opts.idempotencyKey === 'string'\r\n ? opts.idempotencyKey\r\n : generateIdempotencyKey();\r\n headers['idempotency-key'] = key;\r\n }\r\n\r\n // Body só é serializado uma vez — reusado entre tentativas.\r\n const bodyString =\r\n opts.body !== undefined && method !== 'GET' && method !== 'DELETE'\r\n ? JSON.stringify(opts.body)\r\n : undefined;\r\n if (bodyString !== undefined) {\r\n headers['content-type'] = 'application/json';\r\n }\r\n\r\n let lastError: NeetruError | null = null;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n const init: RequestInit = { method, headers };\r\n if (bodyString !== undefined) init.body = bodyString;\r\n // BUG-020 fix (2026-05-13): timeout default 30s. AbortSignal.timeout\r\n // requer Node 18+ ou browsers recentes.\r\n init.signal = AbortSignal.timeout(30_000);\r\n\r\n let res: Response;\r\n try {\r\n res = await config.fetch(url, init);\r\n } catch (err) {\r\n const message =\r\n err instanceof DOMException && err.name === 'TimeoutError'\r\n ? 'Network error: timeout after 30s'\r\n : `Network error: ${err instanceof Error ? err.message : 'fetch failed'}`;\r\n lastError = new NeetruError('network_error', message);\r\n if (attempt < maxRetries) {\r\n await sleep(backoffMs(attempt));\r\n continue;\r\n }\r\n throw lastError;\r\n }\r\n\r\n const requestId =\r\n res.headers.get('x-request-id') ?? res.headers.get('x-correlation-id') ?? undefined;\r\n\r\n if (res.ok) {\r\n // Captura X-Neetru-Version e verifica compat com a SDK major.\r\n const coreVersion = res.headers.get('x-neetru-version');\r\n if (coreVersion) checkCoreVersionCompat(coreVersion);\r\n const parsed = await safeJson(res);\r\n return parsed as T;\r\n }\r\n\r\n const body = (await safeJson(res)) as\r\n | { error?: { code?: string; message?: string } | string }\r\n | undefined;\r\n let code: string = statusToCode(res.status);\r\n let message = `HTTP ${res.status}`;\r\n if (body && typeof body === 'object' && 'error' in body) {\r\n const errField = body.error;\r\n if (typeof errField === 'string') {\r\n message = errField;\r\n } else if (errField && typeof errField === 'object') {\r\n if (typeof errField.code === 'string') code = errField.code;\r\n if (typeof errField.message === 'string') message = errField.message;\r\n }\r\n }\r\n const err = new NeetruError(code, message, res.status, requestId);\r\n lastError = err;\r\n\r\n const isRetryable = RETRYABLE_CODES.has(code as NeetruErrorCode);\r\n if (isRetryable && attempt < maxRetries) {\r\n const retryAfter = parseRetryAfter(res.headers.get('retry-after'));\r\n const delay = retryAfter ?? backoffMs(attempt);\r\n await sleep(delay);\r\n continue;\r\n }\r\n throw err;\r\n }\r\n\r\n // Unreachable — loop sempre retorna ou lança. Safety net.\r\n throw lastError ?? new NeetruError('unknown', 'unexpected httpRequest exit');\r\n}\r\n","/**\n * Validators centralizados pra inputs do SDK que precisam casar com\n * regex do Core.\n *\n * **M-03 fix (Opus review 2026-05-27):** o Core rejeita productId que\n * não bate `/^[a-z0-9][a-z0-9_-]{0,62}$/` com 400 `validation_failed`.\n * O SDK não pré-validava — consumer recebia erro só em runtime de\n * produção, depois do round-trip de rede. Agora SDK throws localmente\n * antes do fetch.\n */\nimport { NeetruError } from './errors';\n\n/**\n * Regex canonical de IDs no Neetru Core (productId, tenantId, etc).\n * Espelha `SDK_ID_RE` em `neetru_core/src/lib/schemas/sdk.ts`.\n */\nexport const SDK_ID_RE = /^[a-z0-9][a-z0-9_-]{0,62}$/;\n\n/**\n * Throws `NeetruError('validation_failed')` se `value` não bater no\n * formato canonical.\n *\n * @example\n * ```ts\n * assertValidSdkId('productId', 'gestovendas'); // ok\n * assertValidSdkId('productId', 'GestoVendas'); // throws\n * ```\n */\nexport function assertValidSdkId(field: string, value: string): void {\n if (typeof value !== 'string' || !SDK_ID_RE.test(value)) {\n throw new NeetruError(\n 'validation_failed',\n `${field} must match ${SDK_ID_RE.source} (lowercase alphanumeric, _, -; first char must be alphanumeric; max 63 chars)`,\n );\n }\n}\n","/**\n * Support namespace — criar e listar tickets via SDK (3.0).\n *\n * **Breaking 3.0**:\n * - Endpoint mudou de `/api/v1/products/{slug}/tickets` (que **NUNCA existiu\n * no Core** — vaporware testado) para `/api/sdk/v1/support/tickets`\n * (canonical SDK path).\n * - `productId` agora é obrigatório no body/query (default resolve do\n * `config.productId`; throw `validation_failed` se ambos ausentes).\n * - `createTicket()` envia `Idempotency-Key` header (anti dup-create\n * em retry transient — Core dedup TTL 24h conforme CORE_CHANGES_REQUIRED §1).\n *\n * **Pré-requisito Core**: endpoint `/api/sdk/v1/support/tickets` (POST + GET)\n * precisa existir no Core antes do SDK 3.0 publish. Vide\n * `docs/CORE_CHANGES_REQUIRED.md` §1 para contrato esperado.\n *\n * Em dev (mocks ativos via factory) tudo é in-memory.\n */\nimport { NeetruError } from './errors';\nimport { httpRequest } from './http';\nimport { assertValidSdkId } from './validators';\nimport type {\n CreateTicketInput,\n ResolvedConfig,\n SupportNamespace,\n SupportSeverity,\n SupportStatus,\n SupportTicket,\n} from './types';\n\nconst VALID_SEVERITIES: readonly SupportSeverity[] = ['low', 'normal', 'high', 'urgent'];\nconst VALID_STATUSES: readonly SupportStatus[] = ['open', 'pending', 'resolved', 'closed'];\n\ninterface RawTicket {\n id?: string;\n productId?: string;\n subject?: string;\n message?: string;\n severity?: string;\n status?: string;\n createdAt?: string;\n updatedAt?: string;\n productSlug?: string;\n}\n\nfunction toTicket(raw: unknown): SupportTicket {\n if (!raw || typeof raw !== 'object') {\n throw new NeetruError('invalid_response', 'Ticket response is not an object');\n }\n const r = raw as RawTicket;\n if (typeof r.id !== 'string') {\n throw new NeetruError('invalid_response', 'Ticket missing id');\n }\n return {\n id: r.id,\n subject: typeof r.subject === 'string' ? r.subject : '',\n message: typeof r.message === 'string' ? r.message : '',\n severity: VALID_SEVERITIES.includes(r.severity as SupportSeverity)\n ? (r.severity as SupportSeverity)\n : 'normal',\n status: VALID_STATUSES.includes(r.status as SupportStatus)\n ? (r.status as SupportStatus)\n : 'open',\n createdAt: typeof r.createdAt === 'string' ? r.createdAt : new Date().toISOString(),\n updatedAt: typeof r.updatedAt === 'string' ? r.updatedAt : undefined,\n productSlug:\n typeof r.productId === 'string'\n ? r.productId\n : typeof r.productSlug === 'string'\n ? r.productSlug\n : undefined,\n };\n}\n\nfunction resolveProductId(\n config: ResolvedConfig,\n ...candidates: Array<string | undefined>\n): string {\n for (const c of candidates) {\n if (typeof c === 'string' && c.length > 0) {\n assertValidSdkId('productSlug', c); // M-03: pré-valida client-side\n return c;\n }\n }\n if (config.productId) {\n assertValidSdkId('productId', config.productId);\n return config.productId;\n }\n throw new NeetruError(\n 'validation_failed',\n 'productId required (pass to input.productSlug or set on createNeetruClient)',\n );\n}\n\nexport function createSupportNamespace(config: ResolvedConfig): SupportNamespace {\n return {\n /**\n * Cria um ticket de suporte. Requer Bearer auth. `productSlug` no input\n * (ou `config.productId` default) vai como `productId` no body do Core.\n *\n * Endpoint canonical: `POST /api/sdk/v1/support/tickets`.\n */\n async createTicket(input: CreateTicketInput): Promise<SupportTicket> {\n if (!input || typeof input !== 'object') {\n throw new NeetruError('validation_failed', 'input is required');\n }\n if (!input.subject || typeof input.subject !== 'string') {\n throw new NeetruError('validation_failed', 'subject is required');\n }\n if (input.subject.length > 200) {\n throw new NeetruError('validation_failed', 'subject max 200 chars');\n }\n if (!input.message || typeof input.message !== 'string') {\n throw new NeetruError('validation_failed', 'message is required');\n }\n if (input.message.length > 10_000) {\n throw new NeetruError('validation_failed', 'message max 10000 chars');\n }\n if (input.severity && !VALID_SEVERITIES.includes(input.severity)) {\n throw new NeetruError(\n 'validation_failed',\n `severity must be one of ${VALID_SEVERITIES.join(', ')}`,\n );\n }\n\n const productId = resolveProductId(config, input.productSlug);\n // M-03: validação acontece dentro de resolveProductId; aqui só usa.\n\n const body: Record<string, unknown> = {\n productId,\n subject: input.subject,\n message: input.message,\n severity: input.severity ?? 'normal',\n };\n\n // L-05 fix: propaga metadata quando consumer enviar.\n if (input.metadata && typeof input.metadata === 'object') {\n body.metadata = input.metadata;\n }\n\n const raw = await httpRequest<RawTicket | { ticket?: RawTicket }>(config, {\n method: 'POST',\n path: '/api/sdk/v1/support/tickets',\n body,\n requireAuth: true,\n idempotencyKey: true,\n });\n\n // Backend pode envelopar como { ticket: {...} } ou retornar direto.\n const candidate =\n raw && typeof raw === 'object' && 'ticket' in raw\n ? (raw as { ticket?: RawTicket }).ticket\n : raw;\n return toTicket(candidate);\n },\n\n /**\n * Lista tickets do customer no produto atual. `productSlug` no contexto.\n *\n * Endpoint canonical: `GET /api/sdk/v1/support/tickets?productId=...`.\n */\n async listMyTickets(options?: { productSlug?: string }): Promise<SupportTicket[]> {\n const productId = resolveProductId(config, options?.productSlug);\n const raw = await httpRequest<{ tickets?: unknown[] } | unknown[]>(config, {\n method: 'GET',\n path: '/api/sdk/v1/support/tickets',\n query: { productId },\n requireAuth: true,\n });\n const list = Array.isArray(raw)\n ? raw\n : raw && typeof raw === 'object' && 'tickets' in raw\n ? ((raw as { tickets?: unknown[] }).tickets ?? [])\n : [];\n return list.map(toTicket);\n },\n };\n}\n"]}
|
package/dist/support.d.cts
CHANGED
package/dist/support.d.ts
CHANGED