@signalium/query 1.0.17 → 1.1.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/CHANGELOG.md +47 -0
- package/dist/cjs/development/index.js +52 -15
- package/dist/cjs/development/index.js.map +1 -1
- package/dist/cjs/production/index.js +52 -15
- package/dist/cjs/production/index.js.map +1 -1
- package/dist/esm/EntityMap.d.ts.map +1 -1
- package/dist/esm/QueryClient.d.ts +3 -2
- package/dist/esm/QueryClient.d.ts.map +1 -1
- package/dist/esm/development/index.js +52 -15
- package/dist/esm/development/index.js.map +1 -1
- package/dist/esm/mutation.d.ts.map +1 -1
- package/dist/esm/pathInterpolator.d.ts +7 -3
- package/dist/esm/pathInterpolator.d.ts.map +1 -1
- package/dist/esm/production/index.js +52 -15
- package/dist/esm/production/index.js.map +1 -1
- package/dist/esm/query.d.ts +14 -8
- package/dist/esm/query.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# @signalium/query
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- af443c5: Add request body support to query() function
|
|
8
|
+
|
|
9
|
+
Queries can now send JSON request bodies for POST requests, enabling read-like operations that require complex data structures (e.g., fetching prices for an array of tokens).
|
|
10
|
+
|
|
11
|
+
**New features:**
|
|
12
|
+
|
|
13
|
+
- Added `body` field to query definitions for specifying request body schema
|
|
14
|
+
- Body parameters are automatically serialized as JSON with `Content-Type: application/json` header
|
|
15
|
+
- Body params work alongside path params and search params
|
|
16
|
+
- All query features (caching, staleTime, deduplication) work with body queries
|
|
17
|
+
|
|
18
|
+
**API changes:**
|
|
19
|
+
|
|
20
|
+
- Query methods are now restricted to `GET` and `POST` only (PUT, PATCH, DELETE should use `mutation()`)
|
|
21
|
+
|
|
22
|
+
**Example:**
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
const getPrices = query(() => ({
|
|
26
|
+
path: '/prices',
|
|
27
|
+
method: 'POST',
|
|
28
|
+
body: {
|
|
29
|
+
tokens: t.array(t.string),
|
|
30
|
+
},
|
|
31
|
+
searchParams: {
|
|
32
|
+
currency: t.string,
|
|
33
|
+
},
|
|
34
|
+
response: {
|
|
35
|
+
prices: t.array(t.object({ token: t.string, price: t.number })),
|
|
36
|
+
},
|
|
37
|
+
cache: { staleTime: 30_000 },
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
// Usage: POST /prices?currency=USD with body: {"tokens":["ETH","BTC"]}
|
|
41
|
+
const result = getPrices({ tokens: ['ETH', 'BTC'], currency: 'USD' });
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 1.0.18
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- 395730a: Fix entity cache keys to include shapeKey, preventing stale entity validation errors after schema changes
|
|
49
|
+
|
|
3
50
|
## 1.0.17
|
|
4
51
|
|
|
5
52
|
### Patch Changes
|
|
@@ -1139,7 +1139,8 @@ class EntityStore {
|
|
|
1139
1139
|
});
|
|
1140
1140
|
}
|
|
1141
1141
|
const warn = this.queryClient.getContext().log?.warn;
|
|
1142
|
-
|
|
1142
|
+
const desc = `${shape.typenameValue}:${id}`;
|
|
1143
|
+
return createEntityProxy(record.key, record, shape, entityRelay, this.queryClient, warn, desc);
|
|
1143
1144
|
}
|
|
1144
1145
|
}
|
|
1145
1146
|
class NetworkManager {
|
|
@@ -1331,7 +1332,7 @@ function parseObjectEntities(obj, objectShape, queryClient, entityRefs) {
|
|
|
1331
1332
|
throw new Error(`Entity id is required: ${typename}`);
|
|
1332
1333
|
}
|
|
1333
1334
|
const desc = `${typename}:${id}`;
|
|
1334
|
-
const key = utils.hashValue(desc);
|
|
1335
|
+
const key = utils.hashValue([desc, entityDef.shapeKey]);
|
|
1335
1336
|
if (entityRefs !== void 0) {
|
|
1336
1337
|
entityRefs.add(key);
|
|
1337
1338
|
}
|
|
@@ -2350,7 +2351,7 @@ class MutationResultImpl {
|
|
|
2350
2351
|
const entityId = obj[idField];
|
|
2351
2352
|
if (entityId !== void 0) {
|
|
2352
2353
|
const typename = entityDef.typenameValue;
|
|
2353
|
-
const entityKey = utils.hashValue(`${typename}:${entityId}
|
|
2354
|
+
const entityKey = utils.hashValue([`${typename}:${entityId}`, entityDef.shapeKey]);
|
|
2354
2355
|
this.queryClient.registerOptimisticUpdate(entityKey, obj);
|
|
2355
2356
|
this._pendingOptimisticKeys.add(entityKey);
|
|
2356
2357
|
}
|
|
@@ -2561,7 +2562,7 @@ var QueryType = /* @__PURE__ */ ((QueryType2) => {
|
|
|
2561
2562
|
return QueryType2;
|
|
2562
2563
|
})(QueryType || {});
|
|
2563
2564
|
function isSignal(value) {
|
|
2564
|
-
return typeof value === "object" && value !== null;
|
|
2565
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && "value" in value && "_id" in value;
|
|
2565
2566
|
}
|
|
2566
2567
|
function extractParamsForKey(params) {
|
|
2567
2568
|
if (params === void 0) {
|
|
@@ -2705,7 +2706,7 @@ function createPathInterpolator(pathTemplate) {
|
|
|
2705
2706
|
lastIndex = paramRegex.lastIndex;
|
|
2706
2707
|
}
|
|
2707
2708
|
segments.push(pathTemplate.slice(lastIndex));
|
|
2708
|
-
|
|
2709
|
+
const interpolate = (params) => {
|
|
2709
2710
|
let result = segments[0];
|
|
2710
2711
|
for (let i = 0; i < paramKeys.length; i++) {
|
|
2711
2712
|
result += encodeURIComponent(String(params[paramKeys[i]])) + segments[i + 1];
|
|
@@ -2724,6 +2725,7 @@ function createPathInterpolator(pathTemplate) {
|
|
|
2724
2725
|
}
|
|
2725
2726
|
return result;
|
|
2726
2727
|
};
|
|
2728
|
+
return { interpolate, pathParamNames: paramKeysSet };
|
|
2727
2729
|
}
|
|
2728
2730
|
const QUERY_DEFINITION_MAP = /* @__PURE__ */ new Map();
|
|
2729
2731
|
function buildQueryFn(queryDefinitionBuilder) {
|
|
@@ -2733,6 +2735,8 @@ function buildQueryFn(queryDefinitionBuilder) {
|
|
|
2733
2735
|
const {
|
|
2734
2736
|
path,
|
|
2735
2737
|
method = "GET",
|
|
2738
|
+
searchParams,
|
|
2739
|
+
body,
|
|
2736
2740
|
response,
|
|
2737
2741
|
requestOptions,
|
|
2738
2742
|
cache,
|
|
@@ -2759,14 +2763,53 @@ function buildQueryFn(queryDefinitionBuilder) {
|
|
|
2759
2763
|
shape = response;
|
|
2760
2764
|
shapeKey = utils.hashValue(shape);
|
|
2761
2765
|
}
|
|
2762
|
-
const interpolatePath = createPathInterpolator(path);
|
|
2766
|
+
const { interpolate: interpolatePath, pathParamNames } = createPathInterpolator(path);
|
|
2767
|
+
const bodyParamNames = /* @__PURE__ */ new Set();
|
|
2768
|
+
const hasBody = body !== void 0 && typeof body === "object" && !(body instanceof ValidatorDef) && !(body instanceof Set);
|
|
2769
|
+
if (hasBody) {
|
|
2770
|
+
for (const key of Object.keys(body)) {
|
|
2771
|
+
bodyParamNames.add(key);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
const searchParamNames = new Set(
|
|
2775
|
+
searchParams && typeof searchParams === "object" && !(searchParams instanceof ValidatorDef) && !(searchParams instanceof Set) ? Object.keys(searchParams) : []
|
|
2776
|
+
);
|
|
2777
|
+
const checkConflicts = (sourceNames, targetNames, sourceLabel, targetLabel) => {
|
|
2778
|
+
const conflicts = [...sourceNames].filter((name) => targetNames.has(name));
|
|
2779
|
+
if (conflicts.length > 0) {
|
|
2780
|
+
throw new Error(
|
|
2781
|
+
`Query definition error: ${sourceLabel} [${conflicts.join(", ")}] conflict with ${targetLabel}. Please rename to avoid this conflict.`
|
|
2782
|
+
);
|
|
2783
|
+
}
|
|
2784
|
+
};
|
|
2785
|
+
checkConflicts(searchParamNames, pathParamNames, "Search param(s)", `path parameter(s) in "${path}"`);
|
|
2786
|
+
checkConflicts(bodyParamNames, pathParamNames, "Body field(s)", `path parameter(s) in "${path}"`);
|
|
2787
|
+
checkConflicts(bodyParamNames, searchParamNames, "Body field(s)", "search param(s)");
|
|
2763
2788
|
const fetchFn = async (context, params) => {
|
|
2764
|
-
|
|
2789
|
+
let bodyData;
|
|
2790
|
+
let urlParams = params;
|
|
2791
|
+
if (hasBody) {
|
|
2792
|
+
bodyData = {};
|
|
2793
|
+
urlParams = params !== void 0 ? {} : void 0;
|
|
2794
|
+
if (params !== void 0) {
|
|
2795
|
+
for (const key in params) {
|
|
2796
|
+
if (bodyParamNames.has(key)) {
|
|
2797
|
+
bodyData[key] = params[key];
|
|
2798
|
+
} else {
|
|
2799
|
+
urlParams[key] = params[key];
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
const interpolatedPath = interpolatePath(urlParams ?? {});
|
|
2765
2805
|
const baseUrl = resolveBaseUrl(requestOptions?.baseUrl) ?? resolveBaseUrl(context.baseUrl);
|
|
2766
2806
|
const fullUrl = baseUrl ? `${baseUrl}${interpolatedPath}` : interpolatedPath;
|
|
2767
|
-
const { baseUrl: _baseUrl, ...fetchOptions } = requestOptions ?? {};
|
|
2807
|
+
const { baseUrl: _baseUrl, headers: userHeaders, ...fetchOptions } = requestOptions ?? {};
|
|
2808
|
+
const headers = bodyData ? { "Content-Type": "application/json", ...userHeaders } : userHeaders;
|
|
2768
2809
|
const response2 = await context.fetch(fullUrl, {
|
|
2769
2810
|
method,
|
|
2811
|
+
headers,
|
|
2812
|
+
body: bodyData ? JSON.stringify(bodyData) : void 0,
|
|
2770
2813
|
...fetchOptions
|
|
2771
2814
|
});
|
|
2772
2815
|
return response2.json();
|
|
@@ -2927,13 +2970,7 @@ function buildMutationFn(mutationDefinitionBuilder) {
|
|
|
2927
2970
|
const id = `mutation:${method}:${path}`;
|
|
2928
2971
|
const { shape: requestShape, shapeKey: requestShapeKey } = processTypeDef(request);
|
|
2929
2972
|
const { shape: responseShape, shapeKey: responseShapeKey } = processTypeDef(response);
|
|
2930
|
-
const interpolatePath = createPathInterpolator(path);
|
|
2931
|
-
const pathParamNames = /* @__PURE__ */ new Set();
|
|
2932
|
-
const paramRegex = /\[([^\]]+)\]/g;
|
|
2933
|
-
let match;
|
|
2934
|
-
while ((match = paramRegex.exec(path)) !== null) {
|
|
2935
|
-
pathParamNames.add(match[1]);
|
|
2936
|
-
}
|
|
2973
|
+
const { interpolate: interpolatePath, pathParamNames } = createPathInterpolator(path);
|
|
2937
2974
|
const mutateFn = async (context, requestData) => {
|
|
2938
2975
|
const pathParams = {};
|
|
2939
2976
|
for (const paramName of pathParamNames) {
|