vovk-cli 0.0.1-draft.99 → 0.0.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.
Files changed (164) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +24 -16
  3. package/client-templates/jsBase/index.d.ts.ejs +21 -0
  4. package/client-templates/jsBase/index.js.ejs +18 -0
  5. package/client-templates/mixins/mixins.d.ts.ejs +64 -0
  6. package/client-templates/mixins/mixins.json.ejs +1 -0
  7. package/client-templates/openapiJs/openapi.d.ts.ejs +4 -0
  8. package/client-templates/openapiJs/openapi.js.ejs +4 -0
  9. package/client-templates/openapiJson/openapi.json.ejs +1 -0
  10. package/client-templates/openapiTs/openapi.ts.ejs +4 -0
  11. package/client-templates/packageJson/package.json.ejs +1 -0
  12. package/client-templates/readme/README.md.ejs +39 -0
  13. package/client-templates/schemaJs/schema.d.ts.ejs +10 -0
  14. package/client-templates/schemaJs/schema.js.ejs +29 -0
  15. package/client-templates/schemaJson/schema.json.ejs +1 -0
  16. package/client-templates/schemaTs/schema.ts.ejs +28 -0
  17. package/client-templates/tsBase/index.ts.ejs +27 -0
  18. package/dist/bundle/index.d.mts +8 -0
  19. package/dist/bundle/index.mjs +76 -0
  20. package/dist/dev/{diffSchema.d.mts → diffSegmentSchema.d.mts} +3 -3
  21. package/dist/dev/{diffSchema.mjs → diffSegmentSchema.mjs} +1 -1
  22. package/dist/dev/ensureSchemaFiles.d.mts +2 -2
  23. package/dist/dev/ensureSchemaFiles.mjs +16 -47
  24. package/dist/dev/index.d.mts +2 -0
  25. package/dist/dev/index.mjs +113 -64
  26. package/dist/dev/logDiffResult.d.mts +2 -2
  27. package/dist/dev/logDiffResult.mjs +6 -6
  28. package/dist/dev/writeMetaJson.d.mts +2 -0
  29. package/dist/dev/writeMetaJson.mjs +19 -0
  30. package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
  31. package/dist/dev/{writeOneSchemaFile.mjs → writeOneSegmentSchemaFile.mjs} +11 -8
  32. package/dist/generate/ensureClient.d.mts +2 -4
  33. package/dist/generate/ensureClient.mjs +26 -28
  34. package/dist/generate/generate.d.mts +13 -0
  35. package/dist/generate/generate.mjs +307 -0
  36. package/dist/generate/getClientTemplateFiles.d.mts +20 -0
  37. package/dist/generate/getClientTemplateFiles.mjs +85 -0
  38. package/dist/generate/getProjectFullSchema.d.mts +9 -0
  39. package/dist/generate/getProjectFullSchema.mjs +64 -0
  40. package/dist/generate/getTemplateClientImports.d.mts +18 -0
  41. package/dist/generate/getTemplateClientImports.mjs +36 -0
  42. package/dist/generate/index.d.mts +31 -11
  43. package/dist/generate/index.mjs +177 -85
  44. package/dist/generate/writeOneClientFile.d.mts +43 -0
  45. package/dist/generate/writeOneClientFile.mjs +150 -0
  46. package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
  47. package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +4 -1
  48. package/dist/getProjectInfo/getConfig/getRelativeSrcRoot.d.mts +3 -0
  49. package/dist/getProjectInfo/{getRelativeSrcRoot.mjs → getConfig/getRelativeSrcRoot.mjs} +3 -3
  50. package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +25 -0
  51. package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +168 -0
  52. package/dist/getProjectInfo/getConfig/getUserConfig.d.mts +9 -0
  53. package/dist/getProjectInfo/getConfig/getUserConfig.mjs +142 -0
  54. package/dist/getProjectInfo/getConfig/index.d.mts +15 -0
  55. package/dist/getProjectInfo/getConfig/index.mjs +92 -0
  56. package/dist/getProjectInfo/getMetaSchema.d.mts +4 -0
  57. package/dist/getProjectInfo/getMetaSchema.mjs +12 -0
  58. package/dist/getProjectInfo/index.d.mts +12 -16
  59. package/dist/getProjectInfo/index.mjs +23 -29
  60. package/dist/index.d.mts +3 -3
  61. package/dist/index.mjs +119 -40
  62. package/dist/init/checkTSConfigForExperimentalDecorators.d.mts +1 -1
  63. package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
  64. package/dist/init/createConfig.d.mts +5 -3
  65. package/dist/init/createConfig.mjs +77 -27
  66. package/dist/init/index.d.mts +2 -2
  67. package/dist/init/index.mjs +118 -103
  68. package/dist/init/installDependencies.d.mts +8 -6
  69. package/dist/init/installDependencies.mjs +7 -5
  70. package/dist/init/logUpdateDependenciesError.d.mts +6 -6
  71. package/dist/init/logUpdateDependenciesError.mjs +8 -4
  72. package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -3
  73. package/dist/init/updateDependenciesWithoutInstalling.mjs +41 -11
  74. package/dist/init/updateNPMScripts.d.mts +7 -2
  75. package/dist/init/updateNPMScripts.mjs +9 -5
  76. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  77. package/dist/init/updateTypeScriptConfig.mjs +12 -8
  78. package/dist/new/addClassToSegmentCode.d.mts +1 -1
  79. package/dist/new/addClassToSegmentCode.mjs +3 -3
  80. package/dist/new/addCommonTerms.d.mts +1 -1
  81. package/dist/new/addCommonTerms.mjs +1 -1
  82. package/dist/new/index.d.mts +2 -1
  83. package/dist/new/index.mjs +6 -5
  84. package/dist/new/newModule.d.mts +5 -3
  85. package/dist/new/newModule.mjs +34 -27
  86. package/dist/new/newSegment.d.mts +5 -2
  87. package/dist/new/newSegment.mjs +23 -16
  88. package/dist/new/render.d.mts +6 -3
  89. package/dist/new/render.mjs +15 -14
  90. package/dist/types.d.mts +64 -61
  91. package/dist/utils/chalkHighlightThing.d.mts +1 -1
  92. package/dist/utils/chalkHighlightThing.mjs +1 -1
  93. package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
  94. package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
  95. package/dist/utils/compileTs.d.mts +12 -0
  96. package/dist/utils/compileTs.mjs +263 -0
  97. package/dist/utils/debounceWithArgs.d.mts +3 -2
  98. package/dist/utils/debounceWithArgs.mjs +1 -1
  99. package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
  100. package/dist/utils/formatLoggedSegmentName.mjs +4 -3
  101. package/dist/utils/generateFnName.d.mts +23 -0
  102. package/dist/utils/generateFnName.mjs +78 -0
  103. package/dist/utils/getAvailablePort.d.mts +1 -2
  104. package/dist/utils/getAvailablePort.mjs +1 -2
  105. package/dist/utils/getFileSystemEntryType.d.mts +1 -1
  106. package/dist/utils/getFileSystemEntryType.mjs +1 -1
  107. package/dist/utils/getLogger.d.mts +1 -1
  108. package/dist/utils/getLogger.mjs +1 -1
  109. package/dist/utils/getNPMPackageMetadata.d.mts +2 -3
  110. package/dist/utils/getNPMPackageMetadata.mjs +1 -1
  111. package/dist/utils/getPackageJson.d.mts +3 -0
  112. package/dist/utils/getPackageJson.mjs +23 -0
  113. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  114. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  115. package/dist/utils/locateSegments.d.mts +12 -0
  116. package/dist/{locateSegments.mjs → utils/locateSegments.mjs} +14 -7
  117. package/dist/utils/normalizeOpenAPIMixin.d.mts +15 -0
  118. package/dist/utils/normalizeOpenAPIMixin.mjs +96 -0
  119. package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
  120. package/dist/utils/pickSegmentFullSchema.mjs +15 -0
  121. package/dist/utils/prettify.d.mts +1 -1
  122. package/dist/utils/prettify.mjs +1 -1
  123. package/dist/utils/removeUnlistedDirectories.d.mts +9 -0
  124. package/dist/utils/removeUnlistedDirectories.mjs +60 -0
  125. package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
  126. package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
  127. package/dist/utils/updateConfigProperty.d.mts +2 -0
  128. package/dist/utils/updateConfigProperty.mjs +132 -0
  129. package/module-templates/arktype/controller.ts.ejs +90 -0
  130. package/module-templates/type/controller.ts.ejs +80 -0
  131. package/module-templates/type/service.ts.ejs +43 -0
  132. package/module-templates/valibot/controller.ts.ejs +91 -0
  133. package/module-templates/zod/controller.ts.ejs +98 -0
  134. package/package.json +50 -25
  135. package/client-templates/main/main.cjs.ejs +0 -15
  136. package/client-templates/main/main.d.cts.ejs +0 -14
  137. package/client-templates/module/module.d.mts.ejs +0 -14
  138. package/client-templates/module/module.mjs.ejs +0 -24
  139. package/client-templates/python/__init__.py +0 -276
  140. package/client-templates/ts/index.ts.ejs +0 -25
  141. package/dist/dev/isSchemaEmpty.d.mts +0 -2
  142. package/dist/dev/isSchemaEmpty.mjs +0 -4
  143. package/dist/dev/writeOneSchemaFile.d.mts +0 -12
  144. package/dist/generate/getClientTemplates.d.mts +0 -16
  145. package/dist/generate/getClientTemplates.mjs +0 -42
  146. package/dist/getProjectInfo/getConfig.d.mts +0 -11
  147. package/dist/getProjectInfo/getConfig.mjs +0 -35
  148. package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
  149. package/dist/getProjectInfo/getRelativeSrcRoot.d.mts +0 -3
  150. package/dist/getProjectInfo/getUserConfig.d.mts +0 -9
  151. package/dist/getProjectInfo/getUserConfig.mjs +0 -27
  152. package/dist/getProjectInfo/importUncachedModule.d.mts +0 -3
  153. package/dist/getProjectInfo/importUncachedModule.mjs +0 -40
  154. package/dist/getProjectInfo/importUncachedModuleWorker.d.mts +0 -1
  155. package/dist/getProjectInfo/importUncachedModuleWorker.mjs +0 -25
  156. package/dist/init/getTemplateFilesFromPackage.d.mts +0 -7
  157. package/dist/init/getTemplateFilesFromPackage.mjs +0 -59
  158. package/dist/initProgram.d.mts +0 -2
  159. package/dist/initProgram.mjs +0 -22
  160. package/dist/locateSegments.d.mts +0 -11
  161. package/dist/postinstall.d.mts +0 -1
  162. package/dist/postinstall.mjs +0 -21
  163. package/templates/controller.ejs +0 -50
  164. package/templates/service.ejs +0 -27
@@ -0,0 +1,263 @@
1
+ import upperFirst from 'lodash/upperFirst.js';
2
+ import camelCase from 'lodash/camelCase.js';
3
+ export function compileTs(options) {
4
+ const context = {
5
+ refs: options.refs || new Map(),
6
+ compiledRefs: new Map(),
7
+ refsInProgress: new Set(),
8
+ };
9
+ // Collect all definitions from the schema
10
+ collectDefinitions(options.schema, context.refs);
11
+ // Ensure the main type name is valid
12
+ const mainTypeName = sanitizeTypeName(options.name);
13
+ const mainType = compileSchema(options.schema, mainTypeName, context);
14
+ // Compile all referenced types, unless dontCreateRefTypes is set
15
+ const compiledRefs = options.dontCreateRefTypes
16
+ ? ''
17
+ : Array.from(context.compiledRefs.entries())
18
+ .map(([, typeDecl]) => typeDecl)
19
+ .join('\n\n');
20
+ return compiledRefs
21
+ ? `${compiledRefs}\n\n${options.schema.description ? `/** ${escapeJSDocComment(options.schema.description)} */\n` : ''}export type ${mainTypeName} = ${mainType};`
22
+ : `${options.schema.description ? `/** ${escapeJSDocComment(options.schema.description)} */\n` : ''}export type ${mainTypeName} = ${mainType};`;
23
+ }
24
+ function collectDefinitions(schema, refs) {
25
+ // Collect from $defs
26
+ if (schema.$defs) {
27
+ Object.entries(schema.$defs).forEach(([key, def]) => {
28
+ if (typeof def === 'object') {
29
+ refs.set(`#/$defs/${key}`, def);
30
+ }
31
+ });
32
+ }
33
+ // Collect from definitions (older spec)
34
+ if (schema.definitions) {
35
+ Object.entries(schema.definitions).forEach(([key, def]) => {
36
+ if (typeof def === 'object') {
37
+ refs.set(`#/definitions/${key}`, def);
38
+ }
39
+ });
40
+ }
41
+ // Collect from components/schemas (OpenAPI spec)
42
+ if (schema?.components?.schemas) {
43
+ Object.entries(schema?.components?.schemas ?? {}).forEach(([key, def]) => {
44
+ if (typeof def === 'object') {
45
+ refs.set(`#/components/schemas/${key}`, def);
46
+ }
47
+ });
48
+ }
49
+ // Recursively collect from nested schemas
50
+ const schemasToProcess = [];
51
+ if (schema.properties) {
52
+ schemasToProcess.push(...Object.values(schema.properties).filter(isSchema));
53
+ }
54
+ if (schema.items) {
55
+ if (Array.isArray(schema.items)) {
56
+ schemasToProcess.push(...schema.items.filter(isSchema));
57
+ }
58
+ else if (isSchema(schema.items)) {
59
+ schemasToProcess.push(schema.items);
60
+ }
61
+ }
62
+ if (schema.additionalProperties && isSchema(schema.additionalProperties)) {
63
+ schemasToProcess.push(schema.additionalProperties);
64
+ }
65
+ if (schema.allOf)
66
+ schemasToProcess.push(...schema.allOf.filter(isSchema));
67
+ if (schema.anyOf)
68
+ schemasToProcess.push(...schema.anyOf.filter(isSchema));
69
+ if (schema.oneOf)
70
+ schemasToProcess.push(...schema.oneOf.filter(isSchema));
71
+ schemasToProcess.forEach((s) => {
72
+ collectDefinitions(s, refs);
73
+ });
74
+ }
75
+ function isSchema(value) {
76
+ return typeof value === 'object' && !Array.isArray(value);
77
+ }
78
+ function compileSchema(schema, name, context) {
79
+ if (typeof schema === 'boolean') {
80
+ return schema ? 'any' : 'never';
81
+ }
82
+ // Handle x-tsType extension
83
+ if ('x-tsType' in schema && typeof schema['x-tsType'] === 'string') {
84
+ return schema['x-tsType'];
85
+ }
86
+ // Handle $ref
87
+ if (schema.$ref) {
88
+ return handleRef(schema.$ref, context);
89
+ }
90
+ // Handle combinators
91
+ if (schema.allOf) {
92
+ return handleAllOf(schema.allOf, name, context);
93
+ }
94
+ if (schema.anyOf) {
95
+ return handleAnyOf(schema.anyOf, name, context);
96
+ }
97
+ if (schema.oneOf) {
98
+ return handleOneOf(schema.oneOf, name, context);
99
+ }
100
+ // Handle type-specific compilation
101
+ if (schema.enum) {
102
+ return handleEnum(schema.enum);
103
+ }
104
+ if (schema.const !== undefined) {
105
+ return handleConst(schema.const);
106
+ }
107
+ if (!schema.type) {
108
+ // No type specified, could be object
109
+ if (schema.properties || schema.additionalProperties) {
110
+ return handleObject(schema, name, context);
111
+ }
112
+ return 'any';
113
+ }
114
+ if (Array.isArray(schema.type)) {
115
+ return schema.type.map((t) => compileSchemaWithType({ ...schema, type: t }, name, context)).join(' | ');
116
+ }
117
+ return compileSchemaWithType(schema, name, context);
118
+ }
119
+ function compileSchemaWithType(schema, name, context) {
120
+ switch (schema.type) {
121
+ case 'null':
122
+ return 'null';
123
+ case 'boolean':
124
+ return 'boolean';
125
+ case 'string':
126
+ return 'string';
127
+ case 'number':
128
+ return 'number';
129
+ case 'integer':
130
+ return 'number';
131
+ case 'array':
132
+ return handleArray(schema, name, context);
133
+ case 'object':
134
+ return handleObject(schema, name, context);
135
+ default:
136
+ return 'any';
137
+ }
138
+ }
139
+ function handleRef(ref, context) {
140
+ const typeName = refToTypeName(ref);
141
+ // Check if we're already compiling this ref (circular reference)
142
+ if (context.refsInProgress.has(ref)) {
143
+ return typeName;
144
+ }
145
+ // Check if already compiled
146
+ if (context.compiledRefs.has(ref)) {
147
+ return typeName;
148
+ }
149
+ // Find the referenced schema
150
+ const referencedSchema = context.refs.get(ref);
151
+ if (!referencedSchema) {
152
+ return 'any'; // Reference not found
153
+ }
154
+ // Mark as in progress
155
+ context.refsInProgress.add(ref);
156
+ // Compile the referenced schema
157
+ const compiledType = compileSchema(referencedSchema, typeName, context);
158
+ const description = referencedSchema.description
159
+ ? `/** ${escapeJSDocComment(referencedSchema.description)} */\n`
160
+ : '';
161
+ context.compiledRefs.set(ref, `${description}export type ${typeName} = ${compiledType};`);
162
+ // Mark as completed
163
+ context.refsInProgress.delete(ref);
164
+ return typeName;
165
+ }
166
+ function handleAllOf(schemas, name, context) {
167
+ const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-all-of-${i}`), context));
168
+ // For allOf, we need to intersect types
169
+ // If they're all objects, we can merge them properly
170
+ const objectTypes = types.filter((t) => t.startsWith('{') && t.endsWith('}'));
171
+ if (objectTypes.length === types.length) {
172
+ // Merge object types
173
+ const merged = objectTypes.map((t) => t.slice(1, -1).trim()).filter((t) => t.length > 0);
174
+ return merged.length > 0 ? `{ ${merged.join('; ')} }` : '{}';
175
+ }
176
+ return types.join(' & ');
177
+ }
178
+ function handleAnyOf(schemas, name, context) {
179
+ const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-any-of-${i}`), context));
180
+ return types.join(' | ');
181
+ }
182
+ function handleOneOf(schemas, name, context) {
183
+ // For TypeScript, oneOf behaves like anyOf
184
+ const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-one-of-${i}`), context));
185
+ return types.join(' | ');
186
+ }
187
+ function handleEnum(enumValues) {
188
+ return enumValues.map((v) => JSON.stringify(v)).join(' | ');
189
+ }
190
+ function handleConst(value) {
191
+ return JSON.stringify(value);
192
+ }
193
+ function handleArray(schema, name, context) {
194
+ if (!schema.items) {
195
+ return 'any[]';
196
+ }
197
+ if (Array.isArray(schema.items)) {
198
+ // Tuple (ignoring min/max as requested)
199
+ const types = schema.items.map((item, i) => compileSchema(item, `${name}Item${i}`, context));
200
+ return `[${types.join(', ')}]`;
201
+ }
202
+ const itemType = compileSchema(schema.items, `${name}Item`, context);
203
+ return `${wrapUnionType(itemType)}[]`;
204
+ }
205
+ function handleObject(schema, name, context) {
206
+ const props = [];
207
+ // Handle known properties
208
+ if (schema.properties) {
209
+ const required = new Set(schema.required || []);
210
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
211
+ if (!isSchema(propSchema))
212
+ continue;
213
+ const isRequired = required.has(propName);
214
+ // Ensure the generated type name for nested properties is valid
215
+ const nestedTypeName = sanitizeTypeName(`${name}-${propName}`);
216
+ const propType = compileSchema(propSchema, nestedTypeName, context);
217
+ const safePropName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName) ? propName : `"${propName}"`;
218
+ // Add JSDoc comment if description is present
219
+ const comment = propSchema.description ? `\n/** ${escapeJSDocComment(propSchema.description)} */\n` : '';
220
+ props.push(`${comment}${safePropName}${isRequired ? '' : '?'}: ${propType}`);
221
+ }
222
+ }
223
+ // Handle additional properties
224
+ if (schema.additionalProperties === true) {
225
+ props.push('[key: string]: any');
226
+ }
227
+ else if (schema.additionalProperties && isSchema(schema.additionalProperties)) {
228
+ const additionalTypeName = sanitizeTypeName(`${name}-additional`);
229
+ const additionalType = compileSchema(schema.additionalProperties, additionalTypeName, context);
230
+ props.push(`[key: string]: ${additionalType}`);
231
+ }
232
+ // Handle pattern properties
233
+ if (schema.patternProperties) {
234
+ // For simplicity, treat pattern properties as string index signature
235
+ const patternTypes = Object.values(schema.patternProperties)
236
+ .filter(isSchema)
237
+ .map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-pattern-${i}`), context));
238
+ if (patternTypes.length > 0) {
239
+ props.push(`[key: string]: ${patternTypes.join(' | ')}`);
240
+ }
241
+ }
242
+ return props.length > 0 ? `{ ${props.join('; ')} }` : '{}';
243
+ }
244
+ function refToTypeName(ref) {
245
+ // Extract the last part of the reference as the type name
246
+ const parts = ref.split('/');
247
+ const name = parts[parts.length - 1];
248
+ // Convert kebab-case to PascalCase
249
+ return upperFirst(camelCase(name));
250
+ }
251
+ function wrapUnionType(type) {
252
+ // Wrap union types in parentheses for array types
253
+ return type.includes('|') ? `(${type})` : type;
254
+ }
255
+ function sanitizeTypeName(name) {
256
+ return upperFirst(camelCase(name));
257
+ }
258
+ // Utility function to escape JSDoc comment terminators in descriptions
259
+ function escapeJSDocComment(description) {
260
+ if (!description)
261
+ return '';
262
+ return description.replace(/\*\//g, '*\\/');
263
+ }
@@ -1,2 +1,3 @@
1
- import { KnownAny } from '../types.mjs';
2
- export default function debounceWithArgs<Callback extends (...args: KnownAny[]) => KnownAny>(callback: Callback, wait: number): (...args: Parameters<Callback>) => Promise<Awaited<ReturnType<Callback>>>;
1
+ type KnownAny = any;
2
+ export declare function debounceWithArgs<Callback extends (...args: KnownAny[]) => KnownAny>(callback: Callback, wait: number): (...args: Parameters<Callback>) => Promise<Awaited<ReturnType<Callback>>>;
3
+ export {};
@@ -1,4 +1,4 @@
1
- export default function debounceWithArgs(callback, wait) {
1
+ export function debounceWithArgs(callback, wait) {
2
2
  // Stores timeouts keyed by the stringified arguments
3
3
  const timeouts = new Map();
4
4
  return (...args) => {
@@ -1,4 +1,6 @@
1
- export default function formatLoggedSegmentName(segmentName: string, { withChalk, upperFirst }?: {
1
+ export declare function formatLoggedSegmentName(segmentName: string, { withChalk, upperFirst, isStatic, segmentType, }?: {
2
2
  withChalk?: boolean;
3
3
  upperFirst?: boolean;
4
+ isStatic?: boolean;
5
+ segmentType?: 'segment' | 'mixin';
4
6
  }): string;
@@ -1,7 +1,8 @@
1
1
  import upperFirstLodash from 'lodash/upperFirst.js';
2
- import chalkHighlightThing from './chalkHighlightThing.mjs';
3
- export default function formatLoggedSegmentName(segmentName, { withChalk = true, upperFirst = false } = {}) {
4
- let text = segmentName ? `segment "${segmentName}"` : 'the root segment';
2
+ import { chalkHighlightThing } from './chalkHighlightThing.mjs';
3
+ export function formatLoggedSegmentName(segmentName, { withChalk = true, upperFirst = false, isStatic = false, segmentType = 'segment', // TODO: Apply to all formatLoggedSegmentName invocations
4
+ } = {}) {
5
+ let text = segmentName ? `${isStatic ? 'static ' : ''}${segmentType} "${segmentName}"` : 'the root segment';
5
6
  text = upperFirst ? upperFirstLodash(text) : text;
6
7
  return withChalk ? chalkHighlightThing(text) : text;
7
8
  }
@@ -0,0 +1,23 @@
1
+ import type { HttpMethod } from 'vovk';
2
+ export interface VerbMapEntry {
3
+ noParams?: string;
4
+ withParams?: string;
5
+ default?: string;
6
+ }
7
+ export declare const VERB_MAP: Record<HttpMethod, VerbMapEntry>;
8
+ export declare function capitalize(str: string): string;
9
+ interface GenerateFnNameOptions {
10
+ /** Segments to strip out (e.g. ['api','v1']) */
11
+ ignoreSegments?: string[];
12
+ }
13
+ /**
14
+ * Turn an HTTP method + OpenAPI path into a camelCased function name.
15
+ *
16
+ * Examples:
17
+ * generateFnName('GET', '/users') // "listUsers"
18
+ * generateFnName('GET', '/users/{id}') // "getUsersById"
19
+ * generateFnName('POST', '/v1/api/orders') // "createOrders"
20
+ * generateFnName('PATCH', '/users/{userId}/profile') // "patchUsersProfileByUserId"
21
+ */
22
+ export declare function generateFnName(method: HttpMethod, rawPath: string, opts?: GenerateFnNameOptions): string;
23
+ export {};
@@ -0,0 +1,78 @@
1
+ export const VERB_MAP = {
2
+ GET: { noParams: 'list', withParams: 'get' },
3
+ POST: { default: 'create' },
4
+ PUT: { default: 'update' },
5
+ PATCH: { default: 'patch' },
6
+ DELETE: { default: 'delete' },
7
+ HEAD: { default: 'head' },
8
+ OPTIONS: { default: 'options' },
9
+ };
10
+ export function capitalize(str) {
11
+ if (str.length === 0)
12
+ return '';
13
+ return str[0].toUpperCase() + str.slice(1);
14
+ }
15
+ const DEFAULT_OPTIONS = {
16
+ ignoreSegments: ['api'],
17
+ };
18
+ /**
19
+ * Turn an HTTP method + OpenAPI path into a camelCased function name.
20
+ *
21
+ * Examples:
22
+ * generateFnName('GET', '/users') // "listUsers"
23
+ * generateFnName('GET', '/users/{id}') // "getUsersById"
24
+ * generateFnName('POST', '/v1/api/orders') // "createOrders"
25
+ * generateFnName('PATCH', '/users/{userId}/profile') // "patchUsersProfileByUserId"
26
+ */
27
+ export function generateFnName(method, rawPath, opts = {}) {
28
+ const { ignoreSegments } = {
29
+ ...DEFAULT_OPTIONS,
30
+ ...opts,
31
+ };
32
+ // 1. Clean & split path
33
+ const parts = rawPath
34
+ .replace(/^\/|\/$/g, '') // strip leading/trailing slash
35
+ .split('/')
36
+ .filter((seg) => !ignoreSegments?.includes(seg.toLowerCase()))
37
+ .filter(Boolean);
38
+ // 2. Separate resource tokens from path-params
39
+ const resources = [];
40
+ const params = [];
41
+ parts.forEach((seg) => {
42
+ const match = seg.match(/^{?([^}]+)}?$/);
43
+ if (match) {
44
+ params.push(match[1]);
45
+ }
46
+ else {
47
+ resources.push(seg);
48
+ }
49
+ });
50
+ // 3. Pick base verb from VERB_MAP
51
+ let baseVerb;
52
+ if (method === 'GET') {
53
+ // biome-ignore lint/style/noNonNullAssertion: TODO
54
+ baseVerb = params.length ? VERB_MAP.GET.withParams : VERB_MAP.GET.noParams;
55
+ }
56
+ else {
57
+ // biome-ignore lint/style/noNonNullAssertion: TODO
58
+ baseVerb = VERB_MAP[method].default;
59
+ }
60
+ // 4. Build the “resource” part
61
+ const resourcePart = resources.map(capitalize).join('');
62
+ // 5. Build the “ByParam” suffix
63
+ const byParams = params.length ? `By${params.map(capitalize).join('')}` : '';
64
+ // 6. Combine and ensure camelCase
65
+ const rawName = `${baseVerb}${resourcePart}${byParams}`;
66
+ return rawName[0].toLowerCase() + rawName.slice(1);
67
+ }
68
+ /*
69
+ // --- Example usage ---
70
+ console.log(generateFnName('GET', '/users')); // listUsers
71
+ console.log(generateFnName('GET', '/users/{id}')); // getUsersById
72
+ console.log(generateFnName('POST', '/users')); // createUsers
73
+ console.log(generateFnName('PATCH', '/users/{userId}/profile')); // patchUsersProfileByUserId
74
+ console.log(generateFnName('DELETE', '/v1/api/orders/{orderId}')); // deleteOrdersByOrderId
75
+
76
+ // You can also enable singularization:
77
+ console.log(generateFnName('GET', '/users/{userId}/orders', { singularizeResources: true })); // getUserOrderByUserId
78
+ */
@@ -6,5 +6,4 @@
6
6
  * @param {(failedPort: number, tryingPort: number) => void} onWarning - The callback function for warnings.
7
7
  * @returns {Promise<string>}
8
8
  */
9
- declare function getAvailablePort(startPort: number, maxAttempts: number, attempt: number, onWarning: (failedPort: number, tryingPort: number) => void): Promise<string>;
10
- export default getAvailablePort;
9
+ export declare function getAvailablePort(startPort: number, maxAttempts: number, attempt: number, onWarning: (failedPort: number, tryingPort: number) => void): Promise<string>;
@@ -24,7 +24,7 @@ function checkPort(port, callback) {
24
24
  * @param {(failedPort: number, tryingPort: number) => void} onWarning - The callback function for warnings.
25
25
  * @returns {Promise<string>}
26
26
  */
27
- function getAvailablePort(startPort, maxAttempts, attempt, onWarning) {
27
+ export function getAvailablePort(startPort, maxAttempts, attempt, onWarning) {
28
28
  return new Promise((resolve, reject) => {
29
29
  checkPort(startPort, (isAvailable) => {
30
30
  if (isAvailable) {
@@ -40,4 +40,3 @@ function getAvailablePort(startPort, maxAttempts, attempt, onWarning) {
40
40
  });
41
41
  });
42
42
  }
43
- export default getAvailablePort;
@@ -2,4 +2,4 @@ export declare enum FileSystemEntryType {
2
2
  FILE = "FILE",
3
3
  DIRECTORY = "DIRECTORY"
4
4
  }
5
- export default function getFileSystemEntryType(filePath: string): Promise<FileSystemEntryType | null>;
5
+ export declare function getFileSystemEntryType(filePath: string): Promise<FileSystemEntryType | null>;
@@ -4,7 +4,7 @@ export var FileSystemEntryType;
4
4
  FileSystemEntryType["FILE"] = "FILE";
5
5
  FileSystemEntryType["DIRECTORY"] = "DIRECTORY";
6
6
  })(FileSystemEntryType || (FileSystemEntryType = {}));
7
- export default async function getFileSystemEntryType(filePath) {
7
+ export async function getFileSystemEntryType(filePath) {
8
8
  try {
9
9
  const stats = await fs.stat(filePath);
10
10
  if (stats.isFile()) {
@@ -1,5 +1,5 @@
1
1
  import loglevel, { type LogLevelNames } from 'loglevel';
2
- export default function getLogger(level: LogLevelNames): {
2
+ export declare function getLogger(level: LogLevelNames): {
3
3
  info: (msg: string) => void;
4
4
  warn: (msg: string) => void;
5
5
  error: (msg: string) => void;
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import loglevel from 'loglevel';
3
- export default function getLogger(level) {
3
+ export function getLogger(level) {
4
4
  const log = {
5
5
  info: (msg) => loglevel.info(chalk.white(`🐺 ${msg}`)),
6
6
  warn: (msg) => loglevel.warn(chalk.yellowBright(`🐺 ${msg}`)),
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Interface representing the structure of NPM package metadata.
3
3
  */
4
- interface NpmPackageMetadata {
4
+ export interface NpmPackageMetadata {
5
5
  'dist-tags': {
6
6
  [tag: string]: string;
7
7
  };
@@ -13,5 +13,4 @@ interface NpmPackageMetadata {
13
13
  };
14
14
  };
15
15
  }
16
- export default function getNPMPackageMetadata(packageName: string): Promise<NpmPackageMetadata>;
17
- export {};
16
+ export declare function getNPMPackageMetadata(packageName: string): Promise<NpmPackageMetadata>;
@@ -1,4 +1,4 @@
1
- export default async function getNPMPackageMetadata(packageName) {
1
+ export async function getNPMPackageMetadata(packageName) {
2
2
  // Fetch package metadata from the npm registry
3
3
  const metadataResponse = await fetch(`https://registry.npmjs.org/${encodeURIComponent(packageName)}`);
4
4
  if (!metadataResponse.ok) {
@@ -0,0 +1,3 @@
1
+ import type { PackageJson } from 'type-fest';
2
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
+ export declare function getPackageJson(cwd: string, log: ProjectInfo['log']): Promise<PackageJson>;
@@ -0,0 +1,23 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { chalkHighlightThing } from './chalkHighlightThing.mjs';
4
+ let cachedPromise;
5
+ export function getPackageJson(cwd, log) {
6
+ const pkgPath = path.join(cwd, 'package.json');
7
+ // If we have a cached promise, return it
8
+ if (cachedPromise) {
9
+ return cachedPromise;
10
+ }
11
+ const promise = fs
12
+ .readFile(pkgPath, 'utf8')
13
+ .then((content) => JSON.parse(content))
14
+ .catch(() => {
15
+ cachedPromise = undefined;
16
+ log.warn(`Unable to load package.json at ${chalkHighlightThing(pkgPath)}. Using an empty fallback.`);
17
+ return {
18
+ name: 'unknown',
19
+ };
20
+ });
21
+ cachedPromise = promise;
22
+ return promise;
23
+ }
@@ -0,0 +1,4 @@
1
+ export declare function getPublicModuleNameFromPath(modulePath: string): {
2
+ moduleName: string | null;
3
+ restPath: string;
4
+ };
@@ -0,0 +1,9 @@
1
+ export function getPublicModuleNameFromPath(modulePath) {
2
+ if (modulePath && !modulePath.startsWith('.') && !modulePath.startsWith('/')) {
3
+ const pathParts = modulePath.split('/');
4
+ const moduleName = pathParts[0].startsWith('@') ? `${pathParts[0]}/${pathParts[1]}` : pathParts[0];
5
+ const restPath = pathParts.slice(pathParts[0].startsWith('@') ? 2 : 1).join('/');
6
+ return { moduleName, restPath };
7
+ }
8
+ return { moduleName: null, restPath: modulePath };
9
+ }
@@ -0,0 +1,12 @@
1
+ import type { VovkStrictConfig } from 'vovk/internal';
2
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
+ export type Segment = {
4
+ routeFilePath: string;
5
+ segmentName: string;
6
+ };
7
+ export declare function locateSegments({ dir, rootDir, config, log, }: {
8
+ dir: string | null;
9
+ rootDir?: string;
10
+ config: VovkStrictConfig | null;
11
+ log: ProjectInfo['log'];
12
+ }): Promise<Segment[]>;
@@ -1,12 +1,20 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import getFileSystemEntryType from './utils/getFileSystemEntryType.mjs';
4
- // config: null is used for testing
5
- export default async function locateSegments({ dir, rootDir, config, }) {
3
+ import { getFileSystemEntryType } from './getFileSystemEntryType.mjs';
4
+ export async function locateSegments({ dir, rootDir, config, log, }) {
6
5
  let results = [];
6
+ if (!dir)
7
+ return results; // If dir is null, return empty results because this isn't a Next.js app
7
8
  rootDir = rootDir ?? dir;
9
+ let list;
8
10
  // Read the contents of the directory
9
- const list = await fs.readdir(dir);
11
+ try {
12
+ list = (await fs.readdir(dir)).toSorted();
13
+ }
14
+ catch {
15
+ // do nothing
16
+ return results;
17
+ }
10
18
  // Iterate through each item in the directory
11
19
  for (const file of list) {
12
20
  const filePath = path.join(dir, file);
@@ -19,12 +27,11 @@ export default async function locateSegments({ dir, rootDir, config, }) {
19
27
  if (await getFileSystemEntryType(routeFilePath)) {
20
28
  // Calculate the basePath relative to the root directory
21
29
  const segmentName = path.relative(rootDir, dir).replace(/\\/g, '/'); // windows fix
22
- const segmentImportPath = path.relative(config?.clientOutDir ?? '.__', routeFilePath);
23
- results.push({ routeFilePath, segmentName, segmentImportPath });
30
+ results.push({ routeFilePath, segmentName });
24
31
  }
25
32
  }
26
33
  // Recursively search inside subdirectories
27
- const subDirResults = await locateSegments({ dir: filePath, rootDir, config });
34
+ const subDirResults = await locateSegments({ dir: filePath, rootDir, config, log });
28
35
  results = results.concat(subDirResults);
29
36
  }
30
37
  }
@@ -0,0 +1,15 @@
1
+ import type { HttpMethod, VovkConfig } from 'vovk';
2
+ import type { VovkOperationObject, VovkStrictConfig } from 'vovk/internal';
3
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
4
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
5
+ export type GetOpenAPINameFn = (config: {
6
+ operationObject: VovkOperationObject;
7
+ method: HttpMethod;
8
+ path: string;
9
+ openAPIObject: OpenAPIObject;
10
+ }) => string;
11
+ export declare function normalizeOpenAPIMixin({ mixinModule, log, cwd, }: {
12
+ mixinModule: NonNullable<NonNullable<NonNullable<NonNullable<VovkConfig['outputConfig']>['segments']>[string]>['openAPIMixin']>;
13
+ log: ProjectInfo['log'];
14
+ cwd?: string;
15
+ }): Promise<NonNullable<NonNullable<NonNullable<NonNullable<VovkStrictConfig['outputConfig']>['segments']>[string]>['openAPIMixin']>>;