@neetru/sdk 3.1.1 → 3.1.2
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.
|
@@ -64,9 +64,22 @@ function makeDocRef(db, collectionName, id) {
|
|
|
64
64
|
}
|
|
65
65
|
function generateId() {
|
|
66
66
|
const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
67
|
+
const cryptoObj = globalThis.crypto;
|
|
68
|
+
if (!cryptoObj?.getRandomValues) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
"[Neetru SDK / firestore-compat] WebCrypto indispon\xEDvel: generateId() exige globalThis.crypto.getRandomValues (Node \u226520 ou ambiente browser)."
|
|
71
|
+
);
|
|
72
|
+
}
|
|
67
73
|
let out = "";
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
while (out.length < 20) {
|
|
75
|
+
const buf = new Uint8Array(20 - out.length);
|
|
76
|
+
cryptoObj.getRandomValues(buf);
|
|
77
|
+
for (const byte of buf) {
|
|
78
|
+
if (byte < 248) {
|
|
79
|
+
out += CHARS[byte % 62];
|
|
80
|
+
if (out.length === 20) break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
70
83
|
}
|
|
71
84
|
return out;
|
|
72
85
|
}
|
|
@@ -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.cjs","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/** Gera um id estilo Firestore (20 chars alfanuméricos) sem depender de crypto. */\r\nfunction generateId(): string {\r\n const CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\r\n let out = '';\r\n for (let i = 0; i < 20; i++) {\r\n out += CHARS[Math.floor(Math.random() * CHARS.length)];\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":";;;AA2EO,SAAS,eAAA,GAA2C;AACzD,EAAA,OAAO,EAAE,YAAY,iBAAA,EAAkB;AACzC;AAeO,SAAS,UAAU,CAAA,EAA8B;AACtD,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,IAAY,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,SAAA;AAAA,MACR,CAAA,wDAAA,EAAwD,MAAA,CAAO,CAAC,CAAC,CAAA,EAAA;AAAA,KACnE;AAAA,EACF;AACA,EAAA,OAAO,EAAE,UAAA,EAAY,WAAA,EAAa,OAAA,EAAS,CAAA,EAAE;AAC/C;;;AC/BA,SAAS,gBAAgB,CAAA,EAAqD;AAC5E,EAAA,OACE,OAAO,CAAA,KAAM,QAAA,IACb,CAAA,KAAM,IAAA,IACN,IAAA,IAAQ,CAAA,IACR,OAAQ,CAAA,CAAuB,EAAA,KAAO,QAAA,IACrC,CAAA,CAAwC,IAAI,UAAA,KAAe,MAAA;AAEhE;AAEA,SAAS,UAAU,MAAA,EAA6C;AAC9D,EAAA,IAAK,MAAA,CAAqB,iBAAA,EAAmB,OAAQ,MAAA,CAAqB,EAAA;AAC1E,EAAA,IAAI,eAAA,CAAgB,MAAM,CAAA,EAAG,OAAO,MAAA,CAAO,EAAA;AAE3C,EAAA,OAAO,MAAA;AACT;AAYO,SAAS,aAAa,MAAA,EAAkC;AAC7D,EAAA,OAAO,EAAE,iBAAA,EAAmB,IAAA,EAAM,EAAA,EAAI,SAAA,CAAU,MAAM,CAAA,EAAE;AAC1D;AAgDO,SAAS,UAAA,CACd,IACA,IAAA,EACwB;AACxB,EAAA,MAAM,QAAA,GAAW,UAAU,EAAE,CAAA;AAC7B,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,YAAA;AAAA,IACN,MAAA,EAAQ,YAAA;AAAA,IACR,EAAA,EAAI,IAAA;AAAA,IACJ,IAAA,EAAM,IAAA;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,GAAA,EAAK,QAAA,CAAS,UAAA,CAAc,IAAI;AAAA,GAClC;AACF;AAWO,SAAS,GAAA,CACd,mBACG,IAAA,EACmB;AAEtB,EAAA,IACE,OAAO,cAAA,KAAmB,QAAA,IAC1B,mBAAmB,IAAA,IAClB,cAAA,CAA0C,WAAW,YAAA,EACtD;AACA,IAAA,MAAM,GAAA,GAAM,cAAA;AACZ,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,CAAC,CAAA,IAAK,UAAA,EAAW;AACjC,IAAA,OAAO,UAAA,CAAc,GAAA,CAAI,EAAA,EAAI,GAAA,CAAI,IAAI,EAAE,CAAA;AAAA,EACzC;AAEA,EAAA,MAAM,QAAA,GAAW,UAAU,cAAuB,CAAA;AAElD,EAAA,IAAI,IAAA,CAAK,UAAU,CAAA,EAAG;AACpB,IAAA,OAAO,UAAA,CAAc,QAAA,EAAU,IAAA,CAAK,CAAC,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAC,CAAA;AAAA,EACjE;AACA,EAAA,IAAI,IAAA,CAAK,CAAC,CAAA,EAAG,QAAA,CAAS,GAAG,CAAA,EAAG;AAC1B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,CAAC,CAAA,CAAE,MAAM,GAAG,CAAA;AAC/B,IAAA,OAAO,UAAA,CAAc,QAAA,EAAU,KAAA,CAAM,CAAC,CAAA,EAAG,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA,IAAK,YAAY,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,WAAc,QAAA,EAAU,IAAA,CAAK,CAAC,CAAA,IAAK,EAAA,EAAI,YAAY,CAAA;AAC5D;AAEA,SAAS,UAAA,CAAc,EAAA,EAAc,cAAA,EAAwB,EAAA,EAAkC;AAC7F,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,UAAA;AAAA,IACN,MAAA,EAAQ,UAAA;AAAA,IACR,EAAA;AAAA,IACA,UAAA,EAAY,cAAA;AAAA,IACZ,IAAA,EAAM,CAAA,EAAG,cAAc,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA;AAAA,IAC7B;AAAA,GACF;AACF;AAaA,SAAS,UAAA,GAAqB;AAC5B,EAAA,MAAM,KAAA,GAAQ,gEAAA;AACd,EAAA,MAAM,YAAa,UAAA,CAAmC,MAAA;AACtD,EAAA,IAAI,CAAC,WAAW,eAAA,EAAiB;AAC/B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,OAAO,GAAA,CAAI,SAAS,EAAA,EAAI;AACtB,IAAA,MAAM,GAAA,GAAM,IAAI,UAAA,CAAW,EAAA,GAAK,IAAI,MAAM,CAAA;AAC1C,IAAA,SAAA,CAAU,gBAAgB,GAAG,CAAA;AAC7B,IAAA,KAAA,MAAW,QAAQ,GAAA,EAAK;AACtB,MAAA,IAAI,OAAO,GAAA,EAAK;AACd,QAAA,GAAA,IAAO,KAAA,CAAM,OAAO,EAAE,CAAA;AACtB,QAAA,IAAI,GAAA,CAAI,WAAW,EAAA,EAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AA4CO,SAAS,KAAA,CACd,WACG,WAAA,EACO;AACV,EAAA,MAAM,IAAA,GACJ,MAAA,CAAO,MAAA,KAAW,OAAA,GACd,MAAA,GACA;AAAA,IACE,IAAA,EAAM,OAAA;AAAA,IACN,MAAA,EAAQ,OAAA;AAAA,IACR,MAAM,MAAA,CAAO,IAAA;AAAA,IACb,YAAY,MAAA,CAAO,EAAA;AAAA,IACnB,IAAI,MAAA,CAAO,EAAA;AAAA,IACX,KAAK,MAAA,CAAO,GAAA;AAAA,IACZ,aAAa;AAAC,GAChB;AACN,EAAA,OAAO,EAAE,GAAG,IAAA,EAAM,WAAA,EAAa,CAAC,GAAG,IAAA,CAAK,WAAA,EAAa,GAAG,WAAW,CAAA,EAAE;AACvE;AAEO,SAAS,KAAA,CAAM,KAAA,EAAe,EAAA,EAAmB,KAAA,EAAiC;AACvF,EAAA,OAAO,EAAE,YAAA,EAAc,OAAA,EAAS,KAAA,EAAO,IAAI,KAAA,EAAM;AACnD;AAEO,SAAS,OAAA,CAAQ,KAAA,EAAe,SAAA,GAA4B,KAAA,EAA0B;AAC3F,EAAA,OAAO,EAAE,YAAA,EAAc,SAAA,EAAW,KAAA,EAAO,SAAA,EAAU;AACrD;AAEO,SAAS,MAAM,KAAA,EAAgC;AACpD,EAAA,OAAO,EAAE,YAAA,EAAc,OAAA,EAAS,KAAA,EAAM;AACxC;AAQO,SAAS,UAAU,WAAA,EAAyC;AACjE,EAAA,MAAM,eAAgC,EAAC;AACvC,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,UAAA;AAEJ,EAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,IAAA,IAAI,CAAA,CAAE,iBAAiB,OAAA,EAAS;AAC9B,MAAA,MAAM,CAAA,GAAI,CAAA;AACV,MAAA,YAAA,CAAa,IAAA,CAAK,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,EAAA,EAAI,WAAA,CAAY,CAAA,CAAE,EAAE,CAAA,EAAG,KAAA,EAAO,CAAA,CAAE,OAAO,CAAA;AAAA,IAC7E,CAAA,MAAA,IAAW,CAAA,CAAE,YAAA,KAAiB,SAAA,EAAW;AACvC,MAAA,MAAM,CAAA,GAAI,CAAA;AACV,MAAA,aAAA,GAAgB,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,SAAA,EAAW,EAAE,SAAA,EAAU;AAAA,IAC3D,CAAA,MAAA,IAAW,CAAA,CAAE,YAAA,KAAiB,OAAA,EAAS;AACrC,MAAA,UAAA,GAAc,CAAA,CAAsB,KAAA;AAAA,IACtC;AAAA,EACF;AAEA,EAAA,MAAM,MAAe,EAAC;AACtB,EAAA,IAAI,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,KAAA,GAAQ,YAAA;AACzC,EAAA,IAAI,aAAA,MAAmB,OAAA,GAAU,aAAA;AACjC,EAAA,IAAI,UAAA,KAAe,MAAA,EAAW,GAAA,CAAI,KAAA,GAAQ,UAAA;AAC1C,EAAA,OAAO,GAAA;AACT;AAWA,SAAS,YAAY,EAAA,EAAwC;AAE3D,EAAA,OAAO,EAAA;AACT;AAOA,SAAS,QAAW,GAAA,EAAiC;AACnD,EAAA,IAAI,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,IAAY,QAAQ,GAAA,EAAK;AACjD,IAAA,MAAM,EAAE,EAAA,EAAI,KAAA,EAAO,GAAG,MAAK,GAAI,GAAA;AAC/B,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,GAAA;AACT;AAEO,IAAM,mBAAN,MAAoD;AAAA,EACzD,WAAA,CACkB,EAAA,EACC,KAAA,EACD,GAAA,EAChB;AAHgB,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AACC,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AACD,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EACf;AAAA,EAHe,EAAA;AAAA,EACC,KAAA;AAAA,EACD,GAAA;AAAA,EAGlB,MAAA,GAAkB;AAChB,IAAA,OAAO,KAAK,KAAA,IAAS,IAAA;AAAA,EACvB;AAAA,EAEA,IAAA,GAAsB;AACpB,IAAA,OAAO,KAAK,KAAA,IAAS,MAAA;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,KAAA,EAAwB;AAC1B,IAAA,OAAO,IAAA,CAAK,KAAA,GAAS,IAAA,CAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,MAAA;AAAA,EACvE;AACF;AAUO,IAAM,gBAAN,MAAiD;AAAA,EACtC,IAAA;AAAA,EACA,KAAA;AAAA,EACA,IAAA;AAAA,EAEhB,WAAA,CACE,IAAA,EACA,EAAA,EACA,cAAA,EACA;AACA,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,GAAA,CAAI,CAAC,EAAE,EAAA,EAAI,MAAK,KAAM;AACrC,MAAA,MAAM,QAAA,GAAW,QAAW,IAAI,CAAA;AAChC,MAAA,MAAM,MACJ,EAAA,IAAM,cAAA,GAAiB,WAAc,EAAA,EAAI,cAAA,EAAgB,EAAE,CAAA,GAAI,MAAA;AACjE,MAAA,OAAO;AAAA,QACL,EAAA;AAAA,QACA,MAAM,MAAM,QAAA;AAAA,QACZ,QAAQ,MAAM,IAAA;AAAA,QACd,GAAA,EAAK,CAAC,KAAA,KAAmB,QAAA,GAAuC,KAAK,CAAA;AAAA;AAAA,QAErE;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AACD,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,MAAA,KAAW,CAAA;AAClC,IAAA,IAAA,CAAK,IAAA,GAAO,KAAK,IAAA,CAAK,MAAA;AAAA,EACxB;AAAA,EAEA,QAAQ,EAAA,EAAmD;AACzD,IAAA,IAAA,CAAK,IAAA,CAAK,QAAQ,EAAE,CAAA;AAAA,EACtB;AACF;AAOA,SAAS,aAAa,MAAA,EAGpB;AACA,EAAA,MAAM,KAAA,GAAQ,MAAA,EAAQ,IAAA,CAAK,CAAC,CAAA;AAC5B,EAAA,OAAO,EAAE,MAAM,KAAA,GAAS,KAAA,CAAM,OAAmC,IAAA,EAAM,EAAA,EAAI,KAAA,EAAO,EAAA,IAAM,IAAA,EAAK;AAC/F;AAEA,eAAsB,OACpB,GAAA,EAC8B;AAC9B,EAAA,MAAM,MAAA,GAAU,MAAM,GAAA,CAAI,EAAA,CAAG,UAAA,CAAW,IAAI,UAAU,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AAGlE,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,YAAA,CAAa,MAAM,CAAA;AACpC,EAAA,OAAO,IAAI,gBAAA;AAAA,IACT,GAAA,CAAI,EAAA;AAAA,IACJ,IAAA,GAAQ,QAAW,EAAE,EAAA,EAAI,IAAI,EAAA,EAAI,GAAG,IAAA,EAAM,CAAA,GAAK,IAAA;AAAA,IAC/C;AAAA,GACF;AACF;AAGA,SAAS,iBACP,MAAA,EACsD;AACtD,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,CAAC,OAAO,EAAE,EAAA,EAAI,CAAA,CAAE,EAAA,EAAI,MAAM,EAAE,GAAI,CAAA,CAAE,IAAA,IAAmC,CAAE,CAAA;AAChG;AAEA,eAAsB,QACpB,MAAA,EAC2B;AAC3B,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA,KAAW,UAAU,SAAA,CAAU,MAAA,CAAO,WAAW,CAAA,GAAI,MAAA;AAC5E,EAAA,MAAM,iBAAiB,MAAA,CAAO,MAAA,KAAW,OAAA,GAAU,MAAA,CAAO,aAAa,MAAA,CAAO,EAAA;AAC9E,EAAA,MAAM,MAAA,GAAU,MAAM,MAAA,CAAO,EAAA,CAC1B,WAAW,cAAc,CAAA,CACzB,KAAK,OAAO,CAAA;AACf,EAAA,OAAO,IAAI,aAAA,CAAiB,gBAAA,CAAiB,MAAM,CAAA,EAAG,MAAA,CAAO,IAAI,cAAc,CAAA;AACjF;AAWA,eAAsB,MAAA,CACpB,GAAA,EACA,IAAA,EACA,OAAA,GAAsB,EAAC,EACR;AACf,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,EAAA,CAAG,UAAA,CAAW,IAAI,UAAU,CAAA;AAC5C,EAAA,MAAM,OAAA,GAAU,IAAA;AAChB,EAAA,IAAI,QAAQ,KAAA,IAAU,OAAA,CAAQ,eAAe,OAAA,CAAQ,WAAA,CAAY,SAAS,CAAA,EAAI;AAI5E,IAAA,MAAM,QAAA,GAAW,MAAM,GAAA,CAAI,GAAA,CAAI,IAAI,EAAE,CAAA;AACrC,IAAA,MAAM,SAAA,GAAY,KAAA,CAAM,OAAA,CAAS,QAAA,EAA0C,IAAI,IACzE,QAAA,CAAiC,IAAA,CAAK,MAAA,GAAS,CAAA,GACjD,QAAA,IAAY,IAAA;AAChB,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,EAAA,EAAI,OAAO,CAAA;AAAA,IAClC,CAAA,MAAO;AACL,MAAA,MAAM,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,OAAO,CAAA;AAAA,IAC/B;AAAA,EACF,CAAA,MAAO;AACL,IAAA,MAAM,GAAA,CAAI,GAAA,CAAI,GAAA,CAAI,EAAA,EAAI,OAAO,CAAA;AAAA,EAC/B;AACF;AAEA,eAAsB,MAAA,CACpB,QACA,IAAA,EAC+B;AAC/B,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,CAAO,EAAA,CAAG,WAAW,MAAA,CAAO,EAAE,CAAA,CAAE,GAAA,CAAI,IAA+B,CAAA;AACxF,EAAA,OAAO,WAAc,MAAA,CAAO,EAAA,EAAI,MAAA,CAAO,EAAA,EAAI,OAAO,EAAE,CAAA;AACtD;AAEA,eAAsB,SAAA,CACpB,KACA,IAAA,EACe;AACf,EAAA,MAAM,GAAA,CAAI,GAAG,UAAA,CAAW,GAAA,CAAI,UAAU,CAAA,CAAE,MAAA,CAAO,GAAA,CAAI,EAAA,EAAI,IAA+B,CAAA;AACxF;AAEA,eAAsB,UAAU,GAAA,EAAuC;AACrE,EAAA,MAAM,GAAA,CAAI,GAAG,UAAA,CAAW,GAAA,CAAI,UAAU,CAAA,CAAE,MAAA,CAAO,IAAI,EAAE,CAAA;AACvD;AAcO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YAA6B,EAAA,EAAc;AAAd,IAAA,IAAA,CAAA,EAAA,GAAA,EAAA;AAAA,EAAe;AAAA,EAAf,EAAA;AAAA,EAFrB,MAAwB,EAAC;AAAA,EAIjC,GAAA,CACE,GAAA,EACA,IAAA,EACA,QAAA,EACM;AACN,IAAA,IAAA,CAAK,IAAI,IAAA,CAAK;AAAA,MACZ,YAAY,GAAA,CAAI,UAAA;AAAA,MAChB,IAAI,EAAE,IAAA,EAAM,OAAO,EAAA,EAAI,GAAA,CAAI,IAAI,IAAA;AAAsC,KACtE,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAA,CAAU,KAA2B,IAAA,EAAkD;AACrF,IAAA,IAAA,CAAK,IAAI,IAAA,CAAK;AAAA,MACZ,YAAY,GAAA,CAAI,UAAA;AAAA,MAChB,IAAI,EAAE,IAAA,EAAM,UAAU,EAAA,EAAI,GAAA,CAAI,IAAI,IAAA;AAAsC,KACzE,CAAA;AACD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,OAAO,GAAA,EAA8B;AACnC,IAAA,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,EAAE,UAAA,EAAY,IAAI,UAAA,EAAY,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAU,EAAA,EAAI,GAAA,CAAI,EAAA,IAAM,CAAA;AAChF,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAM,MAAA,GAAwB;AAE5B,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAoC;AACtD,IAAA,KAAA,MAAW,EAAE,UAAA,EAAY,IAAA,EAAM,EAAA,EAAG,IAAK,KAAK,GAAA,EAAK;AAC/C,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,IAAI,KAAK,EAAC;AACjC,MAAA,IAAA,CAAK,KAAK,EAAE,CAAA;AACZ,MAAA,KAAA,CAAM,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,IACtB;AACA,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,GAAG,CAAA,IAAK,KAAA,CAAM,SAAQ,EAAG;AAEzC,MAAA,MAAM,KAAK,EAAA,CAAG,UAAA,CAAW,IAAI,CAAA,CAAE,MAAM,GAAY,CAAA;AAAA,IACnD;AACA,IAAA,IAAA,CAAK,MAAM,EAAC;AAAA,EACd;AACF;AAEO,SAAS,WAAW,EAAA,EAAuB;AAChD,EAAA,OAAO,IAAI,UAAA,CAAW,SAAA,CAAU,EAAE,CAAC,CAAA;AACrC;AA8CA,IAAI,0BAAA,GAA6B,KAAA;AAEjC,eAAsB,cAAA,CACpB,IACA,cAAA,EACY;AAEZ,EAAA,IAAI,CAAC,0BAAA,EAA4B;AAC/B,IAAA,0BAAA,GAA6B,IAAA;AAC7B,IAAA,OAAA,CAAQ,IAAA;AAAA,MACN;AAAA,KAKF;AAAA,EACF;AAEA,EAAA,MAAM,QAAA,GAAW,UAAU,EAAE,CAAA;AAC7B,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,QAAQ,CAAA;AAErC,EAAA,MAAM,EAAA,GAAkB;AAAA,IACtB,MAAM,IAAO,GAAA,EAAyD;AACpE,MAAA,OAAO,OAAO,GAAG,CAAA;AAAA,IACnB,CAAA;AAAA,IACA,GAAA,CAAI,GAAA,EAAK,IAAA,EAAM,OAAA,EAAS;AACtB,MAAA,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,IAAA,EAAM,OAAO,CAAA;AAC5B,MAAA,OAAO,EAAA;AAAA,IACT,CAAA;AAAA,IACA,MAAA,CAAO,KAAK,IAAA,EAAM;AAChB,MAAA,KAAA,CAAM,MAAA,CAAO,KAAK,IAAI,CAAA;AACtB,MAAA,OAAO,EAAA;AAAA,IACT,CAAA;AAAA,IACA,OAAO,GAAA,EAAK;AACV,MAAA,KAAA,CAAM,OAAO,GAAG,CAAA;AAChB,MAAA,OAAO,EAAA;AAAA,IACT;AAAA,GACF;AAEA,EAAA,MAAM,MAAA,GAAS,MAAM,cAAA,CAAe,EAAE,CAAA;AACtC,EAAA,MAAM,MAAM,MAAA,EAAO;AACnB,EAAA,OAAO,MAAA;AACT;AAsBO,SAAS,UAAA,CACd,MAAA,EACA,MAAA,EACA,OAAA,EACa;AAEb,EAAA,IAAI,MAAA,CAAO,WAAW,UAAA,EAAY;AAChC,IAAA,MAAM,GAAA,GAAM,MAAA;AACZ,IAAA,MAAM,MAAA,GAAS,IAAI,EAAA,CAAG,UAAA,CAAW,IAAI,UAAU,CAAA,CAAE,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AAC3D,IAAA,IAAI;AACF,MAAA,OAAO,MAAA,CAAO,UAAA,CAAW,CAAC,IAAA,KAAS;AACjC,QAAA,MAAM,IAAA,GAAO,IAAA,EAAM,IAAA,CAAK,CAAC,CAAA,EAAG,IAAA;AAC5B,QAAC,MAAA;AAAA,UACC,IAAI,gBAAA;AAAA,YACF,GAAA,CAAI,EAAA;AAAA,YACJ,IAAA,GAAO,QAAW,EAAE,EAAA,EAAI,IAAI,EAAA,EAAI,GAAG,IAAA,EAAM,CAAA,GAAI,IAAA;AAAA,YAC7C;AAAA;AACF,SACF;AAAA,MACF,CAAC,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,OAAA,GAAU,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC7D,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB;AAAA,EACF;AAGA,EAAA,MAAM,iBAAiB,MAAA,CAAO,MAAA,KAAW,OAAA,GAAU,MAAA,CAAO,aAAc,MAAA,CAAkC,EAAA;AAC1G,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA,KAAW,UAAU,SAAA,CAAU,MAAA,CAAO,WAAW,CAAA,GAAI,MAAA;AAC5E,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,EAAA,CAAG,UAAA,CAAW,cAAc,CAAA;AAC/C,EAAA,IAAI;AACF,IAAA,OAAO,GAAA,CAAI,UAAA,CAAW,OAAA,EAAS,CAAC,IAAA,KAAgD;AAC9E,MAAC,MAAA;AAAA,QACC,IAAI,aAAA,CAAiB,gBAAA,CAAiB,IAAI,CAAA,EAAG,MAAA,CAAO,IAAI,cAAc;AAAA,OACxE;AAAA,IACF,CAAC,CAAA;AAAA,EACH,SAAS,GAAA,EAAK;AACZ,IAAA,OAAA,GAAU,GAAA,YAAe,QAAQ,GAAA,GAAM,IAAI,MAAM,MAAA,CAAO,GAAG,CAAC,CAAC,CAAA;AAC7D,IAAA,OAAO,MAAM;AAAA,IAAC,CAAA;AAAA,EAChB;AACF;AAWO,IAAM,SAAA,GAAN,MAAM,UAAA,CAAU;AAAA,EACrB,WAAA,CACkB,SACA,WAAA,EAChB;AAFgB,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA,EACf;AAAA,EAFe,OAAA;AAAA,EACA,WAAA;AAAA,EAGlB,OAAO,GAAA,GAAiB;AACtB,IAAA,OAAO,UAAA,CAAU,UAAA,CAAW,IAAA,CAAK,GAAA,EAAK,CAAA;AAAA,EACxC;AAAA,EAEA,OAAO,SAAS,IAAA,EAAuB;AACrC,IAAA,OAAO,UAAA,CAAU,UAAA,CAAW,IAAA,CAAK,OAAA,EAAS,CAAA;AAAA,EAC5C;AAAA,EAEA,OAAO,WAAW,MAAA,EAA2B;AAC3C,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,KAAA,CAAM,MAAA,GAAS,GAAI,CAAA;AACxC,IAAA,MAAM,WAAA,GAAA,CAAe,MAAA,GAAS,OAAA,GAAU,GAAA,IAAQ,GAAA;AAChD,IAAA,OAAO,IAAI,UAAA,CAAU,OAAA,EAAS,WAAW,CAAA;AAAA,EAC3C;AAAA,EAEA,MAAA,GAAe;AACb,IAAA,OAAO,IAAI,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,CAAA;AAAA,EACjC;AAAA,EAEA,QAAA,GAAmB;AACjB,IAAA,OAAO,KAAK,OAAA,GAAU,GAAA,GAAO,KAAK,KAAA,CAAM,IAAA,CAAK,cAAc,GAAG,CAAA;AAAA,EAChE;AAAA,EAEA,QAAQ,KAAA,EAA2B;AACjC,IAAA,OAAO,KAAK,OAAA,KAAY,KAAA,CAAM,OAAA,IAAW,IAAA,CAAK,gBAAgB,KAAA,CAAM,WAAA;AAAA,EACtE;AAAA,EAEA,OAAA,GAAkB;AAEhB,IAAA,OAAO,GAAG,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAS,CAAE,SAAS,EAAA,EAAI,GAAG,CAAC,CAAA,CAAA,EAAI,KAAK,WAAA,CACzD,QAAA,GACA,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAAA,EACrB;AAAA,EAEA,MAAA,GAAmD;AACjD,IAAA,OAAO,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAS,WAAA,EAAa,KAAK,WAAA,EAAY;AAAA,EAChE;AAAA,EAEA,QAAA,GAAmB;AACjB,IAAA,OAAO,CAAA,kBAAA,EAAqB,IAAA,CAAK,OAAO,CAAA,cAAA,EAAiB,KAAK,WAAW,CAAA,CAAA,CAAA;AAAA,EAC3E;AACF;AAgCO,SAASA,gBAAAA,GAAiC;AAC/C,EAAA,OAAO,eAAA,EAAmB;AAC5B;AAKO,SAASC,WAAU,CAAA,EAA0B;AAClD,EAAA,OAAO,UAAa,CAAC,CAAA;AACvB;AAGO,SAAS,cAAc,QAAA,EAAyC;AACrE,EAAA,OAAO,EAAE,UAAA,EAAY,YAAA,EAAc,QAAA,EAAS;AAC9C;AAGO,SAAS,eAAe,QAAA,EAA0C;AACvE,EAAA,OAAO,EAAE,UAAA,EAAY,aAAA,EAAe,QAAA,EAAS;AAC/C;AAGO,SAAS,WAAA,GAAmC;AACjD,EAAA,OAAO,EAAE,YAAY,aAAA,EAAc;AACrC;AAgBO,IAAM,UAAA,GAAa;AAAA,EACxB,eAAA,EAAAD,gBAAAA;AAAA,EACA,SAAA,EAAAC,UAAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,MAAA,EAAQ;AACV","file":"firestore-compat.cjs","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"]}
|
|
@@ -62,9 +62,22 @@ function makeDocRef(db, collectionName, id) {
|
|
|
62
62
|
}
|
|
63
63
|
function generateId() {
|
|
64
64
|
const CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
65
|
+
const cryptoObj = globalThis.crypto;
|
|
66
|
+
if (!cryptoObj?.getRandomValues) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"[Neetru SDK / firestore-compat] WebCrypto indispon\xEDvel: generateId() exige globalThis.crypto.getRandomValues (Node \u226520 ou ambiente browser)."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
65
71
|
let out = "";
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
while (out.length < 20) {
|
|
73
|
+
const buf = new Uint8Array(20 - out.length);
|
|
74
|
+
cryptoObj.getRandomValues(buf);
|
|
75
|
+
for (const byte of buf) {
|
|
76
|
+
if (byte < 248) {
|
|
77
|
+
out += CHARS[byte % 62];
|
|
78
|
+
if (out.length === 20) break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
68
81
|
}
|
|
69
82
|
return out;
|
|
70
83
|
}
|