@neetru/sdk 3.1.7 → 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.d.cts +1 -1
- package/dist/catalog.d.ts +1 -1
- package/dist/checkout.d.cts +1 -1
- package/dist/checkout.d.ts +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.d.cts +1 -1
- package/dist/entitlements.d.ts +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.d.cts +1 -1
- package/dist/mocks.d.ts +1 -1
- package/dist/notifications.d.cts +1 -1
- package/dist/notifications.d.ts +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.d.cts +1 -1
- package/dist/support.d.ts +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.d.cts +1 -1
- package/dist/webhooks.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db/field-value.ts","../src/firestore-compat.ts"],"names":["serverTimestamp","increment"],"mappings":"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","file":"firestore-compat.mjs","sourcesContent":["/**\r\n * field-value.ts — Sentinels de escrita server-side para o Mundo Documentos.\r\n *\r\n * Resolve duas lacunas confirmadas (bug_05aedb8b) da superfície `client.db`:\r\n *\r\n * 1. **Timestamp resolvido no servidor** — antes deste módulo, o produto que\r\n * quisesse `createdAt`/`updatedAt` confiáveis tinha que usar `Date.now()`\r\n * no CLIENTE, que é spoofável e sofre clock-skew. Agora:\r\n *\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n *\r\n * O marcador atravessa o transporte REST como um objeto JSON namespaceado\r\n * e o Core o substitui por `FieldValue.serverTimestamp()` no momento da\r\n * escrita — relógio do SERVIDOR, não do cliente.\r\n *\r\n * 2. **Increment atômico** — antes, `update(id, { n: x+1 })` era read-modify-write\r\n * no cliente, sujeito a lost-update sob concorrência. Agora:\r\n *\r\n * await counters.update('global', { hits: increment(1) });\r\n *\r\n * O Core aplica `FieldValue.increment(1)` atomicamente no documento.\r\n *\r\n * ## Contrato de fio (wire format)\r\n *\r\n * Os sentinels são objetos JSON simples — sobrevivem `JSON.stringify` no\r\n * transporte REST e ao `IndexedDB structured clone` da camada offline:\r\n *\r\n * serverTimestamp() → `{ \"__neetru__\": \"serverTimestamp\" }`\r\n * increment(n) → `{ \"__neetru__\": \"increment\", \"operand\": n }`\r\n *\r\n * A chave-âncora `__neetru__` é deliberadamente improvável de colidir com\r\n * dados de produto. O Core valida e resolve esses marcadores; qualquer objeto\r\n * com `__neetru__` desconhecido é rejeitado server-side (fail-closed).\r\n *\r\n * ## Aplicação otimista (offline-first)\r\n *\r\n * A camada offline aplica uma APROXIMAÇÃO local imediata (para a UI não piscar):\r\n * - `serverTimestamp()` → `Date.now()` local (substituído pelo valor real no sync).\r\n * - `increment(n)` → soma `n` ao valor numérico atual do campo (0 se ausente).\r\n * O valor canônico vem sempre do servidor no pull/confirm subsequente.\r\n *\r\n * @module\r\n */\r\n\r\n/** Chave-âncora dos marcadores de sentinel no wire format. */\r\nexport const NEETRU_SENTINEL_KEY = '__neetru__' as const;\r\n\r\n/** Marcador de timestamp resolvido pelo servidor. */\r\nexport interface ServerTimestampSentinel {\r\n readonly __neetru__: 'serverTimestamp';\r\n}\r\n\r\n/** Marcador de incremento atômico aplicado pelo servidor. */\r\nexport interface IncrementSentinel {\r\n readonly __neetru__: 'increment';\r\n /** Delta a somar (pode ser negativo para decremento). */\r\n readonly operand: number;\r\n}\r\n\r\n/**\r\n * União de todos os sentinels de campo. Use `serverTimestamp()` / `increment()`\r\n * para construir — nunca monte o objeto à mão.\r\n */\r\nexport type FieldSentinel = ServerTimestampSentinel | IncrementSentinel;\r\n\r\n/**\r\n * Sentinel que instrui o servidor a gravar o timestamp do MOMENTO da escrita\r\n * usando o relógio do servidor (não do cliente).\r\n *\r\n * @example\r\n * ```ts\r\n * import { serverTimestamp } from '@neetru/sdk/db';\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n * ```\r\n */\r\nexport function serverTimestamp(): ServerTimestampSentinel {\r\n return { __neetru__: 'serverTimestamp' };\r\n}\r\n\r\n/**\r\n * Sentinel que instrui o servidor a incrementar atomicamente o campo numérico\r\n * por `n` (negativo decrementa). Se o campo não existir/não for numérico, o\r\n * servidor o trata como `0` antes de somar (semântica `FieldValue.increment`).\r\n *\r\n * @param n — delta inteiro ou fracionário a somar.\r\n * @example\r\n * ```ts\r\n * import { increment } from '@neetru/sdk/db';\r\n * await counters.update('global', { hits: increment(1) });\r\n * await wallet.update(uid, { balance: increment(-50) });\r\n * ```\r\n */\r\nexport function increment(n: number): IncrementSentinel {\r\n if (typeof n !== 'number' || !Number.isFinite(n)) {\r\n throw new TypeError(\r\n `increment(n): n deve ser um número finito (recebido: ${String(n)}).`,\r\n );\r\n }\r\n return { __neetru__: 'increment', operand: n };\r\n}\r\n\r\n// ─── Type guards ────────────────────────────────────────────────────────────\r\n\r\n/** `true` se `v` é qualquer sentinel de campo Neetru. */\r\nexport function isFieldSentinel(v: unknown): v is FieldSentinel {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n NEETRU_SENTINEL_KEY in v &&\r\n typeof (v as Record<string, unknown>)[NEETRU_SENTINEL_KEY] === 'string'\r\n );\r\n}\r\n\r\n/** `true` se `v` é o sentinel `serverTimestamp()`. */\r\nexport function isServerTimestampSentinel(v: unknown): v is ServerTimestampSentinel {\r\n return isFieldSentinel(v) && v.__neetru__ === 'serverTimestamp';\r\n}\r\n\r\n/** `true` se `v` é o sentinel `increment(n)` com `operand` numérico válido. */\r\nexport function isIncrementSentinel(v: unknown): v is IncrementSentinel {\r\n return (\r\n isFieldSentinel(v) &&\r\n v.__neetru__ === 'increment' &&\r\n typeof (v as IncrementSentinel).operand === 'number' &&\r\n Number.isFinite((v as IncrementSentinel).operand)\r\n );\r\n}\r\n\r\n// ─── Aplicação otimista local (offline-first) ────────────────────────────────\r\n\r\n/**\r\n * Resolve sentinels para valores LOCAIS aproximados, para aplicação otimista\r\n * no cache offline. O valor canônico vem sempre do servidor no sync seguinte.\r\n *\r\n * - `serverTimestamp()` → `Date.now()` (epoch ms local).\r\n * - `increment(n)` → `baseValue` numérico atual (ou 0) + n.\r\n * - valores normais → retornados inalterados.\r\n *\r\n * @param value — o valor do campo (pode ser sentinel ou valor literal).\r\n * @param baseValue — valor atual do campo no doc existente (para increment).\r\n */\r\nexport function resolveSentinelLocally(value: unknown, baseValue: unknown): unknown {\r\n if (isServerTimestampSentinel(value)) {\r\n return Date.now();\r\n }\r\n if (isIncrementSentinel(value)) {\r\n const base = typeof baseValue === 'number' && Number.isFinite(baseValue) ? baseValue : 0;\r\n return base + value.operand;\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Aplica `resolveSentinelLocally` a cada campo top-level de `data`, usando\r\n * `existing` como base para increments. Não recursa em objetos aninhados\r\n * (sentinels só são suportados em campos top-level, espelhando a resolução\r\n * top-level do Core).\r\n *\r\n * @param data — payload da mutação (pode conter sentinels).\r\n * @param existing — dados atuais do documento (para base de increment).\r\n */\r\nexport function resolveSentinelsLocally(\r\n data: Record<string, unknown>,\r\n existing: Record<string, unknown> | undefined,\r\n): Record<string, unknown> {\r\n let touched = false;\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(data)) {\r\n if (isFieldSentinel(v)) {\r\n touched = true;\r\n out[k] = resolveSentinelLocally(v, existing?.[k]);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n // Micro-opt: se nenhum sentinel, devolve o objeto original (evita realocar).\r\n return touched ? out : data;\r\n}\r\n\r\n/** `true` se algum campo top-level de `data` é um sentinel. */\r\nexport function hasFieldSentinel(data: Record<string, unknown>): boolean {\r\n for (const v of Object.values(data)) {\r\n if (isFieldSentinel(v)) return true;\r\n }\r\n return false;\r\n}\r\n","/**\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/db/field-value.ts","../src/firestore-compat.ts"],"names":["serverTimestamp","increment"],"mappings":"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","file":"firestore-compat.mjs","sourcesContent":["/**\r\n * field-value.ts — Sentinels de escrita server-side para o Mundo Documentos.\r\n *\r\n * Resolve duas lacunas confirmadas (bug_05aedb8b) da superfície `client.db`:\r\n *\r\n * 1. **Timestamp resolvido no servidor** — antes deste módulo, o produto que\r\n * quisesse `createdAt`/`updatedAt` confiáveis tinha que usar `Date.now()`\r\n * no CLIENTE, que é spoofável e sofre clock-skew. Agora:\r\n *\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n *\r\n * O marcador atravessa o transporte REST como um objeto JSON namespaceado\r\n * e o Core o substitui por `FieldValue.serverTimestamp()` no momento da\r\n * escrita — relógio do SERVIDOR, não do cliente.\r\n *\r\n * 2. **Increment atômico** — antes, `update(id, { n: x+1 })` era read-modify-write\r\n * no cliente, sujeito a lost-update sob concorrência. Agora:\r\n *\r\n * await counters.update('global', { hits: increment(1) });\r\n *\r\n * O Core aplica `FieldValue.increment(1)` atomicamente no documento.\r\n *\r\n * ## Contrato de fio (wire format)\r\n *\r\n * Os sentinels são objetos JSON simples — sobrevivem `JSON.stringify` no\r\n * transporte REST e ao `IndexedDB structured clone` da camada offline:\r\n *\r\n * serverTimestamp() → `{ \"__neetru__\": \"serverTimestamp\" }`\r\n * increment(n) → `{ \"__neetru__\": \"increment\", \"operand\": n }`\r\n *\r\n * A chave-âncora `__neetru__` é deliberadamente improvável de colidir com\r\n * dados de produto. O Core valida e resolve esses marcadores; qualquer objeto\r\n * com `__neetru__` desconhecido é rejeitado server-side (fail-closed).\r\n *\r\n * ## Aplicação otimista (offline-first)\r\n *\r\n * A camada offline aplica uma APROXIMAÇÃO local imediata (para a UI não piscar):\r\n * - `serverTimestamp()` → `Date.now()` local (substituído pelo valor real no sync).\r\n * - `increment(n)` → soma `n` ao valor numérico atual do campo (0 se ausente).\r\n * O valor canônico vem sempre do servidor no pull/confirm subsequente.\r\n *\r\n * @module\r\n */\r\n\r\n/** Chave-âncora dos marcadores de sentinel no wire format. */\r\nexport const NEETRU_SENTINEL_KEY = '__neetru__' as const;\r\n\r\n/** Marcador de timestamp resolvido pelo servidor. */\r\nexport interface ServerTimestampSentinel {\r\n readonly __neetru__: 'serverTimestamp';\r\n}\r\n\r\n/** Marcador de incremento atômico aplicado pelo servidor. */\r\nexport interface IncrementSentinel {\r\n readonly __neetru__: 'increment';\r\n /** Delta a somar (pode ser negativo para decremento). */\r\n readonly operand: number;\r\n}\r\n\r\n/**\r\n * União de todos os sentinels de campo. Use `serverTimestamp()` / `increment()`\r\n * para construir — nunca monte o objeto à mão.\r\n */\r\nexport type FieldSentinel = ServerTimestampSentinel | IncrementSentinel;\r\n\r\n/**\r\n * Sentinel que instrui o servidor a gravar o timestamp do MOMENTO da escrita\r\n * usando o relógio do servidor (não do cliente).\r\n *\r\n * @example\r\n * ```ts\r\n * import { serverTimestamp } from '@neetru/sdk/db';\r\n * await orders.add({ total: 99, createdAt: serverTimestamp() });\r\n * ```\r\n */\r\nexport function serverTimestamp(): ServerTimestampSentinel {\r\n return { __neetru__: 'serverTimestamp' };\r\n}\r\n\r\n/**\r\n * Sentinel que instrui o servidor a incrementar atomicamente o campo numérico\r\n * por `n` (negativo decrementa). Se o campo não existir/não for numérico, o\r\n * servidor o trata como `0` antes de somar (semântica `FieldValue.increment`).\r\n *\r\n * @param n — delta inteiro ou fracionário a somar.\r\n * @example\r\n * ```ts\r\n * import { increment } from '@neetru/sdk/db';\r\n * await counters.update('global', { hits: increment(1) });\r\n * await wallet.update(uid, { balance: increment(-50) });\r\n * ```\r\n */\r\nexport function increment(n: number): IncrementSentinel {\r\n if (typeof n !== 'number' || !Number.isFinite(n)) {\r\n throw new TypeError(\r\n `increment(n): n deve ser um número finito (recebido: ${String(n)}).`,\r\n );\r\n }\r\n return { __neetru__: 'increment', operand: n };\r\n}\r\n\r\n// ─── Type guards ────────────────────────────────────────────────────────────\r\n\r\n/** `true` se `v` é qualquer sentinel de campo Neetru. */\r\nexport function isFieldSentinel(v: unknown): v is FieldSentinel {\r\n return (\r\n typeof v === 'object' &&\r\n v !== null &&\r\n NEETRU_SENTINEL_KEY in v &&\r\n typeof (v as Record<string, unknown>)[NEETRU_SENTINEL_KEY] === 'string'\r\n );\r\n}\r\n\r\n/** `true` se `v` é o sentinel `serverTimestamp()`. */\r\nexport function isServerTimestampSentinel(v: unknown): v is ServerTimestampSentinel {\r\n return isFieldSentinel(v) && v.__neetru__ === 'serverTimestamp';\r\n}\r\n\r\n/** `true` se `v` é o sentinel `increment(n)` com `operand` numérico válido. */\r\nexport function isIncrementSentinel(v: unknown): v is IncrementSentinel {\r\n return (\r\n isFieldSentinel(v) &&\r\n v.__neetru__ === 'increment' &&\r\n typeof (v as IncrementSentinel).operand === 'number' &&\r\n Number.isFinite((v as IncrementSentinel).operand)\r\n );\r\n}\r\n\r\n// ─── Aplicação otimista local (offline-first) ────────────────────────────────\r\n\r\n/**\r\n * Resolve sentinels para valores LOCAIS aproximados, para aplicação otimista\r\n * no cache offline. O valor canônico vem sempre do servidor no sync seguinte.\r\n *\r\n * - `serverTimestamp()` → `Date.now()` (epoch ms local).\r\n * - `increment(n)` → `baseValue` numérico atual (ou 0) + n.\r\n * - valores normais → retornados inalterados.\r\n *\r\n * @param value — o valor do campo (pode ser sentinel ou valor literal).\r\n * @param baseValue — valor atual do campo no doc existente (para increment).\r\n */\r\nexport function resolveSentinelLocally(value: unknown, baseValue: unknown): unknown {\r\n if (isServerTimestampSentinel(value)) {\r\n return Date.now();\r\n }\r\n if (isIncrementSentinel(value)) {\r\n const base = typeof baseValue === 'number' && Number.isFinite(baseValue) ? baseValue : 0;\r\n return base + value.operand;\r\n }\r\n return value;\r\n}\r\n\r\n/**\r\n * Aplica `resolveSentinelLocally` a cada campo top-level de `data`, usando\r\n * `existing` como base para increments. Não recursa em objetos aninhados\r\n * (sentinels só são suportados em campos top-level, espelhando a resolução\r\n * top-level do Core).\r\n *\r\n * @param data — payload da mutação (pode conter sentinels).\r\n * @param existing — dados atuais do documento (para base de increment).\r\n */\r\nexport function resolveSentinelsLocally(\r\n data: Record<string, unknown>,\r\n existing: Record<string, unknown> | undefined,\r\n): Record<string, unknown> {\r\n let touched = false;\r\n const out: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(data)) {\r\n if (isFieldSentinel(v)) {\r\n touched = true;\r\n out[k] = resolveSentinelLocally(v, existing?.[k]);\r\n } else {\r\n out[k] = v;\r\n }\r\n }\r\n // Micro-opt: se nenhum sentinel, devolve o objeto original (evita realocar).\r\n return touched ? out : data;\r\n}\r\n\r\n/** `true` se algum campo top-level de `data` é um sentinel. */\r\nexport function hasFieldSentinel(data: Record<string, unknown>): boolean {\r\n for (const v of Object.values(data)) {\r\n if (isFieldSentinel(v)) return true;\r\n }\r\n return false;\r\n}\r\n","/**\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"]}
|
package/dist/index.cjs
CHANGED
|
@@ -448,7 +448,7 @@ __export(sql_client_exports, {
|
|
|
448
448
|
function mapEnvToCore(sdkEnv) {
|
|
449
449
|
if (sdkEnv === "workspace") return "staging";
|
|
450
450
|
if (sdkEnv === "prod") return "production";
|
|
451
|
-
return "dev";
|
|
451
|
+
return "dev-local";
|
|
452
452
|
}
|
|
453
453
|
function createHttpLeaseFetcher(config) {
|
|
454
454
|
return async (opts) => {
|
|
@@ -957,7 +957,11 @@ function createTelemetryNamespace(config) {
|
|
|
957
957
|
path: "/api/sdk/v1/telemetry/event",
|
|
958
958
|
body,
|
|
959
959
|
requireAuth: true,
|
|
960
|
-
retries: 1
|
|
960
|
+
retries: 1,
|
|
961
|
+
// caça-bruxas: retry sem Idempotency-Key duplicava o evento se o
|
|
962
|
+
// servidor já tinha persistido antes da falha transiente. Mesmo
|
|
963
|
+
// padrão do usage.report (key estável por chamada, reusada no retry).
|
|
964
|
+
idempotencyKey: true
|
|
961
965
|
});
|
|
962
966
|
} catch (err) {
|
|
963
967
|
if (typeof console !== "undefined") {
|
|
@@ -1161,8 +1165,8 @@ function createUsageNamespace(config) {
|
|
|
1161
1165
|
if (!resource || typeof resource !== "string") {
|
|
1162
1166
|
throw new exports.NeetruError("validation_failed", "resource is required");
|
|
1163
1167
|
}
|
|
1164
|
-
if (!Number.isFinite(qty) || qty <= 0) {
|
|
1165
|
-
throw new exports.NeetruError("validation_failed", "qty must be positive integer");
|
|
1168
|
+
if (!Number.isFinite(qty) || qty <= 0 || !Number.isInteger(qty)) {
|
|
1169
|
+
throw new exports.NeetruError("validation_failed", "qty must be a positive integer");
|
|
1166
1170
|
}
|
|
1167
1171
|
const productId = options?.productId ?? config.productId;
|
|
1168
1172
|
const tenantId = options?.tenantId ?? config.tenantId;
|
|
@@ -1411,7 +1415,12 @@ function evaluateFilter(data, filter) {
|
|
|
1411
1415
|
if (!Array.isArray(value)) return true;
|
|
1412
1416
|
return !value.includes(fieldValue);
|
|
1413
1417
|
default:
|
|
1414
|
-
|
|
1418
|
+
if (op === "array-contains-any") {
|
|
1419
|
+
return Array.isArray(fieldValue) && Array.isArray(value) && value.some((v) => fieldValue.includes(v));
|
|
1420
|
+
}
|
|
1421
|
+
throw new Error(
|
|
1422
|
+
`QueryEngine: operador de filtro '${String(op)}' n\xE3o suportado em leitura offline/cache. Use um operador suportado (==, !=, <, <=, >, >=, array-contains, array-contains-any, in, not-in) ou fa\xE7a a query server-side.`
|
|
1423
|
+
);
|
|
1415
1424
|
}
|
|
1416
1425
|
}
|
|
1417
1426
|
function matchesAllFilters(data, filters) {
|
|
@@ -1941,7 +1950,7 @@ var MutationQueue = class {
|
|
|
1941
1950
|
* - `{ removed: true }` — a mutação existente deve ser deletada (add+remove)
|
|
1942
1951
|
* - `null` — não é possível coalescir
|
|
1943
1952
|
*/
|
|
1944
|
-
tryCoalesce(existing, newOp, newPayload, newBatchId) {
|
|
1953
|
+
tryCoalesce(existing, newOp, newPayload, newBatchId, newUpsert) {
|
|
1945
1954
|
if (existing.batchId !== newBatchId) return null;
|
|
1946
1955
|
if (existing.status === "inflight") return null;
|
|
1947
1956
|
const existingOp = existing.op;
|
|
@@ -1949,10 +1958,12 @@ var MutationQueue = class {
|
|
|
1949
1958
|
return { removed: true };
|
|
1950
1959
|
}
|
|
1951
1960
|
if (existingOp === "update" && newOp === "update") {
|
|
1961
|
+
const mergedUpsert = existing.upsert || newUpsert;
|
|
1952
1962
|
return {
|
|
1953
1963
|
coalesced: {
|
|
1954
1964
|
...existing,
|
|
1955
|
-
payload: { ...existing.payload ?? {}, ...newPayload ?? {} }
|
|
1965
|
+
payload: { ...existing.payload ?? {}, ...newPayload ?? {} },
|
|
1966
|
+
...mergedUpsert ? { upsert: true } : {}
|
|
1956
1967
|
}
|
|
1957
1968
|
};
|
|
1958
1969
|
}
|
|
@@ -2001,13 +2012,13 @@ var MutationQueue = class {
|
|
|
2001
2012
|
* Atômico: a persistência final é uma única operação putMutation.
|
|
2002
2013
|
*/
|
|
2003
2014
|
async enqueue(params) {
|
|
2004
|
-
const { collection, op, payload, baseVersion, batchId } = params;
|
|
2015
|
+
const { collection, op, payload, baseVersion, batchId, upsert } = params;
|
|
2005
2016
|
const docId = params.docId ?? (op === "add" ? generateUUIDv4() : "");
|
|
2006
2017
|
await this.syncSeq();
|
|
2007
2018
|
const existingMutations = await this.store.listMutations({ collection, docId });
|
|
2008
2019
|
const lastQueued = existingMutations.filter((m) => m.status === "queued").sort((a, b) => b.seq - a.seq)[0];
|
|
2009
2020
|
if (lastQueued !== void 0) {
|
|
2010
|
-
const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId);
|
|
2021
|
+
const coalesceResult = this.tryCoalesce(lastQueued, op, payload, batchId, upsert);
|
|
2011
2022
|
if (coalesceResult !== null) {
|
|
2012
2023
|
if ("removed" in coalesceResult) {
|
|
2013
2024
|
await this.store.deleteMutation(lastQueued.mutationId);
|
|
@@ -2045,7 +2056,9 @@ var MutationQueue = class {
|
|
|
2045
2056
|
attempts: 0,
|
|
2046
2057
|
lastError: null,
|
|
2047
2058
|
status: "queued",
|
|
2048
|
-
batchId
|
|
2059
|
+
batchId,
|
|
2060
|
+
// bug_0273e4dd: só persiste o flag quando verdadeiro (mantém o doc limpo).
|
|
2061
|
+
...upsert ? { upsert: true } : {}
|
|
2049
2062
|
};
|
|
2050
2063
|
await this.store.putMutation(mutation);
|
|
2051
2064
|
return mutation;
|
|
@@ -2385,10 +2398,15 @@ var SyncEngine = class {
|
|
|
2385
2398
|
await this._applyRejected(mut, res, busChanges);
|
|
2386
2399
|
}
|
|
2387
2400
|
}
|
|
2401
|
+
const processedIds = new Set(result.results.map((r) => r.mutationId));
|
|
2402
|
+
const unprocessed = mutations.filter((m) => !processedIds.has(m.mutationId));
|
|
2403
|
+
for (const mut of unprocessed) {
|
|
2404
|
+
await this._queue.markRetry(mut.mutationId, "partial_push: muta\xE7\xE3o n\xE3o processada no lote");
|
|
2405
|
+
}
|
|
2388
2406
|
if (busChanges.length > 0) {
|
|
2389
2407
|
this._bus.emit(busChanges);
|
|
2390
2408
|
}
|
|
2391
|
-
return
|
|
2409
|
+
return unprocessed.length === 0;
|
|
2392
2410
|
}
|
|
2393
2411
|
/** Atualiza o cache após confirmação de escrita (outcome=confirmed). */
|
|
2394
2412
|
async _applySyncedDoc(collection, docId, serverVersion, serverTimestamp2, mut, busChanges) {
|
|
@@ -3695,7 +3713,7 @@ var DbCollectionRefImpl = class {
|
|
|
3695
3713
|
}]);
|
|
3696
3714
|
return { ok: true };
|
|
3697
3715
|
}
|
|
3698
|
-
async update(id, data) {
|
|
3716
|
+
async update(id, data, opts) {
|
|
3699
3717
|
assertValidId(id);
|
|
3700
3718
|
const existing = await this._store.getDoc(this._collection, id);
|
|
3701
3719
|
const rawData = data;
|
|
@@ -3708,7 +3726,9 @@ var DbCollectionRefImpl = class {
|
|
|
3708
3726
|
op: "update",
|
|
3709
3727
|
payload: rawData,
|
|
3710
3728
|
baseVersion: existing?.meta.serverVersion ?? null,
|
|
3711
|
-
batchId: null
|
|
3729
|
+
batchId: null,
|
|
3730
|
+
// bug_0273e4dd: propaga merge-upsert (setDoc({merge:true}) cria-se-ausente).
|
|
3731
|
+
upsert: opts?.upsert
|
|
3712
3732
|
});
|
|
3713
3733
|
await this._store.putDoc({
|
|
3714
3734
|
collection: this._collection,
|
|
@@ -3832,7 +3852,9 @@ var DbCollectionRefImpl = class {
|
|
|
3832
3852
|
op: "update",
|
|
3833
3853
|
payload: data,
|
|
3834
3854
|
baseVersion: existing?.meta.serverVersion ?? null,
|
|
3835
|
-
batchId
|
|
3855
|
+
batchId,
|
|
3856
|
+
// bug_0273e4dd: propaga merge-upsert (writeBatch.set(merge) cria-se-ausente).
|
|
3857
|
+
upsert: op.upsert
|
|
3836
3858
|
});
|
|
3837
3859
|
await this._store.putDoc({
|
|
3838
3860
|
collection,
|
|
@@ -4094,6 +4116,10 @@ async function createOfflineDocumentsNamespace(opts) {
|
|
|
4094
4116
|
connectivity,
|
|
4095
4117
|
periodicSyncIntervalMs: opts.periodicSyncIntervalMs ?? 5 * 60 * 1e3
|
|
4096
4118
|
});
|
|
4119
|
+
connectivity.start();
|
|
4120
|
+
if (!opts.singleTab && tabCoordinatorLike instanceof TabCoordinator) {
|
|
4121
|
+
tabCoordinatorLike.start();
|
|
4122
|
+
}
|
|
4097
4123
|
return new NeetruDbDocumentsImpl({
|
|
4098
4124
|
store,
|
|
4099
4125
|
queue,
|
|
@@ -4563,11 +4589,11 @@ function createRestSyncTransport(config) {
|
|
|
4563
4589
|
let raw;
|
|
4564
4590
|
if (m.op === "add") {
|
|
4565
4591
|
raw = await httpRequest2(config, {
|
|
4566
|
-
method: "
|
|
4567
|
-
path:
|
|
4592
|
+
method: "PUT",
|
|
4593
|
+
path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
|
|
4568
4594
|
body: { data: m.payload ?? {} },
|
|
4569
4595
|
requireAuth: true,
|
|
4570
|
-
retries:
|
|
4596
|
+
retries: 2,
|
|
4571
4597
|
headers: tenantHeaders
|
|
4572
4598
|
});
|
|
4573
4599
|
} else if (m.op === "set") {
|
|
@@ -4583,7 +4609,7 @@ function createRestSyncTransport(config) {
|
|
|
4583
4609
|
raw = await httpRequest2(config, {
|
|
4584
4610
|
method: "PATCH",
|
|
4585
4611
|
path: DATASTORE_DOC_ENDPOINT(m.collection, m.docId),
|
|
4586
|
-
body: { data: m.payload ?? {} },
|
|
4612
|
+
body: { data: m.payload ?? {}, ...m.upsert ? { upsert: true } : {} },
|
|
4587
4613
|
requireAuth: true,
|
|
4588
4614
|
retries: 2,
|
|
4589
4615
|
headers: tenantHeaders
|
|
@@ -4606,7 +4632,24 @@ function createRestSyncTransport(config) {
|
|
|
4606
4632
|
});
|
|
4607
4633
|
} catch (err) {
|
|
4608
4634
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4609
|
-
|
|
4635
|
+
const code = err?.code;
|
|
4636
|
+
const status = err?.status;
|
|
4637
|
+
const isPermanent = code === "forbidden" || code === "unauthorized" || code === "not_found" || code === "conflict" || code === "validation_failed" || typeof status === "number" && status >= 400 && status < 500 && status !== 408 && status !== 429;
|
|
4638
|
+
if (isPermanent) {
|
|
4639
|
+
const reason = code === "forbidden" || code === "unauthorized" ? "rejected_permission" : "rejected_validation";
|
|
4640
|
+
results.push({
|
|
4641
|
+
mutationId: m.mutationId,
|
|
4642
|
+
outcome: "rejected",
|
|
4643
|
+
reason,
|
|
4644
|
+
serverVersion: null,
|
|
4645
|
+
serverData: null
|
|
4646
|
+
});
|
|
4647
|
+
continue;
|
|
4648
|
+
}
|
|
4649
|
+
if (results.length === 0) {
|
|
4650
|
+
throw new exports.NeetruDbError("db_unavailable", `pushMutations falhou: ${msg}`);
|
|
4651
|
+
}
|
|
4652
|
+
break;
|
|
4610
4653
|
}
|
|
4611
4654
|
}
|
|
4612
4655
|
return { results };
|
|
@@ -6520,15 +6563,15 @@ function createOidcAuthNamespace(config) {
|
|
|
6520
6563
|
const overrideAuthUrl = typeof globalThis.NEETRU_AUTH_URL === "string" ? globalThis.NEETRU_AUTH_URL : null;
|
|
6521
6564
|
const idpOrigin = overrideAuthUrl ?? config.baseUrl.replace(/^https?:\/\/api\./, "https://auth.");
|
|
6522
6565
|
const issuer = idpOrigin;
|
|
6523
|
-
const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks
|
|
6566
|
+
const jwksUrl = `${idpOrigin}/api/v1/oauth/.well-known/jwks`;
|
|
6524
6567
|
const cacheTtlMs = options?.jwksCacheTtlMs ?? DEFAULT_JWKS_TTL_MS;
|
|
6525
6568
|
const audienceFromOptions = options?.audience;
|
|
6526
6569
|
const parsedKey = tryParseApiKey(config.apiKey);
|
|
6527
|
-
const audience = audienceFromOptions ?? parsedKey?.keyId;
|
|
6570
|
+
const audience = audienceFromOptions ?? config.oidcClientId ?? parsedKey?.keyId;
|
|
6528
6571
|
if (!audience) {
|
|
6529
6572
|
throw new exports.NeetruError(
|
|
6530
6573
|
"invalid_config",
|
|
6531
|
-
"verifyToken requires an audience: provide apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
|
|
6574
|
+
"verifyToken requires an audience: provide oidcClientId/apiKey on the client or pass options.audience. Calling jwtVerify without an audience allows tokens issued for other OIDC clients to pass."
|
|
6532
6575
|
);
|
|
6533
6576
|
}
|
|
6534
6577
|
const jwks = _getJwks(jwksUrl, cacheTtlMs);
|
|
@@ -6611,7 +6654,7 @@ function createNeetruClient(config = {}) {
|
|
|
6611
6654
|
init_errors();
|
|
6612
6655
|
init_db_errors();
|
|
6613
6656
|
init_http();
|
|
6614
|
-
var VERSION = "3.1.
|
|
6657
|
+
var VERSION = "3.1.9";
|
|
6615
6658
|
|
|
6616
6659
|
exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
|
|
6617
6660
|
exports.DEV_FIXTURE_USER = DEV_FIXTURE_USER;
|