@marktiderman/genesis-core 1.7.0 → 1.9.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/README.md +182 -0
- package/dist/chunk-52T2EYXH.js +52 -0
- package/dist/chunk-52T2EYXH.js.map +1 -0
- package/dist/chunk-5ORRAXIV.js +30 -0
- package/dist/chunk-5ORRAXIV.js.map +1 -0
- package/dist/{chunk-ZKMAU6NN.js → chunk-BGTXN2E6.js} +5 -4
- package/dist/chunk-BGTXN2E6.js.map +1 -0
- package/dist/chunk-DF2UWEEB.js +38 -0
- package/dist/chunk-DF2UWEEB.js.map +1 -0
- package/dist/{chunk-Y475Z7BM.js → chunk-EF5BKT4B.js} +99 -21
- package/dist/chunk-EF5BKT4B.js.map +1 -0
- package/dist/chunk-HBAPYA5K.js +257 -0
- package/dist/chunk-HBAPYA5K.js.map +1 -0
- package/dist/{chunk-5NHR6ZXS.js → chunk-LG6TUBVM.js} +2 -2
- package/dist/chunk-LG6TUBVM.js.map +1 -0
- package/dist/{chunk-QFR6YJ3R.js → chunk-NMFABW4Y.js} +6 -5
- package/dist/chunk-NMFABW4Y.js.map +1 -0
- package/dist/{chunk-ADD2RIGA.js → chunk-NZPJI7FC.js} +4 -3
- package/dist/chunk-NZPJI7FC.js.map +1 -0
- package/dist/{chunk-Y3SCYMNN.js → chunk-OSPLIKSH.js} +7 -7
- package/dist/{chunk-Y3SCYMNN.js.map → chunk-OSPLIKSH.js.map} +1 -1
- package/dist/chunk-RCAP3Q4T.js +75 -0
- package/dist/chunk-RCAP3Q4T.js.map +1 -0
- package/dist/{chunk-OBUBP4BY.js → chunk-S2ZXUNXB.js} +30 -6
- package/dist/chunk-S2ZXUNXB.js.map +1 -0
- package/dist/dates.d.ts +84 -0
- package/dist/dates.js +117 -0
- package/dist/dates.js.map +1 -0
- package/dist/hooks/draft-controller.d.ts +158 -0
- package/dist/hooks/draft-controller.js +4 -0
- package/dist/hooks/draft-controller.js.map +1 -0
- package/dist/hooks/index.d.ts +45 -3
- package/dist/hooks/index.js +15 -11
- package/dist/hooks/mutation-mode.d.ts +14 -2
- package/dist/hooks/mutation-mode.js +2 -1
- package/dist/hooks/related-keys.d.ts +1 -1
- package/dist/hooks/use-draft.d.ts +33 -0
- package/dist/hooks/use-draft.js +5 -0
- package/dist/hooks/use-draft.js.map +1 -0
- package/dist/hooks/use-infinite-resource.d.ts +1 -1
- package/dist/hooks/use-infinite-resource.js +2 -1
- package/dist/hooks/use-live.d.ts +1 -1
- package/dist/hooks/use-one.d.ts +1 -1
- package/dist/hooks/use-one.js +2 -1
- package/dist/hooks/use-resource-form.d.ts +1 -1
- package/dist/hooks/use-resource-form.js +8 -7
- package/dist/hooks/use-resource.d.ts +1 -1
- package/dist/hooks/use-resource.js +5 -4
- package/dist/hooks/use-write.d.ts +45 -0
- package/dist/hooks/use-write.js +4 -0
- package/dist/hooks/use-write.js.map +1 -0
- package/dist/index.d.ts +77 -5
- package/dist/index.js +16 -12
- package/dist/provider/index.d.ts +87 -8
- package/dist/provider/index.js +3 -3
- package/dist/{read-outcome-C_9HbXvb.d.ts → read-outcome-B6nACXZ7.d.ts} +4 -1
- package/dist/schema/index.d.ts +2 -2
- package/dist/schema/index.js +1 -1
- package/dist/{types-CS9UpxQS.d.ts → types-B9DDfvWa.d.ts} +1 -1
- package/package.json +11 -2
- package/dist/chunk-5NHR6ZXS.js.map +0 -1
- package/dist/chunk-ADD2RIGA.js.map +0 -1
- package/dist/chunk-OBUBP4BY.js.map +0 -1
- package/dist/chunk-QFR6YJ3R.js.map +0 -1
- package/dist/chunk-V3GDAELU.js +0 -3
- package/dist/chunk-V3GDAELU.js.map +0 -1
- package/dist/chunk-Y475Z7BM.js.map +0 -1
- package/dist/chunk-ZKMAU6NN.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider/postgrest-errors.ts","../src/provider/supabase-provider.ts","../src/provider/write-helpers.ts"],"names":["ties","run","from"],"mappings":"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","file":"chunk-EF5BKT4B.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Postgres / PostgREST error codes → Genesis write outcomes\n// ---------------------------------------------------------------------------\n\nimport {\n DataWriteError,\n isDataWriteError,\n type WriteOutcome,\n} from \"../schema/write-outcome\";\nimport type { ReadOutcome } from \"../schema/read-outcome\";\nimport { titleCase } from \"../utils\";\n\n/**\n * Maps a store error code to the read outcome it means: the write mapping,\n * with `22***` and PostgREST request errors (`PGRST1**` other than PGRST116,\n * `42703` unknown column, `42P01` unknown table) read as `invalid`.\n */\nexport function readOutcomeFromCode(code: string | null | undefined): ReadOutcome {\n if (!code) return \"error\";\n if (code === \"42703\" || code === \"42P01\" || (/^PGRST1\\d\\d$/.test(code) && code !== \"PGRST116\")) {\n return \"invalid\";\n }\n const write = writeOutcomeFromCode(code);\n return write === \"forbidden\" || write === \"gone\" || write === \"unauthenticated\" || write === \"invalid\"\n ? write\n : \"error\";\n}\n\n/**\n * Maps a Postgres SQLSTATE or PostgREST error code to the write outcome it\n * means. Anything not listed is `error`: a code is only read as a refusal or\n * a bad value when it says so.\n *\n * - `42501` (insufficient privilege, including \"new row violates row-level\n * security policy\") → `forbidden`\n * - `23505` (unique violation) → `duplicate`\n * - `22***` (data exception: bad type, out of range) and the other `23***`\n * (integrity constraint: `23503` foreign key, `23502` not null, `23514`\n * check) → `invalid`\n * - `PGRST301`–`PGRST303` (JWT missing, expired or rejected), `28000`,\n * `28P01` → `unauthenticated`\n * - `PGRST116` (one row was asked for and none came back) → `gone`\n */\nexport function writeOutcomeFromCode(code: string | null | undefined): WriteOutcome {\n if (!code) return \"error\";\n if (code === \"42501\") return \"forbidden\";\n if (code === \"PGRST116\") return \"gone\";\n if (code === \"PGRST301\" || code === \"PGRST302\" || code === \"PGRST303\") return \"unauthenticated\";\n if (code === \"28000\" || code === \"28P01\") return \"unauthenticated\";\n if (code === \"23505\") return \"duplicate\";\n if (/^2[23][0-9A-Z]{3}$/.test(code)) return \"invalid\";\n return \"error\";\n}\n\n/** A column name as Postgres reports it unquoted; anything else is not mapped. */\nconst COLUMN = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * One index expression as Postgres prints it: a plain column, or one column\n * wrapped in a function, optionally cast (`lower(email::text)`,\n * `lower((email)::text)`). Anything else is not mapped.\n */\nconst WRAPPED_COLUMN = /^[A-Za-z_][A-Za-z0-9_]*\\(\\(?([A-Za-z_][A-Za-z0-9_]*)\\)?(?:::[A-Za-z_][A-Za-z0-9_ ]*)?\\)$/;\n\nfunction columnOf(expression: string): string | undefined {\n if (COLUMN.test(expression)) return expression;\n return WRAPPED_COLUMN.exec(expression)?.[1];\n}\n\n/**\n * The columns of a `Key (a, b)=(...)` detail line, or none when any is not a\n * plain column (or one column in a simple expression index). The key list is\n * read with its parentheses balanced, so an expression index such as\n * `Key (lower(email::text))=(…)` is recognised.\n */\nfunction keyColumns(details: string, tail: RegExp): string[] {\n if (!details.startsWith(\"Key (\") || !tail.test(details)) return [];\n let depth = 1;\n let end = -1;\n for (let i = \"Key (\".length; i < details.length; i++) {\n const ch = details[i];\n if (ch === \"(\") depth++;\n else if (ch === \")\" && --depth === 0) {\n end = i;\n break;\n }\n }\n if (end < 0 || details.slice(end + 1, end + 3) !== \"=(\") return [];\n const parts: string[] = [];\n let level = 0;\n let current = \"\";\n for (const ch of details.slice(\"Key (\".length, end)) {\n if (ch === \"(\") level++;\n else if (ch === \")\") level--;\n if (ch === \",\" && level === 0) {\n parts.push(current.trim());\n current = \"\";\n } else {\n current += ch;\n }\n }\n parts.push(current.trim());\n const columns = parts.map(columnOf);\n return columns.length > 0 && columns.every((c): c is string => !!c) ? columns : [];\n}\n\nfunction listLabels(columns: string[]): string {\n const labels = columns.map((c) => titleCase(c));\n if (labels.length === 1) return labels[0]!;\n return `${labels.slice(0, -1).join(\", \")} and ${labels[labels.length - 1]}`;\n}\n\n/** Field errors read from a store error, with the safe sentence that sums them up. */\nexport interface PostgrestFieldErrors {\n /** Column name → safe message for that input. */\n fields: Record<string, string>;\n /** One safe sentence for the whole error, to replace the store's raw text. */\n message: string;\n}\n\n/**\n * Reads which columns a Postgres constraint error is about, from PostgREST's\n * `message` / `details`, and gives safe messages for them. Only plain column\n * names are taken from the store's text; the values it echoes are never\n * used. A unique violation whose columns cannot be read (a complex\n * expression index, no details), or an insert/update foreign-key violation\n * whose key is an expression, gets the safe sentence with no `fields`, so the\n * store's raw text (constraint name, echoed value) never reaches the user.\n * Returns `undefined` for anything else.\n *\n * - `23505` unique violation, `Key (email)=(…) already exists.` → each\n * column: \"This value is already in use.\" (several columns: \"This\n * combination is already in use.\")\n * - `23502` not-null, `null value in column \"title\" …` → \"This field is\n * required.\"\n * - `23503` foreign key on insert/update, `Key (owner_id)=(…) is not present\n * in table …` → \"The selected item doesn't exist.\"\n */\nexport function fieldErrorsFromPostgrest(error: {\n code?: unknown;\n message?: unknown;\n details?: unknown;\n}): PostgrestFieldErrors | undefined {\n const code = typeof error.code === \"string\" ? error.code : undefined;\n const details = typeof error.details === \"string\" ? error.details.trim() : \"\";\n const message = typeof error.message === \"string\" ? error.message : \"\";\n if (code === \"23505\") {\n const columns = keyColumns(details, /\\) already exists\\.?$/);\n // Not mappable to a column: still never show the store's raw text (it\n // names the constraint and echoes the value).\n if (columns.length === 0) return { fields: {}, message: \"This value is already in use.\" };\n const each = columns.length === 1 ? \"This value is already in use.\" : \"This combination is already in use.\";\n return {\n fields: Object.fromEntries(columns.map((c) => [c, each])),\n message:\n columns.length === 1\n ? `${listLabels(columns)} is already in use.`\n : `This combination of ${listLabels(columns)} is already in use.`,\n };\n }\n if (code === \"23502\") {\n const column = /^null value in column \"([A-Za-z_][A-Za-z0-9_]*)\"(?: |$)/.exec(message)?.[1];\n if (!column || !COLUMN.test(column)) return undefined;\n return {\n fields: { [column]: \"This field is required.\" },\n message: `${titleCase(column)} is required.`,\n };\n }\n if (code === \"23503\") {\n const tail = /\\) is not present in table \"[^\"]*\"\\.?$/;\n if (!tail.test(details)) return undefined;\n const columns = keyColumns(details, tail);\n if (columns.length === 0) return { fields: {}, message: \"The selected item doesn't exist.\" };\n return {\n fields: Object.fromEntries(columns.map((c) => [c, \"The selected item doesn't exist.\"])),\n message: `${listLabels(columns)}: the selected item doesn't exist.`,\n };\n }\n return undefined;\n}\n\n/**\n * Turns anything a write threw, or the `{ message, code }` error a Supabase\n * client returned, into a `DataWriteError`. A `DataWriteError` passes through\n * unchanged; anything else keeps its message and `code`, and is kept as the\n * `cause`.\n *\n * When the error names the columns it is about (`fieldErrorsFromPostgrest`:\n * unique, not-null and foreign-key violations), the result carries `fields`\n * and its message is replaced with a safe sentence; the raw text stays on\n * the `cause`.\n */\nexport function toDataWriteError(error: unknown): DataWriteError {\n if (isDataWriteError(error)) return error;\n const shape = (typeof error === \"object\" && error !== null ? error : {}) as {\n message?: unknown;\n code?: unknown;\n details?: unknown;\n };\n const code = typeof shape.code === \"string\" ? shape.code : undefined;\n const mapped = fieldErrorsFromPostgrest(shape);\n if (mapped) {\n return new DataWriteError(writeOutcomeFromCode(code), mapped.message, {\n code,\n fields: Object.keys(mapped.fields).length > 0 ? mapped.fields : undefined,\n cause: error,\n });\n }\n const message =\n typeof shape.message === \"string\" && shape.message\n ? shape.message\n : error instanceof Error\n ? error.message\n : String(error);\n return new DataWriteError(writeOutcomeFromCode(code), message, { code, cause: error });\n}\n","import type {\n BaseRecord,\n DataProvider,\n FilterParam,\n Identifier,\n InvalidationKey,\n ListParams,\n ResourceChangeEvent,\n WriteEvent,\n} from \"./types\";\nimport { DataWriteError } from \"../schema/write-outcome\";\nimport { DataReadError, readFailureMessage } from \"../schema/read-outcome\";\nimport type { FilterOperator, ProviderCapabilities } from \"../schema/types\";\nimport { readOutcomeFromCode, toDataWriteError } from \"./postgrest-errors\";\n\n// ---------------------------------------------------------------------------\n// Minimal Supabase client shape — no hard dependency on @supabase/supabase-js\n// ---------------------------------------------------------------------------\n\n/** A chainable query builder returned by `client.from(table)`. */\ninterface SupabaseQueryBuilder {\n select(\n columns?: string,\n options?: { count?: \"exact\" | \"planned\" | \"estimated\" },\n ): SupabaseQueryBuilder;\n insert(data: Record<string, unknown> | Record<string, unknown>[]): SupabaseQueryBuilder;\n update(data: Record<string, unknown>): SupabaseQueryBuilder;\n delete(): SupabaseQueryBuilder;\n eq(column: string, value: unknown): SupabaseQueryBuilder;\n neq(column: string, value: unknown): SupabaseQueryBuilder;\n gt(column: string, value: unknown): SupabaseQueryBuilder;\n lt(column: string, value: unknown): SupabaseQueryBuilder;\n gte(column: string, value: unknown): SupabaseQueryBuilder;\n lte(column: string, value: unknown): SupabaseQueryBuilder;\n ilike(column: string, value: string): SupabaseQueryBuilder;\n in(column: string, values: unknown[]): SupabaseQueryBuilder;\n is(column: string, value: null): SupabaseQueryBuilder;\n not(column: string, operator: string, value: unknown): SupabaseQueryBuilder;\n or(filters: string): SupabaseQueryBuilder;\n order(column: string, options?: { ascending?: boolean; nullsFirst?: boolean }): SupabaseQueryBuilder;\n range(from: number, to: number): SupabaseQueryBuilder;\n single(): SupabaseQueryBuilder;\n then: Promise<{ data: unknown; error: unknown; count?: number | null }>[\"then\"];\n}\n\n/**\n * Minimal Supabase client interface — the public shape this package has\n * always exposed for a caller that wants to type its own client or a\n * wrapper around one. Unchanged: `from()` still returns `SupabaseQueryBuilder`\n * so existing consumers that annotate a variable with this type and chain\n * off it keep type-checking exactly as before.\n */\nexport interface SupabaseClient {\n from(table: string): SupabaseQueryBuilder;\n /** Supabase Realtime; needed only for `subscribe` (see `SupabaseProviderOptions.realtime`). */\n channel?(name: string): SupabaseRealtimeChannel;\n /** Releases a channel made by `channel()`. */\n removeChannel?(channel: SupabaseRealtimeChannel): unknown;\n}\n\n/**\n * One `postgres_changes` message, as Supabase Realtime sends it. Only the\n * fields the provider reads; a real payload has more.\n */\nexport interface SupabaseRealtimePayload {\n eventType: \"INSERT\" | \"UPDATE\" | \"DELETE\";\n /** The row after an insert or update (empty for a delete). */\n new?: Record<string, unknown>;\n /** For a delete or update: the old row's key (all of it with `REPLICA IDENTITY FULL`). */\n old?: Record<string, unknown>;\n}\n\n/**\n * The part of a Supabase `RealtimeChannel` the provider uses. A real\n * `@supabase/supabase-js` channel satisfies it.\n */\nexport interface SupabaseRealtimeChannel {\n on(\n type: \"postgres_changes\",\n filter: {\n event: \"*\" | \"INSERT\" | \"UPDATE\" | \"DELETE\";\n schema: string;\n table: string;\n filter?: string;\n },\n callback: (payload: SupabaseRealtimePayload) => void,\n ): SupabaseRealtimeChannel;\n subscribe(callback?: (status: string, error?: unknown) => void): SupabaseRealtimeChannel;\n unsubscribe?(): unknown;\n}\n\n/**\n * What `createSupabaseProvider` actually requires from its `client` argument\n * — deliberately looser than the public `SupabaseClient` above, and used\n * ONLY for that one parameter. A real `@supabase/supabase-js` client's\n * `from()` returns a generic, table-typed `PostgrestQueryBuilder` whose chain\n * methods don't structurally match `SupabaseQueryBuilder`, which used to\n * force callers to pass their client through `as never`. Since `unknown` is\n * a supertype of everything, any real client's `from(table: string)`\n * satisfies THIS shape without a cast; `createSupabaseProvider` narrows to\n * `SupabaseQueryBuilder` internally, once, right after each `from()` call.\n * Loosening only the input here — rather than `SupabaseClient` itself —\n * means a caller relying on `SupabaseClient`'s stricter return type for\n * their own chaining is unaffected.\n */\nexport interface SupabaseClientInput {\n from(table: string): unknown;\n /** Database functions, for a resource whose `source` is an `rpc`. Any real client's `rpc` fits. */\n rpc?(fn: string, args?: Record<string, unknown>, options?: { count?: \"exact\" | \"planned\" | \"estimated\" }): unknown;\n /** Supabase Realtime, for `subscribe`. Any real client's `channel` fits. */\n channel?(name: string): unknown;\n removeChannel?(channel: unknown): unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Provider factory\n// ---------------------------------------------------------------------------\n\n/** What a write hook is told about the write it is part of. */\nexport interface SupabaseWriteContext {\n resource: string;\n action: \"create\" | \"update\";\n /** The row's id, for an update. */\n id?: Identifier;\n}\n\n/** Per-resource rules for `createSupabaseProvider`. Every field is optional. */\nexport interface SupabaseResourceConfig {\n /** Singular noun for failure messages, e.g. \"project\". Defaults to \"record\". */\n noun?: string;\n /** Plural for list messages, when it isn't `noun` + \"s\" (\"categories\"). */\n nounPlural?: string;\n /**\n * The columns a search covers. `[]` means the resource can't be searched,\n * and `capabilities(resource).search` reports false so a UI hides the box.\n * Set, they replace the caller's `search.fields`; omitted, those are used\n * as given.\n */\n searchFields?: readonly string[];\n /**\n * The column a sorted list orders by last, so rows with equal sort values\n * keep one order from page to page (none repeats, none is skipped).\n * Default `\"id\"`; `false` for a table or view without one. With the\n * default, a view that turns out to have no `id` column is read as before\n * (without the key order) and a development warning suggests setting this.\n */\n orderKey?: string | false;\n /**\n * `\"bin\"` for a table whose delete trigger moves the row to a recycle bin\n * instead of removing it (see `SupabaseProviderOptions.binTables`).\n * `\"hard\"` forces a plain delete even when `binTables` lists the table.\n */\n deleteMode?: \"hard\" | \"bin\";\n /**\n * Turns submitted values into the row to write: trim, drop read-only keys,\n * validate. Runs before every create and update, including the `create`\n * and `update` overrides. Throw (ideally `DataWriteError(\"invalid\", ...)`)\n * to refuse the write; nothing is sent.\n */\n prepare?: (\n values: Record<string, unknown>,\n context: SupabaseWriteContext,\n ) => Record<string, unknown> | Promise<Record<string, unknown>>;\n /**\n * Creates the row some other way than an insert, e.g. a security-definer\n * RPC. Return the new row, or its id and the provider reads the row back\n * (if the read-back fails, the create is still reported as saved, with\n * just the id, so the user does not retry and make a second one). Throw to\n * fail; a Supabase `{ message, code }` error is mapped like any other.\n */\n create?: (row: Record<string, unknown>) => Promise<BaseRecord | Identifier>;\n /**\n * Updates the row some other way than an update, e.g. an RPC. Return the\n * updated row, or its id (or `true`) and the provider reads it back.\n * Returning nothing (`undefined`/`null`/`false`) means no row changed: the provider then reads the\n * row to say why (`forbidden` or `gone`), exactly as for a plain update,\n * so a function that silently skips a row never reports a save.\n */\n update?: (\n id: Identifier,\n row: Record<string, unknown>,\n ) => Promise<BaseRecord | Identifier | boolean | null | undefined | void>;\n /** Maps a stored row to edit-form values; see `DataProvider.toFormValues`. */\n toFormValues?: (record: Record<string, unknown>) => Record<string, unknown>;\n /**\n * Other cache keys a committed write to this resource makes stale, e.g.\n * `[[\"staff\"], [\"me\"]]`. Served through `DataProvider.invalidates`, which\n * `useResource` and `useResourceForm` invalidate after their writes.\n */\n invalidates?: InvalidationKey[] | ((event: WriteEvent) => InvalidationKey[]);\n /**\n * Serve `subscribe` for this resource through Supabase Realtime\n * (`postgres_changes`), so `live` lists and records refresh when the table\n * changes. The table must be in the `supabase_realtime` publication, with\n * RLS on. `false` turns it off even when `SupabaseProviderOptions.realtime`\n * covers the resource.\n */\n realtime?: boolean;\n /**\n * Read this resource from somewhere else than its table: a view (joins,\n * counts, aggregations) or a database function that returns rows. Writes\n * still go to the table. Lists keep paging, filters, search, sort, totals\n * and cursors, because PostgREST filters a view or a set-returning function\n * like a table. Realtime still watches the table.\n *\n * Row-level security: a plain view runs as its owner and a SECURITY DEFINER\n * function as its definer, so either one reads past RLS. Create the view\n * `with (security_invoker = true)` and the function `security invoker` (or\n * have it filter by `auth.uid()` itself), or this exposes every row.\n *\n * ```ts\n * resources: {\n * tasks: { source: \"tasks_with_people\" },\n * course_progress: { source: { rpc: \"group_progress\", args: () => ({ group_id: currentGroup() }) } },\n * }\n * ```\n */\n source?: string | { rpc: string; args?: () => Record<string, unknown> };\n /** The columns read when the caller names none, e.g. embedded joins: `\"*, person:people(full_name)\"`. Default `\"*\"`. */\n select?: string;\n /**\n * With a `source` (or a `select` that embeds other tables), the table's own\n * columns: creates and updates write only these, so a form built from the\n * richer read row never sends its joined or computed columns to the table.\n * Without it, an update writes only the columns the table's row has.\n */\n writeColumns?: readonly string[];\n}\n\n/** Options for `createSupabaseProvider`. All optional; none changes reads. */\nexport interface SupabaseProviderOptions {\n /**\n * Recycle-bin tables: their delete trigger turns a delete into a \"binned\"\n * mark and the binned row drops out of the caller's view (RLS or a view\n * filter). The database reports such a delete as zero rows, so for these\n * the provider checks the row is visible, deletes it, and reads it back:\n * gone from view is success, still visible is `forbidden`. A row that was\n * not visible beforehand is `gone`.\n */\n binTables?: readonly string[] | ((resource: string) => boolean);\n /** Per-resource rules, keyed by resource (table) name. */\n resources?: Record<string, SupabaseResourceConfig>;\n /**\n * Called after every committed create, update and delete, e.g. to\n * invalidate caches the Genesis hooks don't know about. Awaited, but a\n * failure is logged and never turns a committed write into a failed one.\n */\n onWrite?: (event: WriteEvent) => void | Promise<void>;\n /**\n * The most rows one list page may ask for (PostgREST's `max-rows` caps\n * reads anyway; default 1000). A larger request is refused with an\n * `invalid` `DataReadError` (its `cause` says why) rather than cut short,\n * so page boundaries never disagree with the caller's `perPage`.\n * `capabilities().maxPerPage` reports it.\n */\n maxPerPage?: number;\n /**\n * Called with every failed read, before it is thrown. The thrown\n * `DataReadError` carries a plain message for users; the store's own\n * error (its raw text and code) is its `cause`, for logging here.\n */\n onReadError?: (\n error: DataReadError,\n context: { resource: string; operation: \"getList\" | \"getOne\" | \"getMany\" },\n ) => void | Promise<void>;\n /**\n * Which resources `subscribe` serves through Supabase Realtime: `true` for\n * all, a list of names, or a test. Off by default; a resource's own\n * `resources[name].realtime` wins. Needs a client with `channel()` (any\n * `@supabase/supabase-js` client).\n */\n realtime?: boolean | readonly string[] | ((resource: string) => boolean);\n}\n\n/** A PostgREST \"undefined column\" error (42703) naming `column`. */\n/**\n * A PostgreSQL \"undefined column\" error (42703) for `table`'s own `column`:\n * `column \"id\" does not exist` or `column <table>.id does not exist`. Not a\n * related table's column (`author.id`) or any other message mentioning it.\n */\nfunction isMissingColumn(error: unknown, table: string, column: string): boolean {\n if (!error || typeof error !== \"object\") return false;\n const { code, message } = error as { code?: unknown; message?: unknown };\n if (code !== \"42703\" || typeof message !== \"string\") return false;\n const match = /^column (?:\"?([^.\"\\s]+)\"?\\.)?\"?([^.\"\\s]+)\"? does not exist$/.exec(message.trim());\n if (!match || match[2] !== column) return false;\n const ownTable = table.split(\".\").pop()?.replace(/\"/g, \"\");\n return match[1] === undefined || match[1] === ownTable;\n}\n\nconst SERVED_OPERATORS: readonly FilterOperator[] = [\n \"eq\", \"neq\", \"gt\", \"lt\", \"gte\", \"lte\", \"contains\", \"in\", \"between\", \"is_null\", \"is_not_null\",\n];\n\nfunction escapeIlike(value: string): string {\n return value.replace(/[%_\\\\]/g, (char) => `\\\\${char}`);\n}\n\nfunction isIdentifier(value: unknown): value is Identifier {\n return typeof value === \"string\" || typeof value === \"number\";\n}\n\n// ---------------------------------------------------------------------------\n// Cursor (keyset) paging\n// ---------------------------------------------------------------------------\n\n/** What a cursor holds: the sort it was made for, and the last row's values. */\ninterface CursorState {\n /** Sort field, or null when ordered by the key alone. */\n s: string | null;\n o: \"asc\" | \"desc\";\n /** The key column. */\n k: string;\n /** The last row's sort value (absent when `s` is null). */\n v?: string | number | boolean | null;\n /** The last row's key value. */\n id: string | number;\n}\n\nconst PLAIN_COLUMN = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n\nfunction encodeCursor(state: CursorState): string {\n const bytes = new TextEncoder().encode(JSON.stringify(state));\n let binary = \"\";\n for (const b of bytes) binary += String.fromCharCode(b);\n return btoa(binary).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/, \"\");\n}\n\nfunction decodeCursor(cursor: string): CursorState | undefined {\n try {\n const binary = atob(cursor.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0));\n const state = JSON.parse(new TextDecoder().decode(bytes)) as CursorState;\n if (!state || typeof state !== \"object\" || typeof state.k !== \"string\" || !isIdentifier(state.id)) return undefined;\n if (state.o !== \"asc\" && state.o !== \"desc\") return undefined;\n return state;\n } catch {\n return undefined;\n }\n}\n\n/** A value quoted for a PostgREST filter, so commas, dots and parens are literal. */\nfunction quoteFilterValue(value: string | number | boolean): string {\n return `\"${String(value).replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n}\n\n/**\n * The PostgREST `or` clauses selecting the rows after `state`, for a list\n * ordered by `s` (`o`, nulls last when ascending and first when descending,\n * as Postgres orders them by default) and then by the key ascending.\n * @internal Exported for tests.\n */\nexport function keysetClauses(state: CursorState): string {\n const k = state.k;\n const K = quoteFilterValue(state.id);\n if (state.s === null) return `${k}.${state.o === \"asc\" ? \"gt\" : \"lt\"}.${K}`;\n const s = state.s;\n const v = state.v;\n const afterInTies = (eq: string) => `and(${eq},${k}.gt.${K})`;\n if (v === null || v === undefined) {\n // Asc: nulls are last, so only later nulls remain. Desc: nulls are\n // first, so later nulls and then every non-null row remain.\n const ties = afterInTies(`${s}.is.null`);\n return state.o === \"asc\" ? ties : `${s}.not.is.null,${ties}`;\n }\n const V = quoteFilterValue(v);\n const ties = afterInTies(`${s}.eq.${V}`);\n return state.o === \"asc\" ? `${s}.gt.${V},${ties},${s}.is.null` : `${s}.lt.${V},${ties}`;\n}\n/** `\"schema.table\"` or `\"table\"` (schema `public`), as Realtime names them. */\nfunction realtimeTable(resource: string): { schema: string; table: string } {\n const dot = resource.indexOf(\".\");\n const strip = (name: string) => name.replace(/\"/g, \"\");\n return dot < 0\n ? { schema: \"public\", table: strip(resource) }\n : { schema: strip(resource.slice(0, dot)), table: strip(resource.slice(dot + 1)) };\n}\n\nlet realtimeChannelSeq = 0;\nconst warnedRealtime = new Set<string>();\n\n/**\n * The Supabase data provider. Reads and writes go through the client you\n * pass, under that client's session and RLS.\n *\n * Writes never report a save that did not happen:\n * - `update` and `deleteOne` ask for the affected rows. Zero rows is a\n * `DataWriteError`: the row is read back, and still visible means\n * `forbidden` (RLS or a trigger refused the change), no longer visible\n * means `gone`.\n * - Database errors on writes are thrown as `DataWriteError` with the\n * outcome their code means (`writeOutcomeFromCode`) and the code kept on\n * `error.code`.\n * - Recycle-bin tables (`binTables`) succeed when the row leaves the\n * caller's view.\n *\n * Reads are unchanged: errors are thrown as an `Error` carrying `code`.\n */\nexport function createSupabaseProvider(\n client: SupabaseClientInput,\n options: SupabaseProviderOptions = {},\n): DataProvider {\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n function applyFilters(\n query: SupabaseQueryBuilder,\n filters: FilterParam[] | undefined,\n ): SupabaseQueryBuilder {\n if (!filters) return query;\n for (const f of filters) {\n switch (f.operator) {\n case \"eq\":\n query = query.eq(f.field, f.value);\n break;\n case \"neq\":\n query = query.neq(f.field, f.value);\n break;\n case \"gt\":\n query = query.gt(f.field, f.value);\n break;\n case \"lt\":\n query = query.lt(f.field, f.value);\n break;\n case \"gte\":\n query = query.gte(f.field, f.value);\n break;\n case \"lte\":\n query = query.lte(f.field, f.value);\n break;\n case \"contains\":\n query = query.ilike(f.field, `%${escapeIlike(String(f.value))}%`);\n break;\n case \"in\":\n query = query.in(f.field, f.value);\n break;\n case \"is_null\":\n query = query.is(f.field, null);\n break;\n case \"is_not_null\":\n query = query.not(f.field, \"is\", null);\n break;\n case \"between\": {\n const [lo, hi] = f.value;\n query = query.gte(f.field, lo).lte(f.field, hi);\n break;\n }\n default:\n throw new Error(`[SupabaseProvider] Unknown filter operator \"${(f as FilterParam).operator}\"`);\n break;\n }\n }\n return query;\n }\n\n const maxPerPage = options.maxPerPage ?? 1000;\n // Resources found to have no \"id\" column for the default orderKey.\n const keylessResources = new Set<string>();\n\n /** Tell `onReadError` (sync or async; its failure is swallowed), then throw. */\n async function reportReadError(\n error: DataReadError,\n resource: string,\n operation: \"getList\" | \"getOne\" | \"getMany\",\n ): Promise<never> {\n try {\n await Promise.resolve(options.onReadError?.(error, { resource, operation }));\n } catch {\n // A logging hook never changes what the caller is told.\n }\n throw error;\n }\n\n /** A failed read, as a `DataReadError` with a message users can be shown. */\n async function throwIfReadError(\n result: { error: unknown },\n resource: string,\n operation: \"getList\" | \"getOne\" | \"getMany\",\n ): Promise<void> {\n if (!result.error) return;\n const raw = result.error as { message?: string; code?: string };\n const code = typeof raw.code === \"string\" ? raw.code : undefined;\n const outcome = readOutcomeFromCode(code);\n const error = new DataReadError(\n outcome,\n readFailureMessage(outcome, nounFor(resource), operation === \"getOne\", configFor(resource).nounPlural),\n { code, cause: result.error },\n );\n await reportReadError(error, resource, operation);\n }\n\n // ---------------------------------------------------------------------------\n // Write helpers\n // ---------------------------------------------------------------------------\n\n const from = (resource: string) => client.from(resource) as SupabaseQueryBuilder;\n\n /** The relation PostgREST names in errors about a read of `resource`: its view, function or table. */\n function readName(resource: string): string {\n const source = configFor(resource).source;\n return typeof source === \"string\" ? source : source ? source.rpc : resource;\n }\n\n /**\n * The values a create or update sends to the table: with `writeColumns`,\n * only those; for an update of a sourced resource without them, only the\n * columns the table's row has, so read-only columns from a view never go out.\n */\n async function tableValues(\n resource: string,\n values: Record<string, unknown>,\n id?: Identifier,\n ): Promise<Record<string, unknown>> {\n const config = configFor(resource);\n if (config.writeColumns) {\n const allowed = new Set(config.writeColumns);\n return Object.fromEntries(Object.entries(values).filter(([key]) => allowed.has(key)));\n }\n if (!config.source || id === undefined) return values;\n const row = await readBack(resource, id);\n if (!row) return values;\n return Object.fromEntries(Object.entries(values).filter(([key]) => key in row));\n }\n\n /** A read of `resource` from its configured source (table, view or function), with its default columns. */\n function read(resource: string, columns: string | undefined, count?: \"exact\"): SupabaseQueryBuilder {\n const config = configFor(resource);\n const cols = columns ?? config.select ?? \"*\";\n const source = config.source;\n if (source && typeof source === \"object\") {\n if (!client.rpc) {\n throw new DataReadError(\"error\", `[SupabaseProvider] \"${resource}\" reads from ${source.rpc}(), but the client has no rpc.`);\n }\n const builder = client.rpc(source.rpc, source.args?.() ?? {}, count ? { count } : undefined) as SupabaseQueryBuilder;\n return builder.select(cols);\n }\n const table = client.from(source ?? resource) as SupabaseQueryBuilder;\n return count ? table.select(cols, { count }) : table.select(cols);\n }\n const configFor = (resource: string): SupabaseResourceConfig =>\n options.resources?.[resource] ?? {};\n const nounFor = (resource: string) => configFor(resource).noun ?? \"record\";\n\n function realtimeEnabled(resource: string): boolean {\n if (typeof client.channel !== \"function\") return false;\n const own = configFor(resource).realtime;\n if (own !== undefined) return own;\n const all = options.realtime;\n if (!all) return false;\n if (all === true) return true;\n return typeof all === \"function\" ? all(resource) : all.includes(resource);\n }\n\n function isBinTable(resource: string): boolean {\n const mode = configFor(resource).deleteMode;\n if (mode) return mode === \"bin\";\n const bin = options.binTables;\n if (!bin) return false;\n return typeof bin === \"function\" ? bin(resource) : bin.includes(resource);\n }\n\n /** Awaits a write-path query: its rows, or a `DataWriteError`. */\n async function run(query: SupabaseQueryBuilder): Promise<unknown[]> {\n let result: { data: unknown; error: unknown };\n try {\n result = (await query) as { data: unknown; error: unknown };\n } catch (error) {\n throw toDataWriteError(error);\n }\n if (result.error) throw toDataWriteError(result.error);\n if (Array.isArray(result.data)) return result.data;\n return result.data == null ? [] : [result.data];\n }\n\n async function isVisible(resource: string, id: Identifier): Promise<boolean> {\n return (await run(from(resource).select(\"id\").eq(\"id\", id))).length > 0;\n }\n\n async function readBack(resource: string, id: Identifier): Promise<BaseRecord | undefined> {\n return (await run(from(resource).select(\"*\").eq(\"id\", id)))[0] as BaseRecord | undefined;\n }\n\n function goneError(resource: string): DataWriteError {\n return new DataWriteError(\n \"gone\",\n `This ${nounFor(resource)} no longer exists, or you can no longer see it.`,\n );\n }\n\n /**\n * A write matched no rows: read the row back to say why. Still visible\n * means the change was refused; not visible means it is gone (deleted, or\n * hidden from this user — the two can't be told apart under RLS).\n */\n async function noRowsError(\n resource: string,\n id: Identifier,\n verb: \"change\" | \"delete\",\n ): Promise<DataWriteError> {\n let visible: boolean;\n try {\n visible = await isVisible(resource, id);\n } catch (error) {\n const cause = toDataWriteError(error);\n return new DataWriteError(\n cause.outcome === \"unauthenticated\" ? \"unauthenticated\" : \"error\",\n `This ${nounFor(resource)} was not ${verb === \"change\" ? \"changed\" : \"deleted\"}, and reading it back failed (${cause.message}).`,\n { code: cause.code, cause: error },\n );\n }\n if (visible) {\n return new DataWriteError(\"forbidden\", `You can't ${verb} this ${nounFor(resource)}.`);\n }\n return goneError(resource);\n }\n\n async function prepare(\n resource: string,\n values: Record<string, unknown>,\n context: SupabaseWriteContext,\n ): Promise<Record<string, unknown>> {\n const rule = configFor(resource).prepare;\n if (!rule) return values;\n try {\n return await rule({ ...values }, context);\n } catch (error) {\n throw toDataWriteError(error);\n }\n }\n\n /** After a committed write. Never turns it into a failure. */\n async function committed(event: WriteEvent): Promise<void> {\n if (!options.onWrite) return;\n try {\n await options.onWrite(event);\n } catch (error) {\n console.error(`[SupabaseProvider] onWrite failed for \"${event.resource}\":`, error);\n }\n }\n\n // ---------------------------------------------------------------------------\n // DataProvider implementation\n // ---------------------------------------------------------------------------\n\n /** The search as PostgREST `or` clauses, or \"\" for none. */\n function searchClauses(resource: string, params: ListParams): string {\n const searchFields = configFor(resource).searchFields ?? params.search?.fields ?? [];\n if (!params.search || !params.search.query || searchFields.length === 0) return \"\";\n const escaped = escapeIlike(params.search.query);\n const safeFieldPattern = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\n return searchFields\n .filter((f) => safeFieldPattern.test(f))\n // Quoted, so commas, dots and parens in the query stay literal (also\n // when nested inside the keyset's `and(or(…))`).\n .map((f) => `${f}.ilike.${quoteFilterValue(`%${escaped}%`)}`)\n .join(\",\");\n }\n\n /** A cursor request this provider can't serve: `invalid`, with why as the cause. */\n function refuseCursor(resource: string, why: string): Promise<never> {\n return reportReadError(\n new DataReadError(\n \"invalid\",\n readFailureMessage(\"invalid\", nounFor(resource), false, configFor(resource).nounPlural),\n { cause: new Error(`[SupabaseProvider] \"${resource}\": ${why}`) },\n ),\n resource,\n \"getList\",\n );\n }\n\n /**\n * One page by keyset: ordered by the sort column and then the key, the\n * rows after the cursor's row, plus one more to tell whether a next page\n * exists. Counted only on the first page.\n */\n async function getCursorPage(\n resource: string,\n params: ListParams,\n key: string | false,\n ): Promise<{ data: unknown[]; total: number; nextCursor: string | null }> {\n const { cursor } = params.pagination!;\n // The look-ahead row must fit under PostgREST's max-rows (maxPerPage), or\n // it is dropped and the list would end after one page. A keyset page can\n // be shorter than asked without skipping or repeating rows.\n const perPage = Math.max(1, Math.min(params.pagination!.perPage, maxPerPage - 1));\n if (!key) {\n return refuseCursor(resource, \"cursor paging needs a unique key column to order by; its orderKey is false. Set orderKey to a unique, non-null column, or page by offset.\");\n }\n if (!PLAIN_COLUMN.test(key)) return refuseCursor(resource, `the key column \"${key}\" can't be used in a cursor.`);\n const sort = params.sort && params.sort.field !== key ? params.sort : undefined;\n if (sort && !PLAIN_COLUMN.test(sort.field)) {\n return refuseCursor(resource, `cursor paging can sort only by a plain column, not \"${sort.field}\".`);\n }\n const order: \"asc\" | \"desc\" = params.sort?.order ?? \"asc\";\n const sortField = sort ? sort.field : null;\n\n let after: CursorState | undefined;\n if (cursor) {\n after = decodeCursor(cursor);\n if (!after) return refuseCursor(resource, \"the cursor is not one this provider made.\");\n if (after.s !== sortField || after.o !== order || after.k !== key) {\n return refuseCursor(resource, \"the cursor was made for a different sort; start again from the first page (cursor: null).\");\n }\n }\n\n let query = read(resource, params.select, cursor ? undefined : \"exact\");\n query = applyFilters(query, params.filters);\n const search = searchClauses(resource, params);\n const keyset = after ? keysetClauses(after) : \"\";\n // Both are OR groups that must hold together: nest them in one `or`.\n if (search && keyset) query = query.or(`and(or(${search}),or(${keyset}))`);\n else if (search || keyset) query = query.or(search || keyset);\n\n if (sortField) {\n // Pinned to Postgres's default null placement, which the keyset assumes.\n query = query.order(sortField, { ascending: order === \"asc\", nullsFirst: order === \"desc\" });\n query = query.order(key, { ascending: true });\n } else {\n query = query.order(key, { ascending: order === \"asc\" });\n }\n query = query.range(0, perPage);\n\n const result = (await query) as unknown as { data: unknown[] | null; error: unknown; count?: number | null };\n await throwIfReadError(result, resource, \"getList\");\n const rows = (result.data ?? []) as Record<string, unknown>[];\n const page = rows.slice(0, perPage);\n let nextCursor: string | null = null;\n if (rows.length > perPage) {\n const last = page[page.length - 1]!;\n const id = last[key];\n const v = sortField ? last[sortField] : undefined;\n if (!isIdentifier(id) || (sortField && !(sortField in last))) {\n return refuseCursor(resource, `the selected columns must include \"${key}\"${sortField ? ` and \"${sortField}\"` : \"\"} to page by cursor.`);\n }\n if (v !== undefined && v !== null && typeof v === \"object\") {\n return refuseCursor(resource, `cursor paging can't sort by \"${sortField}\": its values aren't plain (text, number, date, boolean).`);\n }\n nextCursor = encodeCursor({\n s: sortField,\n o: order,\n k: key,\n ...(sortField ? { v: (v ?? null) as CursorState[\"v\"] } : {}),\n id,\n });\n }\n return { data: page, total: cursor ? -1 : (result.count ?? 0), nextCursor };\n }\n\n const provider: DataProvider = {\n async getList(resource, params) {\n // Refused, not cut short: a shorter page than the caller asked for\n // would shift every later page's boundary and skip or repeat rows.\n if (params.pagination && params.pagination.perPage > maxPerPage) {\n const { perPage } = params.pagination;\n await reportReadError(\n new DataReadError(\n \"invalid\",\n readFailureMessage(\"invalid\", nounFor(resource), false, configFor(resource).nounPlural),\n {\n cause: new Error(\n `[SupabaseProvider] \"${resource}\" asked for ${perPage} rows per page; the most is ${maxPerPage} (maxPerPage). Ask for at most ${maxPerPage} and page through the rest.`,\n ),\n },\n ),\n resource,\n \"getList\",\n );\n }\n\n const configuredKey = configFor(resource).orderKey;\n if (params.pagination && params.pagination.cursor !== undefined) {\n return getCursorPage(resource, params, configuredKey === undefined ? \"id\" : configuredKey) as never;\n }\n const run = (key: string | false) => {\n let query = read(resource, params.select, \"exact\");\n\n query = applyFilters(query, params.filters);\n\n // Search — convert to OR of ilike. A resource's configured\n // `searchFields` win over the caller's.\n const clauses = searchClauses(resource, params);\n if (clauses) {\n query = query.or(clauses);\n }\n\n // Sort, then the key column, so equal sort values keep one order\n // across pages (otherwise a row can repeat on page 2 or be skipped).\n // An unsorted page is ordered by the key alone: without an ORDER BY\n // the database may return pages in any order.\n if (params.sort) {\n query = query.order(params.sort.field, {\n ascending: params.sort.order === \"asc\",\n });\n if (key && key !== params.sort.field) query = query.order(key, { ascending: true });\n } else if (params.pagination && key) {\n query = query.order(key, { ascending: true });\n }\n\n if (params.pagination) {\n const { page, perPage } = params.pagination;\n const from = (page - 1) * perPage;\n const to = from + perPage - 1;\n query = query.range(from, to);\n }\n\n return query as unknown as Promise<{\n data: unknown[];\n error: unknown;\n count?: number | null;\n }>;\n };\n\n // The default key (\"id\") is a guess: a view without that column reads\n // as before, without the key order, and is not ordered by it again.\n const guessKey = configuredKey === undefined && !keylessResources.has(resource);\n let result = await run(configuredKey ?? (keylessResources.has(resource) ? false : \"id\"));\n if (guessKey && isMissingColumn(result.error, readName(resource), \"id\")) {\n keylessResources.add(resource);\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[SupabaseProvider] \"${resource}\" has no \"id\" column, so its pages have no stable order. Set resources.${resource}.orderKey to a unique column (or false to silence this).`,\n );\n }\n result = await run(false);\n }\n await throwIfReadError(result, resource, \"getList\");\n\n return { data: result.data ?? [], total: result.count ?? 0 } as never;\n },\n\n async getOne(resource, params) {\n const result = (await read(resource, params.select)\n .eq(\"id\", params.id)\n .single()) as { data: unknown; error: unknown };\n await throwIfReadError(result, resource, \"getOne\");\n return { data: result.data } as never;\n },\n\n async getMany(resource, params) {\n if (params.ids.length === 0) return { data: [] } as never;\n const result = (await read(resource, undefined)\n .in(\"id\", params.ids)) as { data: unknown[]; error: unknown };\n await throwIfReadError(result, resource, \"getMany\");\n return { data: result.data ?? [] } as never;\n },\n\n async capabilities(resource?: string): Promise<ProviderCapabilities> {\n return {\n search: resource ? configFor(resource).searchFields?.length !== 0 : true,\n sort: true,\n filterOperators: SERVED_OPERATORS,\n bulkWrites: false,\n versionedWrites: false,\n compositeKeys: false,\n multiSort: false,\n create: true,\n delete: true,\n maxPerPage,\n softDelete: resource ? isBinTable(resource) : false,\n cursorPagination: resource ? configFor(resource).orderKey !== false : true,\n realtime: resource\n ? realtimeEnabled(resource)\n : typeof client.channel === \"function\" &&\n (!!options.realtime ||\n Object.values(options.resources ?? {}).some((r) => r.realtime === true)),\n };\n },\n\n subscribe(resource, params, callback) {\n if (!realtimeEnabled(resource)) {\n if (process.env.NODE_ENV !== \"production\" && !warnedRealtime.has(resource)) {\n warnedRealtime.add(resource);\n console.warn(\n typeof client.channel !== \"function\"\n ? `[SupabaseProvider] \"${resource}\" was subscribed to, but the client has no channel() (Supabase Realtime), so nothing will be reported.`\n : `[SupabaseProvider] \"${resource}\" was subscribed to, but realtime is off for it. Set resources.${resource}.realtime (or the realtime option) to true.`,\n );\n }\n return () => {};\n }\n const { schema, table } = realtimeTable(resource);\n // No server-side `id=eq.` filter for one record: Realtime never\n // delivers DELETE events through a filter, so the id is matched below.\n // A unique topic: supabase-js hands back an existing channel for a\n // topic already open, and removing it would end the other subscriber.\n const channel = client.channel!(\n `genesis:${resource}:${++realtimeChannelSeq}`,\n ) as SupabaseRealtimeChannel;\n let closed = false;\n let subscribedOnce = false;\n let interrupted = false;\n const emit = (event: ResourceChangeEvent) => {\n if (closed) return;\n try {\n callback(event);\n } catch (error) {\n console.error(`[SupabaseProvider] a realtime callback for \"${resource}\" threw:`, error);\n }\n };\n channel\n .on(\n \"postgres_changes\",\n { event: \"*\", schema, table },\n (payload) => {\n // Only the id: the payload is a hint, never data to show. Realtime\n // applies RLS to inserts and updates, but a delete's old key is\n // sent to every subscriber, so nothing else is passed on.\n const row = payload.eventType === \"DELETE\" ? payload.old : payload.new;\n const id = row?.[\"id\"];\n if (params.id != null && isIdentifier(id) && String(id) !== String(params.id)) return;\n const type =\n payload.eventType === \"INSERT\" ? \"created\" : payload.eventType === \"UPDATE\" ? \"updated\" : \"deleted\";\n emit(isIdentifier(id) ? { type, id } : { type });\n },\n )\n .subscribe((status) => {\n if (status === \"SUBSCRIBED\") {\n // Back after a drop: changes made meanwhile were not sent.\n if (subscribedOnce && interrupted) emit({ type: \"changed\" });\n subscribedOnce = true;\n interrupted = false;\n } else if (status === \"CHANNEL_ERROR\" || status === \"TIMED_OUT\" || status === \"CLOSED\") {\n interrupted = true;\n }\n });\n return () => {\n if (closed) return;\n closed = true;\n try {\n const done =\n typeof client.removeChannel === \"function\"\n ? client.removeChannel(channel)\n : channel.unsubscribe?.();\n // A failed removal (socket already gone) is nothing to report.\n if (done && typeof (done as Promise<unknown>).catch === \"function\") {\n (done as Promise<unknown>).catch(() => {});\n }\n } catch {\n // As above.\n }\n };\n },\n\n async create(resource, params) {\n const row = await tableValues(\n resource,\n await prepare(resource, params.data as Record<string, unknown>, { resource, action: \"create\" }),\n );\n const override = configFor(resource).create;\n let record: BaseRecord;\n if (override) {\n let returned: BaseRecord | Identifier;\n try {\n returned = await override(row);\n } catch (error) {\n throw toDataWriteError(error);\n }\n if (isIdentifier(returned)) {\n // The row exists from here on. A failed read-back must not report\n // the create as failed: the user would retry and make a second one.\n record = (await readBack(resource, returned).catch(() => undefined)) ?? { id: returned };\n } else if (returned && typeof returned === \"object\" && isIdentifier(returned.id)) {\n record = returned;\n } else {\n throw new DataWriteError(\n \"error\",\n `[SupabaseProvider] The create rule for \"${resource}\" returned neither the new row nor its id.`,\n );\n }\n } else {\n const rows = await run(from(resource).insert(row).select(\"*\").single());\n record = rows[0] as BaseRecord;\n }\n await committed({ resource, kind: \"create\", id: record.id, record });\n return { data: record } as never;\n },\n\n async update(resource, params) {\n const { id } = params;\n const row = await tableValues(\n resource,\n await prepare(resource, params.data as Record<string, unknown>, { resource, action: \"update\", id }),\n id,\n );\n const override = configFor(resource).update;\n let record: BaseRecord;\n if (override) {\n let returned: BaseRecord | Identifier | boolean | null | undefined | void;\n try {\n returned = await override(id, row);\n } catch (error) {\n throw toDataWriteError(error);\n }\n const result: unknown = returned;\n if (result == null || result === false) throw await noRowsError(resource, id, \"change\");\n if (isIdentifier(result) || result === true) {\n // The change committed; a failed read-back still reports it saved.\n const readId = result === true ? id : result;\n record = (await readBack(resource, readId).catch(() => undefined)) ?? { ...row, id: readId };\n } else if (typeof result === \"object\") {\n record = { id, ...(result as Record<string, unknown>) } as BaseRecord;\n } else {\n throw new DataWriteError(\n \"error\",\n `[SupabaseProvider] The update rule for \"${resource}\" returned neither the row, its id, nor nothing.`,\n );\n }\n } else {\n const rows = await run(from(resource).update(row).eq(\"id\", id).select(\"*\"));\n if (rows.length === 0) throw await noRowsError(resource, id, \"change\");\n record = rows[0] as BaseRecord;\n }\n await committed({ resource, kind: \"update\", id, record });\n return { data: record } as never;\n },\n\n async deleteOne(resource, params) {\n const { id } = params;\n let deletedId: Identifier = id;\n if (isBinTable(resource)) {\n // A binned delete reports zero rows whether or not it worked, so the\n // answer comes from visibility before and after.\n if (!(await isVisible(resource, id))) throw goneError(resource);\n await run(from(resource).delete().eq(\"id\", id));\n let stillVisible: boolean;\n try {\n stillVisible = await isVisible(resource, id);\n } catch (error) {\n const cause = toDataWriteError(error);\n throw new DataWriteError(\n \"error\",\n `The ${nounFor(resource)} may not have been deleted: checking it failed (${cause.message}).`,\n { code: cause.code, cause: error },\n );\n }\n if (stillVisible) {\n throw new DataWriteError(\"forbidden\", `You can't delete this ${nounFor(resource)}.`);\n }\n } else {\n const rows = await run(from(resource).delete().eq(\"id\", id).select(\"id\"));\n if (rows.length === 0) throw await noRowsError(resource, id, \"delete\");\n const returnedId = (rows[0] as { id?: unknown }).id;\n if (isIdentifier(returnedId)) deletedId = returnedId;\n }\n await committed({ resource, kind: \"delete\", id: deletedId });\n return { data: { id: deletedId } };\n },\n\n toFormValues(resource, record) {\n const map = configFor(resource).toFormValues;\n return map ? map(record) : record;\n },\n\n invalidates(event) {\n const keys = configFor(event.resource).invalidates;\n if (!keys) return [];\n return typeof keys === \"function\" ? keys(event) : keys;\n },\n };\n\n return provider;\n}\n","// ---------------------------------------------------------------------------\n// Write helpers (README \"One-off writes\")\n//\n// The writes a resource page doesn't cover: a database function (RPC) with\n// plain error words, and \"one live row per key\" (create, or adopt the row\n// that's already there). Both fail with a DataWriteError, like the provider.\n// ---------------------------------------------------------------------------\n\nimport { DataWriteError, isDataWriteError } from \"../schema/write-outcome\";\nimport { toDataWriteError } from \"./postgrest-errors\";\nimport type { BaseRecord, DataProvider, FilterParam } from \"./types\";\n\n/** Anything with Supabase's `rpc`. */\nexport interface RpcClient {\n rpc(fn: string, args?: Record<string, unknown>): PromiseLike<{ data: unknown; error: unknown }>;\n}\n\n/** The words shown for a code when the caller gives none. A guard's own `raise exception` (P0001) keeps its words. */\nexport const DEFAULT_RPC_MESSAGES: Readonly<Record<string, string>> = {\n \"42501\": \"You can't do that.\",\n \"22023\": \"That request isn't valid.\",\n \"23505\": \"That already exists.\",\n \"28000\": \"Sign in to continue.\",\n PGRST202: \"That action isn't available.\",\n PGRST301: \"Sign in to continue.\",\n};\n\nexport const RPC_FAILED = \"That didn't work. Try again.\";\n\nexport interface CallRpcOptions {\n /** Words per error code, e.g. `{ \"55000\": \"This course has already started.\" }`; these win over the defaults. */\n messages?: Record<string, string>;\n}\n\n/**\n * Calls a database function and returns its result. A failure throws a\n * DataWriteError with the usual outcome and code, and words a person can\n * read: yours for the code, a guard's own message (P0001), the defaults\n * above, or a generic sentence. The raw error stays on `cause`.\n */\nexport async function callRpc<T = unknown>(\n client: RpcClient,\n fn: string,\n args: Record<string, unknown> = {},\n options: CallRpcOptions = {},\n): Promise<T> {\n let result: { data: unknown; error: unknown };\n try {\n result = await client.rpc(fn, args);\n } catch (error) {\n // A client that already speaks DataWriteError (signed out, a refusal) keeps its outcome and code.\n if (isDataWriteError(error)) throw error;\n throw new DataWriteError(\"error\", RPC_FAILED, { cause: error });\n }\n if (!result || typeof result !== \"object\") {\n throw new DataWriteError(\"error\", RPC_FAILED, { cause: new Error(`rpc ${fn} returned ${String(result)}`) });\n }\n if (!result.error) return result.data as T;\n const mapped = toDataWriteError(result.error);\n const code = mapped.code;\n const custom = code !== undefined ? options.messages?.[code] : undefined;\n const raw = (result.error as { message?: unknown }).message;\n const guardWords = typeof raw === \"string\" && raw.trim() ? raw : undefined;\n const message =\n custom ??\n (code === \"P0001\" ? guardWords : undefined) ??\n (mapped.fields ? mapped.message : undefined) ??\n (code !== undefined ? DEFAULT_RPC_MESSAGES[code] : undefined) ??\n RPC_FAILED;\n throw new DataWriteError(mapped.outcome, message, { code, fields: mapped.fields, cause: result.error });\n}\n\nexport interface CreateOrAdoptOptions<T extends BaseRecord> {\n /** The fields that make a row unique (its unique index), read from `data`. */\n key: readonly (keyof T & string)[];\n /**\n * Changes to write over a row that was already there; return null to keep it\n * as it is. It runs on the row as read just now, and the update isn't\n * conditional: two callers merging into one row at once can each overwrite\n * the other. Where that matters, merge in a database function instead.\n */\n merge?: (existing: T) => Partial<T> | null;\n}\n\n/**\n * \"One live row per key\": creates the row, or, when one with the same key is\n * already there (a `duplicate`), adopts it, optionally merging changes in.\n * `adopted` says which. A duplicate you can't see is still an error.\n */\nexport async function createOrAdopt<T extends BaseRecord>(\n provider: DataProvider,\n resource: string,\n data: Partial<T>,\n options: CreateOrAdoptOptions<T>,\n): Promise<{ row: T; adopted: boolean }> {\n if (options.key.length === 0) throw new Error(`[genesis-core] createOrAdopt(\"${resource}\") needs a key`);\n try {\n return { row: (await provider.create<T>(resource, { data })).data, adopted: false };\n } catch (error) {\n if (!isDataWriteError(error) || error.outcome !== \"duplicate\") throw error;\n // A clash on some other unique index isn't this key's row.\n if (error.fields && Object.keys(error.fields).some((field) => !options.key.includes(field as keyof T & string))) throw error;\n const filters: FilterParam[] = options.key.map((field) => {\n const value = (data as Record<string, unknown>)[field];\n if (value === null || value === undefined) return { field, operator: \"is_null\" };\n if (typeof value !== \"string\" && typeof value !== \"number\" && typeof value !== \"boolean\") {\n throw new Error(`[genesis-core] createOrAdopt(\"${resource}\") key field ${field} must be a string, number or boolean`);\n }\n return { field, operator: \"eq\", value };\n });\n const { data: rows } = await provider.getList<T>(resource, { filters, pagination: { page: 1, perPage: 2 } });\n if (rows.length !== 1) throw error;\n const existing = rows[0]!;\n const changes = options.merge?.(existing);\n if (!changes || Object.keys(changes).length === 0) return { row: existing, adopted: true };\n return { row: (await provider.update<T>(resource, { id: existing.id, data: changes })).data, adopted: true };\n }\n}\n"]}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { isDataWriteError } from './chunk-LG6TUBVM.js';
|
|
2
|
+
|
|
3
|
+
// src/hooks/draft-controller.ts
|
|
4
|
+
var DEFAULT_DRAFT_DELAY_MS = 800;
|
|
5
|
+
var DRAFT_NOT_SAVED = "Your changes weren't saved.";
|
|
6
|
+
function stableJson(value) {
|
|
7
|
+
return JSON.stringify(
|
|
8
|
+
value,
|
|
9
|
+
(_key, v) => v && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(
|
|
10
|
+
Object.entries(v).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
|
|
11
|
+
) : v
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
function hasContent(v) {
|
|
15
|
+
if (v === void 0 || v === null) return false;
|
|
16
|
+
if (typeof v === "string") return v.trim() !== "";
|
|
17
|
+
if (Array.isArray(v)) return v.some(hasContent);
|
|
18
|
+
if (typeof v === "object") return Object.values(v).some(hasContent);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
function isNetworkFailure(error) {
|
|
22
|
+
if (isDataWriteError(error)) return error.outcome === "error" && error.code === void 0;
|
|
23
|
+
return error instanceof TypeError;
|
|
24
|
+
}
|
|
25
|
+
function defaultId(row) {
|
|
26
|
+
return row && typeof row === "object" && "id" in row ? row.id : row;
|
|
27
|
+
}
|
|
28
|
+
function isDuplicateOutcome(error) {
|
|
29
|
+
return isDataWriteError(error) && error.outcome === "duplicate";
|
|
30
|
+
}
|
|
31
|
+
function plainMessage(error) {
|
|
32
|
+
return isDataWriteError(error) && error.message ? error.message : DRAFT_NOT_SAVED;
|
|
33
|
+
}
|
|
34
|
+
var Halt = class {
|
|
35
|
+
};
|
|
36
|
+
var DraftController = class {
|
|
37
|
+
constructor(options) {
|
|
38
|
+
this.options = options;
|
|
39
|
+
const { seed, revision, toValue } = options();
|
|
40
|
+
this.revision = revision;
|
|
41
|
+
this.saved = seed.row ? toValue(seed.row) : seed.value;
|
|
42
|
+
this.state = { value: seed.value, row: seed.row, status: "idle", error: null, discarded: false, key: 0 };
|
|
43
|
+
}
|
|
44
|
+
options;
|
|
45
|
+
state;
|
|
46
|
+
/** The value as last saved (or as loaded): what "unchanged" compares with. */
|
|
47
|
+
saved;
|
|
48
|
+
/** Fields changed here since the draft started (or since the last revision). */
|
|
49
|
+
dirty = /* @__PURE__ */ new Set();
|
|
50
|
+
chain = Promise.resolve();
|
|
51
|
+
timer;
|
|
52
|
+
revision;
|
|
53
|
+
ended = false;
|
|
54
|
+
/** Bumped by each new revision: a save that started before it lands on nothing. */
|
|
55
|
+
generation = 0;
|
|
56
|
+
/** The value of the write in flight, so a seed that is only that write coming back is known. */
|
|
57
|
+
inflight = null;
|
|
58
|
+
listeners = /* @__PURE__ */ new Set();
|
|
59
|
+
subscribe = (listener) => {
|
|
60
|
+
this.listeners.add(listener);
|
|
61
|
+
return () => {
|
|
62
|
+
this.listeners.delete(listener);
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
getState = () => this.state;
|
|
66
|
+
/** Edits this draft would still save. */
|
|
67
|
+
pending = () => this.active() && !this.same(this.state.value, this.saved);
|
|
68
|
+
/** The editor's new value: its changed fields are noted, and it's saved a moment after the last change. */
|
|
69
|
+
set = (value) => {
|
|
70
|
+
const before = this.state.value;
|
|
71
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(value)])) {
|
|
72
|
+
if (stableJson(before[key]) !== stableJson(value[key])) this.dirty.add(key);
|
|
73
|
+
}
|
|
74
|
+
this.patch({ value, discarded: false });
|
|
75
|
+
if (this.active()) this.schedule();
|
|
76
|
+
};
|
|
77
|
+
/** Save now, after any write already running. Resolves once written or failed; never rejects. */
|
|
78
|
+
flush = (hints = {}) => {
|
|
79
|
+
this.clearTimer();
|
|
80
|
+
const run = this.chain.then(() => this.save(hints.keepalive ?? false)).catch((error) => {
|
|
81
|
+
this.patch({ status: "error", error: (this.options().messageOf ?? plainMessage)(error) });
|
|
82
|
+
});
|
|
83
|
+
this.chain = run;
|
|
84
|
+
return run;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* The final write (a submit), after any write in flight, given the row so far
|
|
88
|
+
* (null before the first save) and the value. On success nothing more is
|
|
89
|
+
* saved and the draft is `stopped`; on failure it is thrown to the caller and
|
|
90
|
+
* autosave carries on.
|
|
91
|
+
*/
|
|
92
|
+
commit = (write) => {
|
|
93
|
+
this.clearTimer();
|
|
94
|
+
const run = this.chain.then(async () => {
|
|
95
|
+
const row = await write(this.state.row, this.state.value);
|
|
96
|
+
this.ended = true;
|
|
97
|
+
this.saved = this.options().toValue(row);
|
|
98
|
+
this.patch({ row, status: "stopped", error: null });
|
|
99
|
+
this.options().onSaved?.(row);
|
|
100
|
+
return row;
|
|
101
|
+
});
|
|
102
|
+
this.chain = run.catch(() => {
|
|
103
|
+
if (this.pending()) this.schedule();
|
|
104
|
+
});
|
|
105
|
+
return run;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* The server's seed again. Ignored unless `revision` changed. Then, in order:
|
|
109
|
+
* - the seed is this draft's own save coming back (the same row, holding what
|
|
110
|
+
* was last saved or is being saved): take its row and keep every edit;
|
|
111
|
+
* - it is another row, with edits pending here: those are saved to the row
|
|
112
|
+
* they belong to first, then the draft moves to the new one;
|
|
113
|
+
* - otherwise the server's copy wins: the draft starts again from it, keeping
|
|
114
|
+
* only what `onRevision` returns.
|
|
115
|
+
*/
|
|
116
|
+
reseed = (seed, revision) => {
|
|
117
|
+
if (revision === this.revision) return;
|
|
118
|
+
this.revision = revision;
|
|
119
|
+
const opts = this.options();
|
|
120
|
+
const idOf = opts.idOf ?? defaultId;
|
|
121
|
+
const current = this.state.row;
|
|
122
|
+
const echo = seed.row !== null && (current === null || idOf(seed.row) === idOf(current)) && (this.same(opts.toValue(seed.row), this.saved) || this.inflight !== null && this.same(opts.toValue(seed.row), this.inflight));
|
|
123
|
+
if (echo && (current !== null || this.inflight !== null)) {
|
|
124
|
+
this.patch({ row: seed.row });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.generation++;
|
|
128
|
+
this.clearTimer();
|
|
129
|
+
const pending = !this.same(this.state.value, this.saved);
|
|
130
|
+
if (pending && current !== null && seed.row !== null && idOf(seed.row) !== idOf(current) && this.active()) {
|
|
131
|
+
const value = this.state.value;
|
|
132
|
+
this.chain = this.chain.then(() => opts.update(current, value, { keepalive: false })).then((row) => opts.onSaved?.(row)).catch(() => void 0);
|
|
133
|
+
this.startFrom(seed, seed.value, false);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const kept = pending ? opts.onRevision?.(this.state.value, seed) ?? null : null;
|
|
137
|
+
this.startFrom(seed, kept ?? seed.value, pending && kept === null);
|
|
138
|
+
if (kept !== null && this.pending()) this.schedule();
|
|
139
|
+
};
|
|
140
|
+
/** Call when `enabled` may have changed: off stops saving; back on saves what's pending. */
|
|
141
|
+
sync = () => {
|
|
142
|
+
if (!this.enabled()) {
|
|
143
|
+
this.clearTimer();
|
|
144
|
+
if (this.state.status !== "stopped") this.patch({ status: "stopped" });
|
|
145
|
+
} else if (this.state.status === "stopped" && !this.ended) {
|
|
146
|
+
this.patch({ status: "idle" });
|
|
147
|
+
if (this.pending()) this.schedule();
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
startFrom(seed, value, discarded) {
|
|
151
|
+
this.saved = seed.row ? this.options().toValue(seed.row) : seed.value;
|
|
152
|
+
this.dirty.clear();
|
|
153
|
+
this.ended = false;
|
|
154
|
+
this.inflight = null;
|
|
155
|
+
this.patch({
|
|
156
|
+
row: seed.row,
|
|
157
|
+
value,
|
|
158
|
+
status: this.enabled() ? "idle" : "stopped",
|
|
159
|
+
error: null,
|
|
160
|
+
discarded,
|
|
161
|
+
key: this.state.key + 1
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
enabled() {
|
|
165
|
+
return this.options().enabled ?? true;
|
|
166
|
+
}
|
|
167
|
+
active() {
|
|
168
|
+
return this.enabled() && !this.ended;
|
|
169
|
+
}
|
|
170
|
+
same(a, b) {
|
|
171
|
+
const equal = this.options().equal;
|
|
172
|
+
return equal ? equal(a, b) : stableJson(a) === stableJson(b);
|
|
173
|
+
}
|
|
174
|
+
schedule() {
|
|
175
|
+
this.clearTimer();
|
|
176
|
+
this.timer = setTimeout(() => {
|
|
177
|
+
this.timer = void 0;
|
|
178
|
+
void this.flush();
|
|
179
|
+
}, this.options().delayMs ?? DEFAULT_DRAFT_DELAY_MS);
|
|
180
|
+
}
|
|
181
|
+
clearTimer() {
|
|
182
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
183
|
+
this.timer = void 0;
|
|
184
|
+
}
|
|
185
|
+
patch(change) {
|
|
186
|
+
this.state = { ...this.state, ...change };
|
|
187
|
+
for (const listener of this.listeners) listener();
|
|
188
|
+
}
|
|
189
|
+
async save(keepalive) {
|
|
190
|
+
if (!this.active()) return;
|
|
191
|
+
const opts = this.options();
|
|
192
|
+
const value = this.state.value;
|
|
193
|
+
if (this.same(value, this.saved)) return;
|
|
194
|
+
const isEmpty = opts.isEmpty ?? ((v) => !Object.values(v).some(hasContent));
|
|
195
|
+
if (!this.state.row && isEmpty(value)) return;
|
|
196
|
+
this.patch({ status: "saving" });
|
|
197
|
+
const generation = this.generation;
|
|
198
|
+
this.inflight = value;
|
|
199
|
+
try {
|
|
200
|
+
const { row, merged } = await this.write(value, keepalive, this.state.row, generation);
|
|
201
|
+
this.inflight = null;
|
|
202
|
+
if (generation !== this.generation) return;
|
|
203
|
+
const stored = opts.toValue(row);
|
|
204
|
+
this.saved = merged ? stored : value;
|
|
205
|
+
if (merged) {
|
|
206
|
+
const next = { ...stored };
|
|
207
|
+
for (const key of this.dirty) next[key] = this.state.value[key];
|
|
208
|
+
this.patch({ row, value: next, status: "saved", error: null, key: this.state.key + 1 });
|
|
209
|
+
} else {
|
|
210
|
+
this.patch({ row, status: "saved", error: null });
|
|
211
|
+
}
|
|
212
|
+
opts.onSaved?.(row);
|
|
213
|
+
if (this.pending()) this.schedule();
|
|
214
|
+
} catch (error) {
|
|
215
|
+
if (generation === this.generation) this.inflight = null;
|
|
216
|
+
if (error instanceof Halt || generation !== this.generation) return;
|
|
217
|
+
this.patch({ status: "error", error: (opts.messageOf ?? plainMessage)(error) });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* One save: change the row, or add it (merging on a duplicate), retrying network failures.
|
|
222
|
+
* The row is the one the save started with, never one a later revision brought in.
|
|
223
|
+
*/
|
|
224
|
+
async write(value, keepalive, start, generation) {
|
|
225
|
+
const opts = this.options();
|
|
226
|
+
const retries = opts.retries ?? 3;
|
|
227
|
+
const isRetryable = opts.isRetryable ?? isNetworkFailure;
|
|
228
|
+
for (let attempt = 0; ; attempt++) {
|
|
229
|
+
try {
|
|
230
|
+
if (start) return { row: await opts.update(start, value, { keepalive }), merged: false };
|
|
231
|
+
try {
|
|
232
|
+
return { row: await opts.create(value, { keepalive }), merged: false };
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (!opts.adopt || !(opts.isDuplicate ?? isDuplicateOutcome)(error)) throw error;
|
|
235
|
+
const adopted = await opts.adopt(value, this.dirty);
|
|
236
|
+
if (generation !== this.generation) throw new Halt();
|
|
237
|
+
if ("final" in adopted) {
|
|
238
|
+
this.ended = true;
|
|
239
|
+
this.saved = opts.toValue(adopted.final);
|
|
240
|
+
this.patch({ row: adopted.final, status: "stopped", error: null });
|
|
241
|
+
opts.onFinal?.(adopted.final);
|
|
242
|
+
throw new Halt();
|
|
243
|
+
}
|
|
244
|
+
return { row: adopted.row, merged: true };
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
if (error instanceof Halt || attempt >= retries || !isRetryable(error)) throw error;
|
|
248
|
+
await new Promise((resolve) => setTimeout(resolve, (opts.retryDelayMs ?? 500) * 2 ** attempt));
|
|
249
|
+
if (!this.active() || generation !== this.generation) throw new Halt();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export { DEFAULT_DRAFT_DELAY_MS, DRAFT_NOT_SAVED, DraftController, isNetworkFailure, stableJson };
|
|
256
|
+
//# sourceMappingURL=chunk-HBAPYA5K.js.map
|
|
257
|
+
//# sourceMappingURL=chunk-HBAPYA5K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/draft-controller.ts"],"names":[],"mappings":"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","file":"chunk-HBAPYA5K.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Autosave a draft (README \"Autosave a draft\")\n//\n// A draft is one row someone edits over time: an answer to a questionnaire, a\n// note, a journal entry. It is saved as they type, a moment after they stop,\n// and the first save adds the row. The app supplies the writes, so this works\n// with any backend; nothing here talks to a database.\n//\n// DraftController holds the rules and is plain TypeScript; `useDraft` is its\n// React wrapper.\n// ---------------------------------------------------------------------------\n\nimport { isDataWriteError } from \"../schema/write-outcome\";\n\n/** A draft's value: a flat map of fields (a table or a list is one field). */\nexport type DraftValue = Record<string, unknown>;\n\nexport type DraftStatus = \"idle\" | \"saving\" | \"saved\" | \"error\" | \"stopped\";\n\nexport interface DraftSeed<V extends DraftValue, R> {\n /** The row as the server has it, or null before the first save. */\n row: R | null;\n /** The value to start from: the row's, or a blank one. */\n value: V;\n}\n\n/** What `adopt` found: the row it merged into, or a row already finished elsewhere. */\nexport type DraftAdoption<R> = { row: R } | { final: R };\n\nexport interface DraftOptions<V extends DraftValue, R> {\n seed: DraftSeed<V, R>;\n /**\n * Changes when the server's copy changes in a way that wins over edits here\n * (it was sent from another tab, a reviewer returned it). The draft then\n * starts again from the new `seed`; see `onRevision`.\n */\n revision?: string | undefined;\n /** The first write: add the row. `keepalive` is set for the save made as the page closes. */\n create: (value: V, hints: { keepalive: boolean }) => Promise<R>;\n /** Later writes. `keepalive` is set for the save made as the page closes. */\n update: (row: R, value: V, hints: { keepalive: boolean }) => Promise<R>;\n /** The value a saved row holds. */\n toValue: (row: R) => V;\n /** A row's identity, to tell the same row from another. Default: its `id`. */\n idOf?: ((row: R) => unknown) | undefined;\n /**\n * The first write found a row already there (another tab started it): merge\n * into it, or report that it is already finished. `dirty` names the fields\n * changed here since the draft started; only those should be written over\n * the existing row's. Without `adopt`, the duplicate is an error.\n */\n adopt?: ((value: V, dirty: ReadonlySet<string>) => Promise<DraftAdoption<R>>) | undefined;\n /** Whether a failed `create` means \"a row is already there\". Default: a `duplicate` write outcome. */\n isDuplicate?: ((error: unknown) => boolean) | undefined;\n /** Never add a row for this value. Default: no field has anything in it. */\n isEmpty?: ((value: V) => boolean) | undefined;\n /** Default: the same JSON, whatever the order of object keys. */\n equal?: ((a: V, b: V) => boolean) | undefined;\n /** How long after the last change to save. Default 800 ms. */\n delayMs?: number | undefined;\n /** False: nothing is written (someone else's draft, or one already sent). Default true. */\n enabled?: boolean | undefined;\n /** After each save, with the row as saved: write it into the app's cache here. */\n onSaved?: ((row: R) => void) | undefined;\n /** `adopt` found the row finished elsewhere: nothing more is saved. */\n onFinal?: ((row: R) => void) | undefined;\n /**\n * `revision` changed with edits not yet saved: return the value to keep (a\n * merge), or null to drop the edits for the server's. Default: drop them.\n */\n onRevision?: ((pending: V, next: DraftSeed<V, R>) => V | null) | undefined;\n /** Whether a failed write is worth trying again. Default: network failures only, never refusals. */\n isRetryable?: ((error: unknown) => boolean) | undefined;\n /** Tries after the first, on a retryable failure. Default 3. */\n retries?: number | undefined;\n /** The first retry's wait; each later one doubles it. Default 500 ms. */\n retryDelayMs?: number | undefined;\n /** The words shown for a failed save. Default: the write error's own plain message. */\n messageOf?: ((error: unknown) => string) | undefined;\n}\n\nexport interface DraftState<V extends DraftValue, R> {\n value: V;\n row: R | null;\n status: DraftStatus;\n /** Why the last save failed, in plain words; null once one succeeds. */\n error: string | null;\n /** True after a new revision replaced edits that weren't saved, so the page can say so. */\n discarded: boolean;\n /**\n * Changes when the value is replaced from outside the editor (merged with\n * another tab's row, or a new revision): remount the editor on it.\n */\n key: number;\n}\n\nexport const DEFAULT_DRAFT_DELAY_MS = 800;\nexport const DRAFT_NOT_SAVED = \"Your changes weren't saved.\";\n\n/** JSON with object keys sorted, so values compare equal whatever order a store keeps keys in. */\nexport function stableJson(value: unknown): string {\n return JSON.stringify(value, (_key, v: unknown) =>\n v && typeof v === \"object\" && !Array.isArray(v)\n ? Object.fromEntries(\n Object.entries(v as Record<string, unknown>).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)),\n )\n : v,\n );\n}\n\n/** Something in it: not undefined, null, blank text, or a list or object of nothing. */\nfunction hasContent(v: unknown): boolean {\n if (v === undefined || v === null) return false;\n if (typeof v === \"string\") return v.trim() !== \"\";\n if (Array.isArray(v)) return v.some(hasContent);\n if (typeof v === \"object\") return Object.values(v as Record<string, unknown>).some(hasContent);\n return true;\n}\n\n/** The default `isRetryable`: a request that never reached the store. A refusal never is. */\nexport function isNetworkFailure(error: unknown): boolean {\n if (isDataWriteError(error)) return error.outcome === \"error\" && error.code === undefined;\n return error instanceof TypeError;\n}\n\nfunction defaultId(row: unknown): unknown {\n return row && typeof row === \"object\" && \"id\" in row ? (row as { id: unknown }).id : row;\n}\n\nfunction isDuplicateOutcome(error: unknown): boolean {\n return isDataWriteError(error) && error.outcome === \"duplicate\";\n}\n\nfunction plainMessage(error: unknown): string {\n return isDataWriteError(error) && error.message ? error.message : DRAFT_NOT_SAVED;\n}\n\n/** Thrown inside a save to end it quietly: the draft stopped while it ran. */\nclass Halt {}\n\n/**\n * The rules of a draft, without React: one write at a time, add then change,\n * no write when nothing changed or for an empty value, merge into a row\n * another tab started, start again on a new revision, retry network failures,\n * and a final write (`commit`) that ends it.\n */\nexport class DraftController<V extends DraftValue, R> {\n private state: DraftState<V, R>;\n /** The value as last saved (or as loaded): what \"unchanged\" compares with. */\n private saved: V;\n /** Fields changed here since the draft started (or since the last revision). */\n private dirty = new Set<string>();\n private chain: Promise<unknown> = Promise.resolve();\n private timer: ReturnType<typeof setTimeout> | undefined;\n private revision: string | undefined;\n private ended = false;\n /** Bumped by each new revision: a save that started before it lands on nothing. */\n private generation = 0;\n /** The value of the write in flight, so a seed that is only that write coming back is known. */\n private inflight: V | null = null;\n private listeners = new Set<() => void>();\n\n constructor(private readonly options: () => DraftOptions<V, R>) {\n const { seed, revision, toValue } = options();\n this.revision = revision;\n this.saved = seed.row ? toValue(seed.row) : seed.value;\n this.state = { value: seed.value, row: seed.row, status: \"idle\", error: null, discarded: false, key: 0 };\n }\n\n readonly subscribe = (listener: () => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n readonly getState = () => this.state;\n\n /** Edits this draft would still save. */\n readonly pending = () => this.active() && !this.same(this.state.value, this.saved);\n\n /** The editor's new value: its changed fields are noted, and it's saved a moment after the last change. */\n readonly set = (value: V) => {\n const before = this.state.value;\n for (const key of new Set([...Object.keys(before), ...Object.keys(value)])) {\n if (stableJson(before[key]) !== stableJson(value[key])) this.dirty.add(key);\n }\n this.patch({ value, discarded: false });\n if (this.active()) this.schedule();\n };\n\n /** Save now, after any write already running. Resolves once written or failed; never rejects. */\n readonly flush = (hints: { keepalive?: boolean } = {}): Promise<void> => {\n this.clearTimer();\n // Never rejects, so one throwing option (isEmpty, equal) can't wedge the chain.\n const run = this.chain\n .then(() => this.save(hints.keepalive ?? false))\n .catch((error: unknown) => {\n this.patch({ status: \"error\", error: (this.options().messageOf ?? plainMessage)(error) });\n });\n this.chain = run;\n return run;\n };\n\n /**\n * The final write (a submit), after any write in flight, given the row so far\n * (null before the first save) and the value. On success nothing more is\n * saved and the draft is `stopped`; on failure it is thrown to the caller and\n * autosave carries on.\n */\n readonly commit = (write: (row: R | null, value: V) => Promise<R>): Promise<R> => {\n this.clearTimer();\n const run = this.chain.then(async () => {\n const row = await write(this.state.row, this.state.value);\n this.ended = true;\n this.saved = this.options().toValue(row);\n this.patch({ row, status: \"stopped\", error: null });\n this.options().onSaved?.(row);\n return row;\n });\n // A failed commit leaves the draft as it was: autosave carries on.\n this.chain = run.catch(() => {\n if (this.pending()) this.schedule();\n });\n return run;\n };\n\n /**\n * The server's seed again. Ignored unless `revision` changed. Then, in order:\n * - the seed is this draft's own save coming back (the same row, holding what\n * was last saved or is being saved): take its row and keep every edit;\n * - it is another row, with edits pending here: those are saved to the row\n * they belong to first, then the draft moves to the new one;\n * - otherwise the server's copy wins: the draft starts again from it, keeping\n * only what `onRevision` returns.\n */\n readonly reseed = (seed: DraftSeed<V, R>, revision: string | undefined) => {\n if (revision === this.revision) return;\n this.revision = revision;\n const opts = this.options();\n const idOf = opts.idOf ?? defaultId;\n const current = this.state.row;\n const echo =\n seed.row !== null &&\n (current === null || idOf(seed.row) === idOf(current)) &&\n (this.same(opts.toValue(seed.row), this.saved) ||\n (this.inflight !== null && this.same(opts.toValue(seed.row), this.inflight)));\n if (echo && (current !== null || this.inflight !== null)) {\n this.patch({ row: seed.row });\n return;\n }\n this.generation++;\n this.clearTimer();\n const pending = !this.same(this.state.value, this.saved);\n if (pending && current !== null && seed.row !== null && idOf(seed.row) !== idOf(current) && this.active()) {\n // Another row: what was typed belongs to the old one, so save it there first.\n const value = this.state.value;\n this.chain = this.chain\n .then(() => opts.update(current, value, { keepalive: false }))\n .then((row) => opts.onSaved?.(row))\n .catch(() => undefined);\n this.startFrom(seed, seed.value, false);\n return;\n }\n const kept = pending ? (opts.onRevision?.(this.state.value, seed) ?? null) : null;\n this.startFrom(seed, kept ?? seed.value, pending && kept === null);\n if (kept !== null && this.pending()) this.schedule();\n };\n\n /** Call when `enabled` may have changed: off stops saving; back on saves what's pending. */\n readonly sync = () => {\n if (!this.enabled()) {\n this.clearTimer();\n if (this.state.status !== \"stopped\") this.patch({ status: \"stopped\" });\n } else if (this.state.status === \"stopped\" && !this.ended) {\n this.patch({ status: \"idle\" });\n if (this.pending()) this.schedule();\n }\n };\n\n private startFrom(seed: DraftSeed<V, R>, value: V, discarded: boolean) {\n this.saved = seed.row ? this.options().toValue(seed.row) : seed.value;\n this.dirty.clear();\n this.ended = false;\n this.inflight = null;\n this.patch({\n row: seed.row,\n value,\n status: this.enabled() ? \"idle\" : \"stopped\",\n error: null,\n discarded,\n key: this.state.key + 1,\n });\n }\n\n private enabled() {\n return this.options().enabled ?? true;\n }\n\n private active() {\n return this.enabled() && !this.ended;\n }\n\n private same(a: V, b: V) {\n const equal = this.options().equal;\n return equal ? equal(a, b) : stableJson(a) === stableJson(b);\n }\n\n private schedule() {\n this.clearTimer();\n this.timer = setTimeout(() => {\n this.timer = undefined;\n void this.flush();\n }, this.options().delayMs ?? DEFAULT_DRAFT_DELAY_MS);\n }\n\n private clearTimer() {\n if (this.timer !== undefined) clearTimeout(this.timer);\n this.timer = undefined;\n }\n\n private patch(change: Partial<DraftState<V, R>>) {\n this.state = { ...this.state, ...change };\n for (const listener of this.listeners) listener();\n }\n\n private async save(keepalive: boolean): Promise<void> {\n if (!this.active()) return;\n const opts = this.options();\n const value = this.state.value;\n if (this.same(value, this.saved)) return;\n const isEmpty = opts.isEmpty ?? ((v: V) => !Object.values(v).some(hasContent));\n if (!this.state.row && isEmpty(value)) return;\n this.patch({ status: \"saving\" });\n const generation = this.generation;\n this.inflight = value;\n try {\n const { row, merged } = await this.write(value, keepalive, this.state.row, generation);\n this.inflight = null;\n // A new revision arrived while this ran: the server's copy won, so this result is stale.\n if (generation !== this.generation) return;\n const stored = opts.toValue(row);\n // What was sent, not what came back: a store that tidies values (trims text, fills\n // defaults) would otherwise look unsaved forever and be written again and again.\n this.saved = merged ? stored : value;\n if (merged) {\n // Another tab's row: show its fields, keeping only those changed here.\n const next = { ...stored } as DraftValue;\n for (const key of this.dirty) next[key] = this.state.value[key];\n this.patch({ row, value: next as V, status: \"saved\", error: null, key: this.state.key + 1 });\n } else {\n this.patch({ row, status: \"saved\", error: null });\n }\n opts.onSaved?.(row);\n // Typed while this save ran, or merged: save again.\n if (this.pending()) this.schedule();\n } catch (error) {\n if (generation === this.generation) this.inflight = null;\n if (error instanceof Halt || generation !== this.generation) return;\n this.patch({ status: \"error\", error: (opts.messageOf ?? plainMessage)(error) });\n }\n }\n\n /**\n * One save: change the row, or add it (merging on a duplicate), retrying network failures.\n * The row is the one the save started with, never one a later revision brought in.\n */\n private async write(\n value: V,\n keepalive: boolean,\n start: R | null,\n generation: number,\n ): Promise<{ row: R; merged: boolean }> {\n const opts = this.options();\n const retries = opts.retries ?? 3;\n const isRetryable = opts.isRetryable ?? isNetworkFailure;\n for (let attempt = 0; ; attempt++) {\n try {\n if (start) return { row: await opts.update(start, value, { keepalive }), merged: false };\n try {\n return { row: await opts.create(value, { keepalive }), merged: false };\n } catch (error) {\n if (!opts.adopt || !(opts.isDuplicate ?? isDuplicateOutcome)(error)) throw error;\n const adopted = await opts.adopt(value, this.dirty);\n if (generation !== this.generation) throw new Halt();\n if (\"final\" in adopted) {\n this.ended = true;\n this.saved = opts.toValue(adopted.final);\n this.patch({ row: adopted.final, status: \"stopped\", error: null });\n opts.onFinal?.(adopted.final);\n throw new Halt();\n }\n return { row: adopted.row, merged: true };\n }\n } catch (error) {\n if (error instanceof Halt || attempt >= retries || !isRetryable(error)) throw error;\n await new Promise((resolve) => setTimeout(resolve, (opts.retryDelayMs ?? 500) * 2 ** attempt));\n // A new revision while waiting: these edits no longer belong to the row it brought.\n if (!this.active() || generation !== this.generation) throw new Halt();\n }\n }\n }\n}\n"]}
|
|
@@ -27,5 +27,5 @@ function isDataWriteError(error) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export { DataWriteError, isDataWriteError };
|
|
30
|
-
//# sourceMappingURL=chunk-
|
|
31
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-LG6TUBVM.js.map
|
|
31
|
+
//# sourceMappingURL=chunk-LG6TUBVM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema/write-outcome.ts"],"names":[],"mappings":";AA2DO,IAAM,cAAA,GAAN,cAA6B,KAAA,CAAM;AAAA,EAC/B,OAAA;AAAA,EACA,OAAA;AAAA,EACA,cAAA;AAAA;AAAA,EAEA,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA;AAAA,EAET,WAAA,CAAY,OAAA,EAAuB,OAAA,EAAiB,OAAA,GAAiC,EAAC,EAAG;AACvF,IAAA,KAAA,CAAM,OAAA,EAAS,QAAQ,KAAA,KAAU,MAAA,GAAY,SAAY,EAAE,KAAA,EAAO,OAAA,CAAQ,KAAA,EAAO,CAAA;AACjF,IAAA,IAAA,CAAK,IAAA,GAAO,gBAAA;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AACf,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AACvB,IAAA,IAAA,CAAK,iBAAiB,OAAA,CAAQ,cAAA;AAC9B,IAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,MAAA,EAAW,IAAA,CAAK,OAAO,OAAA,CAAQ,IAAA;AACpD,IAAA,IAAI,OAAA,CAAQ,UAAU,MAAA,CAAO,IAAA,CAAK,QAAQ,MAAM,CAAA,CAAE,SAAS,CAAA,EAAG;AAC5D,MAAA,IAAA,CAAK,SAAS,MAAA,CAAO,MAAA,CAAO,EAAE,GAAG,OAAA,CAAQ,QAAQ,CAAA;AAAA,IACnD;AAAA,EACF;AACF;AAMO,SAAS,iBAAiB,KAAA,EAAyC;AACxE,EAAA,OACE,iBAAiB,KAAA,IACjB,KAAA,CAAM,SAAS,gBAAA,IACf,OAAQ,MAAgC,OAAA,KAAY,QAAA;AAExD","file":"chunk-LG6TUBVM.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Why a write did not save (DATA-STUDIO.md \"Write safety\")\n//\n// A write that changes nothing is never reported as \"permission denied\" by\n// assumption. The provider re-reads the row and says what actually happened.\n// ---------------------------------------------------------------------------\n\nimport type { DataRow } from \"./types\";\n\n/**\n * - `conflict`: someone else changed the row since it was read. `current`\n * carries the row as it is now.\n * - `gone`: the row was deleted, or is no longer visible to this user. The\n * two cannot be told apart without a privileged look at the table, which\n * would show this user a fact about a row they cannot see.\n * - `forbidden`: the row is visible and unchanged, but the app or the\n * database does not let this user make this change. Only reported when\n * that is proven, never assumed from \"zero rows changed\".\n * - `duplicate`: a row with the same unique key already exists (`23505`).\n * A caller keeping \"one live row per key\" (a draft, a natural-key upsert)\n * adopts the existing row instead; `fields` still names the clashing field.\n * - `invalid`: the store rejected the value (type, constraint, not-null).\n * - `audit_failed`: the audit entry could not be written, so the edit was\n * rolled back with it.\n * - `unauthenticated`: no signed-in user, or the session expired.\n * - `error`: anything else. The edit did not commit.\n */\nexport type WriteOutcome =\n | \"conflict\"\n | \"gone\"\n | \"forbidden\"\n | \"duplicate\"\n | \"invalid\"\n | \"audit_failed\"\n | \"unauthenticated\"\n | \"error\";\n\nexport interface DataWriteErrorOptions {\n /** The row as it is now, for a `conflict`. Only fields the user may read. */\n current?: DataRow;\n /** The row's current version token, for a `conflict`. */\n currentVersion?: string | null;\n /**\n * The store's own error code, when it gave one (a Postgres SQLSTATE such as\n * `23505`, or a PostgREST code such as `PGRST301`).\n */\n code?: string;\n /**\n * Per-field messages, keyed by field (column) name, for a write the store\n * rejected because of particular values. Each message is safe to show next\n * to its input: plain words, never the store's raw text or the value.\n * `useResourceForm` puts each one on its field.\n */\n fields?: Record<string, string>;\n /** The underlying error, kept for logging. */\n cause?: unknown;\n}\n\n/** Thrown by a provider when a write did not commit. */\nexport class DataWriteError extends Error {\n readonly outcome: WriteOutcome;\n readonly current?: DataRow;\n readonly currentVersion?: string | null;\n /** The store's own error code, when it gave one. */\n readonly code?: string;\n /**\n * Per-field messages (field name → safe message), when the store said which\n * values it rejected. See `DataWriteErrorOptions.fields`.\n */\n readonly fields?: Readonly<Record<string, string>>;\n\n constructor(outcome: WriteOutcome, message: string, options: DataWriteErrorOptions = {}) {\n super(message, options.cause === undefined ? undefined : { cause: options.cause });\n this.name = \"DataWriteError\";\n this.outcome = outcome;\n this.current = options.current;\n this.currentVersion = options.currentVersion;\n if (options.code !== undefined) this.code = options.code;\n if (options.fields && Object.keys(options.fields).length > 0) {\n this.fields = Object.freeze({ ...options.fields });\n }\n }\n}\n\n/**\n * `instanceof` fails when two copies of this package are installed, so check\n * the shape instead.\n */\nexport function isDataWriteError(error: unknown): error is DataWriteError {\n return (\n error instanceof Error &&\n error.name === \"DataWriteError\" &&\n typeof (error as { outcome?: unknown }).outcome === \"string\"\n );\n}\n"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { invalidateResource, executeWrite, cachedRecord } from './chunk-S2ZXUNXB.js';
|
|
2
|
+
import { relatedKeys } from './chunk-7NUL32W7.js';
|
|
3
|
+
import { resourceKeys } from './chunk-RCAP3Q4T.js';
|
|
1
4
|
import { useLiveInvalidation } from './chunk-KXVHDK3Q.js';
|
|
2
|
-
import { invalidateResource, executeWrite, cachedRecord } from './chunk-OBUBP4BY.js';
|
|
3
5
|
import { useDataProvider } from './chunk-GGSXSI6Z.js';
|
|
4
|
-
import { relatedKeys } from './chunk-7NUL32W7.js';
|
|
5
6
|
import { useState, useCallback, useMemo } from 'react';
|
|
6
7
|
import { useQueryClient, useQuery, keepPreviousData, useMutation } from '@tanstack/react-query';
|
|
7
8
|
|
|
@@ -81,7 +82,7 @@ function useResource(resource, options = {}) {
|
|
|
81
82
|
return p;
|
|
82
83
|
}, [page, perPage, sort, filters, select, searchQuery, searchFieldsOption]);
|
|
83
84
|
const listQueryKey = useMemo(
|
|
84
|
-
() =>
|
|
85
|
+
() => resourceKeys(resource).list(params),
|
|
85
86
|
[resource, params]
|
|
86
87
|
);
|
|
87
88
|
const list = useQuery({
|
|
@@ -210,5 +211,5 @@ function useResource(resource, options = {}) {
|
|
|
210
211
|
}
|
|
211
212
|
|
|
212
213
|
export { useResource };
|
|
213
|
-
//# sourceMappingURL=chunk-
|
|
214
|
-
//# sourceMappingURL=chunk-
|
|
214
|
+
//# sourceMappingURL=chunk-NMFABW4Y.js.map
|
|
215
|
+
//# sourceMappingURL=chunk-NMFABW4Y.js.map
|