@taruvi/refine-providers 1.3.3 → 1.3.4-beta.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 CHANGED
@@ -108,6 +108,8 @@ mutate({ resource: "posts", id: 1 });
108
108
 
109
109
  ### Filtering
110
110
 
111
+ List requests send **JSON `filters`** (`Database.filters(tree)` in the SDK): `convertRefineFiltersToBackendTree` maps Refine `CrudFilter[]` to the platform tree (nested `and` / `or` preserved). Bracket query keys from `convertRefineFilters` remain for helpers / Storage flatten mode.
112
+
111
113
  Full support for Refine filter operators:
112
114
 
113
115
  ```tsx
@@ -116,25 +118,41 @@ const { data } = useList({
116
118
  filters: [
117
119
  { field: "status", operator: "eq", value: "published" },
118
120
  { field: "views", operator: "gte", value: 100 },
119
- { field: "title", operator: "contains", value: "refine" },
121
+ { field: "title", operator: "containss", value: "refine" },
120
122
  { field: "category", operator: "in", value: ["tech", "news"] },
121
123
  ],
122
124
  });
123
125
  ```
124
126
 
125
- **Supported Operators:**
127
+ **Refine → backend operator mapping (important):**
128
+
129
+ | Refine operator | Backend / JSON leaf operator | Meaning on platform |
130
+ |-----------------|------------------------------|------------------------|
131
+ | `containss` | `contains` | Case-insensitive substring (ILIKE-style) |
132
+ | `contains` | `containss` | Case-sensitive substring |
133
+ | `startswith`, `endswith` | same name | Case-insensitive |
134
+ | `startswiths`, `endswiths` | same name | Case-sensitive |
135
+ | `icontains` / `nicontains` | pass-through | Aliases; platform normalizes `icontains` → `contains` |
136
+
137
+ Other operators (`eq`, `in`, `between`, array/range ops, etc.) follow `REFINE_OPERATOR_MAP` in [`src/utils.ts`](src/utils.ts).
138
+
139
+ **More operators:**
126
140
 
127
141
  | Operator | Description | Example |
128
142
  |----------|-------------|---------|
129
143
  | `eq` | Equal | `status = "active"` |
130
144
  | `ne` | Not equal | `status != "deleted"` |
131
145
  | `lt`, `gt`, `lte`, `gte` | Comparison | `age >= 18` |
132
- | `contains`, `ncontains` | Contains (case-sensitive) | `title contains "hello"` |
133
- | `containss`, `ncontainss` | Contains (case-insensitive) | `title icontains "hello"` |
134
- | `startswith`, `endswith` | String matching | `email endswith "@gmail.com"` |
135
- | `in`, `nin` | Array membership | `status in ["active", "pending"]` |
136
- | `null`, `nnull` | Null checks | `deleted_at is null` |
137
- | `between`, `nbetween` | Range | `price between [10, 100]` |
146
+ | `containss`, `ncontainss` | Case-insensitive contains / not | Prefer for “search” UX |
147
+ | `contains`, `ncontains` | Case-sensitive contains / not | |
148
+ | `startswith`, `endswith` | Case-insensitive prefix/suffix | |
149
+ | `startswiths`, `endswiths` | Case-sensitive prefix/suffix | |
150
+ | `in`, `nin`, `ina`, `nina` | Array membership | |
151
+ | `null`, `nnull` | Null checks | |
152
+ | `between`, `nbetween` | Range | |
153
+ | `acontains`, … `aelement`, `naelement` | PostgreSQL array ops | |
154
+ | `rcontains`, … `rstrictright` | PostgreSQL range ops | |
155
+ | `like`, `ilike`, `search` | Pattern / FTS | |
138
156
 
139
157
  ### Sorting & Pagination
140
158
 
package/dist/index.cjs CHANGED
@@ -11,7 +11,7 @@ var DataLoader__default = /*#__PURE__*/_interopDefault(DataLoader);
11
11
 
12
12
  // package.json
13
13
  var package_default = {
14
- version: "1.3.3"};
14
+ version: "1.3.4-beta.0"};
15
15
 
16
16
  // src/utils.ts
17
17
  var REFINE_OPERATOR_MAP = {
@@ -24,65 +24,231 @@ var REFINE_OPERATOR_MAP = {
24
24
  gt: "gt",
25
25
  lte: "lte",
26
26
  gte: "gte",
27
- // String operations (case-sensitive)
28
- contains: "contains",
29
- ncontains: "ncontains",
30
- startswith: "startswith",
31
- nstartswith: "nstartswith",
32
- endswith: "endswith",
33
- nendswith: "nendswith",
34
- // String operations (case-insensitive)
35
- containss: "icontains",
36
- ncontainss: "nicontains",
37
- startswiths: "istartswith",
38
- nstartswiths: "nistartswith",
39
- endswiths: "iendswith",
40
- nendswiths: "niendswith",
41
- // Array operations
27
+ // String operators mapped to backend-safe case-sensitive LIKE variants.
28
+ contains: "containss",
29
+ ncontains: "ncontainss",
30
+ containss: "containss",
31
+ ncontainss: "ncontainss",
32
+ // String operators mapped to backend-safe case-sensitive prefix/suffix LIKE.
33
+ startswith: "startswiths",
34
+ nstartswith: "nstartswiths",
35
+ endswith: "endswiths",
36
+ nendswith: "nendswiths",
37
+ startswiths: "startswiths",
38
+ nstartswiths: "nstartswiths",
39
+ endswiths: "endswiths",
40
+ nendswiths: "nendswiths",
41
+ // Aliases mapped to backend-safe case-sensitive LIKE variants.
42
+ icontains: "containss",
43
+ nicontains: "ncontainss",
44
+ // Array / membership
42
45
  in: "in",
43
46
  nin: "nin",
47
+ ina: "ina",
48
+ nina: "nina",
44
49
  // Null checks
45
50
  null: "null",
46
51
  nnull: "nnull",
47
52
  // Range
48
53
  between: "between",
49
- nbetween: "nbetween"
54
+ nbetween: "nbetween",
55
+ // PostgreSQL array ops (pass-through suffix names)
56
+ acontains: "acontains",
57
+ nacontains: "nacontains",
58
+ acontainedby: "acontainedby",
59
+ nacontainedby: "nacontainedby",
60
+ aoverlap: "aoverlap",
61
+ naoverlap: "naoverlap",
62
+ aelement: "aelement",
63
+ naelement: "naelement",
64
+ // PostgreSQL range column ops
65
+ rcontains: "rcontains",
66
+ rcontainedby: "rcontainedby",
67
+ roverlaps: "roverlaps",
68
+ radjacent: "radjacent",
69
+ rstrictleft: "rstrictleft",
70
+ rstrictright: "rstrictright",
71
+ // Raw pattern / field search
72
+ like: "like",
73
+ ilike: "ilike",
74
+ search: "search"
50
75
  };
51
- function convertRefineFilters(filters) {
76
+ var COMMA_VALUE_OPERATORS = /* @__PURE__ */ new Set([
77
+ "in",
78
+ "nin",
79
+ "ina",
80
+ "nina",
81
+ "between",
82
+ "nbetween",
83
+ "acontains",
84
+ "nacontains",
85
+ "acontainedby",
86
+ "nacontainedby",
87
+ "aoverlap",
88
+ "naoverlap",
89
+ "aelement",
90
+ "naelement",
91
+ "rcontains",
92
+ "rcontainedby",
93
+ "roverlaps",
94
+ "radjacent",
95
+ "rstrictleft",
96
+ "rstrictright"
97
+ ]);
98
+ function isLogicalFilter(filter) {
99
+ return typeof filter === "object" && filter !== null && "operator" in filter && (filter.operator === "and" || filter.operator === "or") && "value" in filter && Array.isArray(filter.value);
100
+ }
101
+ function isFieldFilter(filter) {
102
+ return typeof filter === "object" && filter !== null && "field" in filter && "operator" in filter && typeof filter.field === "string" && typeof filter.operator === "string";
103
+ }
104
+ function refineOperatorToBackendKey(operator) {
105
+ const suffix = REFINE_OPERATOR_MAP[operator];
106
+ if (suffix === void 0) return operator;
107
+ return suffix === "" ? "eq" : suffix;
108
+ }
109
+ function formatRefineLeafValue(operator, value) {
110
+ if (COMMA_VALUE_OPERATORS.has(operator)) {
111
+ return Array.isArray(value) ? value.join(",") : String(value);
112
+ }
113
+ if (operator === "null" || operator === "nnull") {
114
+ return "true";
115
+ }
116
+ return String(value);
117
+ }
118
+ function encodeLeafFlat(field, operator, value, params) {
119
+ if (value === void 0 || value === null && operator !== "null") {
120
+ return;
121
+ }
122
+ const suffix = REFINE_OPERATOR_MAP[operator];
123
+ if (suffix === void 0) {
124
+ console.warn(`Unknown Refine operator: ${operator}`);
125
+ return;
126
+ }
127
+ const paramKey = suffix ? `${field}__${suffix}` : String(field);
128
+ params[paramKey] = formatRefineLeafValue(operator, value);
129
+ }
130
+ function encodeCrudFilterBracket(filter, path, out) {
131
+ if (isLogicalFilter(filter)) {
132
+ const op = filter.operator;
133
+ out[`${path}[operator]`] = op;
134
+ const children = filter.value;
135
+ children.forEach((child, i) => {
136
+ encodeCrudFilterBracket(child, `${path}[value][${i}]`, out);
137
+ });
138
+ return;
139
+ }
140
+ if (!isFieldFilter(filter)) {
141
+ return;
142
+ }
143
+ const leaf = filter;
144
+ const { field, operator, value } = leaf;
145
+ if (value === void 0 || value === null && operator !== "null") {
146
+ return;
147
+ }
148
+ const suffix = REFINE_OPERATOR_MAP[operator];
149
+ if (suffix === void 0) {
150
+ console.warn(`Unknown Refine operator: ${operator}`);
151
+ return;
152
+ }
153
+ out[`${path}[field]`] = field;
154
+ out[`${path}[operator]`] = refineOperatorToBackendKey(operator);
155
+ out[`${path}[value]`] = formatRefineLeafValue(operator, value);
156
+ }
157
+ function isLogicalBackendNode(n) {
158
+ return !("field" in n) && (n.operator === "and" || n.operator === "or");
159
+ }
160
+ function jsonLeafValue(operator, value) {
161
+ if (operator === "null" || operator === "nnull") return true;
162
+ return value;
163
+ }
164
+ function crudFilterToBackendNodeOrNull(filter) {
165
+ if (isLogicalFilter(filter)) {
166
+ const children = filter.value.map(crudFilterToBackendNodeOrNull).filter((n) => n !== null);
167
+ if (children.length === 0) return null;
168
+ return { operator: filter.operator, value: children };
169
+ }
170
+ if (isFieldFilter(filter)) {
171
+ const leaf = filter;
172
+ if (leaf.value === void 0 || leaf.value === null && leaf.operator !== "null") {
173
+ return null;
174
+ }
175
+ if (REFINE_OPERATOR_MAP[leaf.operator] === void 0) {
176
+ console.warn(`Unknown Refine operator: ${leaf.operator}`);
177
+ return null;
178
+ }
179
+ const backendOp = refineOperatorToBackendKey(leaf.operator);
180
+ return {
181
+ field: leaf.field,
182
+ operator: backendOp,
183
+ value: jsonLeafValue(leaf.operator, leaf.value)
184
+ };
185
+ }
186
+ return null;
187
+ }
188
+ function convertRefineFiltersToBackendTree(filters) {
189
+ if (!filters?.length) return null;
190
+ const nodes = filters.map(crudFilterToBackendNodeOrNull).filter((n) => n !== null);
191
+ if (nodes.length === 0) return null;
192
+ if (nodes.length === 1 && isLogicalBackendNode(nodes[0])) {
193
+ return [nodes[0]];
194
+ }
195
+ return [{ operator: "and", value: nodes }];
196
+ }
197
+ function convertRefineFiltersFlattened(filters) {
52
198
  if (!filters || filters.length === 0) return {};
53
199
  const params = {};
54
200
  for (const filter of filters) {
55
- if ("operator" in filter && (filter.operator === "and" || filter.operator === "or")) {
201
+ if (isLogicalFilter(filter)) {
56
202
  if (filter.value && Array.isArray(filter.value)) {
57
- const nested = convertRefineFilters(filter.value);
203
+ const nested = convertRefineFiltersFlattened(
204
+ filter.value
205
+ );
58
206
  Object.assign(params, nested);
59
207
  }
60
208
  continue;
61
209
  }
62
- if ("field" in filter && filter.field && filter.operator) {
63
- const { field, operator, value } = filter;
64
- if (value === void 0 || value === null && operator !== "null") {
65
- continue;
66
- }
67
- const suffix = REFINE_OPERATOR_MAP[operator];
68
- if (suffix === void 0) {
69
- console.warn(`Unknown Refine operator: ${operator}`);
70
- continue;
71
- }
72
- const paramKey = suffix ? `${field}__${suffix}` : String(field);
73
- if (operator === "in" || operator === "nin") {
74
- params[paramKey] = Array.isArray(value) ? value.join(",") : String(value);
75
- } else if (operator === "between" || operator === "nbetween") {
76
- params[paramKey] = Array.isArray(value) ? value.join(",") : String(value);
77
- } else if (operator === "null" || operator === "nnull") {
78
- params[paramKey] = "true";
79
- } else {
80
- params[paramKey] = String(value);
81
- }
210
+ if (isFieldFilter(filter)) {
211
+ const leaf = filter;
212
+ encodeLeafFlat(leaf.field, leaf.operator, leaf.value, params);
82
213
  }
83
214
  }
84
215
  return params;
85
216
  }
217
+ function convertRefineFilters(filters, options) {
218
+ if (!filters || filters.length === 0) return {};
219
+ if (options?.logicalEncoding === "flatten") {
220
+ return convertRefineFiltersFlattened(filters);
221
+ }
222
+ const hasLogical = filters.some(isLogicalFilter);
223
+ if (!hasLogical) {
224
+ const params = {};
225
+ for (const filter of filters) {
226
+ if (isFieldFilter(filter)) {
227
+ const leaf = filter;
228
+ encodeLeafFlat(leaf.field, leaf.operator, leaf.value, params);
229
+ }
230
+ }
231
+ return params;
232
+ }
233
+ if (filters.length === 1 && isLogicalFilter(filters[0])) {
234
+ const out2 = {};
235
+ encodeCrudFilterBracket(filters[0], "filters[0]", out2);
236
+ return out2;
237
+ }
238
+ const out = {};
239
+ out["filters[0][operator]"] = "and";
240
+ filters.forEach((f, i) => {
241
+ encodeCrudFilterBracket(f, `filters[0][value][${i}]`, out);
242
+ });
243
+ return out;
244
+ }
245
+ function applyRefineQueryParamsToDatabase(query, params) {
246
+ let result = query;
247
+ for (const [key, val] of Object.entries(params)) {
248
+ result = result.filters(key, "eq", val);
249
+ }
250
+ return result;
251
+ }
86
252
  function convertRefineSorters(sorters) {
87
253
  if (!sorters || sorters.length === 0) return void 0;
88
254
  return sorters.map((sort) => sort.order === "desc" ? `-${sort.field}` : sort.field).join(",");
@@ -138,16 +304,7 @@ function formatHaving(having) {
138
304
  continue;
139
305
  }
140
306
  const paramKey = suffix ? `${field}__${suffix}` : String(field);
141
- let paramValue;
142
- if (operator === "in" || operator === "nin") {
143
- paramValue = Array.isArray(value) ? value.join(",") : String(value);
144
- } else if (operator === "between" || operator === "nbetween") {
145
- paramValue = Array.isArray(value) ? value.join(",") : String(value);
146
- } else if (operator === "null" || operator === "nnull") {
147
- paramValue = "true";
148
- } else {
149
- paramValue = String(value);
150
- }
307
+ const paramValue = formatRefineLeafValue(operator, value);
151
308
  params.push(`${paramKey}=${paramValue}`);
152
309
  }
153
310
  }
@@ -181,43 +338,13 @@ function applyAggregations(query, meta) {
181
338
  }
182
339
  function applyFilters(query, filters) {
183
340
  if (!filters || filters.length === 0) return query;
184
- let result = query;
185
- for (const filter of filters) {
186
- if ("operator" in filter && (filter.operator === "and" || filter.operator === "or")) {
187
- if (filter.value && Array.isArray(filter.value)) {
188
- result = applyFilters(result, filter.value);
189
- }
190
- continue;
191
- }
192
- if ("field" in filter && filter.field && filter.operator) {
193
- const { field, operator, value } = filter;
194
- if (value === void 0 || value === null && operator !== "null") continue;
195
- const suffix = REFINE_OPERATOR_MAP[operator];
196
- if (suffix === void 0) {
197
- console.warn(`Unknown Refine operator: ${operator}`);
198
- continue;
199
- }
200
- const paramKey = suffix ? `${field}__${suffix}` : String(field);
201
- let paramValue;
202
- if (operator === "in" || operator === "nin" || operator === "between" || operator === "nbetween") {
203
- paramValue = Array.isArray(value) ? value.join(",") : String(value);
204
- } else if (operator === "null" || operator === "nnull") {
205
- paramValue = "true";
206
- } else {
207
- paramValue = String(value);
208
- }
209
- result = result.filter(paramKey, "eq", paramValue);
210
- }
211
- }
212
- return result;
341
+ const tree = convertRefineFiltersToBackendTree(filters);
342
+ if (!tree) return query;
343
+ return query.filters(tree);
213
344
  }
214
345
  function applySorters(query, sorters) {
215
- if (!sorters || sorters.length === 0) return query;
216
- let result = query;
217
- for (const sorter of sorters) {
218
- result = result.sort(sorter.field, sorter.order === "desc" ? "desc" : "asc");
219
- }
220
- return result;
346
+ const ordering = convertRefineSorters(sorters);
347
+ return ordering ? query.orderBy(ordering) : query;
221
348
  }
222
349
  function applyPagination(query, pagination) {
223
350
  if (!pagination || pagination.mode === "off") return query;
@@ -305,7 +432,7 @@ function dataProvider(client) {
305
432
  }
306
433
  const idColumn = getIdColumn(taruviMeta);
307
434
  let query = new sdk.Database(client).from(tableName);
308
- query = query.filter(idColumn, "in", ids.map(String));
435
+ query = query.filters(idColumn, "in", ids.map(String));
309
436
  query = applyPopulate(query, taruviMeta);
310
437
  const response = await query.execute();
311
438
  return { data: response.data };
@@ -422,7 +549,7 @@ function storageDataProvider(client) {
422
549
  const getBucketName = (resource, meta) => meta?.bucketName ?? resource;
423
550
  const buildFilters = (params) => {
424
551
  const filters = {
425
- ...convertRefineFilters(params.filters),
552
+ ...convertRefineFilters(params.filters, { logicalEncoding: "flatten" }),
426
553
  ...convertRefinePagination(params.pagination)
427
554
  };
428
555
  const ordering = convertRefineSorters(params.sorters);
@@ -930,20 +1057,27 @@ function analyticsDataProvider(client) {
930
1057
  exports._cachedUser = null;
931
1058
  function authProvider(client) {
932
1059
  const auth = new sdk.Auth(client);
933
- function redirectToLogin() {
934
- exports._cachedUser = null;
935
- auth.login();
936
- }
937
1060
  return {
938
- login: async () => {
939
- if (auth.hasToken()) {
1061
+ login: async (params = {}) => {
1062
+ const { callbackUrl, redirect = true } = params;
1063
+ if (auth.isUserAuthenticated()) {
1064
+ return {
1065
+ success: true,
1066
+ redirectTo: callbackUrl || "/"
1067
+ };
1068
+ }
1069
+ if (redirect) {
1070
+ auth.login(callbackUrl);
940
1071
  return {
941
1072
  success: true
942
1073
  };
943
1074
  }
944
- redirectToLogin();
945
1075
  return {
946
- success: true
1076
+ success: false,
1077
+ error: {
1078
+ name: "LoginError",
1079
+ message: "Login failed. Please try again."
1080
+ }
947
1081
  };
948
1082
  },
949
1083
  logout: async (params = {}) => {
@@ -956,20 +1090,19 @@ function authProvider(client) {
956
1090
  };
957
1091
  },
958
1092
  check: async () => {
959
- if (!auth.hasToken()) {
960
- return { authenticated: false };
961
- }
962
- const isValid = await auth.isUserAuthenticated();
963
- if (!isValid) {
964
- return { authenticated: false };
1093
+ if (!auth.isUserAuthenticated()) {
1094
+ return { authenticated: false, redirectTo: "/login" };
965
1095
  }
966
1096
  return { authenticated: true };
967
1097
  },
968
1098
  onError: async (error) => {
969
1099
  const status = error?.statusCode || error?.status || error?.response?.status;
970
1100
  if (status === 401) {
971
- redirectToLogin();
972
- return { error };
1101
+ return {
1102
+ logout: true,
1103
+ redirectTo: "/login",
1104
+ error
1105
+ };
973
1106
  }
974
1107
  if (status === 403) {
975
1108
  return { error };
@@ -1112,15 +1245,20 @@ exports.REFINE_OPERATOR_MAP = REFINE_OPERATOR_MAP;
1112
1245
  exports.accessControlProvider = accessControlProvider;
1113
1246
  exports.analyticsDataProvider = analyticsDataProvider;
1114
1247
  exports.appDataProvider = appDataProvider;
1248
+ exports.applyRefineQueryParamsToDatabase = applyRefineQueryParamsToDatabase;
1115
1249
  exports.authProvider = authProvider;
1116
1250
  exports.buildQueryString = buildQueryString;
1117
1251
  exports.buildRefineQueryParams = buildRefineQueryParams;
1118
1252
  exports.convertRefineFilters = convertRefineFilters;
1253
+ exports.convertRefineFiltersToBackendTree = convertRefineFiltersToBackendTree;
1119
1254
  exports.convertRefinePagination = convertRefinePagination;
1120
1255
  exports.convertRefineSorters = convertRefineSorters;
1121
1256
  exports.dataProvider = dataProvider;
1257
+ exports.encodeCrudFilterBracket = encodeCrudFilterBracket;
1258
+ exports.formatRefineLeafValue = formatRefineLeafValue;
1122
1259
  exports.functionsDataProvider = functionsDataProvider;
1123
1260
  exports.handleError = handleError;
1261
+ exports.refineOperatorToBackendKey = refineOperatorToBackendKey;
1124
1262
  exports.storageDataProvider = storageDataProvider;
1125
1263
  exports.userDataProvider = userDataProvider;
1126
1264
  //# sourceMappingURL=index.cjs.map