@dorval/custom 0.2.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/dist/index.d.ts +24 -0
- package/dist/index.js +143 -0
- package/package.json +23 -0
- package/src/index.test.ts +18 -0
- package/src/index.ts +166 -0
- package/tsconfig.json +12 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ClientGeneratorBuilder, ClientBuilder, ClientHeaderBuilder, ClientDependenciesBuilder, ClientFooterBuilder } from '@dorval/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate custom client builder with service registration
|
|
5
|
+
*/
|
|
6
|
+
declare const generateCustomClient: ClientBuilder;
|
|
7
|
+
/**
|
|
8
|
+
* Generate enhanced custom client header
|
|
9
|
+
*/
|
|
10
|
+
declare const generateCustomHeader: ClientHeaderBuilder;
|
|
11
|
+
/**
|
|
12
|
+
* Get custom client dependencies
|
|
13
|
+
*/
|
|
14
|
+
declare const getCustomDependencies: ClientDependenciesBuilder;
|
|
15
|
+
/**
|
|
16
|
+
* Generate custom client footer
|
|
17
|
+
*/
|
|
18
|
+
declare const generateCustomFooter: ClientFooterBuilder;
|
|
19
|
+
/**
|
|
20
|
+
* Builder factory function (following Orval pattern)
|
|
21
|
+
*/
|
|
22
|
+
declare const builder: () => () => ClientGeneratorBuilder;
|
|
23
|
+
|
|
24
|
+
export { builder, builder as default, generateCustomClient, generateCustomFooter, generateCustomHeader, getCustomDependencies };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
builder: () => builder,
|
|
24
|
+
default: () => index_default,
|
|
25
|
+
generateCustomClient: () => generateCustomClient,
|
|
26
|
+
generateCustomFooter: () => generateCustomFooter,
|
|
27
|
+
generateCustomHeader: () => generateCustomHeader,
|
|
28
|
+
getCustomDependencies: () => getCustomDependencies
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
var generateCustomImplementation = ({
|
|
32
|
+
verb,
|
|
33
|
+
route,
|
|
34
|
+
operationName,
|
|
35
|
+
response,
|
|
36
|
+
body,
|
|
37
|
+
headers,
|
|
38
|
+
queryParams,
|
|
39
|
+
pathParams,
|
|
40
|
+
props,
|
|
41
|
+
mutator,
|
|
42
|
+
override
|
|
43
|
+
}, { output }) => {
|
|
44
|
+
const httpMethod = verb.toLowerCase();
|
|
45
|
+
const hasBody = ["post", "put", "patch"].includes(httpMethod) && body;
|
|
46
|
+
const hasQueryParams = !!queryParams;
|
|
47
|
+
const hasHeaders = !!headers;
|
|
48
|
+
const hasPathParams = pathParams && pathParams.length > 0;
|
|
49
|
+
let pathConstruction = hasPathParams ? `final path = '${route}'${pathParams.map((p) => `.replaceAll('{${p.name}}', ${p.dartName}.toString())`).join("")};` : `const path = '${route}';`;
|
|
50
|
+
let queryParamsConstruction = "";
|
|
51
|
+
if (hasQueryParams) {
|
|
52
|
+
queryParamsConstruction = `
|
|
53
|
+
final queryParameters = <String, dynamic>{
|
|
54
|
+
${queryParams.map((q) => `if (${q.dartName} != null) '${q.name}': ${q.dartName},`).join("\n ")}
|
|
55
|
+
};`;
|
|
56
|
+
}
|
|
57
|
+
let headersConstruction = "";
|
|
58
|
+
if (hasHeaders) {
|
|
59
|
+
headersConstruction = `
|
|
60
|
+
final requestHeaders = <String, String>{
|
|
61
|
+
${headers.map((h) => `if (${h.dartName} != null) '${h.name}': ${h.dartName}.toString(),`).join("\n ")}
|
|
62
|
+
};`;
|
|
63
|
+
}
|
|
64
|
+
let responseHandling = "";
|
|
65
|
+
if (response.isVoid) {
|
|
66
|
+
responseHandling = "return;";
|
|
67
|
+
} else if (response.isList) {
|
|
68
|
+
responseHandling = `
|
|
69
|
+
return (response.data as List<dynamic>)
|
|
70
|
+
.map((item) => ${response.itemType}.fromJson(item as Map<String, dynamic>))
|
|
71
|
+
.toList();`;
|
|
72
|
+
} else if (response.isModel) {
|
|
73
|
+
responseHandling = `
|
|
74
|
+
return ${response.type}.fromJson(response.data as Map<String, dynamic>);`;
|
|
75
|
+
} else if (response.isPrimitive) {
|
|
76
|
+
responseHandling = `
|
|
77
|
+
return response.data as ${response.type};`;
|
|
78
|
+
} else {
|
|
79
|
+
responseHandling = `
|
|
80
|
+
return response.data;`;
|
|
81
|
+
}
|
|
82
|
+
return `
|
|
83
|
+
Future<${response.dartType}> ${operationName}(${props.map((p) => `${p.type} ${p.name}`).join(", ")}) async {
|
|
84
|
+
${pathConstruction}
|
|
85
|
+
${queryParamsConstruction}
|
|
86
|
+
${headersConstruction}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
final response = await client.${httpMethod}(
|
|
90
|
+
path,${hasBody ? `
|
|
91
|
+
data: ${body.dartName}${body.isModel ? ".toJson()" : ""},` : ""}${hasQueryParams ? `
|
|
92
|
+
queryParameters: queryParameters,` : ""}${hasHeaders ? `
|
|
93
|
+
options: Options(headers: requestHeaders),` : ""}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
${responseHandling}
|
|
97
|
+
} on DioException catch (e) {
|
|
98
|
+
throw ApiException(
|
|
99
|
+
statusCode: e.response?.statusCode,
|
|
100
|
+
message: e.message ?? 'Unknown error occurred',
|
|
101
|
+
error: e,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}`;
|
|
105
|
+
};
|
|
106
|
+
var generateCustomClient = (verbOptions, options) => {
|
|
107
|
+
const implementation = generateCustomImplementation(verbOptions, options);
|
|
108
|
+
const imports = [
|
|
109
|
+
"import 'package:dio/dio.dart';",
|
|
110
|
+
"import '../api_client.dart';",
|
|
111
|
+
"import 'api_exception.dart';",
|
|
112
|
+
"import '../models/index.dart';"
|
|
113
|
+
];
|
|
114
|
+
return { implementation, imports };
|
|
115
|
+
};
|
|
116
|
+
var generateCustomHeader = ({ title, isMutator }) => {
|
|
117
|
+
return `
|
|
118
|
+
/// ${title ? `${title} - ` : ""}Service with enhanced custom client
|
|
119
|
+
/// This service uses the enhanced API client with service registration pattern
|
|
120
|
+
`;
|
|
121
|
+
};
|
|
122
|
+
var getCustomDependencies = () => {
|
|
123
|
+
return [];
|
|
124
|
+
};
|
|
125
|
+
var generateCustomFooter = () => {
|
|
126
|
+
return "";
|
|
127
|
+
};
|
|
128
|
+
var customClientBuilder = {
|
|
129
|
+
client: generateCustomClient,
|
|
130
|
+
header: generateCustomHeader,
|
|
131
|
+
dependencies: getCustomDependencies,
|
|
132
|
+
footer: generateCustomFooter
|
|
133
|
+
};
|
|
134
|
+
var builder = () => () => customClientBuilder;
|
|
135
|
+
var index_default = builder;
|
|
136
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
137
|
+
0 && (module.exports = {
|
|
138
|
+
builder,
|
|
139
|
+
generateCustomClient,
|
|
140
|
+
generateCustomFooter,
|
|
141
|
+
generateCustomHeader,
|
|
142
|
+
getCustomDependencies
|
|
143
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dorval/custom",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Custom client implementation for Dorval",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup src/index.ts --dts",
|
|
9
|
+
"dev": "tsup src/index.ts --dts --watch",
|
|
10
|
+
"test": "vitest run"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@dorval/core": "*"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"tsup": "^8.0.1",
|
|
17
|
+
"typescript": "^5.3.3",
|
|
18
|
+
"vitest": "^0.6.3"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"@dorval/core": "*"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateCustomClient } from './index';
|
|
3
|
+
|
|
4
|
+
describe('@dorval/custom', () => {
|
|
5
|
+
it('should export generateCustomClient function', () => {
|
|
6
|
+
expect(generateCustomClient).toBeDefined();
|
|
7
|
+
expect(typeof generateCustomClient).toBe('function');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should be a client builder function', () => {
|
|
11
|
+
// generateCustomClient is a ClientGeneratorBuilder function
|
|
12
|
+
// It returns a builder that will be called by the core generator
|
|
13
|
+
const builder = generateCustomClient;
|
|
14
|
+
expect(builder).toBeDefined();
|
|
15
|
+
expect(typeof builder).toBe('function');
|
|
16
|
+
expect(builder.name).toBe('generateCustomClient');
|
|
17
|
+
});
|
|
18
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientBuilder,
|
|
3
|
+
ClientGeneratorBuilder,
|
|
4
|
+
ClientHeaderBuilder,
|
|
5
|
+
ClientDependenciesBuilder,
|
|
6
|
+
ClientFooterBuilder,
|
|
7
|
+
GeneratorVerbOptions,
|
|
8
|
+
GeneratorOptions,
|
|
9
|
+
GeneratorDependency,
|
|
10
|
+
} from '@dorval/core';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generate enhanced custom client implementation
|
|
14
|
+
* This creates a client with service registration pattern like Supabase/Firebase
|
|
15
|
+
*/
|
|
16
|
+
const generateCustomImplementation = (
|
|
17
|
+
{
|
|
18
|
+
verb,
|
|
19
|
+
route,
|
|
20
|
+
operationName,
|
|
21
|
+
response,
|
|
22
|
+
body,
|
|
23
|
+
headers,
|
|
24
|
+
queryParams,
|
|
25
|
+
pathParams,
|
|
26
|
+
props,
|
|
27
|
+
mutator,
|
|
28
|
+
override,
|
|
29
|
+
}: GeneratorVerbOptions,
|
|
30
|
+
{ output }: GeneratorOptions,
|
|
31
|
+
): string => {
|
|
32
|
+
const httpMethod = verb.toLowerCase();
|
|
33
|
+
const hasBody = ['post', 'put', 'patch'].includes(httpMethod) && body;
|
|
34
|
+
const hasQueryParams = !!queryParams;
|
|
35
|
+
const hasHeaders = !!headers;
|
|
36
|
+
const hasPathParams = pathParams && pathParams.length > 0;
|
|
37
|
+
|
|
38
|
+
// Build path with parameters
|
|
39
|
+
let pathConstruction = hasPathParams
|
|
40
|
+
? `final path = '${route}'${pathParams.map((p: any) => `.replaceAll('{${p.name}}', ${p.dartName}.toString())`).join('')};`
|
|
41
|
+
: `const path = '${route}';`;
|
|
42
|
+
|
|
43
|
+
// Build query parameters
|
|
44
|
+
let queryParamsConstruction = '';
|
|
45
|
+
if (hasQueryParams) {
|
|
46
|
+
queryParamsConstruction = `
|
|
47
|
+
final queryParameters = <String, dynamic>{
|
|
48
|
+
${queryParams.map((q: any) => `if (${q.dartName} != null) '${q.name}': ${q.dartName},`).join('\n ')}
|
|
49
|
+
};`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Build headers
|
|
53
|
+
let headersConstruction = '';
|
|
54
|
+
if (hasHeaders) {
|
|
55
|
+
headersConstruction = `
|
|
56
|
+
final requestHeaders = <String, String>{
|
|
57
|
+
${headers.map((h: any) => `if (${h.dartName} != null) '${h.name}': ${h.dartName}.toString(),`).join('\n ')}
|
|
58
|
+
};`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Build response handling
|
|
62
|
+
let responseHandling = '';
|
|
63
|
+
if (response.isVoid) {
|
|
64
|
+
responseHandling = 'return;';
|
|
65
|
+
} else if (response.isList) {
|
|
66
|
+
responseHandling = `
|
|
67
|
+
return (response.data as List<dynamic>)
|
|
68
|
+
.map((item) => ${response.itemType}.fromJson(item as Map<String, dynamic>))
|
|
69
|
+
.toList();`;
|
|
70
|
+
} else if (response.isModel) {
|
|
71
|
+
responseHandling = `
|
|
72
|
+
return ${response.type}.fromJson(response.data as Map<String, dynamic>);`;
|
|
73
|
+
} else if (response.isPrimitive) {
|
|
74
|
+
responseHandling = `
|
|
75
|
+
return response.data as ${response.type};`;
|
|
76
|
+
} else {
|
|
77
|
+
responseHandling = `
|
|
78
|
+
return response.data;`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Build complete implementation using the enhanced client
|
|
82
|
+
return `
|
|
83
|
+
Future<${response.dartType}> ${operationName}(${props.map((p: any) => `${p.type} ${p.name}`).join(', ')}) async {
|
|
84
|
+
${pathConstruction}
|
|
85
|
+
${queryParamsConstruction}
|
|
86
|
+
${headersConstruction}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
final response = await client.${httpMethod}(
|
|
90
|
+
path,${hasBody ? `
|
|
91
|
+
data: ${body.dartName}${body.isModel ? '.toJson()' : ''},` : ''}${hasQueryParams ? `
|
|
92
|
+
queryParameters: queryParameters,` : ''}${hasHeaders ? `
|
|
93
|
+
options: Options(headers: requestHeaders),` : ''}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
${responseHandling}
|
|
97
|
+
} on DioException catch (e) {
|
|
98
|
+
throw ApiException(
|
|
99
|
+
statusCode: e.response?.statusCode,
|
|
100
|
+
message: e.message ?? 'Unknown error occurred',
|
|
101
|
+
error: e,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}`;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate custom client builder with service registration
|
|
109
|
+
*/
|
|
110
|
+
export const generateCustomClient: ClientBuilder = (
|
|
111
|
+
verbOptions: GeneratorVerbOptions,
|
|
112
|
+
options: GeneratorOptions,
|
|
113
|
+
) => {
|
|
114
|
+
const implementation = generateCustomImplementation(verbOptions, options);
|
|
115
|
+
|
|
116
|
+
const imports = [
|
|
117
|
+
"import 'package:dio/dio.dart';",
|
|
118
|
+
"import '../api_client.dart';",
|
|
119
|
+
"import 'api_exception.dart';",
|
|
120
|
+
"import '../models/index.dart';",
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
return { implementation, imports };
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Generate enhanced custom client header
|
|
128
|
+
*/
|
|
129
|
+
export const generateCustomHeader: ClientHeaderBuilder = ({ title, isMutator }) => {
|
|
130
|
+
return `
|
|
131
|
+
/// ${title ? `${title} - ` : ''}Service with enhanced custom client
|
|
132
|
+
/// This service uses the enhanced API client with service registration pattern
|
|
133
|
+
`;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Get custom client dependencies
|
|
138
|
+
*/
|
|
139
|
+
export const getCustomDependencies: ClientDependenciesBuilder = () => {
|
|
140
|
+
return []; // Dependencies are managed by the user's custom client
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Generate custom client footer
|
|
145
|
+
*/
|
|
146
|
+
export const generateCustomFooter: ClientFooterBuilder = () => {
|
|
147
|
+
return '';
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Custom client builder configuration
|
|
153
|
+
*/
|
|
154
|
+
const customClientBuilder: ClientGeneratorBuilder = {
|
|
155
|
+
client: generateCustomClient,
|
|
156
|
+
header: generateCustomHeader,
|
|
157
|
+
dependencies: getCustomDependencies,
|
|
158
|
+
footer: generateCustomFooter,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Builder factory function (following Orval pattern)
|
|
163
|
+
*/
|
|
164
|
+
export const builder = () => () => customClientBuilder;
|
|
165
|
+
|
|
166
|
+
export default builder;
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist",
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"composite": false,
|
|
7
|
+
"declaration": true
|
|
8
|
+
},
|
|
9
|
+
"include": ["src/**/*"],
|
|
10
|
+
"exclude": ["node_modules", "dist", "**/*.test.ts"],
|
|
11
|
+
"references": []
|
|
12
|
+
}
|