@fayz-ai/core 0.38.0 → 0.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-MFFT663T.js → chunk-6FCJLF55.js} +3 -3
- package/dist/{chunk-MFFT663T.js.map → chunk-6FCJLF55.js.map} +1 -1
- package/dist/chunk-DRDQ5JJG.js +146 -0
- package/dist/chunk-DRDQ5JJG.js.map +1 -0
- package/dist/{chunk-KGZK47XI.js → chunk-G4F6RON5.js} +64 -127
- package/dist/chunk-G4F6RON5.js.map +1 -0
- package/dist/{chunk-EFMUJACX.js → chunk-ITUYZLT6.js} +3 -3
- package/dist/{chunk-EFMUJACX.js.map → chunk-ITUYZLT6.js.map} +1 -1
- package/dist/{chunk-ODTOKMD3.js → chunk-RXIOJQFH.js} +22 -3
- package/dist/chunk-RXIOJQFH.js.map +1 -0
- package/dist/{chunk-X3GYBLGO.js → chunk-VAHWWJC2.js} +4 -4
- package/dist/{chunk-X3GYBLGO.js.map → chunk-VAHWWJC2.js.map} +1 -1
- package/dist/{chunk-QZ22VPRZ.js → chunk-X5EJ532G.js} +4 -4
- package/dist/{chunk-QZ22VPRZ.js.map → chunk-X5EJ532G.js.map} +1 -1
- package/dist/data/index.js +4 -3
- package/dist/data/metadata-fields.d.ts +19 -0
- package/dist/data/metadata-fields.d.ts.map +1 -0
- package/dist/data/resolve.d.ts.map +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/shell-translations.en.d.ts.map +1 -1
- package/dist/i18n/shell-translations.pt-br.d.ts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +123 -19
- package/dist/index.js.map +1 -1
- package/dist/integrations/index.js +4 -3
- package/dist/lib/connectivity.d.ts +17 -0
- package/dist/lib/connectivity.d.ts.map +1 -0
- package/dist/lib/error-message.d.ts +7 -0
- package/dist/lib/error-message.d.ts.map +1 -1
- package/dist/lib/impersonation.d.ts +30 -0
- package/dist/lib/impersonation.d.ts.map +1 -0
- package/dist/plugin/index.js +3 -2
- package/dist/{shell-translations.pt-br-TTVH5GSD.js → shell-translations.pt-br-UM63CUBG.js} +21 -2
- package/dist/shell-translations.pt-br-UM63CUBG.js.map +1 -0
- package/dist/telemetry/errors.d.ts +3 -0
- package/dist/telemetry/errors.d.ts.map +1 -1
- package/dist/testing/index.js +5 -4
- package/dist/types/crud.d.ts +6 -0
- package/dist/types/crud.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-6TVRNKWW.js +0 -17
- package/dist/chunk-6TVRNKWW.js.map +0 -1
- package/dist/chunk-KGZK47XI.js.map +0 -1
- package/dist/chunk-ODTOKMD3.js.map +0 -1
- package/dist/shell-translations.pt-br-TTVH5GSD.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { errorMessage } from './chunk-
|
|
1
|
+
import { errorMessage } from './chunk-DRDQ5JJG.js';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
4
|
// src/data/write-refused.ts
|
|
@@ -369,5 +369,5 @@ function useActiveTenantId() {
|
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
export { DEFAULT_CACHE_TTL, WRITE_REFUSED_MESSAGE, WriteRefusedError, applyRangeToQuery, applyUnitScope, assertWrote, clearGlobalCache, createCacheStore, createSupabaseProvider, getActiveTenantId, getScopedResource, getSupabaseClientOptional, globalCache, isFilterObject, matchesRange, parseRangeFilter, resolveUnitScope, setActiveTenantId, setGlobalSupabaseClient, setScopedResources, stableKey, subscribeActiveTenant, useActiveTenantId };
|
|
372
|
-
//# sourceMappingURL=chunk-
|
|
373
|
-
//# sourceMappingURL=chunk-
|
|
372
|
+
//# sourceMappingURL=chunk-6FCJLF55.js.map
|
|
373
|
+
//# sourceMappingURL=chunk-6FCJLF55.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/data/write-refused.ts","../src/data/filters.ts","../src/data/unit-scope.ts","../src/data/supabase.ts","../src/lib/cache.ts","../src/lib/tenant.ts"],"names":[],"mappings":"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","file":"chunk-MFFT663T.js","sourcesContent":["// ---------------------------------------------------------------------------\n// UMA ESCRITA QUE NÃO PEGOU LINHA NENHUMA NÃO É UM SUCESSO.\n//\n// O PostgREST responde **204** a um DELETE que a RLS recusou — a política não\n// dá erro, ela apenas não deixa a linha entrar no `USING`, e a instrução apaga\n// zero linhas. Do lado do cliente, `error` volta `null`. Todo provider deste\n// SDK lia isso como \"apagado\".\n//\n// O que isso produz na tela é a pior forma de falha que existe: a de sucesso.\n// O diálogo fecha, o brinde diz \"Agendamento excluído\", a lista recarrega\n// inteira — e o agendamento continua lá. Foi assim que chegaram, no mesmo dia,\n// \"não consigo excluir o cliente\" e \"não consigo excluir o agendamento\": o\n// mesmo 204, em dois cadastros diferentes, com a mesma causa — o papel não tem\n// a permissão de exclusão e nada na tela diz isso.\n//\n// A correção é pedir as linhas de volta (`.select('id')`) e tratar a lista\n// vazia como recusa. Custa um campo no corpo da resposta e transforma um\n// engano silencioso numa mensagem.\n//\n// ── Por que a mensagem fala de DUAS causas ────────────────────────────────\n//\n// De fora, \"a RLS recusou\" e \"a linha não existe mais\" são indistinguíveis —\n// as duas apagam zero linhas. Chutar uma delas seria mandar o operador para o\n// lugar errado metade das vezes; dizer as duas o manda para a pergunta certa\n// (\"quem eu sou aqui?\" ou \"alguém já apagou?\").\n// ---------------------------------------------------------------------------\n\n/** O texto que a tela mostra quando uma escrita não pegou linha nenhuma. */\nexport const WRITE_REFUSED_MESSAGE =\n 'Sem permissão para esta ação, ou o registro já não existe.'\n\nexport class WriteRefusedError extends Error {\n /** Para quem quiser distinguir isto de uma falha de rede sem ler a mensagem. */\n readonly refused = true\n constructor(message: string = WRITE_REFUSED_MESSAGE) {\n super(message)\n this.name = 'WriteRefusedError'\n }\n}\n\n/**\n * Confirma que a escrita pegou pelo menos uma linha.\n *\n * `rows` é o que `.select(...)` devolveu. `null` conta como recusa — é o que o\n * PostgREST devolve quando não se pediu representação nenhuma, e nesse caso o\n * chamador não deveria estar usando esta função.\n */\nexport function assertWrote(rows: unknown[] | null | undefined, message?: string): void {\n if (rows && rows.length > 0) return\n throw new WriteRefusedError(message)\n}\n","/**\n * ONE filter grammar, shared by the providers and by the agent's data tools.\n *\n * Models express ranges in whatever dialect they were trained on — `{from,to}`,\n * `{gte,lte}`, Mongo's `{$gte,$lt}`. When only some of those are recognised the\n * unrecognised one falls through to an equality compare against\n * \"[object Object]\", which matches nothing and reads as \"there are no\n * appointments today\" instead of \"I did not understand that filter\".\n */\n\nexport interface RangeFilter {\n gte?: unknown\n gt?: unknown\n lte?: unknown\n lt?: unknown\n}\n\nconst LOWER_INCLUSIVE = ['from', 'gte', 'start', 'after', 'since', 'min']\nconst LOWER_EXCLUSIVE = ['gt']\nconst UPPER_INCLUSIVE = ['to', 'lte', 'end', 'until', 'before', 'max']\nconst UPPER_EXCLUSIVE = ['lt']\n\nfunction pick(source: Record<string, unknown>, names: string[]): unknown {\n for (const name of names) {\n if (source[name] !== undefined && source[name] !== null) return source[name]\n const dollar = `$${name}`\n if (source[dollar] !== undefined && source[dollar] !== null) return source[dollar]\n }\n return undefined\n}\n\n/**\n * Parse a filter value into a range, or return null when it is a plain\n * (equality) value. An object that looks like a filter but carries no\n * recognised bound returns `null` too — callers treat that as \"invalid\",\n * never as \"matches nothing\".\n */\nexport function parseRangeFilter(value: unknown): RangeFilter | null {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return null\n const source = value as Record<string, unknown>\n const range: RangeFilter = {}\n const gte = pick(source, LOWER_INCLUSIVE)\n const gt = pick(source, LOWER_EXCLUSIVE)\n const lte = pick(source, UPPER_INCLUSIVE)\n const lt = pick(source, UPPER_EXCLUSIVE)\n if (gte !== undefined) range.gte = gte\n if (gt !== undefined) range.gt = gt\n if (lte !== undefined) range.lte = lte\n if (lt !== undefined) range.lt = lt\n return Object.keys(range).length ? range : null\n}\n\n/** True for any object value — including ones no bound could be read from. */\nexport function isFilterObject(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction comparable(value: unknown): number {\n if (typeof value === 'number') return value\n if (value instanceof Date) return value.getTime()\n const ts = Date.parse(String(value))\n if (!Number.isNaN(ts)) return ts\n return Number(value)\n}\n\n/** Client-side counterpart of the range a provider would push to SQL. */\nexport function matchesRange(raw: unknown, range: RangeFilter): boolean {\n const value = comparable(raw)\n if (Number.isNaN(value)) return false\n if (range.gte !== undefined && value < comparable(range.gte)) return false\n if (range.gt !== undefined && value <= comparable(range.gt)) return false\n if (range.lte !== undefined && value > comparable(range.lte)) return false\n if (range.lt !== undefined && value >= comparable(range.lt)) return false\n return true\n}\n\n/** Apply a range to a PostgREST query builder. */\nexport function applyRangeToQuery<Q>(\n query: Q,\n column: string,\n range: RangeFilter,\n): Q {\n let q = query as Record<string, (c: string, v: unknown) => unknown>\n for (const op of ['gte', 'gt', 'lte', 'lt'] as const) {\n const bound = range[op]\n if (bound === undefined) continue\n q = q[op](column, bound) as Record<string, (c: string, v: unknown) => unknown>\n }\n return q as Q\n}\n","// ---------------------------------------------------------------------------\n// The one place that turns \"the user is looking at Ipanema\" into a filter.\n//\n// This is a NARROWING and nothing else. Which units a person may reach is\n// decided by row-level security; if this file were deleted the app would show\n// more rows than the switcher promises, and still not one row the database\n// would have refused. That asymmetry is deliberate — it is what lets the filter\n// live in the client at all.\n// ---------------------------------------------------------------------------\n\nexport interface UnitScope {\n column: string\n id: string\n /** Whether rows with no unit (\"belongs to the whole organization\") come too. */\n includeShared: boolean\n}\n\n/** One row of `public.scoped_resources` — which tables carry a unit, and under\n * which column. */\nexport interface ScopedResource {\n unitColumn: string\n shareable: boolean\n}\n\n// The registry, mirrored from the database rather than restated here.\n//\n// The list of scoped tables and their unit columns lives in ONE place —\n// `public.scoped_resources`, seeded by migration 041 — and the shell publishes\n// it at boot. A second copy in TypeScript would drift on the first table anyone\n// adds, and the failure mode of that drift is a screen that quietly stops\n// narrowing while the database still does.\nlet _scoped: Record<string, ScopedResource> = {}\n\nexport function setScopedResources(map: Record<string, ScopedResource>): void {\n _scoped = map\n}\n\nexport function getScopedResource(table: string | undefined): ScopedResource | undefined {\n if (!table) return undefined\n return _scoped[table]\n}\n\nexport interface UnitScopeConfig {\n unitScoped?: boolean\n unitColumn?: string\n /**\n * Default true. A row with no unit belongs to the brand and must survive a\n * unit selection — the Botox that is sold everywhere still shows in Ipanema.\n *\n * False for ledgers and aggregations, where a shared row summed into every\n * unit is a wrong number stated confidently, which is worse than a missing\n * one.\n */\n includeSharedRows?: boolean\n unitId?: () => string | undefined\n}\n\n/**\n * `null` means \"do not narrow\" — unit mode off, \"todas as unidades\", or an\n * entity that never opted in. Callers treat null as a no-op, which is what\n * keeps every provider's behaviour identical for entities that are not scoped.\n */\nexport function resolveUnitScope(config: UnitScopeConfig): UnitScope | null {\n if (!config.unitScoped) return null\n const id = config.unitId?.()\n if (!id) return null\n return {\n column: config.unitColumn ?? 'unit_id',\n id,\n includeShared: config.includeSharedRows !== false,\n }\n}\n\n/**\n * Applies the scope to a PostgREST builder.\n *\n * The shared form is `or(unit.eq.X,unit.is.null)`. Multiple `or=` params AND\n * together in PostgREST, so this composes correctly with the search `.or()` the\n * providers already emit: (name matches) AND (unit is mine or nobody's).\n *\n * Note what is NOT here: an `.in(column, [...myUnits])`. Enumerating the user's\n * units in the browser is the exact mistake this whole design exists to avoid —\n * it would put the answer to \"which units may I see\" in the client, where it\n * can be edited.\n */\nexport function applyUnitScope<Q>(builder: Q, scope: UnitScope | null): Q {\n if (!scope) return builder\n const q = builder as unknown as {\n or: (f: string) => unknown\n eq: (c: string, v: unknown) => unknown\n }\n return (scope.includeShared\n ? q.or(`${scope.column}.eq.${scope.id},${scope.column}.is.null`)\n : q.eq(scope.column, scope.id)) as unknown as Q\n}\n","import { errorMessage } from '../lib/error-message'\nimport { assertWrote } from './write-refused'\nimport type { DataProvider, CrudQuery, CrudResult } from './types'\nimport { applyRangeToQuery, isFilterObject, parseRangeFilter } from './filters'\nimport { applyUnitScope, resolveUnitScope } from './unit-scope'\n\nexport interface SupabaseProviderConfig {\n schema?: string\n tenantId?: string | (() => string | undefined)\n tenantIdColumn?: string\n searchColumns?: string[]\n selectColumns?: string\n columnMap?: Record<string, string>\n filters?: Record<string, string | string[]>\n defaults?: Record<string, unknown>\n supabaseClient?: unknown\n /** Franchise scoping. Absent (every entity today) ⇒ nothing below changes. */\n unitScoped?: boolean\n unitColumn?: string\n includeSharedRows?: boolean\n unitId?: () => string | undefined\n}\n\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (c) => '_' + c.toLowerCase())\n}\n\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase())\n}\n\nfunction mapRow<T>(row: Record<string, unknown>, columnMap?: Record<string, string>): T {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(row)) {\n const camelKey = columnMap?.[key] ?? snakeToCamel(key)\n result[camelKey] = value\n }\n return result as T\n}\n\nfunction mapEntity(entity: Record<string, unknown>, columnMap?: Record<string, string>): Record<string, unknown> {\n const reverseMap: Record<string, string> = {}\n if (columnMap) {\n for (const [snake, camel] of Object.entries(columnMap)) {\n reverseMap[camel] = snake\n }\n }\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(entity)) {\n // `createdBy` entra na lista: quem o escreve é o gatilho do banco, com\n // `auth.uid()`. Deixá-lo passar daria à interface a chance de dizer quem\n // cadastrou — e um autor que o próprio formulário pode escolher não vale\n // como procedência. Ver packages/db/migrations/025_created_by.sql.\n // `ownerId` for the same reason: 041's trigger decides who a record belongs\n // to, and a form that could set it would be handing records to other people.\n if (key === 'id' || key === 'createdAt' || key === 'updatedAt' || key === 'tenantId' || key === 'createdBy' || key === 'ownerId') continue\n const snakeKey = reverseMap[key] ?? camelToSnake(key)\n result[snakeKey] = value\n }\n return result\n}\n\n// Global Supabase client registry — set once via createFayzApp\nlet _globalSupabaseClient: unknown | null = null\n\nexport function setGlobalSupabaseClient(client: unknown): void {\n _globalSupabaseClient = client\n}\n\nexport function getSupabaseClientOptional(): unknown | null {\n return _globalSupabaseClient\n}\n\nexport function createSupabaseProvider<T extends { id: string }>(\n table: string,\n config?: SupabaseProviderConfig,\n): DataProvider<T> {\n const schema = config?.schema ?? 'public'\n const tenantIdCol = config?.tenantIdColumn ?? 'tenant_id'\n const searchCols = config?.searchColumns ?? []\n const selectCols = config?.selectColumns ?? '*'\n const columnMap = config?.columnMap\n\n function resolveTenantId(): string | undefined {\n if (!config?.tenantId) return undefined\n return typeof config.tenantId === 'function' ? config.tenantId() : config.tenantId\n }\n\n function unitScope() {\n return resolveUnitScope(config ?? {})\n }\n\n function getClient() {\n const supabase = (config?.supabaseClient ?? _globalSupabaseClient) as {\n schema: (s: string) => unknown\n from: (t: string) => unknown\n } | null\n if (!supabase) throw new Error(`[@fayz-ai/core] Supabase client not available. Call createFayzApp with a provider first.`)\n return schema === 'public' ? (supabase as { from: (t: string) => unknown }) : (supabase.schema(schema) as { from: (t: string) => unknown })\n }\n\n return {\n async list(query: CrudQuery): Promise<CrudResult<T>> {\n // 'none' skips PostgREST's count aggregate entirely — see CrudQuery.countMode.\n const wantCount = query.countMode !== 'none'\n let q = (getClient().from(table) as { select: (...args: unknown[]) => unknown })\n .select(selectCols, wantCount ? { count: 'exact' } : undefined) as Record<string, unknown>\n\n const tenantId = resolveTenantId()\n if (tenantId) q = (q as { eq: (col: string, val: string) => unknown }).eq(tenantIdCol, tenantId) as Record<string, unknown>\n\n // Narrowing only — the boundary itself is the restrictive policy in the\n // database. Placed before the search `.or()` because PostgREST ANDs the\n // two `or=` params, which is what makes them compose.\n q = applyUnitScope(q, unitScope()) as Record<string, unknown>\n\n if (config?.filters) {\n for (const [col, val] of Object.entries(config.filters)) {\n // UMA LISTA é `in`, não `eq`.\n //\n // Com `eq`, um array vira `kind=eq.a,b,c` — um valor único com\n // vírgulas, que não casa com nada e devolve lista vazia. E o caso que\n // pediu isto não é teórico: `plg_financial_accounts` guarda as contas\n // do RAZÃO, e no Pertinho são 122.224 linhas `person` ao lado de 233\n // contas bancárias de verdade. A tela de contas precisa das cinco\n // espécies de conta bancária, e sem `in` ela lista a casa inteira.\n q = Array.isArray(val)\n ? (q as { in: (c: string, v: readonly string[]) => unknown }).in(col, val) as Record<string, unknown>\n : (q as { eq: (c: string, v: string) => unknown }).eq(col, val) as Record<string, unknown>\n }\n }\n\n // Faceted / dynamic filters from the query, mapped to columns. Equality\n // by default; a range object ({from,to} / {gte,lt} / {$gte,$lt}) becomes\n // real SQL bounds instead of an equality compare against \"[object Object]\".\n if (query.filters) {\n for (const [key, val] of Object.entries(query.filters)) {\n if (val == null || val === '') continue\n const col = columnMap?.[key] ?? camelToSnake(key)\n const range = parseRangeFilter(val)\n if (range) {\n q = applyRangeToQuery(q, col, range)\n continue\n }\n if (isFilterObject(val)) {\n throw new Error(\n `[@fayz-ai/core] Filter \"${key}\" is an object with no recognised bound. ` +\n 'Use {from,to} / {gte,lte,gt,lt} for ranges, or a plain value for equality.',\n )\n }\n q = (q as { eq: (c: string, v: unknown) => unknown }).eq(col, val) as Record<string, unknown>\n }\n }\n\n if (query.search && searchCols.length > 0) {\n const term = `%${query.search}%`\n const orClause = searchCols.map((col) => `${camelToSnake(col)}.ilike.${term}`).join(',')\n q = (q as { or: (c: string) => unknown }).or(orClause) as Record<string, unknown>\n }\n\n if (query.sortBy) {\n q = (q as { order: (col: string, opts: unknown) => unknown })\n .order(camelToSnake(query.sortBy), { ascending: query.sortDir !== 'desc' }) as Record<string, unknown>\n } else {\n q = (q as { order: (col: string, opts: unknown) => unknown })\n .order('created_at', { ascending: false }) as Record<string, unknown>\n }\n\n const page = query.page ?? 1\n const pageSize = query.pageSize ?? 50\n const from = (page - 1) * pageSize\n q = (q as { range: (f: number, t: number) => unknown }).range(from, from + pageSize - 1) as Record<string, unknown>\n\n const { data, error, count } = await q as { data: Record<string, unknown>[] | null; error: unknown; count: number | null }\n if (error) throw error\n\n return {\n data: (data ?? []).map((row) => mapRow<T>(row, columnMap)),\n total: count ?? 0,\n }\n },\n\n async create(data) {\n const row = mapEntity(data as Record<string, unknown>, columnMap)\n const tenantId = resolveTenantId()\n if (tenantId) row[tenantIdCol] = tenantId\n const scope = unitScope()\n if (scope && row[scope.column] === undefined) row[scope.column] = scope.id\n if (config?.defaults) {\n for (const [col, val] of Object.entries(config.defaults)) {\n if (row[col] === undefined) row[col] = val\n }\n }\n\n const { data: created, error } = (await (getClient().from(table) as {\n insert: (r: unknown) => { select: () => { single: () => Promise<{ data: unknown; error: unknown }> } }\n }).insert(row).select().single()) as { data: Record<string, unknown> | null; error: unknown }\n\n if (error) throw error\n return mapRow<T>(created!, columnMap)\n },\n\n async createMany(rows, options) {\n const tenantId = resolveTenantId()\n const size = Math.max(1, options?.chunkSize ?? 200)\n const scope = unitScope()\n const prepared = rows.map((r) => {\n const row = mapEntity(r as Record<string, unknown>, columnMap)\n if (tenantId) row[tenantIdCol] = tenantId\n if (scope && row[scope.column] === undefined) row[scope.column] = scope.id\n if (config?.defaults) {\n for (const [col, val] of Object.entries(config.defaults)) {\n if (row[col] === undefined) row[col] = val\n }\n }\n return row\n })\n\n let created = 0\n const failed: Array<{ index: number; message: string }> = []\n for (let start = 0; start < prepared.length; start += size) {\n const chunk = prepared.slice(start, start + size)\n const { error } = (await (getClient().from(table) as {\n insert: (r: unknown) => Promise<{ error: unknown }>\n }).insert(chunk)) as { error: unknown }\n if (!error) {\n created += chunk.length\n } else {\n // One bad row rejects its whole chunk. Retrying it row by row keeps\n // the other 199 — an import that loses 200 records over one malformed\n // phone number is worse than no import.\n const message = errorMessage(error)\n for (let i = 0; i < chunk.length; i++) {\n const { error: rowError } = (await (getClient().from(table) as {\n insert: (r: unknown) => Promise<{ error: unknown }>\n }).insert(chunk[i])) as { error: unknown }\n if (rowError) {\n failed.push({\n index: start + i,\n message: rowError instanceof Error ? rowError.message : String(rowError ?? message),\n })\n } else created += 1\n }\n }\n options?.onProgress?.(Math.min(start + chunk.length, prepared.length), prepared.length)\n }\n return { created, failed }\n },\n\n async update(id, data) {\n const row = mapEntity(data as Record<string, unknown>, columnMap)\n const { data: updated, error } = (await (getClient().from(table) as {\n update: (r: unknown) => { eq: (c: string, v: string) => { select: () => { single: () => Promise<{ data: unknown; error: unknown }> } } }\n }).update(row).eq('id', id).select().single()) as { data: Record<string, unknown> | null; error: unknown }\n\n if (error) throw error\n return mapRow<T>(updated!, columnMap)\n },\n\n // `.select('id')` e não um delete mudo — ver data/write-refused: a RLS\n // recusa com 204 e zero linhas, que sem isto chega aqui como sucesso.\n async remove(id) {\n const { data, error } = (await (getClient().from(table) as {\n delete: () => { eq: (c: string, v: string) => { select: (c: string) => Promise<{ data: unknown[] | null; error: unknown }> } }\n }).delete().eq('id', id).select('id')) as { data: unknown[] | null; error: unknown }\n if (error) throw error\n assertWrote(data)\n },\n\n async removeMany(ids) {\n if (!ids.length) return\n const { data, error } = (await (getClient().from(table) as {\n delete: () => { in: (c: string, v: string[]) => { select: (c: string) => Promise<{ data: unknown[] | null; error: unknown }> } }\n }).delete().in('id', ids).select('id')) as { data: unknown[] | null; error: unknown }\n if (error) throw error\n assertWrote(data)\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry<T = unknown> {\n data: T\n expiresAt: number\n lastAccess: number\n}\n\nexport interface CacheStore {\n get<T>(key: string): T | undefined\n set<T>(key: string, data: T, ttl?: number): void\n /** Clear all entries whose key starts with `prefix` */\n invalidate(prefix: string): void\n clear(): void\n size(): number\n}\n\nexport interface CacheConfig {\n /** Default time-to-live in ms (default: 60 000 — 1 minute) */\n defaultTTL?: number\n /** Maximum cached entries before LRU eviction (default: 500) */\n maxEntries?: number\n}\n\n// ---------------------------------------------------------------------------\n// Stable key helper — deterministic JSON for cache keys\n// ---------------------------------------------------------------------------\n\nfunction sortDeep(value: unknown): unknown {\n if (value === null || value === undefined || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map(sortDeep)\n const sorted: Record<string, unknown> = {}\n for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n sorted[k] = sortDeep((value as Record<string, unknown>)[k])\n }\n return sorted\n}\n\nexport function stableKey(...parts: unknown[]): string {\n return parts.map((p) => (typeof p === 'string' ? p : JSON.stringify(sortDeep(p)))).join(':')\n}\n\n// ---------------------------------------------------------------------------\n// CacheStore implementation\n// ---------------------------------------------------------------------------\n\n/** Shared so the persistent layer can reconstruct the same `expiresAt` L1 computed. */\nexport const DEFAULT_CACHE_TTL = 60_000\n\nexport function createCacheStore(config?: CacheConfig): CacheStore {\n const defaultTTL = config?.defaultTTL ?? DEFAULT_CACHE_TTL\n const maxEntries = config?.maxEntries ?? 500\n const entries = new Map<string, CacheEntry>()\n\n function evictLRU() {\n if (entries.size <= maxEntries) return\n // Find the entry with the oldest lastAccess\n let oldestKey: string | null = null\n let oldestAccess = Infinity\n for (const [key, entry] of entries) {\n if (entry.lastAccess < oldestAccess) {\n oldestAccess = entry.lastAccess\n oldestKey = key\n }\n }\n if (oldestKey) entries.delete(oldestKey)\n }\n\n return {\n get<T>(key: string): T | undefined {\n const entry = entries.get(key)\n if (!entry) return undefined\n if (Date.now() > entry.expiresAt) {\n entries.delete(key)\n return undefined\n }\n entry.lastAccess = Date.now()\n return entry.data as T\n },\n\n set<T>(key: string, data: T, ttl?: number): void {\n const now = Date.now()\n entries.set(key, {\n data,\n expiresAt: now + (ttl ?? defaultTTL),\n lastAccess: now,\n })\n evictLRU()\n },\n\n invalidate(prefix: string): void {\n for (const key of entries.keys()) {\n if (key.startsWith(prefix)) {\n entries.delete(key)\n }\n }\n },\n\n clear(): void {\n entries.clear()\n },\n\n size(): number {\n return entries.size\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Global singleton — auto-flushes on tenant/org switch\n// ---------------------------------------------------------------------------\n\nexport const globalCache = createCacheStore()\n\n/** Flush the global cache — called by the tenant accessor when the active\n * tenant changes (replaces the old org-store subscription). */\nexport function clearGlobalCache(): void {\n globalCache.clear()\n}\n","// Active tenant context — a global accessor the data layer reads for tenant\n// scoping and cache keying. The host (saas shell / app) sets it from its org\n// store via setActiveTenantId; the data providers stay decoupled from any\n// specific org-store implementation (runtime DI).\nimport * as React from 'react'\nimport { clearGlobalCache } from './cache'\n\nlet _activeTenantId: string | undefined\nconst listeners = new Set<() => void>()\n\nexport function setActiveTenantId(id: string | undefined): void {\n if (id !== _activeTenantId) {\n _activeTenantId = id\n clearGlobalCache()\n for (const l of listeners) l()\n }\n}\n\nexport function getActiveTenantId(): string | undefined {\n return _activeTenantId\n}\n\n/**\n * Subscribes to tenant changes. A getter alone makes every read a race with the\n * shell: a view that mounts before the org store resolves reads `undefined`,\n * and a tenant-scoped fetch that answers \"no tenant\" looks exactly like \"no\n * rows\" — it never retries and the screen stays empty until a remount.\n */\nexport function subscribeActiveTenant(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/** The active tenant, re-rendering when it resolves or changes. Use this to key\n * effects that fetch tenant-scoped data. */\nexport function useActiveTenantId(): string | undefined {\n return React.useSyncExternalStore(subscribeActiveTenant, getActiveTenantId, getActiveTenantId)\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/data/write-refused.ts","../src/data/filters.ts","../src/data/unit-scope.ts","../src/data/supabase.ts","../src/lib/cache.ts","../src/lib/tenant.ts"],"names":[],"mappings":"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","file":"chunk-6FCJLF55.js","sourcesContent":["// ---------------------------------------------------------------------------\n// UMA ESCRITA QUE NÃO PEGOU LINHA NENHUMA NÃO É UM SUCESSO.\n//\n// O PostgREST responde **204** a um DELETE que a RLS recusou — a política não\n// dá erro, ela apenas não deixa a linha entrar no `USING`, e a instrução apaga\n// zero linhas. Do lado do cliente, `error` volta `null`. Todo provider deste\n// SDK lia isso como \"apagado\".\n//\n// O que isso produz na tela é a pior forma de falha que existe: a de sucesso.\n// O diálogo fecha, o brinde diz \"Agendamento excluído\", a lista recarrega\n// inteira — e o agendamento continua lá. Foi assim que chegaram, no mesmo dia,\n// \"não consigo excluir o cliente\" e \"não consigo excluir o agendamento\": o\n// mesmo 204, em dois cadastros diferentes, com a mesma causa — o papel não tem\n// a permissão de exclusão e nada na tela diz isso.\n//\n// A correção é pedir as linhas de volta (`.select('id')`) e tratar a lista\n// vazia como recusa. Custa um campo no corpo da resposta e transforma um\n// engano silencioso numa mensagem.\n//\n// ── Por que a mensagem fala de DUAS causas ────────────────────────────────\n//\n// De fora, \"a RLS recusou\" e \"a linha não existe mais\" são indistinguíveis —\n// as duas apagam zero linhas. Chutar uma delas seria mandar o operador para o\n// lugar errado metade das vezes; dizer as duas o manda para a pergunta certa\n// (\"quem eu sou aqui?\" ou \"alguém já apagou?\").\n// ---------------------------------------------------------------------------\n\n/** O texto que a tela mostra quando uma escrita não pegou linha nenhuma. */\nexport const WRITE_REFUSED_MESSAGE =\n 'Sem permissão para esta ação, ou o registro já não existe.'\n\nexport class WriteRefusedError extends Error {\n /** Para quem quiser distinguir isto de uma falha de rede sem ler a mensagem. */\n readonly refused = true\n constructor(message: string = WRITE_REFUSED_MESSAGE) {\n super(message)\n this.name = 'WriteRefusedError'\n }\n}\n\n/**\n * Confirma que a escrita pegou pelo menos uma linha.\n *\n * `rows` é o que `.select(...)` devolveu. `null` conta como recusa — é o que o\n * PostgREST devolve quando não se pediu representação nenhuma, e nesse caso o\n * chamador não deveria estar usando esta função.\n */\nexport function assertWrote(rows: unknown[] | null | undefined, message?: string): void {\n if (rows && rows.length > 0) return\n throw new WriteRefusedError(message)\n}\n","/**\n * ONE filter grammar, shared by the providers and by the agent's data tools.\n *\n * Models express ranges in whatever dialect they were trained on — `{from,to}`,\n * `{gte,lte}`, Mongo's `{$gte,$lt}`. When only some of those are recognised the\n * unrecognised one falls through to an equality compare against\n * \"[object Object]\", which matches nothing and reads as \"there are no\n * appointments today\" instead of \"I did not understand that filter\".\n */\n\nexport interface RangeFilter {\n gte?: unknown\n gt?: unknown\n lte?: unknown\n lt?: unknown\n}\n\nconst LOWER_INCLUSIVE = ['from', 'gte', 'start', 'after', 'since', 'min']\nconst LOWER_EXCLUSIVE = ['gt']\nconst UPPER_INCLUSIVE = ['to', 'lte', 'end', 'until', 'before', 'max']\nconst UPPER_EXCLUSIVE = ['lt']\n\nfunction pick(source: Record<string, unknown>, names: string[]): unknown {\n for (const name of names) {\n if (source[name] !== undefined && source[name] !== null) return source[name]\n const dollar = `$${name}`\n if (source[dollar] !== undefined && source[dollar] !== null) return source[dollar]\n }\n return undefined\n}\n\n/**\n * Parse a filter value into a range, or return null when it is a plain\n * (equality) value. An object that looks like a filter but carries no\n * recognised bound returns `null` too — callers treat that as \"invalid\",\n * never as \"matches nothing\".\n */\nexport function parseRangeFilter(value: unknown): RangeFilter | null {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return null\n const source = value as Record<string, unknown>\n const range: RangeFilter = {}\n const gte = pick(source, LOWER_INCLUSIVE)\n const gt = pick(source, LOWER_EXCLUSIVE)\n const lte = pick(source, UPPER_INCLUSIVE)\n const lt = pick(source, UPPER_EXCLUSIVE)\n if (gte !== undefined) range.gte = gte\n if (gt !== undefined) range.gt = gt\n if (lte !== undefined) range.lte = lte\n if (lt !== undefined) range.lt = lt\n return Object.keys(range).length ? range : null\n}\n\n/** True for any object value — including ones no bound could be read from. */\nexport function isFilterObject(value: unknown): boolean {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction comparable(value: unknown): number {\n if (typeof value === 'number') return value\n if (value instanceof Date) return value.getTime()\n const ts = Date.parse(String(value))\n if (!Number.isNaN(ts)) return ts\n return Number(value)\n}\n\n/** Client-side counterpart of the range a provider would push to SQL. */\nexport function matchesRange(raw: unknown, range: RangeFilter): boolean {\n const value = comparable(raw)\n if (Number.isNaN(value)) return false\n if (range.gte !== undefined && value < comparable(range.gte)) return false\n if (range.gt !== undefined && value <= comparable(range.gt)) return false\n if (range.lte !== undefined && value > comparable(range.lte)) return false\n if (range.lt !== undefined && value >= comparable(range.lt)) return false\n return true\n}\n\n/** Apply a range to a PostgREST query builder. */\nexport function applyRangeToQuery<Q>(\n query: Q,\n column: string,\n range: RangeFilter,\n): Q {\n let q = query as Record<string, (c: string, v: unknown) => unknown>\n for (const op of ['gte', 'gt', 'lte', 'lt'] as const) {\n const bound = range[op]\n if (bound === undefined) continue\n q = q[op](column, bound) as Record<string, (c: string, v: unknown) => unknown>\n }\n return q as Q\n}\n","// ---------------------------------------------------------------------------\n// The one place that turns \"the user is looking at Ipanema\" into a filter.\n//\n// This is a NARROWING and nothing else. Which units a person may reach is\n// decided by row-level security; if this file were deleted the app would show\n// more rows than the switcher promises, and still not one row the database\n// would have refused. That asymmetry is deliberate — it is what lets the filter\n// live in the client at all.\n// ---------------------------------------------------------------------------\n\nexport interface UnitScope {\n column: string\n id: string\n /** Whether rows with no unit (\"belongs to the whole organization\") come too. */\n includeShared: boolean\n}\n\n/** One row of `public.scoped_resources` — which tables carry a unit, and under\n * which column. */\nexport interface ScopedResource {\n unitColumn: string\n shareable: boolean\n}\n\n// The registry, mirrored from the database rather than restated here.\n//\n// The list of scoped tables and their unit columns lives in ONE place —\n// `public.scoped_resources`, seeded by migration 041 — and the shell publishes\n// it at boot. A second copy in TypeScript would drift on the first table anyone\n// adds, and the failure mode of that drift is a screen that quietly stops\n// narrowing while the database still does.\nlet _scoped: Record<string, ScopedResource> = {}\n\nexport function setScopedResources(map: Record<string, ScopedResource>): void {\n _scoped = map\n}\n\nexport function getScopedResource(table: string | undefined): ScopedResource | undefined {\n if (!table) return undefined\n return _scoped[table]\n}\n\nexport interface UnitScopeConfig {\n unitScoped?: boolean\n unitColumn?: string\n /**\n * Default true. A row with no unit belongs to the brand and must survive a\n * unit selection — the Botox that is sold everywhere still shows in Ipanema.\n *\n * False for ledgers and aggregations, where a shared row summed into every\n * unit is a wrong number stated confidently, which is worse than a missing\n * one.\n */\n includeSharedRows?: boolean\n unitId?: () => string | undefined\n}\n\n/**\n * `null` means \"do not narrow\" — unit mode off, \"todas as unidades\", or an\n * entity that never opted in. Callers treat null as a no-op, which is what\n * keeps every provider's behaviour identical for entities that are not scoped.\n */\nexport function resolveUnitScope(config: UnitScopeConfig): UnitScope | null {\n if (!config.unitScoped) return null\n const id = config.unitId?.()\n if (!id) return null\n return {\n column: config.unitColumn ?? 'unit_id',\n id,\n includeShared: config.includeSharedRows !== false,\n }\n}\n\n/**\n * Applies the scope to a PostgREST builder.\n *\n * The shared form is `or(unit.eq.X,unit.is.null)`. Multiple `or=` params AND\n * together in PostgREST, so this composes correctly with the search `.or()` the\n * providers already emit: (name matches) AND (unit is mine or nobody's).\n *\n * Note what is NOT here: an `.in(column, [...myUnits])`. Enumerating the user's\n * units in the browser is the exact mistake this whole design exists to avoid —\n * it would put the answer to \"which units may I see\" in the client, where it\n * can be edited.\n */\nexport function applyUnitScope<Q>(builder: Q, scope: UnitScope | null): Q {\n if (!scope) return builder\n const q = builder as unknown as {\n or: (f: string) => unknown\n eq: (c: string, v: unknown) => unknown\n }\n return (scope.includeShared\n ? q.or(`${scope.column}.eq.${scope.id},${scope.column}.is.null`)\n : q.eq(scope.column, scope.id)) as unknown as Q\n}\n","import { errorMessage } from '../lib/error-message'\nimport { assertWrote } from './write-refused'\nimport type { DataProvider, CrudQuery, CrudResult } from './types'\nimport { applyRangeToQuery, isFilterObject, parseRangeFilter } from './filters'\nimport { applyUnitScope, resolveUnitScope } from './unit-scope'\n\nexport interface SupabaseProviderConfig {\n schema?: string\n tenantId?: string | (() => string | undefined)\n tenantIdColumn?: string\n searchColumns?: string[]\n selectColumns?: string\n columnMap?: Record<string, string>\n filters?: Record<string, string | string[]>\n defaults?: Record<string, unknown>\n supabaseClient?: unknown\n /** Franchise scoping. Absent (every entity today) ⇒ nothing below changes. */\n unitScoped?: boolean\n unitColumn?: string\n includeSharedRows?: boolean\n unitId?: () => string | undefined\n}\n\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (c) => '_' + c.toLowerCase())\n}\n\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase())\n}\n\nfunction mapRow<T>(row: Record<string, unknown>, columnMap?: Record<string, string>): T {\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(row)) {\n const camelKey = columnMap?.[key] ?? snakeToCamel(key)\n result[camelKey] = value\n }\n return result as T\n}\n\nfunction mapEntity(entity: Record<string, unknown>, columnMap?: Record<string, string>): Record<string, unknown> {\n const reverseMap: Record<string, string> = {}\n if (columnMap) {\n for (const [snake, camel] of Object.entries(columnMap)) {\n reverseMap[camel] = snake\n }\n }\n const result: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(entity)) {\n // `createdBy` entra na lista: quem o escreve é o gatilho do banco, com\n // `auth.uid()`. Deixá-lo passar daria à interface a chance de dizer quem\n // cadastrou — e um autor que o próprio formulário pode escolher não vale\n // como procedência. Ver packages/db/migrations/025_created_by.sql.\n // `ownerId` for the same reason: 041's trigger decides who a record belongs\n // to, and a form that could set it would be handing records to other people.\n if (key === 'id' || key === 'createdAt' || key === 'updatedAt' || key === 'tenantId' || key === 'createdBy' || key === 'ownerId') continue\n const snakeKey = reverseMap[key] ?? camelToSnake(key)\n result[snakeKey] = value\n }\n return result\n}\n\n// Global Supabase client registry — set once via createFayzApp\nlet _globalSupabaseClient: unknown | null = null\n\nexport function setGlobalSupabaseClient(client: unknown): void {\n _globalSupabaseClient = client\n}\n\nexport function getSupabaseClientOptional(): unknown | null {\n return _globalSupabaseClient\n}\n\nexport function createSupabaseProvider<T extends { id: string }>(\n table: string,\n config?: SupabaseProviderConfig,\n): DataProvider<T> {\n const schema = config?.schema ?? 'public'\n const tenantIdCol = config?.tenantIdColumn ?? 'tenant_id'\n const searchCols = config?.searchColumns ?? []\n const selectCols = config?.selectColumns ?? '*'\n const columnMap = config?.columnMap\n\n function resolveTenantId(): string | undefined {\n if (!config?.tenantId) return undefined\n return typeof config.tenantId === 'function' ? config.tenantId() : config.tenantId\n }\n\n function unitScope() {\n return resolveUnitScope(config ?? {})\n }\n\n function getClient() {\n const supabase = (config?.supabaseClient ?? _globalSupabaseClient) as {\n schema: (s: string) => unknown\n from: (t: string) => unknown\n } | null\n if (!supabase) throw new Error(`[@fayz-ai/core] Supabase client not available. Call createFayzApp with a provider first.`)\n return schema === 'public' ? (supabase as { from: (t: string) => unknown }) : (supabase.schema(schema) as { from: (t: string) => unknown })\n }\n\n return {\n async list(query: CrudQuery): Promise<CrudResult<T>> {\n // 'none' skips PostgREST's count aggregate entirely — see CrudQuery.countMode.\n const wantCount = query.countMode !== 'none'\n let q = (getClient().from(table) as { select: (...args: unknown[]) => unknown })\n .select(selectCols, wantCount ? { count: 'exact' } : undefined) as Record<string, unknown>\n\n const tenantId = resolveTenantId()\n if (tenantId) q = (q as { eq: (col: string, val: string) => unknown }).eq(tenantIdCol, tenantId) as Record<string, unknown>\n\n // Narrowing only — the boundary itself is the restrictive policy in the\n // database. Placed before the search `.or()` because PostgREST ANDs the\n // two `or=` params, which is what makes them compose.\n q = applyUnitScope(q, unitScope()) as Record<string, unknown>\n\n if (config?.filters) {\n for (const [col, val] of Object.entries(config.filters)) {\n // UMA LISTA é `in`, não `eq`.\n //\n // Com `eq`, um array vira `kind=eq.a,b,c` — um valor único com\n // vírgulas, que não casa com nada e devolve lista vazia. E o caso que\n // pediu isto não é teórico: `plg_financial_accounts` guarda as contas\n // do RAZÃO, e no Pertinho são 122.224 linhas `person` ao lado de 233\n // contas bancárias de verdade. A tela de contas precisa das cinco\n // espécies de conta bancária, e sem `in` ela lista a casa inteira.\n q = Array.isArray(val)\n ? (q as { in: (c: string, v: readonly string[]) => unknown }).in(col, val) as Record<string, unknown>\n : (q as { eq: (c: string, v: string) => unknown }).eq(col, val) as Record<string, unknown>\n }\n }\n\n // Faceted / dynamic filters from the query, mapped to columns. Equality\n // by default; a range object ({from,to} / {gte,lt} / {$gte,$lt}) becomes\n // real SQL bounds instead of an equality compare against \"[object Object]\".\n if (query.filters) {\n for (const [key, val] of Object.entries(query.filters)) {\n if (val == null || val === '') continue\n const col = columnMap?.[key] ?? camelToSnake(key)\n const range = parseRangeFilter(val)\n if (range) {\n q = applyRangeToQuery(q, col, range)\n continue\n }\n if (isFilterObject(val)) {\n throw new Error(\n `[@fayz-ai/core] Filter \"${key}\" is an object with no recognised bound. ` +\n 'Use {from,to} / {gte,lte,gt,lt} for ranges, or a plain value for equality.',\n )\n }\n q = (q as { eq: (c: string, v: unknown) => unknown }).eq(col, val) as Record<string, unknown>\n }\n }\n\n if (query.search && searchCols.length > 0) {\n const term = `%${query.search}%`\n const orClause = searchCols.map((col) => `${camelToSnake(col)}.ilike.${term}`).join(',')\n q = (q as { or: (c: string) => unknown }).or(orClause) as Record<string, unknown>\n }\n\n if (query.sortBy) {\n q = (q as { order: (col: string, opts: unknown) => unknown })\n .order(camelToSnake(query.sortBy), { ascending: query.sortDir !== 'desc' }) as Record<string, unknown>\n } else {\n q = (q as { order: (col: string, opts: unknown) => unknown })\n .order('created_at', { ascending: false }) as Record<string, unknown>\n }\n\n const page = query.page ?? 1\n const pageSize = query.pageSize ?? 50\n const from = (page - 1) * pageSize\n q = (q as { range: (f: number, t: number) => unknown }).range(from, from + pageSize - 1) as Record<string, unknown>\n\n const { data, error, count } = await q as { data: Record<string, unknown>[] | null; error: unknown; count: number | null }\n if (error) throw error\n\n return {\n data: (data ?? []).map((row) => mapRow<T>(row, columnMap)),\n total: count ?? 0,\n }\n },\n\n async create(data) {\n const row = mapEntity(data as Record<string, unknown>, columnMap)\n const tenantId = resolveTenantId()\n if (tenantId) row[tenantIdCol] = tenantId\n const scope = unitScope()\n if (scope && row[scope.column] === undefined) row[scope.column] = scope.id\n if (config?.defaults) {\n for (const [col, val] of Object.entries(config.defaults)) {\n if (row[col] === undefined) row[col] = val\n }\n }\n\n const { data: created, error } = (await (getClient().from(table) as {\n insert: (r: unknown) => { select: () => { single: () => Promise<{ data: unknown; error: unknown }> } }\n }).insert(row).select().single()) as { data: Record<string, unknown> | null; error: unknown }\n\n if (error) throw error\n return mapRow<T>(created!, columnMap)\n },\n\n async createMany(rows, options) {\n const tenantId = resolveTenantId()\n const size = Math.max(1, options?.chunkSize ?? 200)\n const scope = unitScope()\n const prepared = rows.map((r) => {\n const row = mapEntity(r as Record<string, unknown>, columnMap)\n if (tenantId) row[tenantIdCol] = tenantId\n if (scope && row[scope.column] === undefined) row[scope.column] = scope.id\n if (config?.defaults) {\n for (const [col, val] of Object.entries(config.defaults)) {\n if (row[col] === undefined) row[col] = val\n }\n }\n return row\n })\n\n let created = 0\n const failed: Array<{ index: number; message: string }> = []\n for (let start = 0; start < prepared.length; start += size) {\n const chunk = prepared.slice(start, start + size)\n const { error } = (await (getClient().from(table) as {\n insert: (r: unknown) => Promise<{ error: unknown }>\n }).insert(chunk)) as { error: unknown }\n if (!error) {\n created += chunk.length\n } else {\n // One bad row rejects its whole chunk. Retrying it row by row keeps\n // the other 199 — an import that loses 200 records over one malformed\n // phone number is worse than no import.\n const message = errorMessage(error)\n for (let i = 0; i < chunk.length; i++) {\n const { error: rowError } = (await (getClient().from(table) as {\n insert: (r: unknown) => Promise<{ error: unknown }>\n }).insert(chunk[i])) as { error: unknown }\n if (rowError) {\n failed.push({\n index: start + i,\n message: rowError instanceof Error ? rowError.message : String(rowError ?? message),\n })\n } else created += 1\n }\n }\n options?.onProgress?.(Math.min(start + chunk.length, prepared.length), prepared.length)\n }\n return { created, failed }\n },\n\n async update(id, data) {\n const row = mapEntity(data as Record<string, unknown>, columnMap)\n const { data: updated, error } = (await (getClient().from(table) as {\n update: (r: unknown) => { eq: (c: string, v: string) => { select: () => { single: () => Promise<{ data: unknown; error: unknown }> } } }\n }).update(row).eq('id', id).select().single()) as { data: Record<string, unknown> | null; error: unknown }\n\n if (error) throw error\n return mapRow<T>(updated!, columnMap)\n },\n\n // `.select('id')` e não um delete mudo — ver data/write-refused: a RLS\n // recusa com 204 e zero linhas, que sem isto chega aqui como sucesso.\n async remove(id) {\n const { data, error } = (await (getClient().from(table) as {\n delete: () => { eq: (c: string, v: string) => { select: (c: string) => Promise<{ data: unknown[] | null; error: unknown }> } }\n }).delete().eq('id', id).select('id')) as { data: unknown[] | null; error: unknown }\n if (error) throw error\n assertWrote(data)\n },\n\n async removeMany(ids) {\n if (!ids.length) return\n const { data, error } = (await (getClient().from(table) as {\n delete: () => { in: (c: string, v: string[]) => { select: (c: string) => Promise<{ data: unknown[] | null; error: unknown }> } }\n }).delete().in('id', ids).select('id')) as { data: unknown[] | null; error: unknown }\n if (error) throw error\n assertWrote(data)\n },\n }\n}\n","// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\ninterface CacheEntry<T = unknown> {\n data: T\n expiresAt: number\n lastAccess: number\n}\n\nexport interface CacheStore {\n get<T>(key: string): T | undefined\n set<T>(key: string, data: T, ttl?: number): void\n /** Clear all entries whose key starts with `prefix` */\n invalidate(prefix: string): void\n clear(): void\n size(): number\n}\n\nexport interface CacheConfig {\n /** Default time-to-live in ms (default: 60 000 — 1 minute) */\n defaultTTL?: number\n /** Maximum cached entries before LRU eviction (default: 500) */\n maxEntries?: number\n}\n\n// ---------------------------------------------------------------------------\n// Stable key helper — deterministic JSON for cache keys\n// ---------------------------------------------------------------------------\n\nfunction sortDeep(value: unknown): unknown {\n if (value === null || value === undefined || typeof value !== 'object') return value\n if (Array.isArray(value)) return value.map(sortDeep)\n const sorted: Record<string, unknown> = {}\n for (const k of Object.keys(value as Record<string, unknown>).sort()) {\n sorted[k] = sortDeep((value as Record<string, unknown>)[k])\n }\n return sorted\n}\n\nexport function stableKey(...parts: unknown[]): string {\n return parts.map((p) => (typeof p === 'string' ? p : JSON.stringify(sortDeep(p)))).join(':')\n}\n\n// ---------------------------------------------------------------------------\n// CacheStore implementation\n// ---------------------------------------------------------------------------\n\n/** Shared so the persistent layer can reconstruct the same `expiresAt` L1 computed. */\nexport const DEFAULT_CACHE_TTL = 60_000\n\nexport function createCacheStore(config?: CacheConfig): CacheStore {\n const defaultTTL = config?.defaultTTL ?? DEFAULT_CACHE_TTL\n const maxEntries = config?.maxEntries ?? 500\n const entries = new Map<string, CacheEntry>()\n\n function evictLRU() {\n if (entries.size <= maxEntries) return\n // Find the entry with the oldest lastAccess\n let oldestKey: string | null = null\n let oldestAccess = Infinity\n for (const [key, entry] of entries) {\n if (entry.lastAccess < oldestAccess) {\n oldestAccess = entry.lastAccess\n oldestKey = key\n }\n }\n if (oldestKey) entries.delete(oldestKey)\n }\n\n return {\n get<T>(key: string): T | undefined {\n const entry = entries.get(key)\n if (!entry) return undefined\n if (Date.now() > entry.expiresAt) {\n entries.delete(key)\n return undefined\n }\n entry.lastAccess = Date.now()\n return entry.data as T\n },\n\n set<T>(key: string, data: T, ttl?: number): void {\n const now = Date.now()\n entries.set(key, {\n data,\n expiresAt: now + (ttl ?? defaultTTL),\n lastAccess: now,\n })\n evictLRU()\n },\n\n invalidate(prefix: string): void {\n for (const key of entries.keys()) {\n if (key.startsWith(prefix)) {\n entries.delete(key)\n }\n }\n },\n\n clear(): void {\n entries.clear()\n },\n\n size(): number {\n return entries.size\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// Global singleton — auto-flushes on tenant/org switch\n// ---------------------------------------------------------------------------\n\nexport const globalCache = createCacheStore()\n\n/** Flush the global cache — called by the tenant accessor when the active\n * tenant changes (replaces the old org-store subscription). */\nexport function clearGlobalCache(): void {\n globalCache.clear()\n}\n","// Active tenant context — a global accessor the data layer reads for tenant\n// scoping and cache keying. The host (saas shell / app) sets it from its org\n// store via setActiveTenantId; the data providers stay decoupled from any\n// specific org-store implementation (runtime DI).\nimport * as React from 'react'\nimport { clearGlobalCache } from './cache'\n\nlet _activeTenantId: string | undefined\nconst listeners = new Set<() => void>()\n\nexport function setActiveTenantId(id: string | undefined): void {\n if (id !== _activeTenantId) {\n _activeTenantId = id\n clearGlobalCache()\n for (const l of listeners) l()\n }\n}\n\nexport function getActiveTenantId(): string | undefined {\n return _activeTenantId\n}\n\n/**\n * Subscribes to tenant changes. A getter alone makes every read a race with the\n * shell: a view that mounts before the org store resolves reads `undefined`,\n * and a tenant-scoped fetch that answers \"no tenant\" looks exactly like \"no\n * rows\" — it never retries and the screen stays empty until a remount.\n */\nexport function subscribeActiveTenant(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/** The active tenant, re-rendering when it resolves or changes. Use this to key\n * effects that fetch tenant-scoped data. */\nexport function useActiveTenantId(): string | undefined {\n return React.useSyncExternalStore(subscribeActiveTenant, getActiveTenantId, getActiveTenantId)\n}\n"]}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { tl } from './chunk-RXIOJQFH.js';
|
|
2
|
+
|
|
3
|
+
// src/data/errors.ts
|
|
4
|
+
var RETRYABLE_KINDS = ["offline", "server"];
|
|
5
|
+
var DataError = class extends Error {
|
|
6
|
+
constructor(kind, message, options = {}) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "DataError";
|
|
9
|
+
this.kind = kind;
|
|
10
|
+
this.retryable = RETRYABLE_KINDS.includes(kind);
|
|
11
|
+
this.cause = options.cause;
|
|
12
|
+
this.status = options.status;
|
|
13
|
+
this.code = options.code;
|
|
14
|
+
this.details = options.details;
|
|
15
|
+
this.hint = options.hint;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var CODE_KINDS = {
|
|
19
|
+
// PostgREST
|
|
20
|
+
PGRST100: "invalid",
|
|
21
|
+
// parse error in the query string
|
|
22
|
+
PGRST106: "missing-object",
|
|
23
|
+
// schema not exposed
|
|
24
|
+
PGRST116: "not-found",
|
|
25
|
+
// zero rows where exactly one was requested
|
|
26
|
+
PGRST202: "missing-object",
|
|
27
|
+
// function not found in schema cache
|
|
28
|
+
PGRST204: "missing-object",
|
|
29
|
+
// column not found in schema cache
|
|
30
|
+
PGRST205: "missing-object",
|
|
31
|
+
// table not found in schema cache
|
|
32
|
+
PGRST301: "unauthorized",
|
|
33
|
+
// JWT invalid or expired
|
|
34
|
+
PGRST302: "unauthorized",
|
|
35
|
+
// anonymous access disabled
|
|
36
|
+
// Postgres
|
|
37
|
+
"22007": "invalid",
|
|
38
|
+
// invalid datetime format
|
|
39
|
+
"22P02": "invalid",
|
|
40
|
+
// invalid input syntax
|
|
41
|
+
"23502": "invalid",
|
|
42
|
+
// not-null violation
|
|
43
|
+
"23505": "conflict",
|
|
44
|
+
// unique violation
|
|
45
|
+
"23514": "invalid",
|
|
46
|
+
// check violation
|
|
47
|
+
"42501": "forbidden",
|
|
48
|
+
// insufficient privilege / permission denied
|
|
49
|
+
"42883": "missing-object",
|
|
50
|
+
// undefined function
|
|
51
|
+
"42P01": "missing-object",
|
|
52
|
+
// undefined table
|
|
53
|
+
"53300": "server",
|
|
54
|
+
// too many connections
|
|
55
|
+
"57014": "server"
|
|
56
|
+
// statement timeout, server side
|
|
57
|
+
};
|
|
58
|
+
var OFFLINE_CODES = /* @__PURE__ */ new Set(["ECONNREFUSED", "ECONNRESET", "ENOTFOUND", "ETIMEDOUT", "EAI_AGAIN"]);
|
|
59
|
+
var OFFLINE_TEXT = /failed to fetch|fetch failed|network ?error|networkrequestfailed|load failed|connection (refused|reset|closed)|err_internet_disconnected|aborted|signal is aborted|the operation was aborted|timed out|timeout|sem conexão com a internet|no internet connection/i;
|
|
60
|
+
function statusKind(status) {
|
|
61
|
+
if (status === 401) return "unauthorized";
|
|
62
|
+
if (status === 403) return "forbidden";
|
|
63
|
+
if (status === 404 || status === 406) return "not-found";
|
|
64
|
+
if (status === 408) return "offline";
|
|
65
|
+
if (status === 409) return "conflict";
|
|
66
|
+
if (status === 400 || status === 422) return "invalid";
|
|
67
|
+
if (status >= 500) return "server";
|
|
68
|
+
return "server";
|
|
69
|
+
}
|
|
70
|
+
function textKind(message) {
|
|
71
|
+
const m = message.toLowerCase();
|
|
72
|
+
if (/permission denied|row-level security|row level security|insufficient privilege/.test(m)) return "forbidden";
|
|
73
|
+
if (/does not exist|undefined table|undefined function|schema cache|could not find the (table|function)/.test(m)) return "missing-object";
|
|
74
|
+
if (/jwt|not authenticated|invalid token|unauthorized|no api key/.test(m)) return "unauthorized";
|
|
75
|
+
if (/duplicate key|already exists|conflict/.test(m)) return "conflict";
|
|
76
|
+
if (/invalid input (syntax|value)|violates (not-null|check) constraint|invalid request/.test(m)) return "invalid";
|
|
77
|
+
if (/no rows|not found/.test(m)) return "not-found";
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
function pick(source, key) {
|
|
81
|
+
const value = source[key];
|
|
82
|
+
return value === null ? void 0 : value;
|
|
83
|
+
}
|
|
84
|
+
function readStatus(source) {
|
|
85
|
+
for (const key of ["status", "statusCode", "httpStatus"]) {
|
|
86
|
+
const value = pick(source, key);
|
|
87
|
+
if (typeof value === "number") return value;
|
|
88
|
+
if (typeof value === "string" && /^\d{3}$/.test(value)) return Number(value);
|
|
89
|
+
}
|
|
90
|
+
const response = pick(source, "response");
|
|
91
|
+
if (response && typeof response === "object") {
|
|
92
|
+
const status = response.status;
|
|
93
|
+
if (typeof status === "number") return status;
|
|
94
|
+
}
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
function isOffline(name, message, code, status) {
|
|
98
|
+
if (name === "AbortError" || name === "TimeoutError") return true;
|
|
99
|
+
if (code && OFFLINE_CODES.has(code)) return true;
|
|
100
|
+
if (status !== void 0) return false;
|
|
101
|
+
const nav = globalThis.navigator;
|
|
102
|
+
if (nav && nav.onLine === false) return true;
|
|
103
|
+
return OFFLINE_TEXT.test(message);
|
|
104
|
+
}
|
|
105
|
+
function toDataError(err) {
|
|
106
|
+
if (err instanceof DataError) return err;
|
|
107
|
+
const source = err && typeof err === "object" ? err : {};
|
|
108
|
+
const name = typeof source.name === "string" ? source.name : "";
|
|
109
|
+
const rawMessage = typeof source.message === "string" ? source.message : "";
|
|
110
|
+
const message = rawMessage || (typeof err === "string" ? err : "") || "data request failed";
|
|
111
|
+
const codeValue = pick(source, "code");
|
|
112
|
+
const code = typeof codeValue === "string" ? codeValue : typeof codeValue === "number" ? String(codeValue) : void 0;
|
|
113
|
+
const status = readStatus(source);
|
|
114
|
+
const details = typeof source.details === "string" ? source.details : void 0;
|
|
115
|
+
const hint = typeof source.hint === "string" ? source.hint : void 0;
|
|
116
|
+
let kind = code ? CODE_KINDS[code] : void 0;
|
|
117
|
+
if (!kind && isOffline(name, message, code, status)) kind = "offline";
|
|
118
|
+
if (!kind && status !== void 0) kind = statusKind(status);
|
|
119
|
+
if (!kind) kind = textKind(message);
|
|
120
|
+
return new DataError(kind ?? "server", message, { cause: err, status, code, details, hint });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/lib/error-message.ts
|
|
124
|
+
function errorMessage(err, fallback = "Unknown error") {
|
|
125
|
+
if (err != null && toDataError(err).kind === "offline") return offlineMessage();
|
|
126
|
+
if (err instanceof Error) return err.message.trim() || fallback;
|
|
127
|
+
if (typeof err === "string") return err;
|
|
128
|
+
if (err && typeof err === "object") {
|
|
129
|
+
const e = err;
|
|
130
|
+
const parts = [e.message, e.details, e.hint].filter((p) => typeof p === "string" && p.trim().length > 0);
|
|
131
|
+
if (parts.length > 0) return [...new Set(parts)].join(" \u2014 ");
|
|
132
|
+
if (typeof e.code === "string" && e.code) return `${fallback} (${e.code})`;
|
|
133
|
+
}
|
|
134
|
+
const text = String(err ?? "").trim();
|
|
135
|
+
return text && text !== "[object Object]" ? text : fallback;
|
|
136
|
+
}
|
|
137
|
+
function offlineMessage() {
|
|
138
|
+
return tl("No internet connection.", "Sem conex\xE3o com a internet.");
|
|
139
|
+
}
|
|
140
|
+
function describeError(err, fallback) {
|
|
141
|
+
return errorMessage(err, fallback ?? tl("Something went wrong. Try again.", "Algo deu errado. Tente de novo."));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export { DataError, describeError, errorMessage, offlineMessage, toDataError };
|
|
145
|
+
//# sourceMappingURL=chunk-DRDQ5JJG.js.map
|
|
146
|
+
//# sourceMappingURL=chunk-DRDQ5JJG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/errors.ts","../src/lib/error-message.ts"],"names":[],"mappings":"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","file":"chunk-DRDQ5JJG.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Data error taxonomy — one normalized shape for everything the data door can\n// fail with, so callers branch on a `kind` instead of grepping a message.\n//\n// The split that matters most is 'forbidden' vs 'missing-object': a PostgREST\n// \"permission denied for table x\" (42501) once surfaced to a user as \"analytics\n// engine missing\", sending everyone to look for a migration that was already\n// applied. They are different failures with different fixes — a grant versus a\n// deploy — and the taxonomy has to keep them apart.\n// ---------------------------------------------------------------------------\n\nexport type DataErrorKind =\n /** No usable network: fetch failed, request aborted, timed out. */\n | 'offline'\n /** No identity: missing/expired JWT. Re-authenticate. */\n | 'unauthorized'\n /** Identity known, access refused: RLS or a missing GRANT. */\n | 'forbidden'\n /** The object exists, the row does not. */\n | 'not-found'\n /** The table/view/function itself is not there — a deploy gap, not a data gap. */\n | 'missing-object'\n /** The write lost a race or duplicates a unique key. */\n | 'conflict'\n /** The request itself is wrong: bad syntax, constraint violated, no tenant. */\n | 'invalid'\n /** Anything the backend blames on itself. */\n | 'server'\n\n/** Only these are worth trying again with the same input. */\nconst RETRYABLE_KINDS: readonly DataErrorKind[] = ['offline', 'server']\n\nexport interface DataErrorOptions {\n /** The original throw, preserved for reporters that want the real object. */\n cause?: unknown\n /** HTTP status when the failure came over the wire. */\n status?: number\n /** PostgREST (`PGRST116`) or Postgres (`42501`) code. */\n code?: string\n details?: string\n /** Postgres often puts the actionable fix here — surface it, do not drop it. */\n hint?: string\n}\n\nexport class DataError extends Error {\n readonly kind: DataErrorKind\n readonly retryable: boolean\n readonly cause?: unknown\n readonly status?: number\n readonly code?: string\n readonly details?: string\n readonly hint?: string\n\n constructor(kind: DataErrorKind, message: string, options: DataErrorOptions = {}) {\n super(message)\n this.name = 'DataError'\n this.kind = kind\n this.retryable = RETRYABLE_KINDS.includes(kind)\n this.cause = options.cause\n this.status = options.status\n this.code = options.code\n this.details = options.details\n this.hint = options.hint\n }\n}\n\n// ---------------------------------------------------------------------------\n// Classification\n// ---------------------------------------------------------------------------\n\n/** Postgres + PostgREST codes are stable; branch on them before any text. */\nconst CODE_KINDS: Record<string, DataErrorKind> = {\n // PostgREST\n PGRST100: 'invalid', // parse error in the query string\n PGRST106: 'missing-object', // schema not exposed\n PGRST116: 'not-found', // zero rows where exactly one was requested\n PGRST202: 'missing-object', // function not found in schema cache\n PGRST204: 'missing-object', // column not found in schema cache\n PGRST205: 'missing-object', // table not found in schema cache\n PGRST301: 'unauthorized', // JWT invalid or expired\n PGRST302: 'unauthorized', // anonymous access disabled\n // Postgres\n '22007': 'invalid', // invalid datetime format\n '22P02': 'invalid', // invalid input syntax\n '23502': 'invalid', // not-null violation\n '23505': 'conflict', // unique violation\n '23514': 'invalid', // check violation\n '42501': 'forbidden', // insufficient privilege / permission denied\n '42883': 'missing-object', // undefined function\n '42P01': 'missing-object', // undefined table\n '53300': 'server', // too many connections\n '57014': 'server', // statement timeout, server side\n}\n\n/** Transport-level codes — the request never reached PostgREST. */\nconst OFFLINE_CODES = new Set(['ECONNREFUSED', 'ECONNRESET', 'ENOTFOUND', 'ETIMEDOUT', 'EAI_AGAIN'])\n\nconst OFFLINE_TEXT = /failed to fetch|fetch failed|network ?error|networkrequestfailed|load failed|connection (refused|reset|closed)|err_internet_disconnected|aborted|signal is aborted|the operation was aborted|timed out|timeout|sem conexão com a internet|no internet connection/i\n\nfunction statusKind(status: number): DataErrorKind {\n if (status === 401) return 'unauthorized'\n if (status === 403) return 'forbidden'\n if (status === 404 || status === 406) return 'not-found'\n if (status === 408) return 'offline'\n if (status === 409) return 'conflict'\n if (status === 400 || status === 422) return 'invalid'\n if (status >= 500) return 'server'\n return 'server'\n}\n\nfunction textKind(message: string): DataErrorKind | undefined {\n const m = message.toLowerCase()\n if (/permission denied|row-level security|row level security|insufficient privilege/.test(m)) return 'forbidden'\n if (/does not exist|undefined table|undefined function|schema cache|could not find the (table|function)/.test(m)) return 'missing-object'\n if (/jwt|not authenticated|invalid token|unauthorized|no api key/.test(m)) return 'unauthorized'\n if (/duplicate key|already exists|conflict/.test(m)) return 'conflict'\n if (/invalid input (syntax|value)|violates (not-null|check) constraint|invalid request/.test(m)) return 'invalid'\n if (/no rows|not found/.test(m)) return 'not-found'\n return undefined\n}\n\nfunction pick(source: Record<string, unknown>, key: string): unknown {\n const value = source[key]\n return value === null ? undefined : value\n}\n\nfunction readStatus(source: Record<string, unknown>): number | undefined {\n for (const key of ['status', 'statusCode', 'httpStatus']) {\n const value = pick(source, key)\n if (typeof value === 'number') return value\n if (typeof value === 'string' && /^\\d{3}$/.test(value)) return Number(value)\n }\n const response = pick(source, 'response')\n if (response && typeof response === 'object') {\n const status = (response as Record<string, unknown>).status\n if (typeof status === 'number') return status\n }\n return undefined\n}\n\nfunction isOffline(name: string, message: string, code: string | undefined, status: number | undefined): boolean {\n if (name === 'AbortError' || name === 'TimeoutError') return true\n if (code && OFFLINE_CODES.has(code)) return true\n if (status !== undefined) return false\n // navigator.onLine is a hint, not a verdict — it only decides errors we could\n // not classify from a code or a status.\n const nav = (globalThis as { navigator?: { onLine?: boolean } }).navigator\n if (nav && nav.onLine === false) return true\n return OFFLINE_TEXT.test(message)\n}\n\n/**\n * Normalizes anything thrown by the data layer into a {@link DataError}.\n * Idempotent — a DataError passes through untouched.\n */\nexport function toDataError(err: unknown): DataError {\n if (err instanceof DataError) return err\n\n const source: Record<string, unknown> =\n err && typeof err === 'object' ? (err as Record<string, unknown>) : {}\n const name = typeof source.name === 'string' ? source.name : ''\n const rawMessage = typeof source.message === 'string' ? source.message : ''\n const message = rawMessage || (typeof err === 'string' ? err : '') || 'data request failed'\n const codeValue = pick(source, 'code')\n const code = typeof codeValue === 'string' ? codeValue : typeof codeValue === 'number' ? String(codeValue) : undefined\n const status = readStatus(source)\n const details = typeof source.details === 'string' ? source.details : undefined\n const hint = typeof source.hint === 'string' ? source.hint : undefined\n\n let kind: DataErrorKind | undefined = code ? CODE_KINDS[code] : undefined\n if (!kind && isOffline(name, message, code, status)) kind = 'offline'\n if (!kind && status !== undefined) kind = statusKind(status)\n if (!kind) kind = textKind(message)\n\n return new DataError(kind ?? 'server', message, { cause: err, status, code, details, hint })\n}\n","import { toDataError } from '../data/errors'\nimport { tl } from '../i18n'\n\n/**\n * A readable message out of anything a data layer throws.\n *\n * Supabase/PostgREST reject with a PLAIN OBJECT ({message, details, hint,\n * code}), not an Error — so the usual `err instanceof Error ? err.message :\n * String(err)` renders the literal text \"[object Object]\" and hides the one\n * piece of information the user needed. The workspace wizard showed exactly\n * that for a 400 whose body named the failing constraint.\n *\n * Lives in core because every layer above it catches from Supabase: ui, admin,\n * storefront and the plugins all had their own copy of the broken ternary.\n * The careful branches below are plugin-shop's, which had already met this on\n * its settings screens and worked out what each case has to answer.\n *\n * `fallback` is a parameter because the last resort is the one string a user\n * actually reads, and it belongs in their language: SDK prose is English, but\n * merchant-facing copy is the carve-out (see fayz-ssr/AGENTS.md). plugin-shop\n * passes 'erro desconhecido'.\n */\nexport function errorMessage(err: unknown, fallback = 'Unknown error'): string {\n // \"TypeError: Failed to fetch\" is the browser's wording, not the user's: say what happened.\n if (err != null && toDataError(err).kind === 'offline') return offlineMessage()\n // An Error is answered by an Error, message or not. Falling through to the\n // object branch would reach `String(err)` and render the useless word\n // \"Error\" — a refusal with no reason is worse than a generic one, because\n // there is nothing to search for and nothing to report.\n if (err instanceof Error) return err.message.trim() || fallback\n if (typeof err === 'string') return err\n if (err && typeof err === 'object') {\n const e = err as { message?: unknown; details?: unknown; hint?: unknown; code?: unknown }\n // `hint` is worth keeping when Postgres offers one: on a permission error\n // it names the GRANT that is missing, which is the difference between a\n // shrug and a fix.\n const parts = [e.message, e.details, e.hint]\n .filter((p): p is string => typeof p === 'string' && p.trim().length > 0)\n if (parts.length > 0) return [...new Set(parts)].join(' — ')\n // Only a code to go on. The bare code alone (\"erro 23514\") reads as noise;\n // pairing it with the fallback keeps the sentence in the reader's language\n // and still carries the one token worth searching for.\n if (typeof e.code === 'string' && e.code) return `${fallback} (${e.code})`\n }\n const text = String(err ?? '').trim()\n // Never hand back the stringification of an object nobody can read.\n return text && text !== '[object Object]' ? text : fallback\n}\n\nexport function offlineMessage(): string {\n return tl('No internet connection.', 'Sem conexão com a internet.')\n}\n\n/**\n * The text to put on screen for a failure: offline is named as such, a PostgREST\n * object yields its message, and nothing renders as \"[object Object]\". The\n * fallback is in the reader's language.\n */\nexport function describeError(err: unknown, fallback?: string): string {\n return errorMessage(err, fallback ?? tl('Something went wrong. Try again.', 'Algo deu errado. Tente de novo.'))\n}\n"]}
|