@aginix/vulcan-data-provider 0.1.0 → 0.1.1
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.
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { dataWithVirtualId, dataWithoutVirtualId, encodeId, getOrderBy, getPrimaryKey, getQuery, parseFilters, removePrimaryKey } from "./url_builder.js";
|
|
1
|
+
import { dataWithVirtualId, dataWithoutVirtualId, encodeId, getOrderBy, getPrimaryKey, getQuery, parseFilters, removePrimaryKey, stringifyQuery } from "./url_builder.js";
|
|
2
2
|
import { HttpError } from "ra-core";
|
|
3
|
-
import qs from "qs";
|
|
4
3
|
import { dequal } from "dequal";
|
|
5
4
|
//#region src/data_provider.ts
|
|
6
5
|
/**
|
|
@@ -78,7 +77,7 @@ const unwrapItem = (json) => {
|
|
|
78
77
|
*/
|
|
79
78
|
const vulcanDataProvider = (config) => {
|
|
80
79
|
const { apiUrl, httpClient, defaultListOp = "eq", sortOrder, primaryKeys, pageParam = "page", perPageParam = "perPage" } = config;
|
|
81
|
-
const stringify =
|
|
80
|
+
const stringify = stringifyQuery;
|
|
82
81
|
const sendList = async (url, options, primaryKey) => {
|
|
83
82
|
const { json } = await runRequest(httpClient, url, options);
|
|
84
83
|
const { rows, total } = unwrapList(json);
|
|
@@ -4,7 +4,7 @@ export type ParsedFilters = {
|
|
|
4
4
|
/**
|
|
5
5
|
* A flat record of query-string keys to either a single
|
|
6
6
|
* `<op>.<value>` string (or `(child1,child2)` for `and`/`or`) or an array of
|
|
7
|
-
* those strings when the same key needs to repeat — `
|
|
7
|
+
* those strings when the same key needs to repeat — `stringifyQuery` will
|
|
8
8
|
* expand the array into `field=foo&field=bar`, which the server-side
|
|
9
9
|
* `FilterParser` joins back into an AND.
|
|
10
10
|
*/
|
|
@@ -21,7 +21,7 @@ export type ParsedFilters = {
|
|
|
21
21
|
* - `field@op: value` overrides the operator (e.g. `age@gt: 18`).
|
|
22
22
|
* - Array values map to `field=in.(v1,v2,v3)`.
|
|
23
23
|
* - `like` / `ilike` split the value on whitespace and emit one
|
|
24
|
-
* `field=like.*token*` per token, which `
|
|
24
|
+
* `field=like.*token*` per token, which `stringifyQuery` turns into repeated
|
|
25
25
|
* keys (joined with AND server-side).
|
|
26
26
|
* - `or` / `and` keys recurse and emit `or=(c1.<op>.v1,c2.<op>.v2)`.
|
|
27
27
|
* - The `q` key is a free-form search term — it is forwarded verbatim
|
|
@@ -33,6 +33,25 @@ export type ParsedFilters = {
|
|
|
33
33
|
export declare const parseFilters: (params: {
|
|
34
34
|
filter?: Record<string, any>;
|
|
35
35
|
}, defaultListOp?: VulcanOperator) => ParsedFilters;
|
|
36
|
+
/**
|
|
37
|
+
* Serialize a flat query record into a URL query string.
|
|
38
|
+
*
|
|
39
|
+
* Uses the platform-native `URLSearchParams` (present in every browser and in
|
|
40
|
+
* Node) rather than a userland query-string library, so nothing here drags a
|
|
41
|
+
* Node built-in into browser bundles. `qs` used to do that: its transitive
|
|
42
|
+
* `object-inspect` dependency imports `util.inspect` at module top level and
|
|
43
|
+
* relies on the legacy `package.json#browser` object mapping to stub it out —
|
|
44
|
+
* a mapping some bundlers (e.g. Vite 8's rolldown backend) don't honor, so the
|
|
45
|
+
* externalized stub throws on `.custom` access as soon as the data provider is
|
|
46
|
+
* imported. See issue #29.
|
|
47
|
+
*
|
|
48
|
+
* Array values repeat the bare key once per element (`field=a&field=b`), which
|
|
49
|
+
* matches `qs`'s `arrayFormat: 'repeat'` and is what the server-side
|
|
50
|
+
* `FilterParser` expects — it decodes each repeated value and AND-joins them.
|
|
51
|
+
* The repeat form is shorter than bracket-indexed keys, matches PostgREST URL
|
|
52
|
+
* conventions, and reads better in logs.
|
|
53
|
+
*/
|
|
54
|
+
export declare const stringifyQuery: (query: Record<string, unknown>) => string;
|
|
36
55
|
export declare const getPrimaryKey: (resource: string, primaryKeys?: Map<string, PrimaryKey>) => PrimaryKey;
|
|
37
56
|
export declare const decodeId: (id: Identifier, primaryKey: PrimaryKey) => string[] | number[];
|
|
38
57
|
export declare const encodeId: (data: Record<string, unknown>, primaryKey: PrimaryKey) => Identifier;
|
package/build/src/url_builder.js
CHANGED
|
@@ -28,7 +28,7 @@ const isCompoundKey = (primaryKey) => primaryKey.length > 1;
|
|
|
28
28
|
* - `field@op: value` overrides the operator (e.g. `age@gt: 18`).
|
|
29
29
|
* - Array values map to `field=in.(v1,v2,v3)`.
|
|
30
30
|
* - `like` / `ilike` split the value on whitespace and emit one
|
|
31
|
-
* `field=like.*token*` per token, which `
|
|
31
|
+
* `field=like.*token*` per token, which `stringifyQuery` turns into repeated
|
|
32
32
|
* keys (joined with AND server-side).
|
|
33
33
|
* - `or` / `and` keys recurse and emit `or=(c1.<op>.v1,c2.<op>.v2)`.
|
|
34
34
|
* - The `q` key is a free-form search term — it is forwarded verbatim
|
|
@@ -98,6 +98,36 @@ const parseFilters = (params, defaultListOp = "eq") => {
|
|
|
98
98
|
});
|
|
99
99
|
return result;
|
|
100
100
|
};
|
|
101
|
+
/**
|
|
102
|
+
* Serialize a flat query record into a URL query string.
|
|
103
|
+
*
|
|
104
|
+
* Uses the platform-native `URLSearchParams` (present in every browser and in
|
|
105
|
+
* Node) rather than a userland query-string library, so nothing here drags a
|
|
106
|
+
* Node built-in into browser bundles. `qs` used to do that: its transitive
|
|
107
|
+
* `object-inspect` dependency imports `util.inspect` at module top level and
|
|
108
|
+
* relies on the legacy `package.json#browser` object mapping to stub it out —
|
|
109
|
+
* a mapping some bundlers (e.g. Vite 8's rolldown backend) don't honor, so the
|
|
110
|
+
* externalized stub throws on `.custom` access as soon as the data provider is
|
|
111
|
+
* imported. See issue #29.
|
|
112
|
+
*
|
|
113
|
+
* Array values repeat the bare key once per element (`field=a&field=b`), which
|
|
114
|
+
* matches `qs`'s `arrayFormat: 'repeat'` and is what the server-side
|
|
115
|
+
* `FilterParser` expects — it decodes each repeated value and AND-joins them.
|
|
116
|
+
* The repeat form is shorter than bracket-indexed keys, matches PostgREST URL
|
|
117
|
+
* conventions, and reads better in logs.
|
|
118
|
+
*/
|
|
119
|
+
const stringifyQuery = (query) => {
|
|
120
|
+
const params = new URLSearchParams();
|
|
121
|
+
for (const [key, value] of Object.entries(query)) {
|
|
122
|
+
if (value === void 0 || value === null) continue;
|
|
123
|
+
if (Array.isArray(value)) for (const item of value) {
|
|
124
|
+
if (item === void 0 || item === null) continue;
|
|
125
|
+
params.append(key, String(item));
|
|
126
|
+
}
|
|
127
|
+
else params.append(key, String(value));
|
|
128
|
+
}
|
|
129
|
+
return params.toString();
|
|
130
|
+
};
|
|
101
131
|
const getPrimaryKey = (resource, primaryKeys) => {
|
|
102
132
|
return primaryKeys?.get(resource) ?? ["id"];
|
|
103
133
|
};
|
|
@@ -165,4 +195,4 @@ const getOrderBy = (field, order, primaryKey, sortOrder = "asc,desc") => {
|
|
|
165
195
|
return `${field}.${direction}`;
|
|
166
196
|
};
|
|
167
197
|
//#endregion
|
|
168
|
-
export { dataWithVirtualId, dataWithoutVirtualId, decodeId, encodeId, getOrderBy, getPrimaryKey, getQuery, parseFilters, removePrimaryKey };
|
|
198
|
+
export { dataWithVirtualId, dataWithoutVirtualId, decodeId, encodeId, getOrderBy, getPrimaryKey, getQuery, parseFilters, removePrimaryKey, stringifyQuery };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aginix/vulcan-data-provider",
|
|
3
3
|
"description": "React Admin data provider for APIs built with @aginix/adonis-vulcan",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.1",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24.0.0"
|
|
7
7
|
},
|
|
@@ -54,8 +54,7 @@
|
|
|
54
54
|
"author": "n3n",
|
|
55
55
|
"license": "UNLICENSED",
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"dequal": "^2.0.3"
|
|
58
|
-
"qs": "^6.14.0"
|
|
57
|
+
"dequal": "^2.0.3"
|
|
59
58
|
},
|
|
60
59
|
"devDependencies": {
|
|
61
60
|
"@adonisjs/eslint-config": "^3.0.0",
|
|
@@ -66,7 +65,6 @@
|
|
|
66
65
|
"@poppinss/ts-exec": "^1.4.4",
|
|
67
66
|
"@release-it/conventional-changelog": "^10.0.5",
|
|
68
67
|
"@types/node": "^25.3.5",
|
|
69
|
-
"@types/qs": "^6.9.18",
|
|
70
68
|
"c8": "^11.0.0",
|
|
71
69
|
"eslint": "^10.0.3",
|
|
72
70
|
"prettier": "^3.8.1",
|
|
@@ -107,7 +105,6 @@
|
|
|
107
105
|
"external": [
|
|
108
106
|
"ra-core",
|
|
109
107
|
"react-admin",
|
|
110
|
-
"qs",
|
|
111
108
|
"dequal"
|
|
112
109
|
]
|
|
113
110
|
},
|