@signalium/query 1.0.18 → 1.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.
- package/CHANGELOG.md +47 -0
- package/dist/cjs/development/index.js +88 -12
- package/dist/cjs/development/index.js.map +1 -1
- package/dist/cjs/production/index.js +88 -12
- 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 +88 -12
- 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 +88 -12
- package/dist/esm/production/index.js.map +1 -1
- package/dist/esm/proxy.d.ts.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.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- bb0a5a9: Fix entity proxies not being created for preloaded entities from cache, and `__entityRef` not being resolved in proxy get handler. This fixes validation errors when accessing nested entities loaded from persistent cache.
|
|
8
|
+
|
|
9
|
+
## 1.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- af443c5: Add request body support to query() function
|
|
14
|
+
|
|
15
|
+
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).
|
|
16
|
+
|
|
17
|
+
**New features:**
|
|
18
|
+
|
|
19
|
+
- Added `body` field to query definitions for specifying request body schema
|
|
20
|
+
- Body parameters are automatically serialized as JSON with `Content-Type: application/json` header
|
|
21
|
+
- Body params work alongside path params and search params
|
|
22
|
+
- All query features (caching, staleTime, deduplication) work with body queries
|
|
23
|
+
|
|
24
|
+
**API changes:**
|
|
25
|
+
|
|
26
|
+
- Query methods are now restricted to `GET` and `POST` only (PUT, PATCH, DELETE should use `mutation()`)
|
|
27
|
+
|
|
28
|
+
**Example:**
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
const getPrices = query(() => ({
|
|
32
|
+
path: '/prices',
|
|
33
|
+
method: 'POST',
|
|
34
|
+
body: {
|
|
35
|
+
tokens: t.array(t.string),
|
|
36
|
+
},
|
|
37
|
+
searchParams: {
|
|
38
|
+
currency: t.string,
|
|
39
|
+
},
|
|
40
|
+
response: {
|
|
41
|
+
prices: t.array(t.object({ token: t.string, price: t.number })),
|
|
42
|
+
},
|
|
43
|
+
cache: { staleTime: 30_000 },
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// Usage: POST /prices?currency=USD with body: {"tokens":["ETH","BTC"]}
|
|
47
|
+
const result = getPrices({ tokens: ['ETH', 'BTC'], currency: 'USD' });
|
|
48
|
+
```
|
|
49
|
+
|
|
3
50
|
## 1.0.18
|
|
4
51
|
|
|
5
52
|
### Patch Changes
|
|
@@ -912,6 +912,43 @@ function createEntityProxy(id, entityRecord, def, entityRelay, scopeOwner, warn
|
|
|
912
912
|
if (!Object.hasOwnProperty.call(shape, prop)) {
|
|
913
913
|
return value;
|
|
914
914
|
}
|
|
915
|
+
if (value && typeof value === "object") {
|
|
916
|
+
const ownerWithHydrate = scopeOwner;
|
|
917
|
+
if (typeof value.__entityRef === "number") {
|
|
918
|
+
const nestedRecord = ownerWithHydrate.hydrateEntity?.(
|
|
919
|
+
value.__entityRef,
|
|
920
|
+
propDef
|
|
921
|
+
);
|
|
922
|
+
if (nestedRecord && nestedRecord.proxy) {
|
|
923
|
+
cache.set(prop, nestedRecord.proxy);
|
|
924
|
+
return nestedRecord.proxy;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
928
|
+
const firstItem = value[0];
|
|
929
|
+
if (firstItem && typeof firstItem === "object" && typeof firstItem.__entityRef === "number") {
|
|
930
|
+
const arrayTypeDef = propDef;
|
|
931
|
+
const isArrayType = typeof arrayTypeDef.mask === "number" && (arrayTypeDef.mask & Mask.ARRAY) !== 0;
|
|
932
|
+
const itemDef = isArrayType ? arrayTypeDef.shape : void 0;
|
|
933
|
+
if (itemDef) {
|
|
934
|
+
const hydratedArray = value.map((item) => {
|
|
935
|
+
if (item && typeof item === "object" && typeof item.__entityRef === "number") {
|
|
936
|
+
const nestedRecord = ownerWithHydrate.hydrateEntity?.(
|
|
937
|
+
item.__entityRef,
|
|
938
|
+
itemDef
|
|
939
|
+
);
|
|
940
|
+
if (nestedRecord && nestedRecord.proxy) {
|
|
941
|
+
return nestedRecord.proxy;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return item;
|
|
945
|
+
});
|
|
946
|
+
cache.set(prop, hydratedArray);
|
|
947
|
+
return hydratedArray;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
915
952
|
const parsed = parseValue(value, propDef, `[[${desc}]].${prop}`, false, warn);
|
|
916
953
|
cache.set(prop, parsed);
|
|
917
954
|
return parsed;
|
|
@@ -1052,6 +1089,9 @@ class EntityStore {
|
|
|
1052
1089
|
record.data = mergeValues(record.data, obj);
|
|
1053
1090
|
record.notifier.notify();
|
|
1054
1091
|
record.cache.clear();
|
|
1092
|
+
if (record.proxy === void 0) {
|
|
1093
|
+
record.proxy = this.createEntityProxy(record, shape);
|
|
1094
|
+
}
|
|
1055
1095
|
}
|
|
1056
1096
|
record.entityRefs = entityRefs;
|
|
1057
1097
|
return record;
|
|
@@ -2562,7 +2602,7 @@ var QueryType = /* @__PURE__ */ ((QueryType2) => {
|
|
|
2562
2602
|
return QueryType2;
|
|
2563
2603
|
})(QueryType || {});
|
|
2564
2604
|
function isSignal(value) {
|
|
2565
|
-
return typeof value === "object" && value !== null;
|
|
2605
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && "value" in value && "_id" in value;
|
|
2566
2606
|
}
|
|
2567
2607
|
function extractParamsForKey(params) {
|
|
2568
2608
|
if (params === void 0) {
|
|
@@ -2706,7 +2746,7 @@ function createPathInterpolator(pathTemplate) {
|
|
|
2706
2746
|
lastIndex = paramRegex.lastIndex;
|
|
2707
2747
|
}
|
|
2708
2748
|
segments.push(pathTemplate.slice(lastIndex));
|
|
2709
|
-
|
|
2749
|
+
const interpolate = (params) => {
|
|
2710
2750
|
let result = segments[0];
|
|
2711
2751
|
for (let i = 0; i < paramKeys.length; i++) {
|
|
2712
2752
|
result += encodeURIComponent(String(params[paramKeys[i]])) + segments[i + 1];
|
|
@@ -2725,6 +2765,7 @@ function createPathInterpolator(pathTemplate) {
|
|
|
2725
2765
|
}
|
|
2726
2766
|
return result;
|
|
2727
2767
|
};
|
|
2768
|
+
return { interpolate, pathParamNames: paramKeysSet };
|
|
2728
2769
|
}
|
|
2729
2770
|
const QUERY_DEFINITION_MAP = /* @__PURE__ */ new Map();
|
|
2730
2771
|
function buildQueryFn(queryDefinitionBuilder) {
|
|
@@ -2734,6 +2775,8 @@ function buildQueryFn(queryDefinitionBuilder) {
|
|
|
2734
2775
|
const {
|
|
2735
2776
|
path,
|
|
2736
2777
|
method = "GET",
|
|
2778
|
+
searchParams,
|
|
2779
|
+
body,
|
|
2737
2780
|
response,
|
|
2738
2781
|
requestOptions,
|
|
2739
2782
|
cache,
|
|
@@ -2760,14 +2803,53 @@ function buildQueryFn(queryDefinitionBuilder) {
|
|
|
2760
2803
|
shape = response;
|
|
2761
2804
|
shapeKey = utils.hashValue(shape);
|
|
2762
2805
|
}
|
|
2763
|
-
const interpolatePath = createPathInterpolator(path);
|
|
2806
|
+
const { interpolate: interpolatePath, pathParamNames } = createPathInterpolator(path);
|
|
2807
|
+
const bodyParamNames = /* @__PURE__ */ new Set();
|
|
2808
|
+
const hasBody = body !== void 0 && typeof body === "object" && !(body instanceof ValidatorDef) && !(body instanceof Set);
|
|
2809
|
+
if (hasBody) {
|
|
2810
|
+
for (const key of Object.keys(body)) {
|
|
2811
|
+
bodyParamNames.add(key);
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
const searchParamNames = new Set(
|
|
2815
|
+
searchParams && typeof searchParams === "object" && !(searchParams instanceof ValidatorDef) && !(searchParams instanceof Set) ? Object.keys(searchParams) : []
|
|
2816
|
+
);
|
|
2817
|
+
const checkConflicts = (sourceNames, targetNames, sourceLabel, targetLabel) => {
|
|
2818
|
+
const conflicts = [...sourceNames].filter((name) => targetNames.has(name));
|
|
2819
|
+
if (conflicts.length > 0) {
|
|
2820
|
+
throw new Error(
|
|
2821
|
+
`Query definition error: ${sourceLabel} [${conflicts.join(", ")}] conflict with ${targetLabel}. Please rename to avoid this conflict.`
|
|
2822
|
+
);
|
|
2823
|
+
}
|
|
2824
|
+
};
|
|
2825
|
+
checkConflicts(searchParamNames, pathParamNames, "Search param(s)", `path parameter(s) in "${path}"`);
|
|
2826
|
+
checkConflicts(bodyParamNames, pathParamNames, "Body field(s)", `path parameter(s) in "${path}"`);
|
|
2827
|
+
checkConflicts(bodyParamNames, searchParamNames, "Body field(s)", "search param(s)");
|
|
2764
2828
|
const fetchFn = async (context, params) => {
|
|
2765
|
-
|
|
2829
|
+
let bodyData;
|
|
2830
|
+
let urlParams = params;
|
|
2831
|
+
if (hasBody) {
|
|
2832
|
+
bodyData = {};
|
|
2833
|
+
urlParams = params !== void 0 ? {} : void 0;
|
|
2834
|
+
if (params !== void 0) {
|
|
2835
|
+
for (const key in params) {
|
|
2836
|
+
if (bodyParamNames.has(key)) {
|
|
2837
|
+
bodyData[key] = params[key];
|
|
2838
|
+
} else {
|
|
2839
|
+
urlParams[key] = params[key];
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
const interpolatedPath = interpolatePath(urlParams ?? {});
|
|
2766
2845
|
const baseUrl = resolveBaseUrl(requestOptions?.baseUrl) ?? resolveBaseUrl(context.baseUrl);
|
|
2767
2846
|
const fullUrl = baseUrl ? `${baseUrl}${interpolatedPath}` : interpolatedPath;
|
|
2768
|
-
const { baseUrl: _baseUrl, ...fetchOptions } = requestOptions ?? {};
|
|
2847
|
+
const { baseUrl: _baseUrl, headers: userHeaders, ...fetchOptions } = requestOptions ?? {};
|
|
2848
|
+
const headers = bodyData ? { "Content-Type": "application/json", ...userHeaders } : userHeaders;
|
|
2769
2849
|
const response2 = await context.fetch(fullUrl, {
|
|
2770
2850
|
method,
|
|
2851
|
+
headers,
|
|
2852
|
+
body: bodyData ? JSON.stringify(bodyData) : void 0,
|
|
2771
2853
|
...fetchOptions
|
|
2772
2854
|
});
|
|
2773
2855
|
return response2.json();
|
|
@@ -2928,13 +3010,7 @@ function buildMutationFn(mutationDefinitionBuilder) {
|
|
|
2928
3010
|
const id = `mutation:${method}:${path}`;
|
|
2929
3011
|
const { shape: requestShape, shapeKey: requestShapeKey } = processTypeDef(request);
|
|
2930
3012
|
const { shape: responseShape, shapeKey: responseShapeKey } = processTypeDef(response);
|
|
2931
|
-
const interpolatePath = createPathInterpolator(path);
|
|
2932
|
-
const pathParamNames = /* @__PURE__ */ new Set();
|
|
2933
|
-
const paramRegex = /\[([^\]]+)\]/g;
|
|
2934
|
-
let match;
|
|
2935
|
-
while ((match = paramRegex.exec(path)) !== null) {
|
|
2936
|
-
pathParamNames.add(match[1]);
|
|
2937
|
-
}
|
|
3013
|
+
const { interpolate: interpolatePath, pathParamNames } = createPathInterpolator(path);
|
|
2938
3014
|
const mutateFn = async (context, requestData) => {
|
|
2939
3015
|
const pathParams = {};
|
|
2940
3016
|
for (const paramName of pathParamNames) {
|