@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.
Files changed (65) hide show
  1. package/README.md +223 -0
  2. package/dist/chunk-5NHR6ZXS.js +31 -0
  3. package/dist/chunk-5NHR6ZXS.js.map +1 -0
  4. package/dist/chunk-7NUL32W7.js +14 -0
  5. package/dist/chunk-7NUL32W7.js.map +1 -0
  6. package/dist/chunk-BQOJX67X.js +31 -0
  7. package/dist/chunk-BQOJX67X.js.map +1 -0
  8. package/dist/{chunk-QFTMHOEH.js → chunk-DVW5GXWZ.js} +84 -31
  9. package/dist/chunk-DVW5GXWZ.js.map +1 -0
  10. package/dist/{chunk-6V2TIN3G.js → chunk-GGSXSI6Z.js} +6 -5
  11. package/dist/chunk-GGSXSI6Z.js.map +1 -0
  12. package/dist/chunk-HT43MPKW.js +3 -0
  13. package/dist/{chunk-EL7YGOTY.js.map → chunk-HT43MPKW.js.map} +1 -1
  14. package/dist/chunk-HUNGCTRA.js +33 -0
  15. package/dist/chunk-HUNGCTRA.js.map +1 -0
  16. package/dist/{chunk-5FTUB2M6.js → chunk-IZT2O57C.js} +160 -40
  17. package/dist/chunk-IZT2O57C.js.map +1 -0
  18. package/dist/chunk-QH4P5MI2.js +278 -0
  19. package/dist/chunk-QH4P5MI2.js.map +1 -0
  20. package/dist/chunk-RLY7TZUY.js +444 -0
  21. package/dist/chunk-RLY7TZUY.js.map +1 -0
  22. package/dist/chunk-XQO4OH2N.js +32 -0
  23. package/dist/chunk-XQO4OH2N.js.map +1 -0
  24. package/dist/{chunk-3P4PHPMJ.js → chunk-ZK5C3OOT.js} +3 -3
  25. package/dist/{chunk-3P4PHPMJ.js.map → chunk-ZK5C3OOT.js.map} +1 -1
  26. package/dist/form-types.d.ts +6 -0
  27. package/dist/hooks/index.d.ts +5 -1
  28. package/dist/hooks/index.js +11 -6
  29. package/dist/hooks/mutation-mode.d.ts +109 -0
  30. package/dist/hooks/mutation-mode.js +3 -0
  31. package/dist/hooks/mutation-mode.js.map +1 -0
  32. package/dist/hooks/related-keys.d.ts +12 -0
  33. package/dist/hooks/related-keys.js +3 -0
  34. package/dist/hooks/related-keys.js.map +1 -0
  35. package/dist/hooks/use-capabilities.d.ts +21 -0
  36. package/dist/hooks/use-capabilities.js +5 -0
  37. package/dist/hooks/use-capabilities.js.map +1 -0
  38. package/dist/hooks/use-one.d.ts +2 -1
  39. package/dist/hooks/use-one.js +2 -2
  40. package/dist/hooks/use-resource-form.d.ts +53 -2
  41. package/dist/hooks/use-resource-form.js +7 -3
  42. package/dist/hooks/use-resource.d.ts +42 -5
  43. package/dist/hooks/use-resource.js +4 -2
  44. package/dist/hooks/use-unsaved-changes-guard.d.ts +33 -0
  45. package/dist/hooks/use-unsaved-changes-guard.js +3 -0
  46. package/dist/hooks/use-unsaved-changes-guard.js.map +1 -0
  47. package/dist/index.d.ts +7 -3
  48. package/dist/index.js +14 -9
  49. package/dist/provider/index.d.ts +212 -6
  50. package/dist/provider/index.js +5 -3
  51. package/dist/read-outcome-BQkYcV4t.d.ts +88 -0
  52. package/dist/schema/index.d.ts +4 -3
  53. package/dist/schema/index.js +2 -1
  54. package/dist/{types-DUbRb8qV.d.ts → types-C5bjsbxz.d.ts} +13 -255
  55. package/dist/types-Dsqqr9s7.d.ts +281 -0
  56. package/package.json +2 -2
  57. package/dist/chunk-4W4W7F4I.js +0 -20
  58. package/dist/chunk-4W4W7F4I.js.map +0 -1
  59. package/dist/chunk-5FTUB2M6.js.map +0 -1
  60. package/dist/chunk-6AA4ISUA.js +0 -125
  61. package/dist/chunk-6AA4ISUA.js.map +0 -1
  62. package/dist/chunk-6V2TIN3G.js.map +0 -1
  63. package/dist/chunk-EL7YGOTY.js +0 -3
  64. package/dist/chunk-QFTMHOEH.js.map +0 -1
  65. package/dist/write-outcome-aYToY3dK.d.ts +0 -38
@@ -0,0 +1,281 @@
1
+ import { A as AuditEntry, g as ResourceSchema, h as ResourceSummary, P as ProviderCapabilities, R as RecordComment } from './types-C5bjsbxz.js';
2
+
3
+ /** Record identifier — supports both string UUIDs and numeric auto-increment IDs. */
4
+ type Identifier = string | number;
5
+ /** Base record type — all resources must have an id. */
6
+ type BaseRecord = {
7
+ id: Identifier;
8
+ [key: string]: unknown;
9
+ };
10
+ /** Pagination parameters (offset-based, with optional cursor for future use). */
11
+ interface PaginationParam {
12
+ page: number;
13
+ perPage: number;
14
+ cursor?: string;
15
+ }
16
+ /** Sort direction for a single field. */
17
+ interface SortParam {
18
+ field: string;
19
+ order: "asc" | "desc";
20
+ }
21
+ /**
22
+ * Filter expressions — discriminated union by operator.
23
+ * Filters are AND-ed together in queries.
24
+ */
25
+ /** Operators that compare a field against a single scalar value. */
26
+ interface ScalarFilter {
27
+ field: string;
28
+ operator: "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "contains";
29
+ value: string | number | boolean;
30
+ }
31
+ /** Operator that checks field membership in a list. */
32
+ interface ArrayFilter {
33
+ field: string;
34
+ operator: "in";
35
+ value: (string | number | boolean)[];
36
+ }
37
+ /** Operator that checks field is within a range. */
38
+ interface RangeFilter {
39
+ field: string;
40
+ operator: "between";
41
+ value: [string | number, string | number];
42
+ }
43
+ /** Operators that check for null/non-null — no value needed. */
44
+ interface NullFilter {
45
+ field: string;
46
+ operator: "is_null" | "is_not_null";
47
+ }
48
+ type FilterParam = ScalarFilter | ArrayFilter | RangeFilter | NullFilter;
49
+ /** Parameters for `getList`. */
50
+ interface ListParams {
51
+ pagination?: PaginationParam;
52
+ sort?: SortParam;
53
+ /**
54
+ * Sort by several fields, first one first. Wins over `sort` on a provider
55
+ * whose capabilities report `multiSort`; others use only `sort`.
56
+ */
57
+ sorts?: SortParam[];
58
+ filters?: FilterParam[];
59
+ /** Supabase select syntax, e.g. "*, author:profiles(name)". Mock provider ignores this. */
60
+ select?: string;
61
+ /** Multi-field text search (case-insensitive). */
62
+ search?: {
63
+ query: string;
64
+ fields: string[];
65
+ };
66
+ }
67
+ /** Result of `getList`. */
68
+ interface ListResult<T> {
69
+ data: T[];
70
+ total: number;
71
+ }
72
+ /** Parameters for `getOne`. */
73
+ interface GetOneParams {
74
+ id: Identifier;
75
+ /** Supabase select syntax, e.g. "*, author:profiles(name)". Defaults to "*". */
76
+ select?: string;
77
+ }
78
+ /** Result of `getOne`. */
79
+ interface GetOneResult<T> {
80
+ data: T;
81
+ }
82
+ /** Parameters for `getMany`. */
83
+ interface GetManyParams {
84
+ ids: Identifier[];
85
+ }
86
+ /** Result of `getMany`. */
87
+ interface GetManyResult<T> {
88
+ data: T[];
89
+ }
90
+ /** Parameters for `create`. */
91
+ interface CreateParams<T> {
92
+ data: Partial<T>;
93
+ }
94
+ /** Result of `create`. */
95
+ interface CreateResult<T> {
96
+ data: T;
97
+ /** The audit entry committed with the new row, from a provider that audits. */
98
+ audit?: AuditEntry;
99
+ }
100
+ /** Parameters for `update`. */
101
+ interface UpdateParams<T> {
102
+ /**
103
+ * The record's identifier. For a composite key, the canonical string from
104
+ * `encodeRecordKey` (`@marktiderman/genesis-core/schema`).
105
+ */
106
+ id: Identifier;
107
+ data: Partial<T>;
108
+ /**
109
+ * The version token the caller read the row at. A provider that reports
110
+ * `versionedWrites` in its capabilities rejects the update with a
111
+ * `DataWriteError` (`conflict`) when the row has moved on since, instead of
112
+ * overwriting someone else's edit. Providers without versioned writes
113
+ * ignore it.
114
+ */
115
+ expectedVersion?: string | null;
116
+ }
117
+ /** Result of `update`. */
118
+ interface UpdateResult<T> {
119
+ data: T;
120
+ /** The row's new version token, from a provider with versioned writes. */
121
+ version?: string | null;
122
+ /** The audit entry committed with the edit, from a provider that audits. */
123
+ audit?: AuditEntry;
124
+ }
125
+ /** Parameters for `deleteOne`. */
126
+ interface DeleteParams {
127
+ id: Identifier;
128
+ /** The version token the row was read at; see `UpdateParams.expectedVersion`. */
129
+ expectedVersion?: string | null;
130
+ }
131
+ /** Result of `deleteOne`. */
132
+ interface DeleteResult {
133
+ data: {
134
+ id: Identifier;
135
+ };
136
+ /** The audit entry committed with the deletion, from a provider that audits. */
137
+ audit?: AuditEntry;
138
+ }
139
+ /** One row's change in a bulk update. */
140
+ interface BulkChange {
141
+ id: Identifier;
142
+ data: Record<string, unknown>;
143
+ /** The version token the row was read at. */
144
+ expectedVersion?: string | null;
145
+ }
146
+ /** Why one row of a bulk write or import didn't go through. */
147
+ interface RowFailure {
148
+ /** The row's position in the request, from 0. */
149
+ index: number;
150
+ id?: Identifier;
151
+ outcome: string;
152
+ message: string;
153
+ }
154
+ /** Result of a bulk write: every row changed, or none did. */
155
+ interface BulkResult {
156
+ /** Rows changed; always the number requested when `ok`. */
157
+ count: number;
158
+ ok: boolean;
159
+ /** Present when `ok` is false: every row that failed, so the caller can fix them all at once. */
160
+ failures?: RowFailure[];
161
+ /** One audit entry per row changed. */
162
+ audit?: AuditEntry[];
163
+ }
164
+ /** Parameters for `importRows`. */
165
+ interface ImportParams {
166
+ rows: Record<string, unknown>[];
167
+ /** Check every row against the database and change nothing. */
168
+ dryRun: boolean;
169
+ }
170
+ /** Parameters for `history`. */
171
+ interface HistoryParams {
172
+ id: Identifier;
173
+ /** Most recent first; default and maximum are the provider's. */
174
+ limit?: number;
175
+ }
176
+ /** A write that committed, as passed to a provider's `onWrite` and `invalidates`. */
177
+ interface WriteEvent {
178
+ resource: string;
179
+ kind: "create" | "update" | "delete";
180
+ id: Identifier;
181
+ /** The row as written, when the provider has it (not for a delete). */
182
+ record?: BaseRecord;
183
+ }
184
+ /**
185
+ * A cache key to invalidate after a write. Shaped like a TanStack Query
186
+ * `QueryKey` (`readonly unknown[]`), matched by prefix.
187
+ */
188
+ type InvalidationKey = readonly unknown[];
189
+ interface DataProvider {
190
+ getList<T extends BaseRecord = BaseRecord>(resource: string, params: ListParams): Promise<ListResult<T>>;
191
+ getOne<T extends BaseRecord = BaseRecord>(resource: string, params: GetOneParams): Promise<GetOneResult<T>>;
192
+ getMany<T extends BaseRecord = BaseRecord>(resource: string, params: GetManyParams): Promise<GetManyResult<T>>;
193
+ create<T extends BaseRecord = BaseRecord>(resource: string, params: CreateParams<T>): Promise<CreateResult<T>>;
194
+ update<T extends BaseRecord = BaseRecord>(resource: string, params: UpdateParams<T>): Promise<UpdateResult<T>>;
195
+ deleteOne(resource: string, params: DeleteParams): Promise<DeleteResult>;
196
+ /**
197
+ * The resource's real shape for the CURRENT user: fields, key, version
198
+ * field, and per-field read and update rights. Fields the user may not read
199
+ * are omitted entirely.
200
+ */
201
+ describe?(resource: string): Promise<ResourceSchema>;
202
+ /** Every resource this user may see, for a sidebar or a picker. */
203
+ listResources?(): Promise<ResourceSummary[]>;
204
+ /**
205
+ * What this provider can do, optionally for one resource. A UI hides the
206
+ * controls a provider cannot serve instead of letting them error.
207
+ */
208
+ capabilities?(resource?: string): Promise<ProviderCapabilities>;
209
+ /**
210
+ * A record's audit history, most recent first, limited to the fields this
211
+ * user may read.
212
+ */
213
+ history?(resource: string, params: HistoryParams): Promise<AuditEntry[]>;
214
+ /**
215
+ * Update many rows in ONE transaction. All of them change or none do: a
216
+ * single conflict, refusal or invalid value rolls back the rest, and the
217
+ * result lists every row that failed.
218
+ */
219
+ updateMany?(resource: string, params: {
220
+ changes: BulkChange[];
221
+ }): Promise<BulkResult>;
222
+ /** Delete many rows in one transaction, all or none, each at its version. */
223
+ deleteMany?(resource: string, params: {
224
+ items: {
225
+ id: Identifier;
226
+ expectedVersion?: string | null;
227
+ }[];
228
+ }): Promise<BulkResult>;
229
+ /**
230
+ * Add rows from an import. With `dryRun`, every row is tried against the
231
+ * database and nothing is kept, so the result lists every row that would
232
+ * fail; without it, all rows are added in one transaction or none are.
233
+ */
234
+ importRows?(resource: string, params: ImportParams): Promise<BulkResult>;
235
+ /**
236
+ * Undo one audited change with a compensating write: put the changed
237
+ * fields back, remove an added row, or restore a deleted one. Refused when
238
+ * the row has changed since.
239
+ */
240
+ undo?(resource: string, params: {
241
+ auditId: string;
242
+ }): Promise<{
243
+ audit?: AuditEntry;
244
+ }>;
245
+ /**
246
+ * Run one of the resource's row actions (`ResourceSchema.actions`) on a
247
+ * row, at the version it was read. The app's function does the work; the
248
+ * row's change is audited.
249
+ */
250
+ runAction?(resource: string, params: {
251
+ id: Identifier;
252
+ action: string;
253
+ expectedVersion?: string | null;
254
+ }): Promise<{
255
+ data: Record<string, unknown> | null;
256
+ audit?: AuditEntry;
257
+ }>;
258
+ /** A record's comments, oldest first. */
259
+ comments?(resource: string, params: {
260
+ id: Identifier;
261
+ }): Promise<RecordComment[]>;
262
+ /** Add a comment to a record, as the signed-in user. */
263
+ addComment?(resource: string, params: {
264
+ id: Identifier;
265
+ body: string;
266
+ }): Promise<RecordComment>;
267
+ /**
268
+ * Maps a stored row to the values an edit form starts from, for a resource
269
+ * whose form shape differs from its table (the reverse of a write's
270
+ * `prepare`). `useResourceForm` applies it in edit mode.
271
+ */
272
+ toFormValues?(resource: string, record: Record<string, unknown>): Record<string, unknown>;
273
+ /**
274
+ * Other cache keys a committed write makes stale, beyond the resource's
275
+ * own `[resource]` keys. `useResource` and `useResourceForm` invalidate
276
+ * them after their writes.
277
+ */
278
+ invalidates?(event: WriteEvent): InvalidationKey[];
279
+ }
280
+
281
+ export type { ArrayFilter as A, BaseRecord as B, CreateParams as C, DataProvider as D, FilterParam as F, GetManyParams as G, HistoryParams as H, Identifier as I, ListParams as L, NullFilter as N, PaginationParam as P, RangeFilter as R, ScalarFilter as S, UpdateParams as U, WriteEvent as W, BulkChange as a, BulkResult as b, CreateResult as c, DeleteParams as d, DeleteResult as e, GetManyResult as f, GetOneParams as g, GetOneResult as h, ImportParams as i, InvalidationKey as j, ListResult as k, RowFailure as l, SortParam as m, UpdateResult as n };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marktiderman/genesis-core",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -62,7 +62,7 @@
62
62
  "react-hook-form": "^7.56.4",
63
63
  "tsup": "^8.3.0",
64
64
  "typescript": "^5.9.3",
65
- "vitest": "^2.1.0",
65
+ "vitest": "^4.1.12",
66
66
  "zod": "^3.25.67"
67
67
  },
68
68
  "scripts": {
@@ -1,20 +0,0 @@
1
- // src/schema/write-outcome.ts
2
- var DataWriteError = class extends Error {
3
- outcome;
4
- current;
5
- currentVersion;
6
- constructor(outcome, message, options = {}) {
7
- super(message);
8
- this.name = "DataWriteError";
9
- this.outcome = outcome;
10
- this.current = options.current;
11
- this.currentVersion = options.currentVersion;
12
- }
13
- };
14
- function isDataWriteError(error) {
15
- return error instanceof Error && error.name === "DataWriteError" && typeof error.outcome === "string";
16
- }
17
-
18
- export { DataWriteError, isDataWriteError };
19
- //# sourceMappingURL=chunk-4W4W7F4I.js.map
20
- //# sourceMappingURL=chunk-4W4W7F4I.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/schema/write-outcome.ts"],"names":[],"mappings":";AAyCO,IAAM,cAAA,GAAN,cAA6B,KAAA,CAAM;AAAA,EAC/B,OAAA;AAAA,EACA,OAAA;AAAA,EACA,cAAA;AAAA,EAET,WAAA,CAAY,OAAA,EAAuB,OAAA,EAAiB,OAAA,GAAiC,EAAC,EAAG;AACvF,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,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;AAAA,EAChC;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-4W4W7F4I.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 * - `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 | \"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\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\n constructor(outcome: WriteOutcome, message: string, options: DataWriteErrorOptions = {}) {\n super(message);\n this.name = \"DataWriteError\";\n this.outcome = outcome;\n this.current = options.current;\n this.currentVersion = options.currentVersion;\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 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-resource-form.ts"],"names":[],"mappings":"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","file":"chunk-5FTUB2M6.js","sourcesContent":["import { useMemo, useCallback, useEffect } from \"react\";\nimport { useForm, type UseFormReturn, type Resolver } from \"react-hook-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { z } from \"zod\";\nimport { useOne } from \"./use-one\";\nimport { useDataProvider, useStorage } from \"../provider/context\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { titleCase } from \"../utils\";\nimport type { ResourceFormFieldDef, FieldType } from \"../form-types\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface UseResourceFormOptions {\n resource: string;\n action: \"create\" | \"edit\";\n id?: string;\n initialData?: Record<string, unknown>;\n fields?: ResourceFormFieldDef[];\n /** Full dataset used to auto-detect select fields from unique values. */\n listData?: Record<string, unknown>[];\n onSuccess?: (data: Record<string, unknown>) => void;\n onError?: (error: Error) => void;\n /** Pre-fill create form defaults from URL query params. Default true. */\n prefillFromUrl?: boolean;\n /** Validation trigger mode. Default \"onBlur\". */\n validationMode?: \"onBlur\" | \"onChange\" | \"onSubmit\";\n /** Enable autosave drafts to localStorage for create mode. */\n autosave?: boolean | { debounceMs?: number };\n}\n\nexport interface UseResourceFormReturn {\n form: UseFormReturn<Record<string, unknown>>;\n resolvedFields: ResourceFormFieldDef[];\n onSubmit: (e?: { preventDefault?: () => void }) => Promise<void>;\n /** Raw save handler — use with form.handleSubmit(handleSave, onInvalid). */\n handleSave: (data: Record<string, unknown>) => Promise<void>;\n isSaving: boolean;\n isLoading: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Infer a field type from a value */\nfunction inferFieldType(value: unknown): FieldType {\n if (typeof value === \"boolean\") return \"switch\";\n if (typeof value === \"number\") return \"number\";\n if (typeof value === \"string\") {\n if (/^\\d{4}-\\d{2}-\\d{2}/.test(value)) return \"date\";\n if (value.includes(\"@\") && value.includes(\".\")) return \"email\";\n if (/^https?:\\/\\//.test(value)) return \"url\";\n if (value.length > 100) return \"textarea\";\n }\n return \"text\";\n}\n\n/** Try to infer a select field from unique values across the full dataset */\nfunction inferSelectFromData(\n key: string,\n allData: Record<string, unknown>[] | undefined,\n): { type: \"select\"; options: { value: string; label: string }[] } | null {\n if (!allData || allData.length === 0) return null;\n const uniqueValues = new Set<string>();\n for (const row of allData) {\n const v = row[key];\n if (typeof v === \"string\" && v.length > 0) uniqueValues.add(v);\n }\n if (uniqueValues.size > 0 && uniqueValues.size <= 10) {\n return {\n type: \"select\" as const,\n options: Array.from(uniqueValues)\n .sort()\n .map((v) => ({ value: v, label: v })),\n };\n }\n return null;\n}\n\n/** Generate a sensible placeholder for an auto-inferred field */\nfunction defaultPlaceholder(key: string, type: FieldType): string {\n switch (type) {\n case \"email\": return \"name@example.com\";\n case \"url\": return \"https://...\";\n case \"number\": return \"0\";\n case \"date\": return \"YYYY-MM-DD\";\n case \"textarea\": return `Enter ${titleCase(key).toLowerCase()}...`;\n default: return `Enter ${titleCase(key).toLowerCase()}`;\n }\n}\n\n/** Resolve fields from data keys when none provided */\nfunction resolveFields(\n fields: ResourceFormFieldDef[] | undefined,\n data: Record<string, unknown> | undefined,\n allData?: Record<string, unknown>[],\n): ResourceFormFieldDef[] {\n if (fields && fields.length > 0) {\n return fields.map((f) => {\n const inferred = f.type ?? (data ? inferFieldType(data[f.key]) : \"text\");\n return {\n ...f,\n label: f.label ?? titleCase(f.key),\n type: inferred,\n placeholder: f.placeholder ?? defaultPlaceholder(f.key, inferred),\n required: f.required ?? (inferred !== \"switch\" && inferred !== \"checkbox\" && inferred !== \"hidden\"),\n };\n });\n }\n\n if (!data) return [];\n\n return Object.keys(data)\n .filter((key) => key !== \"id\" && key !== \"created_at\" && key !== \"updated_at\")\n .map((key) => {\n // Check for select-like field first (string with few unique values)\n if (typeof data[key] === \"string\") {\n const selectInfo = inferSelectFromData(key, allData);\n if (selectInfo) {\n return {\n key,\n label: titleCase(key),\n type: selectInfo.type,\n options: selectInfo.options,\n placeholder: `Select ${titleCase(key).toLowerCase()}...`,\n required: true,\n };\n }\n }\n\n const inferred = inferFieldType(data[key]);\n return {\n key,\n label: titleCase(key),\n type: inferred,\n placeholder: defaultPlaceholder(key, inferred),\n required: inferred !== \"switch\" && inferred !== \"checkbox\" && inferred !== \"hidden\",\n };\n });\n}\n\n/** Build a zod schema from resolved fields */\nfunction buildSchema(fields: ResourceFormFieldDef[]) {\n const shape: Record<string, z.ZodTypeAny> = {};\n\n for (const field of fields) {\n const fieldType = field.type ?? \"text\";\n\n if (field.hidden || fieldType === \"hidden\") continue;\n\n // File/image fields store File objects — z.any() is intentional here because\n // the File API shape varies by platform (browser File, React Native asset, etc.)\n // and form libraries may wrap files differently.\n if (fieldType === \"file\" || fieldType === \"image\") {\n shape[field.key] = field.required !== false ? z.unknown().refine((v) => v != null, { message: `${field.label ?? titleCase(field.key)} is required` }) : z.unknown().optional();\n continue;\n }\n\n switch (fieldType) {\n case \"number\": {\n let schema = z.coerce.number({ message: \"Must be a number\" });\n if (field.min != null) schema = schema.min(field.min);\n if (field.max != null) schema = schema.max(field.max);\n shape[field.key] = field.required !== false\n ? schema\n : schema.optional();\n break;\n }\n case \"array\": {\n if (field.arrayFields) {\n const itemShape: Record<string, z.ZodTypeAny> = {};\n for (const sub of field.arrayFields) {\n const subType = sub.type ?? \"text\";\n if (subType === \"number\") {\n let s = z.coerce.number({ message: \"Must be a number\" });\n if (sub.min != null) s = s.min(sub.min);\n if (sub.max != null) s = s.max(sub.max);\n itemShape[sub.key] = sub.required !== false ? s : s.optional();\n } else if (subType === \"switch\" || subType === \"checkbox\") {\n itemShape[sub.key] = z.boolean().optional();\n } else {\n let s = z.string();\n if (sub.required !== false) {\n s = s.min(1, `${sub.label ?? titleCase(sub.key)} is required`);\n }\n if (subType === \"email\") s = s.email(\"Invalid email address\");\n if (subType === \"url\") s = s.url(\"Invalid URL\");\n itemShape[sub.key] = sub.required === false ? s.optional().or(z.literal(\"\")) : s;\n }\n }\n let arraySchema: z.ZodTypeAny = z.array(z.object(itemShape));\n if (field.arrayMin != null) arraySchema = (arraySchema as z.ZodArray<z.ZodTypeAny>).min(field.arrayMin, `Minimum ${field.arrayMin} items required`);\n if (field.arrayMax != null) arraySchema = (arraySchema as z.ZodArray<z.ZodTypeAny>).max(field.arrayMax, `Maximum ${field.arrayMax} items allowed`);\n shape[field.key] = arraySchema;\n }\n break;\n }\n case \"switch\":\n case \"checkbox\": {\n shape[field.key] = z.boolean().optional();\n break;\n }\n default: {\n // text, email, url, textarea, date, select\n let schema = z.string();\n if (field.required !== false) {\n schema = schema.min(1, `${field.label ?? titleCase(field.key)} is required`);\n }\n if (fieldType === \"email\") {\n schema = schema.email(\"Invalid email address\");\n }\n if (fieldType === \"url\") {\n schema = schema.url(\"Invalid URL\");\n }\n shape[field.key] = field.required === false\n ? schema.optional().or(z.literal(\"\"))\n : schema;\n break;\n }\n }\n }\n\n return z.object(shape).passthrough();\n}\n\n/** Build default values for a single array item from its sub-field definitions */\nexport function buildArrayItemDefaults(\n arrayFields: ResourceFormFieldDef[],\n): Record<string, unknown> {\n const defaults: Record<string, unknown> = {};\n for (const sub of arrayFields) {\n const subType = sub.type ?? \"text\";\n if (subType === \"number\") defaults[sub.key] = undefined;\n else if (subType === \"switch\" || subType === \"checkbox\") defaults[sub.key] = false;\n else defaults[sub.key] = sub.defaultValue ?? \"\";\n }\n return defaults;\n}\n\n/** Build default values from fields and existing data */\nfunction buildDefaults(\n fields: ResourceFormFieldDef[],\n data: Record<string, unknown> | undefined,\n action: \"create\" | \"edit\",\n prefillFromUrl: boolean,\n): Record<string, unknown> {\n const defaults: Record<string, unknown> = {};\n for (const field of fields) {\n if (data && field.key in data) {\n defaults[field.key] = data[field.key];\n } else if (field.defaultValue !== undefined) {\n defaults[field.key] = field.defaultValue;\n } else {\n const fieldType = field.type ?? \"text\";\n switch (fieldType) {\n case \"array\":\n defaults[field.key] = field.arrayMin\n ? Array.from({ length: field.arrayMin }, () =>\n buildArrayItemDefaults(field.arrayFields ?? []),\n )\n : [];\n break;\n case \"number\":\n defaults[field.key] = undefined;\n break;\n case \"switch\":\n case \"checkbox\":\n defaults[field.key] = false;\n break;\n default:\n defaults[field.key] = \"\";\n break;\n }\n }\n }\n\n // URL pre-fill for create mode\n if (typeof window !== \"undefined\" && typeof window.location !== \"undefined\" && typeof window.location.search === \"string\" && action === \"create\" && prefillFromUrl !== false) {\n const params = new URLSearchParams(window.location.search);\n for (const field of fields) {\n const val = params.get(field.key);\n if (val != null) {\n defaults[field.key] = field.type === \"number\" ? Number(val) : val;\n }\n }\n }\n\n return defaults;\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useResourceForm(options: UseResourceFormOptions): UseResourceFormReturn {\n const {\n resource,\n action,\n id,\n initialData,\n fields: fieldsProp,\n listData,\n onSuccess,\n onError,\n prefillFromUrl = true,\n validationMode,\n autosave,\n } = options;\n\n const autosaveEnabled = !!autosave;\n const debounceMs = typeof autosave === \"object\" ? autosave.debounceMs ?? 1000 : 1000;\n\n const provider = useDataProvider();\n const storage = useStorage();\n const queryClient = useQueryClient();\n\n // Fetch record for edit mode when no initialData\n const needsFetch = action === \"edit\" && !initialData && !!id;\n const fetchResult = useOne(\n resource,\n needsFetch ? id : undefined,\n { enabled: needsFetch },\n );\n\n const fetchedData = fetchResult.data ?? undefined;\n const effectiveData = initialData ?? fetchedData;\n const isLoading = needsFetch && fetchResult.isLoading;\n\n // Resolve fields\n const resolvedFields = useMemo(\n () => resolveFields(fieldsProp, effectiveData, listData),\n [fieldsProp, effectiveData, listData],\n );\n\n // Build schema\n const schema = useMemo(\n () => buildSchema(resolvedFields),\n [resolvedFields],\n );\n\n // Build defaults (with optional draft restoration)\n const defaultValues = useMemo(() => {\n const defaults = buildDefaults(resolvedFields, effectiveData, action, prefillFromUrl);\n if (autosaveEnabled && action === \"create\") {\n const key = `genesis-draft:${resource}:new`;\n const saved = storage.getItem(key);\n if (saved) {\n try {\n const draft = JSON.parse(saved);\n if (draft && typeof draft === \"object\" && !Array.isArray(draft)) {\n // Only restore keys that exist in the field definitions\n const validKeys = new Set(resolvedFields.map((f) => f.key));\n for (const k of Object.keys(draft)) {\n if (validKeys.has(k)) {\n defaults[k] = draft[k];\n }\n }\n } else {\n console.warn(`[useResourceForm] Draft for \"${resource}\" has unexpected shape, removing`);\n storage.removeItem(key);\n }\n } catch {\n console.warn(`[useResourceForm] Corrupt draft for \"${resource}\", removing`);\n storage.removeItem(key);\n }\n }\n }\n return defaults;\n }, [resolvedFields, effectiveData, action, prefillFromUrl, autosaveEnabled, resource, storage]);\n\n // Form\n // Cast the resolver because zod v4's passthrough() produces `unknown` for input,\n // but react-hook-form expects FieldValues (Record<string, any>).\n const resolver = useMemo(\n () => zodResolver(schema) as unknown as Resolver<Record<string, unknown>>,\n [schema],\n );\n\n const form = useForm<Record<string, unknown>>({\n resolver,\n defaultValues,\n values: action === \"edit\" ? defaultValues : undefined,\n mode: validationMode ?? \"onBlur\",\n reValidateMode: \"onChange\",\n shouldUnregister: true,\n });\n\n // Autosave effect — persist form values to storage for create mode\n useEffect(() => {\n if (!autosaveEnabled || action !== \"create\") return;\n const key = `genesis-draft:${resource}:new`;\n let timer: ReturnType<typeof setTimeout>;\n\n const subscription = form.watch((values) => {\n clearTimeout(timer);\n timer = setTimeout(() => {\n try {\n storage.setItem(key, JSON.stringify(values));\n } catch (err) {\n console.warn(`[useResourceForm] Autosave failed for \"${resource}\":`, err instanceof Error ? err.message : String(err));\n }\n }, debounceMs);\n });\n\n return () => {\n clearTimeout(timer);\n subscription.unsubscribe();\n };\n }, [autosaveEnabled, resource, action, form, debounceMs, storage]);\n\n // Submit handler\n const handleSubmit = useCallback(\n async (formData: Record<string, unknown>) => {\n try {\n let result: Record<string, unknown>;\n if (action === \"create\") {\n const res = await provider.create(resource, { data: formData });\n result = res.data;\n } else {\n const recordId = id ?? String((effectiveData as Record<string, unknown>)?.id ?? \"\");\n if (!recordId) {\n throw new Error(\"Cannot update record: no ID available. Provide an id prop or ensure the record has an id field.\");\n }\n const res = await provider.update(resource, { id: recordId, data: formData });\n result = res.data;\n }\n // Clear draft on successful save\n if (autosaveEnabled && action === \"create\") {\n try {\n storage.removeItem(`genesis-draft:${resource}:new`);\n } catch (err) {\n console.warn(`[useResourceForm] Draft cleanup failed for \"${resource}\":`, err instanceof Error ? err.message : String(err));\n }\n }\n // Invalidate queries to refresh data\n await queryClient.invalidateQueries({ queryKey: [resource] });\n onSuccess?.(result);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n form.setError(\"root\", { message: error.message });\n if (onError) {\n onError(error);\n } else {\n console.error(`[useResourceForm] ${action} failed for \"${resource}\":`, error);\n }\n }\n },\n // form is stable (from useForm) but included for correctness since we call form.setError\n [action, provider, resource, id, effectiveData, queryClient, onSuccess, onError, autosaveEnabled, form, storage],\n );\n\n const onSubmit = useCallback(\n (e?: { preventDefault?: () => void }) => form.handleSubmit(handleSubmit)(e as React.BaseSyntheticEvent),\n [form, handleSubmit],\n );\n\n const isSaving = form.formState.isSubmitting;\n\n return {\n form,\n resolvedFields,\n onSubmit,\n handleSave: handleSubmit,\n isSaving,\n isLoading,\n };\n}\n"]}
@@ -1,125 +0,0 @@
1
- // src/provider/supabase-provider.ts
2
- function escapeIlike(value) {
3
- return value.replace(/[%_\\]/g, (char) => `\\${char}`);
4
- }
5
- function createSupabaseProvider(client) {
6
- function applyFilters(query, filters) {
7
- if (!filters) return query;
8
- for (const f of filters) {
9
- switch (f.operator) {
10
- case "eq":
11
- query = query.eq(f.field, f.value);
12
- break;
13
- case "neq":
14
- query = query.neq(f.field, f.value);
15
- break;
16
- case "gt":
17
- query = query.gt(f.field, f.value);
18
- break;
19
- case "lt":
20
- query = query.lt(f.field, f.value);
21
- break;
22
- case "gte":
23
- query = query.gte(f.field, f.value);
24
- break;
25
- case "lte":
26
- query = query.lte(f.field, f.value);
27
- break;
28
- case "contains":
29
- query = query.ilike(f.field, `%${escapeIlike(String(f.value))}%`);
30
- break;
31
- case "in":
32
- query = query.in(f.field, f.value);
33
- break;
34
- case "is_null":
35
- query = query.is(f.field, null);
36
- break;
37
- case "is_not_null":
38
- query = query.not(f.field, "is", null);
39
- break;
40
- case "between": {
41
- const [lo, hi] = f.value;
42
- query = query.gte(f.field, lo).lte(f.field, hi);
43
- break;
44
- }
45
- default:
46
- throw new Error(`[SupabaseProvider] Unknown filter operator "${f.operator}"`);
47
- }
48
- }
49
- return query;
50
- }
51
- function throwIfError(result) {
52
- if (result.error) {
53
- const pgError = result.error;
54
- const message = pgError.message ?? String(result.error);
55
- const err = new Error(message, { cause: result.error });
56
- Object.assign(err, { code: pgError.code, hint: pgError.hint });
57
- throw err;
58
- }
59
- }
60
- const provider = {
61
- async getList(resource, params) {
62
- let query = client.from(resource).select(
63
- params.select ?? "*",
64
- { count: "exact" }
65
- );
66
- query = applyFilters(query, params.filters);
67
- if (params.search && params.search.query && params.search.fields.length > 0) {
68
- const escaped = escapeIlike(params.search.query);
69
- const safeFieldPattern = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;
70
- const clauses = params.search.fields.filter((f) => safeFieldPattern.test(f)).map((f) => `${f}.ilike.%${escaped}%`).join(",");
71
- if (clauses) {
72
- query = query.or(clauses);
73
- }
74
- }
75
- if (params.sort) {
76
- query = query.order(params.sort.field, {
77
- ascending: params.sort.order === "asc"
78
- });
79
- }
80
- if (params.pagination) {
81
- const { page, perPage } = params.pagination;
82
- const from = (page - 1) * perPage;
83
- const to = from + perPage - 1;
84
- query = query.range(from, to);
85
- }
86
- const result = await query;
87
- throwIfError(result);
88
- return { data: result.data ?? [], total: result.count ?? 0 };
89
- },
90
- async getOne(resource, params) {
91
- const result = await client.from(resource).select(params.select ?? "*").eq("id", params.id).single();
92
- throwIfError(result);
93
- return { data: result.data };
94
- },
95
- async getMany(resource, params) {
96
- if (params.ids.length === 0) return { data: [] };
97
- const result = await client.from(resource).select("*").in("id", params.ids);
98
- throwIfError(result);
99
- return { data: result.data ?? [] };
100
- },
101
- async create(resource, params) {
102
- const result = await client.from(resource).insert(params.data).select("*").single();
103
- throwIfError(result);
104
- return { data: result.data };
105
- },
106
- async update(resource, params) {
107
- const result = await client.from(resource).update(params.data).eq("id", params.id).select("*").single();
108
- throwIfError(result);
109
- return { data: result.data };
110
- },
111
- async deleteOne(resource, params) {
112
- const result = await client.from(resource).delete().eq("id", params.id).select("id").single();
113
- throwIfError(result);
114
- if (!result.data) {
115
- throw new Error(`[SupabaseProvider] Delete failed: no row found with id "${params.id}" in "${resource}"`);
116
- }
117
- return { data: result.data };
118
- }
119
- };
120
- return provider;
121
- }
122
-
123
- export { createSupabaseProvider };
124
- //# sourceMappingURL=chunk-6AA4ISUA.js.map
125
- //# sourceMappingURL=chunk-6AA4ISUA.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/provider/supabase-provider.ts"],"names":[],"mappings":"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","file":"chunk-6AA4ISUA.js","sourcesContent":["import type {\n DataProvider,\n FilterParam,\n} from \"./types\";\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 }): 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}\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}\n\n// ---------------------------------------------------------------------------\n// Provider factory\n// ---------------------------------------------------------------------------\n\nfunction escapeIlike(value: string): string {\n return value.replace(/[%_\\\\]/g, (char) => `\\\\${char}`);\n}\n\nexport function createSupabaseProvider(client: SupabaseClientInput): 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 function throwIfError(result: { error: unknown }): void {\n if (result.error) {\n const pgError = result.error as { message?: string; code?: string; hint?: string };\n const message = pgError.message ?? String(result.error);\n const err = new Error(message, { cause: result.error });\n Object.assign(err, { code: pgError.code, hint: pgError.hint });\n throw err;\n }\n }\n\n // ---------------------------------------------------------------------------\n // DataProvider implementation\n // ---------------------------------------------------------------------------\n\n const provider: DataProvider = {\n async getList(resource, params) {\n let query = (client.from(resource) as SupabaseQueryBuilder).select(\n params.select ?? \"*\",\n { count: \"exact\" },\n );\n\n query = applyFilters(query, params.filters);\n\n // Search — convert to OR of ilike.\n if (params.search && params.search.query && params.search.fields.length > 0) {\n const escaped = escapeIlike(params.search.query);\n const safeFieldPattern = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\n const clauses = params.search.fields\n .filter((f) => safeFieldPattern.test(f))\n .map((f) => `${f}.ilike.%${escaped}%`)\n .join(\",\");\n if (clauses) {\n query = query.or(clauses);\n }\n }\n\n // Sort.\n if (params.sort) {\n query = query.order(params.sort.field, {\n ascending: params.sort.order === \"asc\",\n });\n }\n\n // Pagination (offset).\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 const result = (await query) as {\n data: unknown[];\n error: unknown;\n count?: number | null;\n };\n throwIfError(result);\n\n return { data: result.data ?? [], total: result.count ?? 0 } as never;\n },\n\n async getOne(resource, params) {\n const result = (await (client.from(resource) as SupabaseQueryBuilder)\n .select(params.select ?? \"*\")\n .eq(\"id\", params.id)\n .single()) as { data: unknown; error: unknown };\n throwIfError(result);\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 (client.from(resource) as SupabaseQueryBuilder)\n .select(\"*\")\n .in(\"id\", params.ids)) as { data: unknown[]; error: unknown };\n throwIfError(result);\n return { data: result.data ?? [] } as never;\n },\n\n async create(resource, params) {\n const result = (await (client.from(resource) as SupabaseQueryBuilder)\n .insert(params.data as Record<string, unknown>)\n .select(\"*\")\n .single()) as { data: unknown; error: unknown };\n throwIfError(result);\n return { data: result.data } as never;\n },\n\n async update(resource, params) {\n const result = (await (client.from(resource) as SupabaseQueryBuilder)\n .update(params.data as Record<string, unknown>)\n .eq(\"id\", params.id)\n .select(\"*\")\n .single()) as { data: unknown; error: unknown };\n throwIfError(result);\n return { data: result.data } as never;\n },\n\n async deleteOne(resource, params) {\n const result = (await (client.from(resource) as SupabaseQueryBuilder)\n .delete()\n .eq(\"id\", params.id)\n .select(\"id\")\n .single()) as { data: { id: string | number } | null; error: unknown };\n throwIfError(result);\n if (!result.data) {\n throw new Error(`[SupabaseProvider] Delete failed: no row found with id \"${params.id}\" in \"${resource}\"`);\n }\n return { data: result.data };\n },\n };\n\n return provider;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/provider/mock-provider.ts","../src/provider/context.tsx"],"names":[],"mappings":"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","file":"chunk-6V2TIN3G.js","sourcesContent":["import type {\n DataProvider,\n ListParams,\n FilterParam,\n} from \"./types\";\n\n// ---------------------------------------------------------------------------\n// Mock DataProvider — operates on in-memory arrays, deep-cloned on creation\n// ---------------------------------------------------------------------------\n\nexport interface MockProviderOptions {\n /** Simulated latency in ms (default 0). */\n delay?: number;\n}\n\ntype Row = Record<string, unknown>;\n\nexport function createMockProvider(\n datasets: Record<string, Row[]>,\n options: MockProviderOptions = {},\n): DataProvider {\n const { delay = 0 } = options;\n\n // Deep clone so mutations don't affect the original data.\n const store: Record<string, Row[]> = JSON.parse(JSON.stringify(datasets));\n\n // Instance-scoped ID counter (NOT module-level).\n let nextId = 1;\n\n // ---------------------------------------------------------------------------\n // Helpers\n // ---------------------------------------------------------------------------\n\n function getTable(resource: string): Row[] {\n if (!store[resource]) {\n store[resource] = [];\n }\n return store[resource];\n }\n\n async function withDelay<T>(value: T): Promise<T> {\n if (delay > 0) {\n await new Promise((r) => setTimeout(r, delay));\n }\n return value;\n }\n\n function matchesFilter(row: Row, filter: FilterParam): boolean {\n const val = row[filter.field];\n\n switch (filter.operator) {\n case \"eq\":\n return val === filter.value;\n case \"neq\":\n return val !== filter.value;\n case \"gt\":\n return (val as number) > (filter.value as number);\n case \"lt\":\n return (val as number) < (filter.value as number);\n case \"gte\":\n return (val as number) >= (filter.value as number);\n case \"lte\":\n return (val as number) <= (filter.value as number);\n case \"contains\":\n return typeof val === \"string\" &&\n val.toLowerCase().includes(String(filter.value).toLowerCase());\n case \"in\":\n return filter.value.includes(val as string | number | boolean);\n case \"is_null\":\n return val === null || val === undefined;\n case \"is_not_null\":\n return val !== null && val !== undefined;\n case \"between\": {\n const [lo, hi] = filter.value;\n return (val as number) >= (lo as number) && (val as number) <= (hi as number);\n }\n }\n }\n\n function matchesSearch(\n row: Row,\n search: { query: string; fields: string[] },\n ): boolean {\n const q = search.query.toLowerCase();\n return search.fields.some((field) => {\n const val = row[field];\n return typeof val === \"string\" && val.toLowerCase().includes(q);\n });\n }\n\n function compareValues(a: unknown, b: unknown): number {\n if (typeof a === \"string\" && typeof b === \"string\") {\n return a.localeCompare(b, undefined, { numeric: true });\n }\n if (typeof a === \"number\" && typeof b === \"number\") {\n return a - b;\n }\n // Fallback: coerce to string.\n return String(a ?? \"\").localeCompare(String(b ?? \"\"), undefined, {\n numeric: true,\n });\n }\n\n function applyListParams(table: Row[], params: ListParams): { data: Row[]; total: number } {\n let rows = [...table];\n\n // Filters (AND).\n if (params.filters) {\n for (const f of params.filters) {\n rows = rows.filter((r) => matchesFilter(r, f));\n }\n }\n\n // Search (OR across fields, AND with filters).\n if (params.search && params.search.query) {\n rows = rows.filter((r) => matchesSearch(r, params.search!));\n }\n\n const total = rows.length;\n\n // Sort.\n if (params.sort) {\n const { field, order } = params.sort;\n rows.sort((a, b) => {\n const cmp = compareValues(a[field], b[field]);\n return order === \"desc\" ? -cmp : cmp;\n });\n }\n\n // Pagination (offset-based).\n if (params.pagination) {\n const { page, perPage } = params.pagination;\n const start = (page - 1) * perPage;\n rows = rows.slice(start, start + perPage);\n }\n\n return { data: rows, total };\n }\n\n // ---------------------------------------------------------------------------\n // DataProvider implementation\n // ---------------------------------------------------------------------------\n\n const provider: DataProvider = {\n async getList(_resource, params) {\n const table = getTable(_resource);\n const result = applyListParams(table, params);\n return withDelay(result) as never;\n },\n\n async getOne(_resource, params) {\n const table = getTable(_resource);\n const row = table.find((r) => String(r.id) === String(params.id));\n if (!row) {\n throw new Error(\n `[MockProvider] Record not found: ${_resource}#${params.id}`,\n );\n }\n return withDelay({ data: JSON.parse(JSON.stringify(row)) }) as never;\n },\n\n async getMany(_resource, params) {\n const table = getTable(_resource);\n const idSet = new Set(params.ids.map(String));\n const rows = table.filter((r) => idSet.has(String(r.id)));\n return withDelay({ data: JSON.parse(JSON.stringify(rows)) }) as never;\n },\n\n async create(_resource, params) {\n const table = getTable(_resource);\n const id = String(nextId++);\n const row = { id, ...params.data } as Row;\n table.push(row);\n return withDelay({ data: JSON.parse(JSON.stringify(row)) }) as never;\n },\n\n async update(_resource, params) {\n const table = getTable(_resource);\n const idx = table.findIndex((r) => String(r.id) === String(params.id));\n if (idx === -1) {\n throw new Error(\n `[MockProvider] Record not found: ${_resource}#${params.id}`,\n );\n }\n table[idx] = { ...table[idx], ...params.data };\n return withDelay({ data: JSON.parse(JSON.stringify(table[idx])) }) as never;\n },\n\n async deleteOne(_resource, params) {\n const table = getTable(_resource);\n const idx = table.findIndex((r) => String(r.id) === String(params.id));\n if (idx === -1) {\n throw new Error(\n `[MockProvider] Record not found: ${_resource}#${params.id}`,\n );\n }\n table.splice(idx, 1);\n return withDelay({ data: { id: params.id } });\n },\n };\n\n return provider;\n}\n","import {\n createContext,\n useContext,\n useMemo,\n type ReactNode,\n} from \"react\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport type { DataProvider } from \"./types\";\nimport { createMockProvider, type MockProviderOptions } from \"./mock-provider\";\nimport type { StorageAdapter } from \"../storage/types\";\nimport { createLocalStorageAdapter } from \"../storage/adapters\";\n\n// ---------------------------------------------------------------------------\n// Context\n// ---------------------------------------------------------------------------\n\nconst DataProviderContext = createContext<DataProvider | null>(null);\nconst StorageContext = createContext<StorageAdapter>(createLocalStorageAdapter());\n\n// ---------------------------------------------------------------------------\n// DataProviderRoot — thin context wrapper\n// ---------------------------------------------------------------------------\n\nexport interface DataProviderRootProps {\n provider: DataProvider;\n children: ReactNode;\n}\n\nexport function DataProviderRoot({ provider, children }: DataProviderRootProps) {\n return (\n <DataProviderContext.Provider value={provider}>\n {children}\n </DataProviderContext.Provider>\n );\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function useDataProvider(): DataProvider {\n const ctx = useContext(DataProviderContext);\n if (!ctx) {\n throw new Error(\n \"useDataProvider must be used inside <DataProviderRoot> or <GenesisProvider>. \" +\n \"Wrap your app (or the relevant subtree) in one of these providers.\",\n );\n }\n return ctx;\n}\n\nexport function useStorage(): StorageAdapter {\n return useContext(StorageContext);\n}\n\n// ---------------------------------------------------------------------------\n// GenesisProvider — convenience wrapper\n// ---------------------------------------------------------------------------\n\ntype Row = Record<string, unknown>;\n\nexport type GenesisProviderProps =\n | {\n children: ReactNode;\n /** Pass a ready-made DataProvider. Mutually exclusive with `mock`. */\n provider: DataProvider;\n mock?: never;\n /** Platform-specific storage adapter. Defaults to localStorage (SSR-safe). */\n storage?: StorageAdapter;\n }\n | {\n children: ReactNode;\n provider?: never;\n /** Pass mock datasets. Mutually exclusive with `provider`. */\n mock: {\n datasets: Record<string, Row[]>;\n options?: MockProviderOptions;\n };\n /** Platform-specific storage adapter. Defaults to localStorage (SSR-safe). */\n storage?: StorageAdapter;\n };\n\nconst defaultStorage = createLocalStorageAdapter();\n\nexport function GenesisProvider({\n children,\n provider,\n mock,\n storage,\n}: GenesisProviderProps) {\n if (provider && mock) {\n if (process.env.NODE_ENV !== \"production\") {\n console.warn(\"[GenesisProvider] Both `provider` and `mock` were passed. `provider` takes precedence. This is likely a mistake.\");\n }\n }\n\n // Build provider from mock datasets when no explicit provider is given.\n const resolvedProvider = useMemo(() => {\n if (provider) return provider;\n if (mock) return createMockProvider(mock.datasets, mock.options);\n throw new Error(\n \"<GenesisProvider> requires either a `provider` or `mock` prop.\",\n );\n }, [provider, mock]);\n\n // Internal QueryClient with sensible defaults.\n const queryClient = useMemo(\n () =>\n new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 5 * 60 * 1000, // 5 minutes\n },\n },\n }),\n [],\n );\n\n const resolvedStorage = storage ?? defaultStorage;\n\n return (\n <QueryClientProvider client={queryClient}>\n <StorageContext.Provider value={resolvedStorage}>\n <DataProviderRoot provider={resolvedProvider}>\n {children}\n </DataProviderRoot>\n </StorageContext.Provider>\n </QueryClientProvider>\n );\n}\n"]}
@@ -1,3 +0,0 @@
1
-
2
- //# sourceMappingURL=chunk-EL7YGOTY.js.map
3
- //# sourceMappingURL=chunk-EL7YGOTY.js.map