@qualisero/openapi-endpoint 0.2.3

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 ADDED
@@ -0,0 +1,62 @@
1
+ # Vue OpenAPI Query
2
+
3
+ [![npm version](https://badge.fury.io/js/@qualisero%2Fopenapi-endpoint.svg)](https://badge.fury.io/js/@qualisero%2Fopenapi-endpoint)
4
+ [![CI](https://github.com/qualisero/openapi-endpoint/workflows/CI/badge.svg)](https://github.com/qualisero/openapi-endpoint/actions/workflows/ci.yml)
5
+ [![codecov](https://codecov.io/gh/qualisero/openapi-endpoint/branch/main/graph/badge.svg)](https://codecov.io/gh/qualisero/openapi-endpoint)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![npm bundle size](https://img.shields.io/bundlephobia/minzip/@qualisero/openapi-endpoint)](https://bundlephobia.com/package/@qualisero/openapi-endpoint)
8
+
9
+ Type-safe OpenAPI integration for Vue Query (TanStack Query).
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @qualisero/openapi-endpoint
15
+ ```
16
+
17
+ ## Code Generation
18
+
19
+ This package includes a command-line tool to generate TypeScript types and operation definitions from your OpenAPI specification:
20
+
21
+ ```bash
22
+ # Generate from local file
23
+ npx @qualisero/openapi-endpoint ./api/openapi.json ./src/generated
24
+
25
+ # Generate from remote URL
26
+ npx @qualisero/openapi-endpoint https://api.example.com/openapi.json ./src/api
27
+ ```
28
+
29
+ This will generate two files in your specified output directory:
30
+
31
+ - `openapi-types.ts` - TypeScript type definitions for your API
32
+ - `api-operations.ts` - Operation IDs and metadata for use with this library
33
+
34
+ ## Usage
35
+
36
+ ### 1. Initialize the package
37
+
38
+ ```typescript
39
+ // api/init.ts
40
+ import { useOpenApi } from '@qualisero/openapi-endpoint'
41
+ import { QueryClient } from '@tanstack/vue-query'
42
+ import axios from 'axios'
43
+
44
+ // Import your generated OpenAPI types and operations
45
+ import type { operations } from './generated/openapi-types'
46
+ import { OperationId, OPERATION_INFO } from './generated/api-operations'
47
+
48
+ // Create axios instance
49
+ const axiosInstance = axios.create({
50
+ baseURL: 'https://api.example.com',
51
+ })
52
+
53
+ // Properly type the operations for the library
54
+ const operationInfoDict = OPERATION_INFO
55
+ type OperationsWithInfo = operations & typeof operationInfoDict
56
+
57
+ // Initialize the package
58
+ const api = useOpenApi({
59
+ operations: operationInfoDict as OperationsWithInfo,
60
+ axios: axiosInstance,
61
+ })
62
+ ```
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url'
4
+ import { dirname, join } from 'path'
5
+ import { spawn } from 'child_process'
6
+
7
+ const __filename = fileURLToPath(import.meta.url)
8
+ const __dirname = dirname(__filename)
9
+
10
+ // Execute the TypeScript CLI script
11
+ const scriptPath = join(__dirname, '..', 'dist', 'cli.js')
12
+
13
+ const child = spawn('node', [scriptPath, ...process.argv.slice(2)], {
14
+ stdio: 'inherit',
15
+ shell: false,
16
+ })
17
+
18
+ child.on('exit', (code) => {
19
+ process.exit(code || 0)
20
+ })
21
+
22
+ child.on('error', (error) => {
23
+ console.error('Error executing CLI:', error)
24
+ process.exit(1)
25
+ })
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,176 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { exec } from 'child_process';
4
+ import { promisify } from 'util';
5
+ const execAsync = promisify(exec);
6
+ async function fetchOpenAPISpec(input) {
7
+ // Check if input is a URL
8
+ if (input.startsWith('http://') || input.startsWith('https://')) {
9
+ console.log(`📡 Fetching OpenAPI spec from URL: ${input}`);
10
+ // Use node's built-in fetch (available in Node 18+)
11
+ try {
12
+ const response = await fetch(input);
13
+ if (!response.ok) {
14
+ throw new Error(`HTTP error! status: ${response.status}`);
15
+ }
16
+ const content = await response.text();
17
+ return content;
18
+ }
19
+ catch (error) {
20
+ throw new Error(`Failed to fetch OpenAPI spec from URL: ${error}`);
21
+ }
22
+ }
23
+ else {
24
+ // Local file
25
+ console.log(`📂 Reading OpenAPI spec from file: ${input}`);
26
+ if (!fs.existsSync(input)) {
27
+ throw new Error(`File not found: ${input}`);
28
+ }
29
+ return fs.readFileSync(input, 'utf8');
30
+ }
31
+ }
32
+ async function generateTypes(openapiContent, outputDir) {
33
+ console.log('🔨 Generating TypeScript types using openapi-typescript...');
34
+ // Write the OpenAPI spec to a temporary file
35
+ const tempSpecPath = path.join(outputDir, 'temp-openapi.json');
36
+ fs.writeFileSync(tempSpecPath, openapiContent);
37
+ try {
38
+ // Run openapi-typescript
39
+ const typesOutputPath = path.join(outputDir, 'openapi-types.ts');
40
+ const command = `npx openapi-typescript "${tempSpecPath}" --output "${typesOutputPath}"`;
41
+ await execAsync(command);
42
+ console.log(`✅ Generated types file: ${typesOutputPath}`);
43
+ }
44
+ finally {
45
+ // Clean up temp file
46
+ if (fs.existsSync(tempSpecPath)) {
47
+ fs.unlinkSync(tempSpecPath);
48
+ }
49
+ }
50
+ }
51
+ function parseOperationsFromSpec(openapiContent) {
52
+ const openApiSpec = JSON.parse(openapiContent);
53
+ if (!openApiSpec.paths) {
54
+ throw new Error('Invalid OpenAPI spec: missing paths');
55
+ }
56
+ const operationIds = [];
57
+ const operationInfoMap = {};
58
+ // Iterate through all paths and methods to extract operationIds
59
+ Object.entries(openApiSpec.paths).forEach(([pathUrl, pathItem]) => {
60
+ Object.entries(pathItem).forEach(([method, operation]) => {
61
+ // Skip non-HTTP methods (like parameters)
62
+ const httpMethods = ['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'trace'];
63
+ if (!httpMethods.includes(method.toLowerCase())) {
64
+ return;
65
+ }
66
+ const op = operation;
67
+ if (op.operationId) {
68
+ operationIds.push(op.operationId);
69
+ operationInfoMap[op.operationId] = {
70
+ path: pathUrl,
71
+ method: method.toUpperCase(),
72
+ };
73
+ }
74
+ });
75
+ });
76
+ // Sort operationIds for consistent output
77
+ operationIds.sort();
78
+ return { operationIds, operationInfoMap };
79
+ }
80
+ function generateApiOperationsContent(operationIds, operationInfoMap) {
81
+ // Generate enum-like object
82
+ const enumContent = operationIds.map((id) => ` ${id}: '${id}',`).join('\n');
83
+ // Generate dictionary
84
+ const dictionaryContent = operationIds
85
+ .map((id) => {
86
+ const info = operationInfoMap[id];
87
+ return ` ${id}: {\n path: '${info.path}',\n method: HttpMethod.${info.method},\n },`;
88
+ })
89
+ .join('\n');
90
+ return `// Auto-generated from OpenAPI specification
91
+ // Do not edit this file manually
92
+
93
+ export enum HttpMethod {
94
+ GET = 'GET',
95
+ POST = 'POST',
96
+ PUT = 'PUT',
97
+ PATCH = 'PATCH',
98
+ DELETE = 'DELETE',
99
+ HEAD = 'HEAD',
100
+ OPTIONS = 'OPTIONS',
101
+ TRACE = 'TRACE',
102
+ }
103
+
104
+ export const OperationId = {
105
+ ${enumContent}
106
+ } as const
107
+
108
+ export type OperationId = (typeof OperationId)[keyof typeof OperationId]
109
+
110
+ export const OPERATION_INFO = {
111
+ ${dictionaryContent}
112
+ } as const
113
+ `;
114
+ }
115
+ async function generateApiOperations(openapiContent, outputDir) {
116
+ console.log('🔨 Generating api-operations.ts file...');
117
+ const { operationIds, operationInfoMap } = parseOperationsFromSpec(openapiContent);
118
+ // Generate TypeScript content
119
+ const tsContent = generateApiOperationsContent(operationIds, operationInfoMap);
120
+ // Write to output file
121
+ const outputPath = path.join(outputDir, 'api-operations.ts');
122
+ fs.writeFileSync(outputPath, tsContent);
123
+ console.log(`✅ Generated api-operations file: ${outputPath}`);
124
+ console.log(`📊 Found ${operationIds.length} operations`);
125
+ }
126
+ function printUsage() {
127
+ console.log(`
128
+ Usage: npx @qualisero/openapi-endpoint <openapi-input> <output-directory>
129
+
130
+ Arguments:
131
+ openapi-input Path to OpenAPI JSON file or URL to fetch it from
132
+ output-directory Directory where generated files will be saved
133
+
134
+ Examples:
135
+ npx @qualisero/openapi-endpoint ./api/openapi.json ./src/generated
136
+ npx @qualisero/openapi-endpoint https://api.example.com/openapi.json ./src/api
137
+
138
+ This command will generate:
139
+ - openapi-types.ts (TypeScript types from OpenAPI spec)
140
+ - api-operations.ts (Operation IDs and info for use with this library)
141
+ `);
142
+ }
143
+ async function main() {
144
+ const args = process.argv.slice(2);
145
+ if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
146
+ printUsage();
147
+ process.exit(0);
148
+ }
149
+ if (args.length !== 2) {
150
+ console.error('❌ Error: Exactly 2 arguments are required');
151
+ printUsage();
152
+ process.exit(1);
153
+ }
154
+ const [openapiInput, outputDir] = args;
155
+ try {
156
+ // Ensure output directory exists
157
+ if (!fs.existsSync(outputDir)) {
158
+ fs.mkdirSync(outputDir, { recursive: true });
159
+ console.log(`📁 Created output directory: ${outputDir}`);
160
+ }
161
+ // Fetch OpenAPI spec content
162
+ const openapiContent = await fetchOpenAPISpec(openapiInput);
163
+ // Generate both files
164
+ await Promise.all([generateTypes(openapiContent, outputDir), generateApiOperations(openapiContent, outputDir)]);
165
+ console.log('🎉 Code generation completed successfully!');
166
+ }
167
+ catch (error) {
168
+ console.error('❌ Error:', error instanceof Error ? error.message : error);
169
+ process.exit(1);
170
+ }
171
+ }
172
+ // Auto-execute main function
173
+ main().catch((error) => {
174
+ console.error('❌ Unexpected error:', error);
175
+ process.exit(1);
176
+ });
@@ -0,0 +1,90 @@
1
+ import type { MaybeRefOrGetter } from 'vue';
2
+ import { QueryClient } from '@tanstack/vue-query';
3
+ import { EndpointQueryReturn } from './openapi-query';
4
+ import { Operations, GetPathParameters, OpenApiConfig, QueryOptions, MutationOptions, IsQueryOperation } from './types';
5
+ export type { OperationInfo, QueryOptions } from './types';
6
+ export { type EndpointQueryReturn, useEndpointQuery } from './openapi-query';
7
+ export { type EndpointMutationReturn, useEndpointMutation } from './openapi-mutation';
8
+ export declare const queryClient: QueryClient;
9
+ export declare function useOpenApi<Ops extends Operations<Ops>>(config: OpenApiConfig<Ops>): {
10
+ useQuery: <Op extends keyof Ops>(operationId: IsQueryOperation<Ops, Op> extends true ? Op : never, pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | QueryOptions<Ops, Op>, optionsOrNull?: QueryOptions<Ops, Op>) => EndpointQueryReturn<Ops, Op>;
11
+ useMutation: <Op extends keyof Ops>(operationId: IsQueryOperation<Ops, Op> extends false ? Op : never, pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | MutationOptions<Ops, Op>, optionsOrNull?: MutationOptions<Ops, Op>) => {
12
+ data: import("vue").ComputedRef<import("./types").GetResponseData<Ops, Op> | undefined>;
13
+ isEnabled: import("vue").ComputedRef<boolean>;
14
+ extraPathParams: import("vue").Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
15
+ error: import("vue").Ref<null, null>;
16
+ isError: import("vue").Ref<false, false>;
17
+ isPending: import("vue").Ref<false, false>;
18
+ isSuccess: import("vue").Ref<false, false>;
19
+ status: import("vue").Ref<"idle", "idle">;
20
+ failureCount: import("vue").Ref<number, number>;
21
+ failureReason: import("vue").Ref<Error | null, Error | null>;
22
+ isPaused: import("vue").Ref<boolean, boolean>;
23
+ variables: import("vue").Ref<undefined, undefined>;
24
+ isIdle: import("vue").Ref<true, true>;
25
+ context: import("vue").Ref<unknown, unknown>;
26
+ submittedAt: import("vue").Ref<number, number>;
27
+ mutate: (variables: import("./types").MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown> | undefined) => void;
28
+ mutateAsync: import("@tanstack/query-core").MutateFunction<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown>;
29
+ reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
30
+ } | {
31
+ data: import("vue").ComputedRef<import("./types").GetResponseData<Ops, Op> | undefined>;
32
+ isEnabled: import("vue").ComputedRef<boolean>;
33
+ extraPathParams: import("vue").Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
34
+ error: import("vue").Ref<null, null>;
35
+ isError: import("vue").Ref<false, false>;
36
+ isPending: import("vue").Ref<true, true>;
37
+ isSuccess: import("vue").Ref<false, false>;
38
+ status: import("vue").Ref<"pending", "pending">;
39
+ failureCount: import("vue").Ref<number, number>;
40
+ failureReason: import("vue").Ref<Error | null, Error | null>;
41
+ isPaused: import("vue").Ref<boolean, boolean>;
42
+ variables: import("vue").Ref<import("./types").MutationVars<Ops, Op>, import("./types").MutationVars<Ops, Op>>;
43
+ isIdle: import("vue").Ref<false, false>;
44
+ context: import("vue").Ref<unknown, unknown>;
45
+ submittedAt: import("vue").Ref<number, number>;
46
+ mutate: (variables: import("./types").MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown> | undefined) => void;
47
+ mutateAsync: import("@tanstack/query-core").MutateFunction<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown>;
48
+ reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
49
+ } | {
50
+ data: import("vue").ComputedRef<import("./types").GetResponseData<Ops, Op> | undefined>;
51
+ isEnabled: import("vue").ComputedRef<boolean>;
52
+ extraPathParams: import("vue").Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
53
+ error: import("vue").Ref<Error, Error>;
54
+ isError: import("vue").Ref<true, true>;
55
+ isPending: import("vue").Ref<false, false>;
56
+ isSuccess: import("vue").Ref<false, false>;
57
+ status: import("vue").Ref<"error", "error">;
58
+ failureCount: import("vue").Ref<number, number>;
59
+ failureReason: import("vue").Ref<Error | null, Error | null>;
60
+ isPaused: import("vue").Ref<boolean, boolean>;
61
+ variables: import("vue").Ref<import("./types").MutationVars<Ops, Op>, import("./types").MutationVars<Ops, Op>>;
62
+ isIdle: import("vue").Ref<false, false>;
63
+ context: import("vue").Ref<unknown, unknown>;
64
+ submittedAt: import("vue").Ref<number, number>;
65
+ mutate: (variables: import("./types").MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown> | undefined) => void;
66
+ mutateAsync: import("@tanstack/query-core").MutateFunction<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown>;
67
+ reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
68
+ } | {
69
+ data: import("vue").ComputedRef<import("./types").GetResponseData<Ops, Op> | undefined>;
70
+ isEnabled: import("vue").ComputedRef<boolean>;
71
+ extraPathParams: import("vue").Ref<GetPathParameters<Ops, Op>, GetPathParameters<Ops, Op>>;
72
+ error: import("vue").Ref<null, null>;
73
+ isError: import("vue").Ref<false, false>;
74
+ isPending: import("vue").Ref<false, false>;
75
+ isSuccess: import("vue").Ref<true, true>;
76
+ status: import("vue").Ref<"success", "success">;
77
+ failureCount: import("vue").Ref<number, number>;
78
+ failureReason: import("vue").Ref<Error | null, Error | null>;
79
+ isPaused: import("vue").Ref<boolean, boolean>;
80
+ variables: import("vue").Ref<import("./types").MutationVars<Ops, Op>, import("./types").MutationVars<Ops, Op>>;
81
+ isIdle: import("vue").Ref<false, false>;
82
+ context: import("vue").Ref<unknown, unknown>;
83
+ submittedAt: import("vue").Ref<number, number>;
84
+ mutate: (variables: import("./types").MutationVars<Ops, Op>, options?: import("@tanstack/query-core").MutateOptions<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown> | undefined) => void;
85
+ mutateAsync: import("@tanstack/query-core").MutateFunction<import("./types").GetResponseData<Ops, Op>, Error, import("./types").MutationVars<Ops, Op>, unknown>;
86
+ reset: import("@tanstack/query-core").MutationObserverResult<TData, TError, TVariables, TOnMutateResult>["reset"];
87
+ };
88
+ useEndpoint: <Op extends keyof Ops>(operationId: Op, pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | (IsQueryOperation<Ops, Op> extends true ? QueryOptions<Ops, Op> : MutationOptions<Ops, Op>), optionsOrNull?: IsQueryOperation<Ops, Op> extends true ? QueryOptions<Ops, Op> : MutationOptions<Ops, Op>) => EndpointQueryReturn<Ops, Op> | import("./openapi-mutation").EndpointMutationReturn<Ops, Op>;
89
+ };
90
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAGjD,OAAO,EAAE,mBAAmB,EAAoB,MAAM,iBAAiB,CAAA;AAEvE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAEvH,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC1D,OAAO,EAAE,KAAK,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAC5E,OAAO,EAAE,KAAK,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAErF,eAAO,MAAM,WAAW,aAItB,CAAA;AAEF,wBAAgB,UAAU,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC;eAE1D,EAAE,SAAS,MAAM,GAAG,eACzB,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,IAAI,GAAG,EAAE,GAAG,KAAK,wBAC1C,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,kBAC7F,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,KACpC,mBAAmB,CAAC,GAAG,EAAE,EAAE,CAAC;kBAMR,EAAE,SAAS,MAAM,GAAG,eAC5B,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,KAAK,GAAG,EAAE,GAAG,KAAK,wBAC3C,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,kBAChG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAOnB,EAAE,SAAS,MAAM,GAAG,eAC5B,EAAE,wBAEX,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAC/D,CAAC,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,kBAC/E,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC;EAO9G"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ import { QueryClient } from '@tanstack/vue-query';
2
+ import { useEndpoint } from './openapi-endpoint';
3
+ import { useEndpointQuery } from './openapi-query';
4
+ import { useEndpointMutation } from './openapi-mutation';
5
+ import { getHelpers } from './openapi-helpers';
6
+ export { useEndpointQuery } from './openapi-query';
7
+ export { useEndpointMutation } from './openapi-mutation';
8
+ export const queryClient = new QueryClient({
9
+ defaultOptions: {
10
+ queries: { staleTime: 1000 * 60 * 5 },
11
+ },
12
+ });
13
+ export function useOpenApi(config) {
14
+ return {
15
+ useQuery: function (operationId, pathParamsOrOptions, optionsOrNull) {
16
+ const helpers = getHelpers(config);
17
+ return useEndpointQuery(operationId, helpers, pathParamsOrOptions, optionsOrNull);
18
+ },
19
+ useMutation: function (operationId, pathParamsOrOptions, optionsOrNull) {
20
+ const helpers = getHelpers(config);
21
+ return useEndpointMutation(operationId, helpers, pathParamsOrOptions, optionsOrNull);
22
+ },
23
+ useEndpoint: function (operationId, pathParamsOrOptions, optionsOrNull) {
24
+ const helpers = getHelpers(config);
25
+ return useEndpoint(operationId, helpers, pathParamsOrOptions, optionsOrNull);
26
+ },
27
+ };
28
+ }
@@ -0,0 +1,18 @@
1
+ import { type MaybeRefOrGetter } from 'vue';
2
+ import { EndpointQueryReturn } from './openapi-query';
3
+ import { EndpointMutationReturn } from './openapi-mutation';
4
+ import type { GetPathParameters, QueryOptions, MutationOptions, Operations, IsQueryOperation } from './types';
5
+ import { getHelpers } from './openapi-helpers';
6
+ /**
7
+ * Composable for performing a strictly typed OpenAPI operation (query or mutation) using Vue Query.
8
+ * Automatically detects whether the operation is a query or mutation and delegates to the appropriate composable.
9
+ * Returns a reactive query or mutation object with strict typing and helpers.
10
+ *
11
+ * @template T OperationId type representing the OpenAPI operation.
12
+ * @param operationId The OpenAPI operation ID to execute.
13
+ * @param pathParamsOrOptions Optional path parameters for the endpoint, can be reactive.
14
+ * @param optionsOrNull Optional query or mutation options, including Vue Query options.
15
+ * @returns Query or mutation object with strict typing and helpers.
16
+ */
17
+ export declare function useEndpoint<Ops extends Operations<Ops>, Op extends keyof Ops>(operationId: Op, helpers: ReturnType<typeof getHelpers<Ops, Op>>, pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | (IsQueryOperation<Ops, Op> extends true ? QueryOptions<Ops, Op> : MutationOptions<Ops, Op>), optionsOrNull?: IsQueryOperation<Ops, Op> extends true ? QueryOptions<Ops, Op> : MutationOptions<Ops, Op>): EndpointQueryReturn<Ops, Op> | EndpointMutationReturn<Ops, Op>;
18
+ //# sourceMappingURL=openapi-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-endpoint.d.ts","sourceRoot":"","sources":["../src/openapi-endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,mBAAmB,EAAoB,MAAM,iBAAiB,CAAA;AACvE,OAAO,EAAE,sBAAsB,EAAuB,MAAM,oBAAoB,CAAA;AAChF,OAAO,KAAK,EAEV,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,UAAU,EACV,gBAAgB,EACjB,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAmB9C;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,EAC3E,WAAW,EAAE,EAAE,EACf,OAAO,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAC/C,mBAAmB,CAAC,EAChB,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,GAC/D,CAAC,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAC/F,aAAa,GAAE,gBAAgB,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,IAAI,GAAG,YAAY,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,eAAe,CAAC,GAAG,EAAE,EAAE,CAAM,GAC5G,mBAAmB,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,sBAAsB,CAAC,GAAG,EAAE,EAAE,CAAC,CAahE"}
@@ -0,0 +1,39 @@
1
+ import { useEndpointQuery } from './openapi-query';
2
+ import { useEndpointMutation } from './openapi-mutation';
3
+ // NOTE: rather than using conditional overloads, we are adjusting the signature in the calling code based on IsQueryOperation
4
+ // Conditional overload: if operation is a query, use QueryOptions and return QueryReturn
5
+ // export function useEndpoint<Ops extends Operations<Ops>, Op extends keyof Ops>(
6
+ // config: OpenApiConfig<Ops>,
7
+ // operationId: IsQueryOperation<Ops, Op> extends true ? Op : never,
8
+ // pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | QueryOptions<Ops, Op>,
9
+ // optionsOrNull?: QueryOptions<Ops, Op>
10
+ // ): EndpointQueryReturn<Ops, Op>
11
+ // // Conditional overload: if operation is a mutation, use MutationOptions and return MutationReturn
12
+ // export function useEndpoint<Ops extends Operations<Ops>, Op extends keyof Ops>(
13
+ // config: OpenApiConfig<Ops>,
14
+ // operationId: IsQueryOperation<Ops, Op> extends false ? Op : never,
15
+ // pathParamsOrOptions?: MaybeRefOrGetter<GetPathParameters<Ops, Op> | null | undefined> | MutationOptions<Ops, Op>,
16
+ // optionsOrNull?: MutationOptions<Ops, Op>
17
+ // ): EndpointMutationReturn<Ops, Op>
18
+ /**
19
+ * Composable for performing a strictly typed OpenAPI operation (query or mutation) using Vue Query.
20
+ * Automatically detects whether the operation is a query or mutation and delegates to the appropriate composable.
21
+ * Returns a reactive query or mutation object with strict typing and helpers.
22
+ *
23
+ * @template T OperationId type representing the OpenAPI operation.
24
+ * @param operationId The OpenAPI operation ID to execute.
25
+ * @param pathParamsOrOptions Optional path parameters for the endpoint, can be reactive.
26
+ * @param optionsOrNull Optional query or mutation options, including Vue Query options.
27
+ * @returns Query or mutation object with strict typing and helpers.
28
+ */
29
+ export function useEndpoint(operationId, helpers, pathParamsOrOptions, optionsOrNull = {}) {
30
+ if (helpers.isMutationOperation(operationId)) {
31
+ return useEndpointMutation(operationId, helpers, pathParamsOrOptions, optionsOrNull);
32
+ }
33
+ else if (helpers.isQueryOperation(operationId)) {
34
+ return useEndpointQuery(operationId, helpers, pathParamsOrOptions, optionsOrNull);
35
+ }
36
+ else {
37
+ throw new Error(`Operation ${String(operationId)} is neither a query nor a mutation operation`);
38
+ }
39
+ }
@@ -0,0 +1,10 @@
1
+ import { type OperationInfo, OpenApiConfig, Operations } from './types';
2
+ export declare function getHelpers<Ops extends Operations<Ops>, Op extends keyof Ops>(config: OpenApiConfig<Ops>): {
3
+ getOperationInfo: (operationId: Op) => OperationInfo;
4
+ getListOperationPath: (operationId: Op) => string | null;
5
+ getCrudListPathPrefix: (operationId: Op) => string | null;
6
+ isQueryOperation: (operationId: Op) => boolean;
7
+ isMutationOperation: (operationId: Op) => boolean;
8
+ axios: import("axios").AxiosInstance;
9
+ };
10
+ //# sourceMappingURL=openapi-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-helpers.d.ts","sourceRoot":"","sources":["../src/openapi-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAiBnF,wBAAgB,UAAU,CAAC,GAAG,SAAS,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC;oCAE/D,EAAE,KAAG,aAAa;wCAMd,EAAE,KAAG,MAAM,GAAG,IAAI;yCAiCjB,EAAE,KAAG,MAAM,GAAG,IAAI;oCAgBvB,EAAE,KAAG,OAAO;uCAOT,EAAE,KAAG,OAAO;;EAcvD"}
@@ -0,0 +1,89 @@
1
+ import { HttpMethod } from './types';
2
+ // helper returning the operationId prefix given an http method
3
+ function _getMethodPrefix(method) {
4
+ const methodPrefixes = {
5
+ [HttpMethod.GET]: 'get', // or 'list' depending on the operationId
6
+ [HttpMethod.POST]: 'create',
7
+ [HttpMethod.PUT]: 'update',
8
+ [HttpMethod.PATCH]: 'update',
9
+ [HttpMethod.DELETE]: 'delete',
10
+ [HttpMethod.HEAD]: null,
11
+ [HttpMethod.OPTIONS]: null,
12
+ [HttpMethod.TRACE]: null,
13
+ };
14
+ return methodPrefixes[method];
15
+ }
16
+ export function getHelpers(config) {
17
+ // Helper function to get operation info by ID
18
+ function getOperationInfo(operationId) {
19
+ return config.operations[operationId];
20
+ }
21
+ // Helper to return a url path for matching list endpoint (e.g. /items/123 -> /items/)
22
+ // Based on operationId prefix: createItem, updateItem -> listItems
23
+ function getListOperationPath(operationId) {
24
+ const opInfo = getOperationInfo(operationId);
25
+ const operationIdStr = operationId;
26
+ const operationPrefix = opInfo ? _getMethodPrefix(opInfo.method) : null;
27
+ // Make sure operationId matches `<operationPrefix><upercase resourceName>` pattern
28
+ if (!operationPrefix || !/^[A-Z]/.test(operationIdStr.charAt(operationPrefix.length)))
29
+ // If not, fallback to CRUD heuristic
30
+ return getCrudListPathPrefix(operationId);
31
+ const resourceName = operationIdStr.substring(operationPrefix.length);
32
+ const listOperationId = `list${resourceName}`;
33
+ let listOperationInfo = getOperationInfo(listOperationId);
34
+ if (!listOperationInfo) {
35
+ // Try pluralizing the resource name (simple heuristic)
36
+ let pluralResourceName = resourceName;
37
+ const addEsSuffixes = ['s', 'x', 'z', 'ch', 'sh', 'o'];
38
+ if (resourceName.endsWith('y')) {
39
+ pluralResourceName = resourceName.slice(0, -1) + 'ies';
40
+ }
41
+ else if (addEsSuffixes.some((suffix) => resourceName.endsWith(suffix))) {
42
+ pluralResourceName = resourceName + 'es';
43
+ }
44
+ else {
45
+ pluralResourceName = resourceName + 's';
46
+ }
47
+ listOperationInfo = getOperationInfo(`list${pluralResourceName}`);
48
+ }
49
+ if (listOperationInfo && listOperationInfo.method === HttpMethod.GET) {
50
+ return listOperationInfo.path;
51
+ }
52
+ return null;
53
+ }
54
+ // Fallback to return a url path prefix matching list endpoint (e.g. /items/123 -> /items/)
55
+ // based on Assumes standard CRUD Restful patterns.
56
+ function getCrudListPathPrefix(operationId) {
57
+ const { path } = getOperationInfo(operationId);
58
+ // for PUT/PATCH/DELETE, strip last segment if it's a path parameter
59
+ const segments = path.split('/').filter((segment) => segment.length > 0);
60
+ if (segments.length >= 2 && /^{[^}]+}$/.test(segments.at(-1))) {
61
+ return '/' + segments.slice(0, -1).join('/') + '/';
62
+ }
63
+ return null;
64
+ }
65
+ // Helper function to get all operation IDs
66
+ // function getAllOperationIds(): OperationId<C>[] {
67
+ // return Object.values(OperationId)
68
+ // }
69
+ // Helper to check if an operation is a query method at runtime
70
+ function isQueryOperation(operationId) {
71
+ const { method } = getOperationInfo(operationId);
72
+ const queryMethods = [HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS];
73
+ return queryMethods.includes(method);
74
+ }
75
+ // Helper to check if an operation is a mutation method at runtime
76
+ function isMutationOperation(operationId) {
77
+ const { method } = getOperationInfo(operationId);
78
+ const mutationMethods = [HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH, HttpMethod.DELETE];
79
+ return mutationMethods.includes(method);
80
+ }
81
+ return {
82
+ getOperationInfo,
83
+ getListOperationPath,
84
+ getCrudListPathPrefix,
85
+ isQueryOperation,
86
+ isMutationOperation,
87
+ axios: config.axios,
88
+ };
89
+ }