@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 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.posts.$get({ query: { page: 1 } }), {
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.posts.$post);
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.posts.$get(), {
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 JSON body before request
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.posts.$post);
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 for query, body, formData, urlEncoded, and response.
64
+ * Enables response data transformation.
89
65
  *
90
- * Supports both sync and async transformer functions. All data is deep-cloned
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, transformedData } = useRead(
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
- * // useWrite with body transform
122
- * trigger({
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 BodyTransformer, type FormDataTransformer, type InferTransformedData, type MaybePromise, type QueryTransformer, type ResponseTransformer, type TransformConfig, type TransformInfiniteReadOptions, type TransformOptions, type TransformPluginConfig, type TransformReadOptions, type TransformReadResult, type TransformResultField, type TransformWriteOptions, type TransformWriteResult, type UrlEncodedTransformer, transformPlugin };
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 for query, body, formData, urlEncoded, and response.
64
+ * Enables response data transformation.
89
65
  *
90
- * Supports both sync and async transformer functions. All data is deep-cloned
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, transformedData } = useRead(
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
- * // useWrite with body transform
122
- * trigger({
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 BodyTransformer, type FormDataTransformer, type InferTransformedData, type MaybePromise, type QueryTransformer, type ResponseTransformer, type TransformConfig, type TransformInfiniteReadOptions, type TransformOptions, type TransformPluginConfig, type TransformReadOptions, type TransformReadResult, type TransformResultField, type TransformWriteOptions, type TransformWriteResult, type UrlEncodedTransformer, transformPlugin };
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.setPluginResult(context.queryKey, {
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.setPluginResult(context.queryKey, {
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.1.4",
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.4.0"
36
+ "@spoosh/core": ">=0.6.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@spoosh/core": "0.4.2",
39
+ "@spoosh/core": "0.6.0",
40
40
  "@spoosh/test-utils": "0.1.5"
41
41
  },
42
42
  "scripts": {