@marktiderman/genesis-core 1.3.0 → 1.5.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 +223 -0
- package/dist/chunk-5NHR6ZXS.js +31 -0
- package/dist/chunk-5NHR6ZXS.js.map +1 -0
- package/dist/chunk-7NUL32W7.js +14 -0
- package/dist/chunk-7NUL32W7.js.map +1 -0
- package/dist/chunk-BQOJX67X.js +31 -0
- package/dist/chunk-BQOJX67X.js.map +1 -0
- package/dist/{chunk-QFTMHOEH.js → chunk-DVW5GXWZ.js} +84 -31
- package/dist/chunk-DVW5GXWZ.js.map +1 -0
- package/dist/{chunk-6V2TIN3G.js → chunk-GGSXSI6Z.js} +6 -5
- package/dist/chunk-GGSXSI6Z.js.map +1 -0
- package/dist/chunk-HT43MPKW.js +3 -0
- package/dist/{chunk-EL7YGOTY.js.map → chunk-HT43MPKW.js.map} +1 -1
- package/dist/chunk-HUNGCTRA.js +33 -0
- package/dist/chunk-HUNGCTRA.js.map +1 -0
- package/dist/{chunk-5FTUB2M6.js → chunk-IZT2O57C.js} +160 -40
- package/dist/chunk-IZT2O57C.js.map +1 -0
- package/dist/chunk-QH4P5MI2.js +278 -0
- package/dist/chunk-QH4P5MI2.js.map +1 -0
- package/dist/chunk-RLY7TZUY.js +444 -0
- package/dist/chunk-RLY7TZUY.js.map +1 -0
- package/dist/chunk-XQO4OH2N.js +32 -0
- package/dist/chunk-XQO4OH2N.js.map +1 -0
- package/dist/{chunk-3P4PHPMJ.js → chunk-ZK5C3OOT.js} +3 -3
- package/dist/{chunk-3P4PHPMJ.js.map → chunk-ZK5C3OOT.js.map} +1 -1
- package/dist/form-types.d.ts +6 -0
- package/dist/hooks/index.d.ts +5 -1
- package/dist/hooks/index.js +11 -6
- package/dist/hooks/mutation-mode.d.ts +109 -0
- package/dist/hooks/mutation-mode.js +3 -0
- package/dist/hooks/mutation-mode.js.map +1 -0
- package/dist/hooks/related-keys.d.ts +12 -0
- package/dist/hooks/related-keys.js +3 -0
- package/dist/hooks/related-keys.js.map +1 -0
- package/dist/hooks/use-capabilities.d.ts +21 -0
- package/dist/hooks/use-capabilities.js +5 -0
- package/dist/hooks/use-capabilities.js.map +1 -0
- package/dist/hooks/use-one.d.ts +2 -1
- package/dist/hooks/use-one.js +2 -2
- package/dist/hooks/use-resource-form.d.ts +53 -2
- package/dist/hooks/use-resource-form.js +7 -3
- package/dist/hooks/use-resource.d.ts +42 -5
- package/dist/hooks/use-resource.js +4 -2
- package/dist/hooks/use-unsaved-changes-guard.d.ts +33 -0
- package/dist/hooks/use-unsaved-changes-guard.js +3 -0
- package/dist/hooks/use-unsaved-changes-guard.js.map +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +14 -9
- package/dist/provider/index.d.ts +212 -6
- package/dist/provider/index.js +5 -3
- package/dist/read-outcome-BQkYcV4t.d.ts +88 -0
- package/dist/schema/index.d.ts +4 -3
- package/dist/schema/index.js +2 -1
- package/dist/{types-DUbRb8qV.d.ts → types-C5bjsbxz.d.ts} +13 -255
- package/dist/types-Dsqqr9s7.d.ts +281 -0
- package/package.json +2 -2
- package/dist/chunk-4W4W7F4I.js +0 -20
- package/dist/chunk-4W4W7F4I.js.map +0 -1
- package/dist/chunk-5FTUB2M6.js.map +0 -1
- package/dist/chunk-6AA4ISUA.js +0 -125
- package/dist/chunk-6AA4ISUA.js.map +0 -1
- package/dist/chunk-6V2TIN3G.js.map +0 -1
- package/dist/chunk-EL7YGOTY.js +0 -3
- package/dist/chunk-QFTMHOEH.js.map +0 -1
- package/dist/write-outcome-aYToY3dK.d.ts +0 -38
package/dist/provider/index.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import { D as DataProvider } from '../types-
|
|
2
|
-
export { A as ArrayFilter,
|
|
3
|
-
|
|
1
|
+
import { D as DataProvider, I as Identifier, B as BaseRecord, j as InvalidationKey, W as WriteEvent } from '../types-Dsqqr9s7.js';
|
|
2
|
+
export { A as ArrayFilter, a as BulkChange, b as BulkResult, C as CreateParams, c as CreateResult, d as DeleteParams, e as DeleteResult, F as FilterParam, G as GetManyParams, f as GetManyResult, g as GetOneParams, h as GetOneResult, H as HistoryParams, i as ImportParams, L as ListParams, k as ListResult, N as NullFilter, P as PaginationParam, R as RangeFilter, l as RowFailure, S as ScalarFilter, m as SortParam, U as UpdateParams, n as UpdateResult } from '../types-Dsqqr9s7.js';
|
|
3
|
+
import { D as DataReadError, R as ReadOutcome, b as DataWriteError, W as WriteOutcome } from '../read-outcome-BQkYcV4t.js';
|
|
4
|
+
export { a as DataReadErrorOptions, c as DataWriteErrorOptions, i as isDataReadError, d as isDataWriteError, r as readFailureMessage } from '../read-outcome-BQkYcV4t.js';
|
|
4
5
|
import * as react from 'react';
|
|
5
6
|
import { ReactNode } from 'react';
|
|
7
|
+
import { QueryClient } from '@tanstack/react-query';
|
|
6
8
|
import { S as StorageAdapter } from '../types-DKcxHXxq.js';
|
|
9
|
+
import '../types-C5bjsbxz.js';
|
|
7
10
|
import '../form-types.js';
|
|
8
11
|
|
|
9
12
|
interface MockProviderOptions {
|
|
@@ -70,8 +73,195 @@ interface SupabaseClient {
|
|
|
70
73
|
interface SupabaseClientInput {
|
|
71
74
|
from(table: string): unknown;
|
|
72
75
|
}
|
|
73
|
-
|
|
76
|
+
/** What a write hook is told about the write it is part of. */
|
|
77
|
+
interface SupabaseWriteContext {
|
|
78
|
+
resource: string;
|
|
79
|
+
action: "create" | "update";
|
|
80
|
+
/** The row's id, for an update. */
|
|
81
|
+
id?: Identifier;
|
|
82
|
+
}
|
|
83
|
+
/** Per-resource rules for `createSupabaseProvider`. Every field is optional. */
|
|
84
|
+
interface SupabaseResourceConfig {
|
|
85
|
+
/** Singular noun for failure messages, e.g. "project". Defaults to "record". */
|
|
86
|
+
noun?: string;
|
|
87
|
+
/** Plural for list messages, when it isn't `noun` + "s" ("categories"). */
|
|
88
|
+
nounPlural?: string;
|
|
89
|
+
/**
|
|
90
|
+
* The columns a search covers. `[]` means the resource can't be searched,
|
|
91
|
+
* and `capabilities(resource).search` reports false so a UI hides the box.
|
|
92
|
+
* Set, they replace the caller's `search.fields`; omitted, those are used
|
|
93
|
+
* as given.
|
|
94
|
+
*/
|
|
95
|
+
searchFields?: readonly string[];
|
|
96
|
+
/**
|
|
97
|
+
* The column a sorted list orders by last, so rows with equal sort values
|
|
98
|
+
* keep one order from page to page (none repeats, none is skipped).
|
|
99
|
+
* Default `"id"`; `false` for a table or view without one. With the
|
|
100
|
+
* default, a view that turns out to have no `id` column is read as before
|
|
101
|
+
* (without the key order) and a development warning suggests setting this.
|
|
102
|
+
*/
|
|
103
|
+
orderKey?: string | false;
|
|
104
|
+
/**
|
|
105
|
+
* `"bin"` for a table whose delete trigger moves the row to a recycle bin
|
|
106
|
+
* instead of removing it (see `SupabaseProviderOptions.binTables`).
|
|
107
|
+
* `"hard"` forces a plain delete even when `binTables` lists the table.
|
|
108
|
+
*/
|
|
109
|
+
deleteMode?: "hard" | "bin";
|
|
110
|
+
/**
|
|
111
|
+
* Turns submitted values into the row to write: trim, drop read-only keys,
|
|
112
|
+
* validate. Runs before every create and update, including the `create`
|
|
113
|
+
* and `update` overrides. Throw (ideally `DataWriteError("invalid", ...)`)
|
|
114
|
+
* to refuse the write; nothing is sent.
|
|
115
|
+
*/
|
|
116
|
+
prepare?: (values: Record<string, unknown>, context: SupabaseWriteContext) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
117
|
+
/**
|
|
118
|
+
* Creates the row some other way than an insert, e.g. a security-definer
|
|
119
|
+
* RPC. Return the new row, or its id and the provider reads the row back
|
|
120
|
+
* (if the read-back fails, the create is still reported as saved, with
|
|
121
|
+
* just the id, so the user does not retry and make a second one). Throw to
|
|
122
|
+
* fail; a Supabase `{ message, code }` error is mapped like any other.
|
|
123
|
+
*/
|
|
124
|
+
create?: (row: Record<string, unknown>) => Promise<BaseRecord | Identifier>;
|
|
125
|
+
/**
|
|
126
|
+
* Updates the row some other way than an update, e.g. an RPC. Return the
|
|
127
|
+
* updated row, or its id (or `true`) and the provider reads it back.
|
|
128
|
+
* Returning nothing (`undefined`/`null`/`false`) means no row changed: the provider then reads the
|
|
129
|
+
* row to say why (`forbidden` or `gone`), exactly as for a plain update,
|
|
130
|
+
* so a function that silently skips a row never reports a save.
|
|
131
|
+
*/
|
|
132
|
+
update?: (id: Identifier, row: Record<string, unknown>) => Promise<BaseRecord | Identifier | boolean | null | undefined | void>;
|
|
133
|
+
/** Maps a stored row to edit-form values; see `DataProvider.toFormValues`. */
|
|
134
|
+
toFormValues?: (record: Record<string, unknown>) => Record<string, unknown>;
|
|
135
|
+
/**
|
|
136
|
+
* Other cache keys a committed write to this resource makes stale, e.g.
|
|
137
|
+
* `[["staff"], ["me"]]`. Served through `DataProvider.invalidates`, which
|
|
138
|
+
* `useResource` and `useResourceForm` invalidate after their writes.
|
|
139
|
+
*/
|
|
140
|
+
invalidates?: InvalidationKey[] | ((event: WriteEvent) => InvalidationKey[]);
|
|
141
|
+
}
|
|
142
|
+
/** Options for `createSupabaseProvider`. All optional; none changes reads. */
|
|
143
|
+
interface SupabaseProviderOptions {
|
|
144
|
+
/**
|
|
145
|
+
* Recycle-bin tables: their delete trigger turns a delete into a "binned"
|
|
146
|
+
* mark and the binned row drops out of the caller's view (RLS or a view
|
|
147
|
+
* filter). The database reports such a delete as zero rows, so for these
|
|
148
|
+
* the provider checks the row is visible, deletes it, and reads it back:
|
|
149
|
+
* gone from view is success, still visible is `forbidden`. A row that was
|
|
150
|
+
* not visible beforehand is `gone`.
|
|
151
|
+
*/
|
|
152
|
+
binTables?: readonly string[] | ((resource: string) => boolean);
|
|
153
|
+
/** Per-resource rules, keyed by resource (table) name. */
|
|
154
|
+
resources?: Record<string, SupabaseResourceConfig>;
|
|
155
|
+
/**
|
|
156
|
+
* Called after every committed create, update and delete, e.g. to
|
|
157
|
+
* invalidate caches the Genesis hooks don't know about. Awaited, but a
|
|
158
|
+
* failure is logged and never turns a committed write into a failed one.
|
|
159
|
+
*/
|
|
160
|
+
onWrite?: (event: WriteEvent) => void | Promise<void>;
|
|
161
|
+
/**
|
|
162
|
+
* The most rows one list page may ask for (PostgREST's `max-rows` caps
|
|
163
|
+
* reads anyway; default 1000). A larger request is refused with an
|
|
164
|
+
* `invalid` `DataReadError` (its `cause` says why) rather than cut short,
|
|
165
|
+
* so page boundaries never disagree with the caller's `perPage`.
|
|
166
|
+
* `capabilities().maxPerPage` reports it.
|
|
167
|
+
*/
|
|
168
|
+
maxPerPage?: number;
|
|
169
|
+
/**
|
|
170
|
+
* Called with every failed read, before it is thrown. The thrown
|
|
171
|
+
* `DataReadError` carries a plain message for users; the store's own
|
|
172
|
+
* error (its raw text and code) is its `cause`, for logging here.
|
|
173
|
+
*/
|
|
174
|
+
onReadError?: (error: DataReadError, context: {
|
|
175
|
+
resource: string;
|
|
176
|
+
operation: "getList" | "getOne" | "getMany";
|
|
177
|
+
}) => void | Promise<void>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The Supabase data provider. Reads and writes go through the client you
|
|
181
|
+
* pass, under that client's session and RLS.
|
|
182
|
+
*
|
|
183
|
+
* Writes never report a save that did not happen:
|
|
184
|
+
* - `update` and `deleteOne` ask for the affected rows. Zero rows is a
|
|
185
|
+
* `DataWriteError`: the row is read back, and still visible means
|
|
186
|
+
* `forbidden` (RLS or a trigger refused the change), no longer visible
|
|
187
|
+
* means `gone`.
|
|
188
|
+
* - Database errors on writes are thrown as `DataWriteError` with the
|
|
189
|
+
* outcome their code means (`writeOutcomeFromCode`) and the code kept on
|
|
190
|
+
* `error.code`.
|
|
191
|
+
* - Recycle-bin tables (`binTables`) succeed when the row leaves the
|
|
192
|
+
* caller's view.
|
|
193
|
+
*
|
|
194
|
+
* Reads are unchanged: errors are thrown as an `Error` carrying `code`.
|
|
195
|
+
*/
|
|
196
|
+
declare function createSupabaseProvider(client: SupabaseClientInput, options?: SupabaseProviderOptions): DataProvider;
|
|
74
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Maps a store error code to the read outcome it means: the write mapping,
|
|
200
|
+
* with `22***` and PostgREST request errors (`PGRST1**` other than PGRST116,
|
|
201
|
+
* `42703` unknown column, `42P01` unknown table) read as `invalid`.
|
|
202
|
+
*/
|
|
203
|
+
declare function readOutcomeFromCode(code: string | null | undefined): ReadOutcome;
|
|
204
|
+
/**
|
|
205
|
+
* Maps a Postgres SQLSTATE or PostgREST error code to the write outcome it
|
|
206
|
+
* means. Anything not listed is `error`: a code is only read as a refusal or
|
|
207
|
+
* a bad value when it says so.
|
|
208
|
+
*
|
|
209
|
+
* - `42501` (insufficient privilege, including "new row violates row-level
|
|
210
|
+
* security policy") → `forbidden`
|
|
211
|
+
* - `22***` (data exception: bad type, out of range) and `23***` (integrity
|
|
212
|
+
* constraint: `23505` unique, `23503` foreign key, `23502` not null,
|
|
213
|
+
* `23514` check) → `invalid`
|
|
214
|
+
* - `PGRST301`–`PGRST303` (JWT missing, expired or rejected), `28000`,
|
|
215
|
+
* `28P01` → `unauthenticated`
|
|
216
|
+
* - `PGRST116` (one row was asked for and none came back) → `gone`
|
|
217
|
+
*/
|
|
218
|
+
declare function writeOutcomeFromCode(code: string | null | undefined): WriteOutcome;
|
|
219
|
+
/** Field errors read from a store error, with the safe sentence that sums them up. */
|
|
220
|
+
interface PostgrestFieldErrors {
|
|
221
|
+
/** Column name → safe message for that input. */
|
|
222
|
+
fields: Record<string, string>;
|
|
223
|
+
/** One safe sentence for the whole error, to replace the store's raw text. */
|
|
224
|
+
message: string;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Reads which columns a Postgres constraint error is about, from PostgREST's
|
|
228
|
+
* `message` / `details`, and gives safe messages for them. Only plain column
|
|
229
|
+
* names are taken from the store's text; the values it echoes are never
|
|
230
|
+
* used. Returns `undefined` when nothing can be mapped safely.
|
|
231
|
+
*
|
|
232
|
+
* - `23505` unique violation, `Key (email)=(…) already exists.` → each
|
|
233
|
+
* column: "This value is already in use." (several columns: "This
|
|
234
|
+
* combination is already in use.")
|
|
235
|
+
* - `23502` not-null, `null value in column "title" …` → "This field is
|
|
236
|
+
* required."
|
|
237
|
+
* - `23503` foreign key on insert/update, `Key (owner_id)=(…) is not present
|
|
238
|
+
* in table …` → "The selected item doesn't exist."
|
|
239
|
+
*/
|
|
240
|
+
declare function fieldErrorsFromPostgrest(error: {
|
|
241
|
+
code?: unknown;
|
|
242
|
+
message?: unknown;
|
|
243
|
+
details?: unknown;
|
|
244
|
+
}): PostgrestFieldErrors | undefined;
|
|
245
|
+
/**
|
|
246
|
+
* Turns anything a write threw, or the `{ message, code }` error a Supabase
|
|
247
|
+
* client returned, into a `DataWriteError`. A `DataWriteError` passes through
|
|
248
|
+
* unchanged; anything else keeps its message and `code`, and is kept as the
|
|
249
|
+
* `cause`.
|
|
250
|
+
*
|
|
251
|
+
* When the error names the columns it is about (`fieldErrorsFromPostgrest`:
|
|
252
|
+
* unique, not-null and foreign-key violations), the result carries `fields`
|
|
253
|
+
* and its message is replaced with a safe sentence; the raw text stays on
|
|
254
|
+
* the `cause`.
|
|
255
|
+
*/
|
|
256
|
+
declare function toDataWriteError(error: unknown): DataWriteError;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Puts a `DataProvider` in context, and nothing else. Mount it inside your
|
|
260
|
+
* app's own `QueryClientProvider` and the Genesis hooks share that cache:
|
|
261
|
+
* the same query keys (`[resource, "list", params]`,
|
|
262
|
+
* `[resource, "detail", id, select]`), so invalidating `[resource]` from app
|
|
263
|
+
* code refreshes Genesis lists too, and vice versa.
|
|
264
|
+
*/
|
|
75
265
|
interface DataProviderRootProps {
|
|
76
266
|
provider: DataProvider;
|
|
77
267
|
children: ReactNode;
|
|
@@ -87,6 +277,14 @@ type GenesisProviderProps = {
|
|
|
87
277
|
mock?: never;
|
|
88
278
|
/** Platform-specific storage adapter. Defaults to localStorage (SSR-safe). */
|
|
89
279
|
storage?: StorageAdapter;
|
|
280
|
+
/**
|
|
281
|
+
* The TanStack Query client to use. Without it, GenesisProvider creates
|
|
282
|
+
* its OWN client — a second cache beside any the app already has, so app
|
|
283
|
+
* queries and Genesis queries would not see each other's invalidations.
|
|
284
|
+
* An app with a `QueryClientProvider` should pass its client here, or
|
|
285
|
+
* use `DataProviderRoot` inside that provider instead.
|
|
286
|
+
*/
|
|
287
|
+
queryClient?: QueryClient;
|
|
90
288
|
} | {
|
|
91
289
|
children: ReactNode;
|
|
92
290
|
provider?: never;
|
|
@@ -97,7 +295,15 @@ type GenesisProviderProps = {
|
|
|
97
295
|
};
|
|
98
296
|
/** Platform-specific storage adapter. Defaults to localStorage (SSR-safe). */
|
|
99
297
|
storage?: StorageAdapter;
|
|
298
|
+
/** See the `provider` variant. */
|
|
299
|
+
queryClient?: QueryClient;
|
|
100
300
|
};
|
|
101
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Convenience root: a `QueryClientProvider`, storage, and a data provider
|
|
303
|
+
* (yours, or a mock built from datasets). It creates its own `QueryClient`
|
|
304
|
+
* unless you pass `queryClient`; an app that already has one should pass it,
|
|
305
|
+
* or mount `DataProviderRoot` inside its existing `QueryClientProvider`.
|
|
306
|
+
*/
|
|
307
|
+
declare function GenesisProvider({ children, provider, mock, storage, queryClient: queryClientProp, }: GenesisProviderProps): react.JSX.Element;
|
|
102
308
|
|
|
103
|
-
export { DataProvider, DataProviderRoot, type DataProviderRootProps, GenesisProvider, type GenesisProviderProps, type MockProviderOptions, type SupabaseClient, type SupabaseClientInput, createMockProvider, createSupabaseProvider, useDataProvider, useStorage };
|
|
309
|
+
export { BaseRecord, DataProvider, DataProviderRoot, type DataProviderRootProps, DataReadError, DataWriteError, GenesisProvider, type GenesisProviderProps, Identifier, InvalidationKey, type MockProviderOptions, type PostgrestFieldErrors, ReadOutcome, type SupabaseClient, type SupabaseClientInput, type SupabaseProviderOptions, type SupabaseResourceConfig, type SupabaseWriteContext, WriteEvent, WriteOutcome, createMockProvider, createSupabaseProvider, fieldErrorsFromPostgrest, readOutcomeFromCode, toDataWriteError, useDataProvider, useStorage, writeOutcomeFromCode };
|
package/dist/provider/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
export { createSupabaseProvider } from '../chunk-
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
1
|
+
export { createSupabaseProvider, fieldErrorsFromPostgrest, readOutcomeFromCode, toDataWriteError, writeOutcomeFromCode } from '../chunk-RLY7TZUY.js';
|
|
2
|
+
export { DataReadError, isDataReadError, readFailureMessage } from '../chunk-HUNGCTRA.js';
|
|
3
|
+
export { DataWriteError, isDataWriteError } from '../chunk-5NHR6ZXS.js';
|
|
4
|
+
import '../chunk-RYEXWQFD.js';
|
|
5
|
+
export { DataProviderRoot, GenesisProvider, createMockProvider, useDataProvider, useStorage } from '../chunk-GGSXSI6Z.js';
|
|
4
6
|
import '../chunk-ZYJUEJ4Z.js';
|
|
5
7
|
//# sourceMappingURL=index.js.map
|
|
6
8
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { D as DataRow } from './types-C5bjsbxz.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* - `conflict`: someone else changed the row since it was read. `current`
|
|
5
|
+
* carries the row as it is now.
|
|
6
|
+
* - `gone`: the row was deleted, or is no longer visible to this user. The
|
|
7
|
+
* two cannot be told apart without a privileged look at the table, which
|
|
8
|
+
* would show this user a fact about a row they cannot see.
|
|
9
|
+
* - `forbidden`: the row is visible and unchanged, but the app or the
|
|
10
|
+
* database does not let this user make this change. Only reported when
|
|
11
|
+
* that is proven, never assumed from "zero rows changed".
|
|
12
|
+
* - `invalid`: the store rejected the value (type, constraint, not-null).
|
|
13
|
+
* - `audit_failed`: the audit entry could not be written, so the edit was
|
|
14
|
+
* rolled back with it.
|
|
15
|
+
* - `unauthenticated`: no signed-in user, or the session expired.
|
|
16
|
+
* - `error`: anything else. The edit did not commit.
|
|
17
|
+
*/
|
|
18
|
+
type WriteOutcome = "conflict" | "gone" | "forbidden" | "invalid" | "audit_failed" | "unauthenticated" | "error";
|
|
19
|
+
interface DataWriteErrorOptions {
|
|
20
|
+
/** The row as it is now, for a `conflict`. Only fields the user may read. */
|
|
21
|
+
current?: DataRow;
|
|
22
|
+
/** The row's current version token, for a `conflict`. */
|
|
23
|
+
currentVersion?: string | null;
|
|
24
|
+
/**
|
|
25
|
+
* The store's own error code, when it gave one (a Postgres SQLSTATE such as
|
|
26
|
+
* `23505`, or a PostgREST code such as `PGRST301`).
|
|
27
|
+
*/
|
|
28
|
+
code?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Per-field messages, keyed by field (column) name, for a write the store
|
|
31
|
+
* rejected because of particular values. Each message is safe to show next
|
|
32
|
+
* to its input: plain words, never the store's raw text or the value.
|
|
33
|
+
* `useResourceForm` puts each one on its field.
|
|
34
|
+
*/
|
|
35
|
+
fields?: Record<string, string>;
|
|
36
|
+
/** The underlying error, kept for logging. */
|
|
37
|
+
cause?: unknown;
|
|
38
|
+
}
|
|
39
|
+
/** Thrown by a provider when a write did not commit. */
|
|
40
|
+
declare class DataWriteError extends Error {
|
|
41
|
+
readonly outcome: WriteOutcome;
|
|
42
|
+
readonly current?: DataRow;
|
|
43
|
+
readonly currentVersion?: string | null;
|
|
44
|
+
/** The store's own error code, when it gave one. */
|
|
45
|
+
readonly code?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Per-field messages (field name → safe message), when the store said which
|
|
48
|
+
* values it rejected. See `DataWriteErrorOptions.fields`.
|
|
49
|
+
*/
|
|
50
|
+
readonly fields?: Readonly<Record<string, string>>;
|
|
51
|
+
constructor(outcome: WriteOutcome, message: string, options?: DataWriteErrorOptions);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* `instanceof` fails when two copies of this package are installed, so check
|
|
55
|
+
* the shape instead.
|
|
56
|
+
*/
|
|
57
|
+
declare function isDataWriteError(error: unknown): error is DataWriteError;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* - `unauthenticated`: no signed-in user, or the session expired.
|
|
61
|
+
* - `forbidden`: this user may not read these records.
|
|
62
|
+
* - `gone`: the one record asked for doesn't exist, or isn't visible.
|
|
63
|
+
* - `invalid`: the request itself was wrong (an unknown column, a bad value).
|
|
64
|
+
* - `error`: anything else.
|
|
65
|
+
*/
|
|
66
|
+
type ReadOutcome = "unauthenticated" | "forbidden" | "gone" | "invalid" | "error";
|
|
67
|
+
interface DataReadErrorOptions {
|
|
68
|
+
/** The store's own error code (a Postgres SQLSTATE or PostgREST code). */
|
|
69
|
+
code?: string;
|
|
70
|
+
/** The underlying error, with its raw text, kept for logging. */
|
|
71
|
+
cause?: unknown;
|
|
72
|
+
}
|
|
73
|
+
/** Thrown by a provider when a read failed. Its `message` is safe to show. */
|
|
74
|
+
declare class DataReadError extends Error {
|
|
75
|
+
readonly outcome: ReadOutcome;
|
|
76
|
+
/** The store's own error code, when it gave one. */
|
|
77
|
+
readonly code?: string;
|
|
78
|
+
constructor(outcome: ReadOutcome, message: string, options?: DataReadErrorOptions);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* `instanceof` fails when two copies of this package are installed, so check
|
|
82
|
+
* the shape instead.
|
|
83
|
+
*/
|
|
84
|
+
declare function isDataReadError(error: unknown): error is DataReadError;
|
|
85
|
+
/** The plain sentence for a failed read of `noun` records (one, or a list). */
|
|
86
|
+
declare function readFailureMessage(outcome: ReadOutcome, noun: string, one: boolean, plural?: string): string;
|
|
87
|
+
|
|
88
|
+
export { DataReadError as D, type ReadOutcome as R, type WriteOutcome as W, type DataReadErrorOptions as a, DataWriteError as b, type DataWriteErrorOptions as c, isDataWriteError as d, isDataReadError as i, readFailureMessage as r };
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
|
|
1
|
+
import { e as ResourceContract, D as DataRow } from '../types-C5bjsbxz.js';
|
|
2
|
+
export { A as AuditEntry, F as FieldPermissions, a as FieldReference, b as FieldSchema, c as FilterOperator, P as ProviderCapabilities, R as RecordComment, d as ResourceAccess, f as ResourceRelation, g as ResourceSchema, h as ResourceSummary, i as RowAction, S as StorageType, V as VersionField } from '../types-C5bjsbxz.js';
|
|
3
|
+
import { I as Identifier } from '../types-Dsqqr9s7.js';
|
|
4
|
+
export { D as DataReadError, a as DataReadErrorOptions, b as DataWriteError, c as DataWriteErrorOptions, R as ReadOutcome, W as WriteOutcome, i as isDataReadError, d as isDataWriteError, r as readFailureMessage } from '../read-outcome-BQkYcV4t.js';
|
|
4
5
|
export { FieldType as WidgetHint } from '../form-types.js';
|
|
5
6
|
|
|
6
7
|
/** The key of a contract as a list, whichever form `key` was declared in. */
|
package/dist/schema/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { decodeRecordKey, encodeRecordKey, keyFields } from '../chunk-KV2HW4G4.js';
|
|
2
|
-
export {
|
|
2
|
+
export { DataReadError, isDataReadError, readFailureMessage } from '../chunk-HUNGCTRA.js';
|
|
3
|
+
export { DataWriteError, isDataWriteError } from '../chunk-5NHR6ZXS.js';
|
|
3
4
|
//# sourceMappingURL=index.js.map
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -70,7 +70,7 @@ interface FieldSchema {
|
|
|
70
70
|
* One declaration of a resource: its fields and its identity.
|
|
71
71
|
*
|
|
72
72
|
* `key` names the identity field, or several for a composite key (a junction
|
|
73
|
-
* table keyed on `(
|
|
73
|
+
* table keyed on `(team_id, user_id)`). Use {@link keyFields} to read it as
|
|
74
74
|
* a list without caring which form it took.
|
|
75
75
|
*/
|
|
76
76
|
interface ResourceContract<Row extends DataRow = DataRow> {
|
|
@@ -239,6 +239,17 @@ interface ProviderCapabilities {
|
|
|
239
239
|
readonly actions?: boolean;
|
|
240
240
|
/** `comments` and `addComment` are served. */
|
|
241
241
|
readonly comments?: boolean;
|
|
242
|
+
/**
|
|
243
|
+
* The most rows one `getList` page may ask for. A request for more is
|
|
244
|
+
* refused (it is not cut short, which would shift later page boundaries),
|
|
245
|
+
* so a client that wants every row must page at this size or less.
|
|
246
|
+
*/
|
|
247
|
+
readonly maxPerPage?: number;
|
|
248
|
+
/**
|
|
249
|
+
* A delete moves the record to a recycle bin (it can be restored) instead
|
|
250
|
+
* of removing it. Asked per resource: `capabilities(resource)`.
|
|
251
|
+
*/
|
|
252
|
+
readonly softDelete?: boolean;
|
|
242
253
|
}
|
|
243
254
|
/** One audit record, as committed alongside the edit it describes. */
|
|
244
255
|
interface AuditEntry {
|
|
@@ -260,257 +271,4 @@ interface AuditEntry {
|
|
|
260
271
|
readonly after: DataRow | null;
|
|
261
272
|
}
|
|
262
273
|
|
|
263
|
-
|
|
264
|
-
type Identifier = string | number;
|
|
265
|
-
/** Base record type — all resources must have an id. */
|
|
266
|
-
type BaseRecord = {
|
|
267
|
-
id: Identifier;
|
|
268
|
-
[key: string]: unknown;
|
|
269
|
-
};
|
|
270
|
-
/** Pagination parameters (offset-based, with optional cursor for future use). */
|
|
271
|
-
interface PaginationParam {
|
|
272
|
-
page: number;
|
|
273
|
-
perPage: number;
|
|
274
|
-
cursor?: string;
|
|
275
|
-
}
|
|
276
|
-
/** Sort direction for a single field. */
|
|
277
|
-
interface SortParam {
|
|
278
|
-
field: string;
|
|
279
|
-
order: "asc" | "desc";
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Filter expressions — discriminated union by operator.
|
|
283
|
-
* Filters are AND-ed together in queries.
|
|
284
|
-
*/
|
|
285
|
-
/** Operators that compare a field against a single scalar value. */
|
|
286
|
-
interface ScalarFilter {
|
|
287
|
-
field: string;
|
|
288
|
-
operator: "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "contains";
|
|
289
|
-
value: string | number | boolean;
|
|
290
|
-
}
|
|
291
|
-
/** Operator that checks field membership in a list. */
|
|
292
|
-
interface ArrayFilter {
|
|
293
|
-
field: string;
|
|
294
|
-
operator: "in";
|
|
295
|
-
value: (string | number | boolean)[];
|
|
296
|
-
}
|
|
297
|
-
/** Operator that checks field is within a range. */
|
|
298
|
-
interface RangeFilter {
|
|
299
|
-
field: string;
|
|
300
|
-
operator: "between";
|
|
301
|
-
value: [string | number, string | number];
|
|
302
|
-
}
|
|
303
|
-
/** Operators that check for null/non-null — no value needed. */
|
|
304
|
-
interface NullFilter {
|
|
305
|
-
field: string;
|
|
306
|
-
operator: "is_null" | "is_not_null";
|
|
307
|
-
}
|
|
308
|
-
type FilterParam = ScalarFilter | ArrayFilter | RangeFilter | NullFilter;
|
|
309
|
-
/** Parameters for `getList`. */
|
|
310
|
-
interface ListParams {
|
|
311
|
-
pagination?: PaginationParam;
|
|
312
|
-
sort?: SortParam;
|
|
313
|
-
/**
|
|
314
|
-
* Sort by several fields, first one first. Wins over `sort` on a provider
|
|
315
|
-
* whose capabilities report `multiSort`; others use only `sort`.
|
|
316
|
-
*/
|
|
317
|
-
sorts?: SortParam[];
|
|
318
|
-
filters?: FilterParam[];
|
|
319
|
-
/** Supabase select syntax, e.g. "*, author:profiles(name)". Mock provider ignores this. */
|
|
320
|
-
select?: string;
|
|
321
|
-
/** Multi-field text search (case-insensitive). */
|
|
322
|
-
search?: {
|
|
323
|
-
query: string;
|
|
324
|
-
fields: string[];
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
/** Result of `getList`. */
|
|
328
|
-
interface ListResult<T> {
|
|
329
|
-
data: T[];
|
|
330
|
-
total: number;
|
|
331
|
-
}
|
|
332
|
-
/** Parameters for `getOne`. */
|
|
333
|
-
interface GetOneParams {
|
|
334
|
-
id: Identifier;
|
|
335
|
-
/** Supabase select syntax, e.g. "*, author:profiles(name)". Defaults to "*". */
|
|
336
|
-
select?: string;
|
|
337
|
-
}
|
|
338
|
-
/** Result of `getOne`. */
|
|
339
|
-
interface GetOneResult<T> {
|
|
340
|
-
data: T;
|
|
341
|
-
}
|
|
342
|
-
/** Parameters for `getMany`. */
|
|
343
|
-
interface GetManyParams {
|
|
344
|
-
ids: Identifier[];
|
|
345
|
-
}
|
|
346
|
-
/** Result of `getMany`. */
|
|
347
|
-
interface GetManyResult<T> {
|
|
348
|
-
data: T[];
|
|
349
|
-
}
|
|
350
|
-
/** Parameters for `create`. */
|
|
351
|
-
interface CreateParams<T> {
|
|
352
|
-
data: Partial<T>;
|
|
353
|
-
}
|
|
354
|
-
/** Result of `create`. */
|
|
355
|
-
interface CreateResult<T> {
|
|
356
|
-
data: T;
|
|
357
|
-
/** The audit entry committed with the new row, from a provider that audits. */
|
|
358
|
-
audit?: AuditEntry;
|
|
359
|
-
}
|
|
360
|
-
/** Parameters for `update`. */
|
|
361
|
-
interface UpdateParams<T> {
|
|
362
|
-
/**
|
|
363
|
-
* The record's identifier. For a composite key, the canonical string from
|
|
364
|
-
* `encodeRecordKey` (`@marktiderman/genesis-core/schema`).
|
|
365
|
-
*/
|
|
366
|
-
id: Identifier;
|
|
367
|
-
data: Partial<T>;
|
|
368
|
-
/**
|
|
369
|
-
* The version token the caller read the row at. A provider that reports
|
|
370
|
-
* `versionedWrites` in its capabilities rejects the update with a
|
|
371
|
-
* `DataWriteError` (`conflict`) when the row has moved on since, instead of
|
|
372
|
-
* overwriting someone else's edit. Providers without versioned writes
|
|
373
|
-
* ignore it.
|
|
374
|
-
*/
|
|
375
|
-
expectedVersion?: string | null;
|
|
376
|
-
}
|
|
377
|
-
/** Result of `update`. */
|
|
378
|
-
interface UpdateResult<T> {
|
|
379
|
-
data: T;
|
|
380
|
-
/** The row's new version token, from a provider with versioned writes. */
|
|
381
|
-
version?: string | null;
|
|
382
|
-
/** The audit entry committed with the edit, from a provider that audits. */
|
|
383
|
-
audit?: AuditEntry;
|
|
384
|
-
}
|
|
385
|
-
/** Parameters for `deleteOne`. */
|
|
386
|
-
interface DeleteParams {
|
|
387
|
-
id: Identifier;
|
|
388
|
-
/** The version token the row was read at; see `UpdateParams.expectedVersion`. */
|
|
389
|
-
expectedVersion?: string | null;
|
|
390
|
-
}
|
|
391
|
-
/** Result of `deleteOne`. */
|
|
392
|
-
interface DeleteResult {
|
|
393
|
-
data: {
|
|
394
|
-
id: Identifier;
|
|
395
|
-
};
|
|
396
|
-
/** The audit entry committed with the deletion, from a provider that audits. */
|
|
397
|
-
audit?: AuditEntry;
|
|
398
|
-
}
|
|
399
|
-
/** One row's change in a bulk update. */
|
|
400
|
-
interface BulkChange {
|
|
401
|
-
id: Identifier;
|
|
402
|
-
data: Record<string, unknown>;
|
|
403
|
-
/** The version token the row was read at. */
|
|
404
|
-
expectedVersion?: string | null;
|
|
405
|
-
}
|
|
406
|
-
/** Why one row of a bulk write or import didn't go through. */
|
|
407
|
-
interface RowFailure {
|
|
408
|
-
/** The row's position in the request, from 0. */
|
|
409
|
-
index: number;
|
|
410
|
-
id?: Identifier;
|
|
411
|
-
outcome: string;
|
|
412
|
-
message: string;
|
|
413
|
-
}
|
|
414
|
-
/** Result of a bulk write: every row changed, or none did. */
|
|
415
|
-
interface BulkResult {
|
|
416
|
-
/** Rows changed; always the number requested when `ok`. */
|
|
417
|
-
count: number;
|
|
418
|
-
ok: boolean;
|
|
419
|
-
/** Present when `ok` is false: every row that failed, so the caller can fix them all at once. */
|
|
420
|
-
failures?: RowFailure[];
|
|
421
|
-
/** One audit entry per row changed. */
|
|
422
|
-
audit?: AuditEntry[];
|
|
423
|
-
}
|
|
424
|
-
/** Parameters for `importRows`. */
|
|
425
|
-
interface ImportParams {
|
|
426
|
-
rows: Record<string, unknown>[];
|
|
427
|
-
/** Check every row against the database and change nothing. */
|
|
428
|
-
dryRun: boolean;
|
|
429
|
-
}
|
|
430
|
-
/** Parameters for `history`. */
|
|
431
|
-
interface HistoryParams {
|
|
432
|
-
id: Identifier;
|
|
433
|
-
/** Most recent first; default and maximum are the provider's. */
|
|
434
|
-
limit?: number;
|
|
435
|
-
}
|
|
436
|
-
interface DataProvider {
|
|
437
|
-
getList<T extends BaseRecord = BaseRecord>(resource: string, params: ListParams): Promise<ListResult<T>>;
|
|
438
|
-
getOne<T extends BaseRecord = BaseRecord>(resource: string, params: GetOneParams): Promise<GetOneResult<T>>;
|
|
439
|
-
getMany<T extends BaseRecord = BaseRecord>(resource: string, params: GetManyParams): Promise<GetManyResult<T>>;
|
|
440
|
-
create<T extends BaseRecord = BaseRecord>(resource: string, params: CreateParams<T>): Promise<CreateResult<T>>;
|
|
441
|
-
update<T extends BaseRecord = BaseRecord>(resource: string, params: UpdateParams<T>): Promise<UpdateResult<T>>;
|
|
442
|
-
deleteOne(resource: string, params: DeleteParams): Promise<DeleteResult>;
|
|
443
|
-
/**
|
|
444
|
-
* The resource's real shape for the CURRENT user: fields, key, version
|
|
445
|
-
* field, and per-field read and update rights. Fields the user may not read
|
|
446
|
-
* are omitted entirely.
|
|
447
|
-
*/
|
|
448
|
-
describe?(resource: string): Promise<ResourceSchema>;
|
|
449
|
-
/** Every resource this user may see, for a sidebar or a picker. */
|
|
450
|
-
listResources?(): Promise<ResourceSummary[]>;
|
|
451
|
-
/**
|
|
452
|
-
* What this provider can do, optionally for one resource. A UI hides the
|
|
453
|
-
* controls a provider cannot serve instead of letting them error.
|
|
454
|
-
*/
|
|
455
|
-
capabilities?(resource?: string): Promise<ProviderCapabilities>;
|
|
456
|
-
/**
|
|
457
|
-
* A record's audit history, most recent first, limited to the fields this
|
|
458
|
-
* user may read.
|
|
459
|
-
*/
|
|
460
|
-
history?(resource: string, params: HistoryParams): Promise<AuditEntry[]>;
|
|
461
|
-
/**
|
|
462
|
-
* Update many rows in ONE transaction. All of them change or none do: a
|
|
463
|
-
* single conflict, refusal or invalid value rolls back the rest, and the
|
|
464
|
-
* result lists every row that failed.
|
|
465
|
-
*/
|
|
466
|
-
updateMany?(resource: string, params: {
|
|
467
|
-
changes: BulkChange[];
|
|
468
|
-
}): Promise<BulkResult>;
|
|
469
|
-
/** Delete many rows in one transaction, all or none, each at its version. */
|
|
470
|
-
deleteMany?(resource: string, params: {
|
|
471
|
-
items: {
|
|
472
|
-
id: Identifier;
|
|
473
|
-
expectedVersion?: string | null;
|
|
474
|
-
}[];
|
|
475
|
-
}): Promise<BulkResult>;
|
|
476
|
-
/**
|
|
477
|
-
* Add rows from an import. With `dryRun`, every row is tried against the
|
|
478
|
-
* database and nothing is kept, so the result lists every row that would
|
|
479
|
-
* fail; without it, all rows are added in one transaction or none are.
|
|
480
|
-
*/
|
|
481
|
-
importRows?(resource: string, params: ImportParams): Promise<BulkResult>;
|
|
482
|
-
/**
|
|
483
|
-
* Undo one audited change with a compensating write: put the changed
|
|
484
|
-
* fields back, remove an added row, or restore a deleted one. Refused when
|
|
485
|
-
* the row has changed since.
|
|
486
|
-
*/
|
|
487
|
-
undo?(resource: string, params: {
|
|
488
|
-
auditId: string;
|
|
489
|
-
}): Promise<{
|
|
490
|
-
audit?: AuditEntry;
|
|
491
|
-
}>;
|
|
492
|
-
/**
|
|
493
|
-
* Run one of the resource's row actions (`ResourceSchema.actions`) on a
|
|
494
|
-
* row, at the version it was read. The app's function does the work; the
|
|
495
|
-
* row's change is audited.
|
|
496
|
-
*/
|
|
497
|
-
runAction?(resource: string, params: {
|
|
498
|
-
id: Identifier;
|
|
499
|
-
action: string;
|
|
500
|
-
expectedVersion?: string | null;
|
|
501
|
-
}): Promise<{
|
|
502
|
-
data: Record<string, unknown> | null;
|
|
503
|
-
audit?: AuditEntry;
|
|
504
|
-
}>;
|
|
505
|
-
/** A record's comments, oldest first. */
|
|
506
|
-
comments?(resource: string, params: {
|
|
507
|
-
id: Identifier;
|
|
508
|
-
}): Promise<RecordComment[]>;
|
|
509
|
-
/** Add a comment to a record, as the signed-in user. */
|
|
510
|
-
addComment?(resource: string, params: {
|
|
511
|
-
id: Identifier;
|
|
512
|
-
body: string;
|
|
513
|
-
}): Promise<RecordComment>;
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
export type { ArrayFilter as A, BaseRecord as B, CreateParams as C, DataProvider as D, StorageType as E, FieldPermissions as F, GetManyParams as G, HistoryParams as H, Identifier as I, UpdateResult as J, ListParams as L, NullFilter as N, PaginationParam as P, RangeFilter as R, ScalarFilter as S, UpdateParams as U, VersionField as V, AuditEntry as a, BulkChange as b, BulkResult as c, CreateResult as d, DataRow as e, DeleteParams as f, DeleteResult as g, FieldReference as h, FieldSchema as i, FilterOperator as j, FilterParam as k, GetManyResult as l, GetOneParams as m, GetOneResult as n, ImportParams as o, ListResult as p, ProviderCapabilities as q, RecordComment as r, ResourceAccess as s, ResourceContract as t, ResourceRelation as u, ResourceSchema as v, ResourceSummary as w, RowAction as x, RowFailure as y, SortParam as z };
|
|
274
|
+
export type { AuditEntry as A, DataRow as D, FieldPermissions as F, ProviderCapabilities as P, RecordComment as R, StorageType as S, VersionField as V, FieldReference as a, FieldSchema as b, FilterOperator as c, ResourceAccess as d, ResourceContract as e, ResourceRelation as f, ResourceSchema as g, ResourceSummary as h, RowAction as i };
|