@spoosh/plugin-transform 0.1.4 → 0.3.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 +5 -7
- package/dist/index.d.mts +6 -37
- package/dist/index.d.ts +6 -37
- package/dist/index.js +1 -49
- package/dist/index.mjs +1 -49
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -20,14 +20,14 @@ import { transformPlugin } from "@spoosh/plugin-transform";
|
|
|
20
20
|
|
|
21
21
|
const client = new Spoosh<ApiSchema, Error>("/api").use([transformPlugin()]);
|
|
22
22
|
|
|
23
|
-
const { data } = useRead((api) => api.
|
|
23
|
+
const { data } = useRead((api) => api("posts").GET({ query: { page: 1 } }), {
|
|
24
24
|
transform: {
|
|
25
25
|
query: (q) => ({ ...q, limit: 10 }),
|
|
26
26
|
},
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
// Transform body in useWrite
|
|
30
|
-
const { trigger } = useWrite((api) => api
|
|
30
|
+
const { trigger } = useWrite((api) => api("posts").POST);
|
|
31
31
|
|
|
32
32
|
trigger({
|
|
33
33
|
body: { title: "New Post" },
|
|
@@ -42,7 +42,7 @@ trigger({
|
|
|
42
42
|
Response transforms produce a separate `transformedData` field while preserving the original `data`:
|
|
43
43
|
|
|
44
44
|
```typescript
|
|
45
|
-
const { data, transformedData } = useRead((api) => api.
|
|
45
|
+
const { data, transformedData } = useRead((api) => api("posts").GET(), {
|
|
46
46
|
transform: {
|
|
47
47
|
response: (posts) => ({
|
|
48
48
|
count: posts.length,
|
|
@@ -61,9 +61,7 @@ const { data, transformedData } = useRead((api) => api.posts.$get(), {
|
|
|
61
61
|
The plugin supports transformation of:
|
|
62
62
|
|
|
63
63
|
- **query** - Transform query parameters before request
|
|
64
|
-
- **body** - Transform
|
|
65
|
-
- **formData** - Transform form data before request
|
|
66
|
-
- **urlEncoded** - Transform URL-encoded data before request
|
|
64
|
+
- **body** - Transform request body before request (files auto-detected)
|
|
67
65
|
- **response** - Transform response data after request (produces `transformedData`)
|
|
68
66
|
|
|
69
67
|
## Features
|
|
@@ -84,7 +82,7 @@ type TransformedPost = {
|
|
|
84
82
|
postId: number;
|
|
85
83
|
};
|
|
86
84
|
|
|
87
|
-
const { transformedData } = useWrite((api) => api
|
|
85
|
+
const { transformedData } = useWrite((api) => api("posts").POST);
|
|
88
86
|
|
|
89
87
|
// Type assertion required
|
|
90
88
|
const typed = transformedData as TransformedPost | undefined;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
import { ResolverContext, SpooshPlugin } from '@spoosh/core';
|
|
2
2
|
|
|
3
3
|
type MaybePromise<T> = T | Promise<T>;
|
|
4
|
-
/** Suggests keys from TIn but all optional and any type, plus allows extra keys */
|
|
5
|
-
type FlexibleOutput<TIn> = {
|
|
6
|
-
[K in keyof TIn]?: unknown;
|
|
7
|
-
} & Record<string, unknown>;
|
|
8
|
-
type QueryTransformer<TIn = unknown> = (query: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
9
|
-
type BodyTransformer<TIn = unknown> = (body: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
10
|
-
type FormDataTransformer<TIn = unknown> = (formData: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
11
|
-
type UrlEncodedTransformer<TIn = unknown> = (urlEncoded: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
12
4
|
type ResponseTransformer<TIn = unknown, TOut = unknown> = (response: TIn) => MaybePromise<TOut>;
|
|
13
5
|
interface TransformConfig {
|
|
14
|
-
/** Transform query parameters before request. Return undefined to remove query. */
|
|
15
|
-
query?: QueryTransformer;
|
|
16
|
-
/** Transform JSON body before request. Return undefined to remove body. */
|
|
17
|
-
body?: BodyTransformer;
|
|
18
|
-
/** Transform form data before request. Return undefined to remove formData. */
|
|
19
|
-
formData?: FormDataTransformer;
|
|
20
|
-
/** Transform URL-encoded data before request. Return undefined to remove urlEncoded. */
|
|
21
|
-
urlEncoded?: UrlEncodedTransformer;
|
|
22
6
|
/** Transform response data after request. Returns transformedData (can be any type). */
|
|
23
7
|
response?: ResponseTransformer<unknown, unknown>;
|
|
24
8
|
}
|
|
@@ -60,14 +44,6 @@ type TransformResultField<TOptions> = [
|
|
|
60
44
|
transformedData: InferTransformedData<TOptions> | undefined;
|
|
61
45
|
};
|
|
62
46
|
type ResolvedTransformConfig<TContext extends ResolverContext> = {
|
|
63
|
-
/** Transform query parameters before request. Return undefined to remove query. */
|
|
64
|
-
query?: QueryTransformer<TContext["input"]["query"]>;
|
|
65
|
-
/** Transform JSON body before request. Return undefined to remove body. */
|
|
66
|
-
body?: BodyTransformer<TContext["input"]["body"]>;
|
|
67
|
-
/** Transform form data before request. Return undefined to remove formData. */
|
|
68
|
-
formData?: FormDataTransformer<TContext["input"]["formData"]>;
|
|
69
|
-
/** Transform URL-encoded data before request. Return undefined to remove urlEncoded. */
|
|
70
|
-
urlEncoded?: UrlEncodedTransformer<TContext["input"]["urlEncoded"]>;
|
|
71
47
|
/** Transform response data after request. Returns transformedData (can be any type). */
|
|
72
48
|
response?: ResponseTransformer<TContext["data"], unknown>;
|
|
73
49
|
};
|
|
@@ -85,10 +61,9 @@ declare module "@spoosh/core" {
|
|
|
85
61
|
}
|
|
86
62
|
|
|
87
63
|
/**
|
|
88
|
-
* Enables data transformation
|
|
64
|
+
* Enables response data transformation.
|
|
89
65
|
*
|
|
90
|
-
* Supports both sync and async transformer functions.
|
|
91
|
-
* before transformation to prevent mutation of original objects.
|
|
66
|
+
* Supports both sync and async transformer functions.
|
|
92
67
|
*
|
|
93
68
|
* All transforms are per-request for full type inference.
|
|
94
69
|
*
|
|
@@ -105,11 +80,10 @@ declare module "@spoosh/core" {
|
|
|
105
80
|
* ]);
|
|
106
81
|
*
|
|
107
82
|
* // Per-request transforms with full type inference
|
|
108
|
-
* const { data,
|
|
83
|
+
* const { data, meta } = useRead(
|
|
109
84
|
* (api) => api.posts.$get(),
|
|
110
85
|
* {
|
|
111
86
|
* transform: {
|
|
112
|
-
* query: (q) => ({ ...q, timestamp: Date.now() }),
|
|
113
87
|
* response: (posts) => ({
|
|
114
88
|
* count: posts.length,
|
|
115
89
|
* hasMore: posts.length >= 10,
|
|
@@ -118,13 +92,8 @@ declare module "@spoosh/core" {
|
|
|
118
92
|
* }
|
|
119
93
|
* );
|
|
120
94
|
*
|
|
121
|
-
* //
|
|
122
|
-
*
|
|
123
|
-
* body: { name: "test" },
|
|
124
|
-
* transform: {
|
|
125
|
-
* body: (b) => ({ ...b, createdAt: Date.now() }),
|
|
126
|
-
* },
|
|
127
|
-
* });
|
|
95
|
+
* // Access transformed data via meta
|
|
96
|
+
* console.log(meta.transformedData);
|
|
128
97
|
* ```
|
|
129
98
|
*/
|
|
130
99
|
declare function transformPlugin(): SpooshPlugin<{
|
|
@@ -135,4 +104,4 @@ declare function transformPlugin(): SpooshPlugin<{
|
|
|
135
104
|
writeResult: TransformWriteResult;
|
|
136
105
|
}>;
|
|
137
106
|
|
|
138
|
-
export { type
|
|
107
|
+
export { type InferTransformedData, type MaybePromise, type ResponseTransformer, type TransformConfig, type TransformInfiniteReadOptions, type TransformOptions, type TransformPluginConfig, type TransformReadOptions, type TransformReadResult, type TransformResultField, type TransformWriteOptions, type TransformWriteResult, transformPlugin };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
import { ResolverContext, SpooshPlugin } from '@spoosh/core';
|
|
2
2
|
|
|
3
3
|
type MaybePromise<T> = T | Promise<T>;
|
|
4
|
-
/** Suggests keys from TIn but all optional and any type, plus allows extra keys */
|
|
5
|
-
type FlexibleOutput<TIn> = {
|
|
6
|
-
[K in keyof TIn]?: unknown;
|
|
7
|
-
} & Record<string, unknown>;
|
|
8
|
-
type QueryTransformer<TIn = unknown> = (query: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
9
|
-
type BodyTransformer<TIn = unknown> = (body: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
10
|
-
type FormDataTransformer<TIn = unknown> = (formData: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
11
|
-
type UrlEncodedTransformer<TIn = unknown> = (urlEncoded: TIn) => MaybePromise<FlexibleOutput<TIn> | undefined>;
|
|
12
4
|
type ResponseTransformer<TIn = unknown, TOut = unknown> = (response: TIn) => MaybePromise<TOut>;
|
|
13
5
|
interface TransformConfig {
|
|
14
|
-
/** Transform query parameters before request. Return undefined to remove query. */
|
|
15
|
-
query?: QueryTransformer;
|
|
16
|
-
/** Transform JSON body before request. Return undefined to remove body. */
|
|
17
|
-
body?: BodyTransformer;
|
|
18
|
-
/** Transform form data before request. Return undefined to remove formData. */
|
|
19
|
-
formData?: FormDataTransformer;
|
|
20
|
-
/** Transform URL-encoded data before request. Return undefined to remove urlEncoded. */
|
|
21
|
-
urlEncoded?: UrlEncodedTransformer;
|
|
22
6
|
/** Transform response data after request. Returns transformedData (can be any type). */
|
|
23
7
|
response?: ResponseTransformer<unknown, unknown>;
|
|
24
8
|
}
|
|
@@ -60,14 +44,6 @@ type TransformResultField<TOptions> = [
|
|
|
60
44
|
transformedData: InferTransformedData<TOptions> | undefined;
|
|
61
45
|
};
|
|
62
46
|
type ResolvedTransformConfig<TContext extends ResolverContext> = {
|
|
63
|
-
/** Transform query parameters before request. Return undefined to remove query. */
|
|
64
|
-
query?: QueryTransformer<TContext["input"]["query"]>;
|
|
65
|
-
/** Transform JSON body before request. Return undefined to remove body. */
|
|
66
|
-
body?: BodyTransformer<TContext["input"]["body"]>;
|
|
67
|
-
/** Transform form data before request. Return undefined to remove formData. */
|
|
68
|
-
formData?: FormDataTransformer<TContext["input"]["formData"]>;
|
|
69
|
-
/** Transform URL-encoded data before request. Return undefined to remove urlEncoded. */
|
|
70
|
-
urlEncoded?: UrlEncodedTransformer<TContext["input"]["urlEncoded"]>;
|
|
71
47
|
/** Transform response data after request. Returns transformedData (can be any type). */
|
|
72
48
|
response?: ResponseTransformer<TContext["data"], unknown>;
|
|
73
49
|
};
|
|
@@ -85,10 +61,9 @@ declare module "@spoosh/core" {
|
|
|
85
61
|
}
|
|
86
62
|
|
|
87
63
|
/**
|
|
88
|
-
* Enables data transformation
|
|
64
|
+
* Enables response data transformation.
|
|
89
65
|
*
|
|
90
|
-
* Supports both sync and async transformer functions.
|
|
91
|
-
* before transformation to prevent mutation of original objects.
|
|
66
|
+
* Supports both sync and async transformer functions.
|
|
92
67
|
*
|
|
93
68
|
* All transforms are per-request for full type inference.
|
|
94
69
|
*
|
|
@@ -105,11 +80,10 @@ declare module "@spoosh/core" {
|
|
|
105
80
|
* ]);
|
|
106
81
|
*
|
|
107
82
|
* // Per-request transforms with full type inference
|
|
108
|
-
* const { data,
|
|
83
|
+
* const { data, meta } = useRead(
|
|
109
84
|
* (api) => api.posts.$get(),
|
|
110
85
|
* {
|
|
111
86
|
* transform: {
|
|
112
|
-
* query: (q) => ({ ...q, timestamp: Date.now() }),
|
|
113
87
|
* response: (posts) => ({
|
|
114
88
|
* count: posts.length,
|
|
115
89
|
* hasMore: posts.length >= 10,
|
|
@@ -118,13 +92,8 @@ declare module "@spoosh/core" {
|
|
|
118
92
|
* }
|
|
119
93
|
* );
|
|
120
94
|
*
|
|
121
|
-
* //
|
|
122
|
-
*
|
|
123
|
-
* body: { name: "test" },
|
|
124
|
-
* transform: {
|
|
125
|
-
* body: (b) => ({ ...b, createdAt: Date.now() }),
|
|
126
|
-
* },
|
|
127
|
-
* });
|
|
95
|
+
* // Access transformed data via meta
|
|
96
|
+
* console.log(meta.transformedData);
|
|
128
97
|
* ```
|
|
129
98
|
*/
|
|
130
99
|
declare function transformPlugin(): SpooshPlugin<{
|
|
@@ -135,4 +104,4 @@ declare function transformPlugin(): SpooshPlugin<{
|
|
|
135
104
|
writeResult: TransformWriteResult;
|
|
136
105
|
}>;
|
|
137
106
|
|
|
138
|
-
export { type
|
|
107
|
+
export { type InferTransformedData, type MaybePromise, type ResponseTransformer, type TransformConfig, type TransformInfiniteReadOptions, type TransformOptions, type TransformPluginConfig, type TransformReadOptions, type TransformReadResult, type TransformResultField, type TransformWriteOptions, type TransformWriteResult, transformPlugin };
|
package/dist/index.js
CHANGED
|
@@ -25,64 +25,16 @@ __export(src_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(src_exports);
|
|
26
26
|
|
|
27
27
|
// src/plugin.ts
|
|
28
|
-
function deepClone(value) {
|
|
29
|
-
try {
|
|
30
|
-
if (value === void 0 || value === null) {
|
|
31
|
-
return value;
|
|
32
|
-
}
|
|
33
|
-
return structuredClone(value);
|
|
34
|
-
} catch {
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
28
|
function transformPlugin() {
|
|
39
29
|
return {
|
|
40
30
|
name: "spoosh:transform",
|
|
41
31
|
operations: ["read", "write", "infiniteRead"],
|
|
42
|
-
middleware: async (context, next) => {
|
|
43
|
-
const { requestOptions } = context;
|
|
44
|
-
const pluginOptions = context.pluginOptions;
|
|
45
|
-
const transform = pluginOptions?.transform;
|
|
46
|
-
if (transform?.query && requestOptions.query) {
|
|
47
|
-
const cloned = deepClone(requestOptions.query);
|
|
48
|
-
const transformed = await transform.query(cloned);
|
|
49
|
-
context.requestOptions = {
|
|
50
|
-
...context.requestOptions,
|
|
51
|
-
query: transformed
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
if (transform?.body && requestOptions.body) {
|
|
55
|
-
const cloned = deepClone(requestOptions.body);
|
|
56
|
-
const transformed = await transform.body(cloned);
|
|
57
|
-
context.requestOptions = {
|
|
58
|
-
...context.requestOptions,
|
|
59
|
-
body: transformed
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
if (transform?.formData && requestOptions.formData) {
|
|
63
|
-
const cloned = deepClone(requestOptions.formData);
|
|
64
|
-
const transformed = await transform.formData(cloned);
|
|
65
|
-
context.requestOptions = {
|
|
66
|
-
...context.requestOptions,
|
|
67
|
-
formData: transformed
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
if (transform?.urlEncoded && requestOptions.urlEncoded) {
|
|
71
|
-
const cloned = deepClone(requestOptions.urlEncoded);
|
|
72
|
-
const transformed = await transform.urlEncoded(cloned);
|
|
73
|
-
context.requestOptions = {
|
|
74
|
-
...context.requestOptions,
|
|
75
|
-
urlEncoded: transformed
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
return next();
|
|
79
|
-
},
|
|
80
32
|
onResponse: async (context, response) => {
|
|
81
33
|
const pluginOptions = context.pluginOptions;
|
|
82
34
|
const responseTransformer = pluginOptions?.transform?.response;
|
|
83
35
|
if (responseTransformer && response.data !== void 0) {
|
|
84
36
|
const transformedData = await responseTransformer(response.data);
|
|
85
|
-
context.stateManager.
|
|
37
|
+
context.stateManager.setMeta(context.queryKey, {
|
|
86
38
|
transformedData
|
|
87
39
|
});
|
|
88
40
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,62 +1,14 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
|
-
function deepClone(value) {
|
|
3
|
-
try {
|
|
4
|
-
if (value === void 0 || value === null) {
|
|
5
|
-
return value;
|
|
6
|
-
}
|
|
7
|
-
return structuredClone(value);
|
|
8
|
-
} catch {
|
|
9
|
-
return value;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
2
|
function transformPlugin() {
|
|
13
3
|
return {
|
|
14
4
|
name: "spoosh:transform",
|
|
15
5
|
operations: ["read", "write", "infiniteRead"],
|
|
16
|
-
middleware: async (context, next) => {
|
|
17
|
-
const { requestOptions } = context;
|
|
18
|
-
const pluginOptions = context.pluginOptions;
|
|
19
|
-
const transform = pluginOptions?.transform;
|
|
20
|
-
if (transform?.query && requestOptions.query) {
|
|
21
|
-
const cloned = deepClone(requestOptions.query);
|
|
22
|
-
const transformed = await transform.query(cloned);
|
|
23
|
-
context.requestOptions = {
|
|
24
|
-
...context.requestOptions,
|
|
25
|
-
query: transformed
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
if (transform?.body && requestOptions.body) {
|
|
29
|
-
const cloned = deepClone(requestOptions.body);
|
|
30
|
-
const transformed = await transform.body(cloned);
|
|
31
|
-
context.requestOptions = {
|
|
32
|
-
...context.requestOptions,
|
|
33
|
-
body: transformed
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
if (transform?.formData && requestOptions.formData) {
|
|
37
|
-
const cloned = deepClone(requestOptions.formData);
|
|
38
|
-
const transformed = await transform.formData(cloned);
|
|
39
|
-
context.requestOptions = {
|
|
40
|
-
...context.requestOptions,
|
|
41
|
-
formData: transformed
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
if (transform?.urlEncoded && requestOptions.urlEncoded) {
|
|
45
|
-
const cloned = deepClone(requestOptions.urlEncoded);
|
|
46
|
-
const transformed = await transform.urlEncoded(cloned);
|
|
47
|
-
context.requestOptions = {
|
|
48
|
-
...context.requestOptions,
|
|
49
|
-
urlEncoded: transformed
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
return next();
|
|
53
|
-
},
|
|
54
6
|
onResponse: async (context, response) => {
|
|
55
7
|
const pluginOptions = context.pluginOptions;
|
|
56
8
|
const responseTransformer = pluginOptions?.transform?.response;
|
|
57
9
|
if (responseTransformer && response.data !== void 0) {
|
|
58
10
|
const transformedData = await responseTransformer(response.data);
|
|
59
|
-
context.stateManager.
|
|
11
|
+
context.stateManager.setMeta(context.queryKey, {
|
|
60
12
|
transformedData
|
|
61
13
|
});
|
|
62
14
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spoosh/plugin-transform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Data transformation plugin for Spoosh with sync/async support",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"@spoosh/core": ">=0.
|
|
36
|
+
"@spoosh/core": ">=0.6.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@spoosh/core": "0.
|
|
39
|
+
"@spoosh/core": "0.6.0",
|
|
40
40
|
"@spoosh/test-utils": "0.1.5"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|