genoc 0.1.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.
Files changed (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/analyzer/naming.d.ts +24 -0
  4. package/dist/analyzer/naming.js +122 -0
  5. package/dist/analyzer/path-analyzer.d.ts +53 -0
  6. package/dist/analyzer/path-analyzer.js +222 -0
  7. package/dist/analyzer/schema-mapper.d.ts +48 -0
  8. package/dist/analyzer/schema-mapper.js +435 -0
  9. package/dist/cli/app.d.ts +9 -0
  10. package/dist/cli/app.js +60 -0
  11. package/dist/cli/errors.d.ts +3 -0
  12. package/dist/cli/errors.js +6 -0
  13. package/dist/cli/impl.d.ts +3 -0
  14. package/dist/cli/impl.js +45 -0
  15. package/dist/cli/index.d.ts +2 -0
  16. package/dist/cli/index.js +5 -0
  17. package/dist/generator/client-generator.d.ts +21 -0
  18. package/dist/generator/client-generator.js +287 -0
  19. package/dist/generator/contracts-generator.d.ts +16 -0
  20. package/dist/generator/contracts-generator.js +525 -0
  21. package/dist/generator/error-types.d.ts +24 -0
  22. package/dist/generator/error-types.js +94 -0
  23. package/dist/generator/method-generator.d.ts +9 -0
  24. package/dist/generator/method-generator.js +249 -0
  25. package/dist/index.d.ts +7 -0
  26. package/dist/index.js +8 -0
  27. package/dist/parser/ref-resolver.d.ts +24 -0
  28. package/dist/parser/ref-resolver.js +119 -0
  29. package/dist/parser/spec-reader.d.ts +4 -0
  30. package/dist/parser/spec-reader.js +116 -0
  31. package/dist/parser/validators.d.ts +7 -0
  32. package/dist/parser/validators.js +79 -0
  33. package/dist/parser/version/index.d.ts +18 -0
  34. package/dist/parser/version/index.js +16 -0
  35. package/dist/parser/version/normalized-spec.d.ts +199 -0
  36. package/dist/parser/version/normalized-spec.js +1 -0
  37. package/dist/parser/version/registry.d.ts +28 -0
  38. package/dist/parser/version/registry.js +44 -0
  39. package/dist/parser/version/v3.0/index.d.ts +3 -0
  40. package/dist/parser/version/v3.0/index.js +3 -0
  41. package/dist/parser/version/v3.0/normalizer.d.ts +15 -0
  42. package/dist/parser/version/v3.0/normalizer.js +389 -0
  43. package/dist/parser/version/v3.0/strategy.d.ts +27 -0
  44. package/dist/parser/version/v3.0/strategy.js +96 -0
  45. package/dist/parser/version/v3.0/validator.d.ts +13 -0
  46. package/dist/parser/version/v3.0/validator.js +117 -0
  47. package/dist/parser/version/v3.1/index.d.ts +1 -0
  48. package/dist/parser/version/v3.1/index.js +1 -0
  49. package/dist/parser/version/v3.1/strategy.d.ts +42 -0
  50. package/dist/parser/version/v3.1/strategy.js +513 -0
  51. package/dist/parser/version/v3.2/index.d.ts +4 -0
  52. package/dist/parser/version/v3.2/index.js +4 -0
  53. package/dist/parser/version/v3.2/strategy.d.ts +39 -0
  54. package/dist/parser/version/v3.2/strategy.js +57 -0
  55. package/dist/parser/version/version-detector.d.ts +4 -0
  56. package/dist/parser/version/version-detector.js +34 -0
  57. package/dist/parser/version/version-strategy.d.ts +31 -0
  58. package/dist/parser/version/version-strategy.js +1 -0
  59. package/dist/types/client.d.ts +25 -0
  60. package/dist/types/client.js +1 -0
  61. package/dist/types/contracts.d.ts +13 -0
  62. package/dist/types/contracts.js +1 -0
  63. package/dist/types/openapi.d.ts +173 -0
  64. package/dist/types/openapi.js +1 -0
  65. package/dist/utils/case.d.ts +5 -0
  66. package/dist/utils/case.js +51 -0
  67. package/dist/utils/generator-helpers.d.ts +23 -0
  68. package/dist/utils/generator-helpers.js +66 -0
  69. package/dist/utils/string.d.ts +34 -0
  70. package/dist/utils/string.js +182 -0
  71. package/dist/utils/url.d.ts +10 -0
  72. package/dist/utils/url.js +40 -0
  73. package/package.json +60 -0
@@ -0,0 +1,31 @@
1
+ import type { ValidationResult } from '../validators.js';
2
+ import type { NormalizedSpec } from './normalized-spec.js';
3
+ /**
4
+ * Version strategy interface for multi-version OpenAPI support
5
+ */
6
+ export interface VersionStrategy {
7
+ /**
8
+ * Get the supported OpenAPI version
9
+ */
10
+ version(): string;
11
+ /**
12
+ * Check if this strategy matches the given OpenAPI specification
13
+ */
14
+ matches(spec: unknown): boolean;
15
+ /**
16
+ * Normalize a raw OpenAPI specification to a consistent format
17
+ */
18
+ normalizeSpec(rawSpec: unknown): NormalizedSpec;
19
+ /**
20
+ * Validate the normalized specification
21
+ */
22
+ validateSpec(spec: NormalizedSpec): ValidationResult;
23
+ /**
24
+ * Resolve a reference within the document context
25
+ */
26
+ resolveRef(ref: string, doc: unknown, context?: unknown): unknown;
27
+ /**
28
+ * Get supported features for this version
29
+ */
30
+ getSupportedFeatures(): string[];
31
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ export type MethodNameStrategy = 'path-based' | 'operationId' | 'operationId-with-fallback';
2
+ export type GeneratorConfig = {
3
+ input: string;
4
+ outputDir: string;
5
+ methodNameStrategy?: MethodNameStrategy;
6
+ requesterModuleName?: string;
7
+ specVersion?: string;
8
+ strictVersion?: boolean;
9
+ };
10
+ export type RequesterFunction = <TResponse>(method: string, path: string, options: {
11
+ query?: Record<string, unknown>;
12
+ body?: unknown;
13
+ headers?: Record<string, string>;
14
+ }) => Promise<TResponse>;
15
+ export type GeneratedMethod = {
16
+ name: string;
17
+ jsDoc: string;
18
+ signature: string;
19
+ implementation: string;
20
+ };
21
+ export type ClientOutput = {
22
+ imports: string[];
23
+ methods: GeneratedMethod[];
24
+ errorTypes: string[];
25
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ export type TypeMappingResult = {
2
+ tsType: string;
3
+ imports: string[];
4
+ };
5
+ export type ContractEntry = {
6
+ name: string;
7
+ kind: 'interface' | 'type' | 'enum';
8
+ definition: string;
9
+ jsDoc?: string;
10
+ };
11
+ export type ContractsOutput = {
12
+ entries: ContractEntry[];
13
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,173 @@
1
+ export type ReferenceObject = {
2
+ $ref: string;
3
+ };
4
+ export type SchemaObject = {
5
+ type?: string | string[];
6
+ format?: string;
7
+ properties?: Record<string, SchemaObject>;
8
+ required?: string[];
9
+ items?: SchemaObject;
10
+ additionalProperties?: boolean | SchemaObject;
11
+ $ref?: string;
12
+ allOf?: SchemaObject[];
13
+ oneOf?: SchemaObject[];
14
+ anyOf?: SchemaObject[];
15
+ enum?: unknown[];
16
+ const?: unknown;
17
+ default?: unknown;
18
+ description?: string;
19
+ nullable?: boolean;
20
+ readOnly?: boolean;
21
+ writeOnly?: boolean;
22
+ externalDocs?: {
23
+ description?: string;
24
+ url: string;
25
+ };
26
+ discriminator?: {
27
+ propertyName: string;
28
+ mapping?: Record<string, string>;
29
+ };
30
+ };
31
+ export type MediaTypeObject = {
32
+ schema?: SchemaObject | ReferenceObject;
33
+ examples?: Record<string, unknown>;
34
+ example?: unknown;
35
+ encoding?: Record<string, unknown>;
36
+ };
37
+ export type ParameterObject = {
38
+ name: string;
39
+ in: 'query' | 'path' | 'header' | 'cookie';
40
+ description?: string;
41
+ required?: boolean;
42
+ deprecated?: boolean;
43
+ schema?: SchemaObject | ReferenceObject;
44
+ style?: string;
45
+ explode?: boolean;
46
+ allowEmptyValue?: boolean;
47
+ example?: unknown;
48
+ examples?: Record<string, unknown>;
49
+ };
50
+ export type RequestBodyObject = {
51
+ description?: string;
52
+ content: Record<string, MediaTypeObject>;
53
+ required?: boolean;
54
+ };
55
+ export type ResponseObject = {
56
+ description: string;
57
+ headers?: Record<string, unknown>;
58
+ content?: Record<string, MediaTypeObject>;
59
+ links?: Record<string, unknown>;
60
+ };
61
+ export type ResponsesObject = Record<string, ResponseObject | ReferenceObject>;
62
+ export type OperationObject = {
63
+ tags?: string[];
64
+ summary?: string;
65
+ description?: string;
66
+ operationId?: string;
67
+ parameters?: (ParameterObject | ReferenceObject)[];
68
+ requestBody?: RequestBodyObject | ReferenceObject;
69
+ responses: ResponsesObject;
70
+ deprecated?: boolean;
71
+ security?: unknown[];
72
+ servers?: unknown[];
73
+ };
74
+ export type PathItemObject = {
75
+ $ref?: string;
76
+ summary?: string;
77
+ description?: string;
78
+ get?: OperationObject;
79
+ put?: OperationObject;
80
+ post?: OperationObject;
81
+ delete?: OperationObject;
82
+ options?: OperationObject;
83
+ head?: OperationObject;
84
+ patch?: OperationObject;
85
+ trace?: OperationObject;
86
+ parameters?: (ParameterObject | ReferenceObject)[];
87
+ servers?: unknown[];
88
+ };
89
+ export type PathsObject = Record<string, PathItemObject>;
90
+ export type ServerVariableObject = {
91
+ default: string;
92
+ description?: string;
93
+ enum?: string[];
94
+ };
95
+ export type ServerObject = {
96
+ url: string;
97
+ description?: string;
98
+ variables?: Record<string, ServerVariableObject>;
99
+ };
100
+ export type InfoObject = {
101
+ title: string;
102
+ version: string;
103
+ description?: string;
104
+ termsOfService?: string;
105
+ contact?: {
106
+ name?: string;
107
+ url?: string;
108
+ email?: string;
109
+ };
110
+ license?: {
111
+ name: string;
112
+ url?: string;
113
+ identifier?: string;
114
+ };
115
+ };
116
+ export type OAuth2FlowBase = {
117
+ refreshUrl?: string;
118
+ scopes: Record<string, string>;
119
+ };
120
+ export type OAuth2FlowImplicit = OAuth2FlowBase & {
121
+ authorizationUrl: string;
122
+ };
123
+ export type OAuth2FlowPassword = OAuth2FlowBase & {
124
+ tokenUrl: string;
125
+ };
126
+ export type OAuth2FlowClientCredentials = OAuth2FlowBase & {
127
+ tokenUrl: string;
128
+ };
129
+ export type OAuth2FlowAuthorizationCode = OAuth2FlowBase & {
130
+ authorizationUrl: string;
131
+ tokenUrl: string;
132
+ };
133
+ export type OAuth2FlowsObject = {
134
+ implicit?: OAuth2FlowImplicit;
135
+ password?: OAuth2FlowPassword;
136
+ clientCredentials?: OAuth2FlowClientCredentials;
137
+ authorizationCode?: OAuth2FlowAuthorizationCode;
138
+ };
139
+ export type SecuritySchemeObject = {
140
+ type: 'apiKey' | 'http' | 'oauth2' | 'openIdConnect';
141
+ description?: string;
142
+ name?: string;
143
+ in?: 'query' | 'header' | 'cookie';
144
+ scheme?: string;
145
+ bearerFormat?: string;
146
+ flows?: OAuth2FlowsObject;
147
+ openIdConnectUrl?: string;
148
+ };
149
+ export type ComponentsObject = {
150
+ schemas?: Record<string, SchemaObject | ReferenceObject>;
151
+ responses?: Record<string, ResponseObject | ReferenceObject>;
152
+ parameters?: Record<string, ParameterObject | ReferenceObject>;
153
+ requestBodies?: Record<string, RequestBodyObject | ReferenceObject>;
154
+ headers?: Record<string, unknown>;
155
+ securitySchemes?: Record<string, SecuritySchemeObject>;
156
+ links?: Record<string, unknown>;
157
+ callbacks?: Record<string, unknown>;
158
+ examples?: Record<string, unknown>;
159
+ };
160
+ export type OpenAPIDocument = {
161
+ openapi: string;
162
+ info: InfoObject;
163
+ servers?: ServerObject[];
164
+ paths?: PathsObject;
165
+ components?: ComponentsObject;
166
+ security?: unknown[];
167
+ tags?: unknown[];
168
+ externalDocs?: {
169
+ description?: string;
170
+ url: string;
171
+ };
172
+ webhooks?: Record<string, PathItemObject>;
173
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare function kebabToPascalCase(str: string): string;
2
+ export declare function camelCase(str: string): string;
3
+ export declare function pascalCase(str: string): string;
4
+ export declare function toPascalCaseSegment(segment: string): string;
5
+ export declare function formatToBrandTypeName(format: string, openApiType: string): string;
@@ -0,0 +1,51 @@
1
+ export function kebabToPascalCase(str) {
2
+ if (!str)
3
+ return '';
4
+ return str
5
+ .split('-')
6
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
7
+ .join('');
8
+ }
9
+ export function camelCase(str) {
10
+ if (!str)
11
+ return '';
12
+ return str.charAt(0).toLowerCase() + str.slice(1).toLowerCase();
13
+ }
14
+ export function pascalCase(str) {
15
+ if (!str)
16
+ return '';
17
+ return str
18
+ .split(/[\s-_]+/)
19
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
20
+ .join('');
21
+ }
22
+ export function toPascalCaseSegment(segment) {
23
+ if (!segment)
24
+ return '';
25
+ if (/^[A-Z]/.test(segment) && /^[A-Za-z0-9]+$/.test(segment)) {
26
+ return segment;
27
+ }
28
+ if (segment.includes('-')) {
29
+ return kebabToPascalCase(segment);
30
+ }
31
+ return segment.charAt(0).toUpperCase() + segment.slice(1);
32
+ }
33
+ export function formatToBrandTypeName(format, openApiType) {
34
+ const formatName = pascalCase(format);
35
+ let baseType;
36
+ switch (openApiType) {
37
+ case 'string':
38
+ baseType = 'String';
39
+ break;
40
+ case 'number':
41
+ case 'integer':
42
+ baseType = 'Number';
43
+ break;
44
+ case 'boolean':
45
+ baseType = 'Boolean';
46
+ break;
47
+ default:
48
+ baseType = openApiType.charAt(0).toUpperCase() + openApiType.slice(1);
49
+ }
50
+ return formatName + baseType;
51
+ }
@@ -0,0 +1,23 @@
1
+ import type { AnalyzedOperation } from '../analyzer/path-analyzer.js';
2
+ /**
3
+ * Convert a string to PascalCase, handling camelCase, kebab-case, snake_case,
4
+ * and colon-separated segments.
5
+ */
6
+ export declare function toPascalCase(str: string): string;
7
+ /**
8
+ * Build a PascalCase type-name prefix from an operation's method + path.
9
+ * get + /api/v1/products → "GetApiV1Products"
10
+ */
11
+ export declare function getOperationTypePrefix(op: AnalyzedOperation): string;
12
+ /**
13
+ * Determine the success return type for an operation.
14
+ */
15
+ export declare function getSuccessType(op: AnalyzedOperation): string;
16
+ /**
17
+ * Determine the error type name for an operation.
18
+ */
19
+ export declare function getErrorType(op: AnalyzedOperation): string;
20
+ /**
21
+ * Generate the auto-generated header comment.
22
+ */
23
+ export declare function makeHeader(version: string): string;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Convert a string to PascalCase, handling camelCase, kebab-case, snake_case,
3
+ * and colon-separated segments.
4
+ */
5
+ export function toPascalCase(str) {
6
+ return str
7
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
8
+ .split(/[-_\s:]/)
9
+ .filter(Boolean)
10
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
11
+ .join('');
12
+ }
13
+ /**
14
+ * Build a PascalCase type-name prefix from an operation's method + path.
15
+ * get + /api/v1/products → "GetApiV1Products"
16
+ */
17
+ export function getOperationTypePrefix(op) {
18
+ const methodPascal = op.method.charAt(0).toUpperCase() + op.method.slice(1).toLowerCase();
19
+ const segments = op.path
20
+ .split('/')
21
+ .filter((s) => s.length > 0)
22
+ .map((s) => {
23
+ const cleaned = s.replace(/[{}]/g, '');
24
+ return toPascalCase(cleaned);
25
+ });
26
+ return methodPascal + segments.join('');
27
+ }
28
+ /**
29
+ * Determine the success return type for an operation.
30
+ */
31
+ export function getSuccessType(op) {
32
+ const successResponses = op.responses.filter((r) => r.isSuccess);
33
+ if (successResponses.length === 0) {
34
+ return 'unknown';
35
+ }
36
+ const noContent = successResponses.find((r) => r.tsType === 'void');
37
+ const hasOnlyNoContent = noContent && successResponses.every((r) => r.tsType === 'void');
38
+ if (hasOnlyNoContent) {
39
+ return 'void';
40
+ }
41
+ const withSchema = successResponses.filter((r) => r.tsType !== 'void');
42
+ if (withSchema.length === 0) {
43
+ return 'void';
44
+ }
45
+ const prefix = getOperationTypePrefix(op);
46
+ const types = withSchema.map(() => `${prefix}Response`);
47
+ const unique = [...new Set(types)];
48
+ return unique.join(' | ');
49
+ }
50
+ /**
51
+ * Determine the error type name for an operation.
52
+ */
53
+ export function getErrorType(op) {
54
+ const prefix = getOperationTypePrefix(op);
55
+ const errorResponses = op.responses.filter((r) => !r.isSuccess && r.statusCode !== 'default');
56
+ if (errorResponses.length === 0) {
57
+ return 'never';
58
+ }
59
+ return `${prefix}Errors`;
60
+ }
61
+ /**
62
+ * Generate the auto-generated header comment.
63
+ */
64
+ export function makeHeader(version) {
65
+ return `// Auto-generated by genoc from OpenAPI ${version} spec. DO NOT EDIT.`;
66
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * String utility functions for OpenAPI client generator
3
+ */
4
+ /**
5
+ * Indent each line of a string by the specified level (2 spaces per level)
6
+ * @param str The string to indent
7
+ * @param level Number of levels to indent (each level = 2 spaces)
8
+ * @returns Indented string, or empty string if input is empty
9
+ */
10
+ export declare function indent(str: string, level: number): string;
11
+ /**
12
+ * Capitalize the first character of a string
13
+ * @param str The string to capitalize
14
+ * @returns String with first character uppercase, rest unchanged
15
+ */
16
+ export declare function capitalize(str: string): string;
17
+ /**
18
+ * Check if a string is a JavaScript/TypeScript reserved word
19
+ * @param str The string to check
20
+ * @returns True if the string is a reserved word
21
+ */
22
+ export declare function isReservedWord(str: string): boolean;
23
+ /**
24
+ * Sanitize a string to make it safe for TypeScript identifier usage
25
+ * @param str The string to sanitize
26
+ * @returns Safe TypeScript identifier
27
+ */
28
+ export declare function sanitizeIdentifier(str: string): string;
29
+ /**
30
+ * Quote a key if needed for TypeScript object literals
31
+ * @param key The key to potentially quote
32
+ * @returns Quoted key if needed, original key otherwise
33
+ */
34
+ export declare function quoteKey(key: string): string;
@@ -0,0 +1,182 @@
1
+ /**
2
+ * String utility functions for OpenAPI client generator
3
+ */
4
+ /**
5
+ * Indent each line of a string by the specified level (2 spaces per level)
6
+ * @param str The string to indent
7
+ * @param level Number of levels to indent (each level = 2 spaces)
8
+ * @returns Indented string, or empty string if input is empty
9
+ */
10
+ export function indent(str, level) {
11
+ if (!str)
12
+ return '';
13
+ const prefix = ' '.repeat(Math.max(0, level));
14
+ return str
15
+ .split('\n')
16
+ .map((line) => (line ? `${prefix}${line}` : line)) // preserve empty lines without adding extra indentation
17
+ .join('\n');
18
+ }
19
+ /**
20
+ * Capitalize the first character of a string
21
+ * @param str The string to capitalize
22
+ * @returns String with first character uppercase, rest unchanged
23
+ */
24
+ export function capitalize(str) {
25
+ if (!str)
26
+ return str;
27
+ return str[0].toUpperCase() + str.slice(1);
28
+ }
29
+ /**
30
+ * Check if a string is a JavaScript/TypeScript reserved word
31
+ * @param str The string to check
32
+ * @returns True if the string is a reserved word
33
+ */
34
+ export function isReservedWord(str) {
35
+ if (!str)
36
+ return false;
37
+ const reservedWords = new Set([
38
+ // Keywords
39
+ 'class',
40
+ 'return',
41
+ 'const',
42
+ 'let',
43
+ 'var',
44
+ 'function',
45
+ 'if',
46
+ 'else',
47
+ 'for',
48
+ 'while',
49
+ 'do',
50
+ 'switch',
51
+ 'case',
52
+ 'break',
53
+ 'continue',
54
+ 'try',
55
+ 'catch',
56
+ 'finally',
57
+ 'throw',
58
+ 'new',
59
+ 'this',
60
+ 'super',
61
+ 'extends',
62
+ 'implements',
63
+ 'interface',
64
+ 'type',
65
+ 'enum',
66
+ 'abstract',
67
+ 'async',
68
+ 'await',
69
+ 'yield',
70
+ 'import',
71
+ 'export',
72
+ 'from',
73
+ 'default',
74
+ 'delete',
75
+ 'in',
76
+ 'instanceof',
77
+ 'typeof',
78
+ 'void',
79
+ 'with',
80
+ 'debugger',
81
+ // Literals
82
+ 'null',
83
+ 'true',
84
+ 'false',
85
+ 'undefined',
86
+ 'nan',
87
+ 'NaN',
88
+ 'Infinity',
89
+ // TypeScript specific
90
+ 'of',
91
+ 'as',
92
+ 'keyof',
93
+ 'readonly',
94
+ 'declare',
95
+ 'namespace',
96
+ 'module',
97
+ 'require',
98
+ 'public',
99
+ 'private',
100
+ 'protected',
101
+ 'static',
102
+ 'constructor',
103
+ ]);
104
+ return reservedWords.has(str.toLowerCase());
105
+ }
106
+ /**
107
+ * Sanitize a string to make it safe for TypeScript identifier usage
108
+ * @param str The string to sanitize
109
+ * @returns Safe TypeScript identifier
110
+ */
111
+ export function sanitizeIdentifier(str) {
112
+ if (!str)
113
+ return '_';
114
+ if (/^\d/.test(str)) {
115
+ return `_${str}`;
116
+ }
117
+ if (isReservedWord(str)) {
118
+ return `_${str}`;
119
+ }
120
+ const segments = str
121
+ .replace(/[^a-zA-Z0-9]/g, ' ')
122
+ .split(/\s+/)
123
+ .filter(Boolean);
124
+ const commonReserved = new Set([
125
+ 'class',
126
+ 'const',
127
+ 'function',
128
+ 'if',
129
+ 'else',
130
+ 'for',
131
+ 'while',
132
+ 'return',
133
+ 'var',
134
+ 'let',
135
+ ]);
136
+ if (segments.length > 0 && commonReserved.has(segments[0].toLowerCase())) {
137
+ const sanitized = str.replace(/[^a-zA-Z0-9]/g, '_');
138
+ const final = sanitized.replace(/_+/g, '_');
139
+ return `_${final}`;
140
+ }
141
+ const ascii = str.replace(/[üöäÜÖÄ]/g, (match) => {
142
+ switch (match) {
143
+ case 'ü':
144
+ return 'u';
145
+ case 'ö':
146
+ return 'o';
147
+ case 'ä':
148
+ return 'a';
149
+ case 'Ü':
150
+ return 'U';
151
+ case 'Ö':
152
+ return 'O';
153
+ case 'Ä':
154
+ return 'A';
155
+ default:
156
+ return match;
157
+ }
158
+ });
159
+ const sanitized = ascii.replace(/[^a-zA-Z0-9$]/g, '_');
160
+ const final = sanitized.replace(/_+/g, '_');
161
+ if (!final || final === '_' || final === '$' || /^[_$]+$/.test(final)) {
162
+ return '_';
163
+ }
164
+ return final;
165
+ }
166
+ /**
167
+ * Quote a key if needed for TypeScript object literals
168
+ * @param key The key to potentially quote
169
+ * @returns Quoted key if needed, original key otherwise
170
+ */
171
+ export function quoteKey(key) {
172
+ if (!key)
173
+ return '""';
174
+ // Check if the key needs quoting
175
+ const needsQuoting = isReservedWord(key) ||
176
+ /[^a-zA-Z0-9_$]/.test(key) ||
177
+ key.startsWith(' ') ||
178
+ key.endsWith(' ') ||
179
+ key === '' ||
180
+ /^\d/.test(key);
181
+ return needsQuoting ? `"${key}"` : key;
182
+ }
@@ -0,0 +1,10 @@
1
+ export declare function isUrl(str: string): boolean;
2
+ export declare function resolveUrl(base: string, ref: string): string;
3
+ export declare function parseJsonPointer(pointer: string): string[];
4
+ export declare function pathSegments(path: string): string[];
5
+ export declare function getPathSegmentsWithParamInfo(path: string): Array<{
6
+ segment: string;
7
+ isParam: boolean;
8
+ }>;
9
+ export declare function isPathParam(segment: string): boolean;
10
+ export declare function extractParamName(segment: string): string;
@@ -0,0 +1,40 @@
1
+ export function isUrl(str) {
2
+ return str.startsWith('http://') || str.startsWith('https://');
3
+ }
4
+ export function resolveUrl(base, ref) {
5
+ if (ref.startsWith('#')) {
6
+ return base + ref;
7
+ }
8
+ if (ref.startsWith('/')) {
9
+ return ref;
10
+ }
11
+ return base + ref;
12
+ }
13
+ export function parseJsonPointer(pointer) {
14
+ if (pointer === '') {
15
+ return [];
16
+ }
17
+ const segments = pointer.split('/').slice(1);
18
+ return segments.map((segment) => {
19
+ return segment.replace(/~1/g, '/').replace(/~0/g, '~');
20
+ });
21
+ }
22
+ export function pathSegments(path) {
23
+ return path.split(/[/:]/).filter((segment) => segment.length > 0);
24
+ }
25
+ export function getPathSegmentsWithParamInfo(path) {
26
+ const segments = path.split(/[/:]/).filter((segment) => segment.length > 0);
27
+ return segments.map((segment) => ({
28
+ segment,
29
+ isParam: segment.startsWith('{') && segment.endsWith('}'),
30
+ }));
31
+ }
32
+ export function isPathParam(segment) {
33
+ return segment.startsWith('{') && segment.endsWith('}');
34
+ }
35
+ export function extractParamName(segment) {
36
+ if (!isPathParam(segment)) {
37
+ throw new Error(`Segment is not a path parameter: ${segment}`);
38
+ }
39
+ return segment.slice(1, -1);
40
+ }