vovk-cli 0.0.1-draft.3 → 0.0.1-draft.300

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 (152) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +29 -1
  3. package/client-templates/cjs/index.cjs.ejs +19 -0
  4. package/client-templates/cjs/index.d.cts.ejs +25 -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/mjs/index.d.mts.ejs +25 -0
  8. package/client-templates/mjs/index.mjs.ejs +23 -0
  9. package/client-templates/packageJson/package.json.ejs +1 -0
  10. package/client-templates/readme/README.md.ejs +38 -0
  11. package/client-templates/schemaCjs/schema.cjs.ejs +26 -0
  12. package/client-templates/schemaCjs/schema.d.cts.ejs +10 -0
  13. package/client-templates/schemaJson/schema.json.ejs +1 -0
  14. package/client-templates/schemaTs/schema.ts.ejs +35 -0
  15. package/client-templates/ts/index.ts.ejs +33 -0
  16. package/dist/bundle/index.d.mts +8 -0
  17. package/dist/bundle/index.mjs +90 -0
  18. package/dist/dev/diffSegmentSchema.d.mts +36 -0
  19. package/dist/{watcher/diffSchema.mjs → dev/diffSegmentSchema.mjs} +4 -12
  20. package/dist/{watcher → dev}/ensureSchemaFiles.d.mts +3 -0
  21. package/dist/{watcher → dev}/ensureSchemaFiles.mjs +17 -21
  22. package/dist/dev/index.d.mts +9 -0
  23. package/dist/dev/index.mjs +388 -0
  24. package/dist/dev/logDiffResult.d.mts +3 -0
  25. package/dist/dev/logDiffResult.mjs +57 -0
  26. package/dist/dev/writeMetaJson.d.mts +2 -0
  27. package/dist/dev/writeMetaJson.mjs +17 -0
  28. package/dist/dev/writeOneSegmentSchemaFile.d.mts +12 -0
  29. package/dist/dev/writeOneSegmentSchemaFile.mjs +32 -0
  30. package/dist/generate/ensureClient.d.mts +3 -0
  31. package/dist/generate/ensureClient.mjs +32 -0
  32. package/dist/generate/generate.d.mts +15 -0
  33. package/dist/generate/generate.mjs +291 -0
  34. package/dist/generate/getClientTemplateFiles.d.mts +20 -0
  35. package/dist/generate/getClientTemplateFiles.mjs +89 -0
  36. package/dist/generate/getProjectFullSchema.d.mts +7 -0
  37. package/dist/generate/getProjectFullSchema.mjs +65 -0
  38. package/dist/generate/getTemplateClientImports.d.mts +18 -0
  39. package/dist/generate/getTemplateClientImports.mjs +38 -0
  40. package/dist/generate/index.d.mts +33 -0
  41. package/dist/generate/index.mjs +189 -0
  42. package/dist/generate/mergePackages.d.mts +7 -0
  43. package/dist/generate/mergePackages.mjs +55 -0
  44. package/dist/generate/writeOneClientFile.d.mts +36 -0
  45. package/dist/generate/writeOneClientFile.mjs +120 -0
  46. package/dist/getProjectInfo/getConfig/getConfigAbsolutePaths.d.mts +5 -0
  47. package/dist/getProjectInfo/{getConfigAbsolutePaths.mjs → getConfig/getConfigAbsolutePaths.mjs} +6 -3
  48. package/dist/getProjectInfo/{getRelativeSrcRoot.d.mts → getConfig/getRelativeSrcRoot.d.mts} +1 -1
  49. package/dist/getProjectInfo/getConfig/getRelativeSrcRoot.mjs +12 -0
  50. package/dist/getProjectInfo/getConfig/getTemplateDefs.d.mts +16 -0
  51. package/dist/getProjectInfo/getConfig/getTemplateDefs.mjs +98 -0
  52. package/dist/getProjectInfo/{getUserConfig.d.mts → getConfig/getUserConfig.d.mts} +3 -2
  53. package/dist/getProjectInfo/{getUserConfig.mjs → getConfig/getUserConfig.mjs} +7 -5
  54. package/dist/getProjectInfo/{importUncachedModule.mjs → getConfig/importUncachedModule.mjs} +1 -5
  55. package/dist/getProjectInfo/{importUncachedModuleWorker.mjs → getConfig/importUncachedModuleWorker.mjs} +0 -1
  56. package/dist/getProjectInfo/getConfig/index.d.mts +120 -0
  57. package/dist/getProjectInfo/getConfig/index.mjs +94 -0
  58. package/dist/getProjectInfo/index.d.mts +12 -9
  59. package/dist/getProjectInfo/index.mjs +22 -23
  60. package/dist/index.d.mts +2 -24
  61. package/dist/index.mjs +106 -69
  62. package/dist/init/checkTSConfigForExperimentalDecorators.mjs +2 -2
  63. package/dist/init/createConfig.d.mts +3 -4
  64. package/dist/init/createConfig.mjs +22 -16
  65. package/dist/init/getTemplateFilesFromPackage.d.mts +2 -1
  66. package/dist/init/getTemplateFilesFromPackage.mjs +13 -9
  67. package/dist/init/index.d.mts +2 -3
  68. package/dist/init/index.mjs +119 -138
  69. package/dist/init/installDependencies.d.mts +4 -1
  70. package/dist/init/installDependencies.mjs +6 -4
  71. package/dist/init/logUpdateDependenciesError.d.mts +13 -0
  72. package/dist/init/logUpdateDependenciesError.mjs +51 -0
  73. package/dist/init/updateDependenciesWithoutInstalling.d.mts +3 -2
  74. package/dist/init/updateDependenciesWithoutInstalling.mjs +50 -15
  75. package/dist/init/updateNPMScripts.d.mts +3 -1
  76. package/dist/init/updateNPMScripts.mjs +10 -7
  77. package/dist/init/updateTypeScriptConfig.d.mts +4 -1
  78. package/dist/init/updateTypeScriptConfig.mjs +13 -9
  79. package/dist/initProgram.d.mts +2 -0
  80. package/dist/initProgram.mjs +22 -0
  81. package/dist/locateSegments.d.mts +8 -1
  82. package/dist/locateSegments.mjs +16 -6
  83. package/dist/new/addClassToSegmentCode.d.mts +1 -2
  84. package/dist/new/addClassToSegmentCode.mjs +9 -5
  85. package/dist/new/addCommonTerms.mjs +1 -0
  86. package/dist/new/index.d.mts +2 -2
  87. package/dist/new/index.mjs +14 -3
  88. package/dist/new/newModule.d.mts +7 -2
  89. package/dist/new/newModule.mjs +61 -35
  90. package/dist/new/newSegment.d.mts +4 -2
  91. package/dist/new/newSegment.mjs +22 -13
  92. package/dist/new/render.d.mts +9 -9
  93. package/dist/new/render.mjs +38 -13
  94. package/dist/types.d.mts +73 -28
  95. package/dist/utils/compileJSONSchemaToTypeScriptType.d.mts +5 -0
  96. package/dist/utils/compileJSONSchemaToTypeScriptType.mjs +9 -0
  97. package/dist/utils/compileTs.d.mts +12 -0
  98. package/dist/utils/compileTs.mjs +261 -0
  99. package/dist/utils/debounceWithArgs.d.mts +2 -2
  100. package/dist/utils/debounceWithArgs.mjs +24 -9
  101. package/dist/utils/formatLoggedSegmentName.d.mts +3 -1
  102. package/dist/utils/formatLoggedSegmentName.mjs +4 -3
  103. package/dist/utils/getAvailablePort.mjs +3 -2
  104. package/dist/utils/getFileSystemEntryType.mjs +1 -1
  105. package/dist/utils/getPackageJson.d.mts +3 -0
  106. package/dist/utils/getPackageJson.mjs +22 -0
  107. package/dist/utils/getPublicModuleNameFromPath.d.mts +4 -0
  108. package/dist/utils/getPublicModuleNameFromPath.mjs +9 -0
  109. package/dist/utils/normalizeOpenAPIMixins.d.mts +7 -0
  110. package/dist/utils/normalizeOpenAPIMixins.mjs +67 -0
  111. package/dist/utils/pickSegmentFullSchema.d.mts +3 -0
  112. package/dist/utils/pickSegmentFullSchema.mjs +15 -0
  113. package/dist/utils/removeUnlistedDirectories.d.mts +10 -0
  114. package/dist/utils/removeUnlistedDirectories.mjs +61 -0
  115. package/dist/utils/resolveAbsoluteModulePath.d.mts +2 -0
  116. package/dist/utils/resolveAbsoluteModulePath.mjs +32 -0
  117. package/module-templates/controller.ts.ejs +56 -0
  118. package/module-templates/service.ts.ejs +28 -0
  119. package/package.json +42 -21
  120. package/dist/generateClient.d.mts +0 -7
  121. package/dist/generateClient.mjs +0 -97
  122. package/dist/getProjectInfo/directoryExists.d.mts +0 -1
  123. package/dist/getProjectInfo/directoryExists.mjs +0 -10
  124. package/dist/getProjectInfo/getConfig.d.mts +0 -11
  125. package/dist/getProjectInfo/getConfig.mjs +0 -29
  126. package/dist/getProjectInfo/getConfigAbsolutePaths.d.mts +0 -4
  127. package/dist/getProjectInfo/getRelativeSrcRoot.mjs +0 -12
  128. package/dist/postinstall.d.mts +0 -1
  129. package/dist/postinstall.mjs +0 -22
  130. package/dist/watcher/diffSchema.d.mts +0 -43
  131. package/dist/watcher/index.d.mts +0 -6
  132. package/dist/watcher/index.mjs +0 -295
  133. package/dist/watcher/isMetadataEmpty.d.mts +0 -2
  134. package/dist/watcher/isMetadataEmpty.mjs +0 -4
  135. package/dist/watcher/logDiffResult.d.mts +0 -3
  136. package/dist/watcher/logDiffResult.mjs +0 -90
  137. package/dist/watcher/writeOneSchemaFile.d.mts +0 -11
  138. package/dist/watcher/writeOneSchemaFile.mjs +0 -27
  139. package/templates/controller.ejs +0 -50
  140. package/templates/service.ejs +0 -7
  141. package/templates/worker.ejs +0 -1
  142. package/templates_old/MyThingController.c.only.template.ts +0 -32
  143. package/templates_old/MyThingController.c.template.ts +0 -34
  144. package/templates_old/MyThingService.s.template.ts +0 -18
  145. package/templates_old/controller.ejs +0 -85
  146. package/templates_old/service.ejs +0 -9
  147. package/templates_old/worker.ejs +0 -9
  148. package/templates_old/zod/MyThingController.c.only.template.ts +0 -32
  149. package/templates_old/zod/MyThingController.c.template.ts +0 -39
  150. package/templates_old/zod/MyThingService.s.template.ts +0 -18
  151. /package/dist/getProjectInfo/{importUncachedModule.d.mts → getConfig/importUncachedModule.d.mts} +0 -0
  152. /package/dist/getProjectInfo/{importUncachedModuleWorker.d.mts → getConfig/importUncachedModuleWorker.d.mts} +0 -0
package/dist/types.d.mts CHANGED
@@ -1,39 +1,84 @@
1
- import { LogLevelNames } from 'loglevel';
2
- export type KnownAny = any;
1
+ import type { LogLevelNames } from 'loglevel';
2
+ import type { VovkStrictConfig } from 'vovk';
3
+ export type VovkModuleRenderResult = {
4
+ fileName: string;
5
+ dir: string;
6
+ sourceName?: string;
7
+ compiledName?: string;
8
+ code: string;
9
+ };
10
+ export interface DevOptions {
11
+ schemaOut?: string;
12
+ nextDev?: boolean;
13
+ exit?: boolean;
14
+ }
15
+ export interface GenerateOptions {
16
+ prettify?: boolean;
17
+ configPath?: string;
18
+ schemaPath?: string;
19
+ openapiSpec?: string[];
20
+ openapiGetModuleName?: string[];
21
+ openapiGetMethodName?: string[];
22
+ openapiRootUrl?: string[];
23
+ openapiMixinName?: string[];
24
+ watch?: boolean | string;
25
+ forceTsStandalone?: boolean;
26
+ composedFrom?: string[];
27
+ composedOut?: string;
28
+ composedOnly?: boolean;
29
+ composedIncludeSegments?: string[];
30
+ composedExcludeSegments?: string[];
31
+ segmentedFrom?: string[];
32
+ segmentedOut?: string;
33
+ segmentedOnly?: boolean;
34
+ segmentedIncludeSegments?: string[];
35
+ segmentedExcludeSegments?: string[];
36
+ }
37
+ export interface BundleOptions extends Partial<Omit<VovkStrictConfig['bundle'], 'requires'>> {
38
+ config?: string;
39
+ schema?: string;
40
+ openapiSpec?: string[];
41
+ openapiGetModuleName?: string[];
42
+ openapiGetMethodName?: string[];
43
+ openapiRootUrl?: string[];
44
+ openapiMixinName?: string[];
45
+ forceTsStandalone?: boolean;
46
+ }
47
+ export interface InitOptions {
48
+ yes?: boolean;
49
+ logLevel: LogLevelNames;
50
+ useNpm?: boolean;
51
+ useYarn?: boolean;
52
+ usePnpm?: boolean;
53
+ useBun?: boolean;
54
+ skipInstall?: boolean;
55
+ updateTsConfig?: boolean;
56
+ updateScripts?: 'implicit' | 'explicit';
57
+ validationLibrary?: string | null;
58
+ reactQuery?: boolean;
59
+ dryRun?: boolean;
60
+ lang?: string[];
61
+ channel?: 'latest' | 'beta' | 'draft';
62
+ }
63
+ export interface NewOptions {
64
+ dryRun?: boolean;
65
+ templates?: string[];
66
+ dir?: string;
67
+ overwrite?: boolean;
68
+ noSegmentUpdate?: boolean;
69
+ empty?: boolean;
70
+ static?: boolean;
71
+ }
3
72
  export type VovkEnv = {
4
73
  PORT?: string;
5
- VOVK_CLIENT_OUT_DIR?: string;
6
74
  VOVK_SCHEMA_OUT_DIR?: string;
7
- VOVK_FETCHER?: string;
8
- VOVK_VALIDATE_ON_CLIENT?: string;
9
- VOVK_MODULES_DIR?: string;
10
- VOVK_VALIDATION_LIBRARY?: string;
11
75
  VOVK_ORIGIN?: string;
12
76
  VOVK_ROOT_ENTRY?: string;
13
77
  VOVK_API_ENTRY_POINT?: string;
14
- VOVK_ROOT_SEGMENT_MODULES_DIR_NAME?: string;
15
78
  VOVK_LOG_LEVEL?: LogLevelNames;
16
79
  VOVK_PRETTIFY_CLIENT?: string;
17
80
  VOVK_DEV_HTTPS?: string;
18
81
  __VOVK_START_WATCHER_IN_STANDALONE_MODE__?: 'true';
19
- };
20
- export type VovkConfig = {
21
- clientOutDir?: string;
22
- schemaOutDir?: string;
23
- fetcher?: string;
24
- validateOnClient?: string | null;
25
- modulesDir?: string;
26
- validationLibrary?: string | null;
27
- rootEntry?: string;
28
- origin?: string;
29
- rootSegmentModulesDirName?: string;
30
- logLevel?: LogLevelNames;
31
- prettifyClient?: boolean;
32
- devHttps?: boolean;
33
- templates?: {
34
- service?: string;
35
- controller?: string;
36
- worker?: string;
37
- [key: string]: string | undefined;
38
- };
82
+ __VOVK_SCHEMA_OUT_FLAG__?: string;
83
+ __VOVK_EXIT__?: 'true' | 'false';
39
84
  };
@@ -0,0 +1,5 @@
1
+ import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
+ import type { JSONSchema7 } from 'json-schema';
3
+ export declare function compileJSONSchemaToTypeScriptType(schema: JSONSchema7, typeName: string, components?: NonNullable<OpenAPIObject['components']>, options?: {
4
+ dontCreateRefTypes?: boolean;
5
+ }): string;
@@ -0,0 +1,9 @@
1
+ import { compileTs } from './compileTs.mjs';
2
+ export function compileJSONSchemaToTypeScriptType(schema, typeName, components = {}, options = {}) {
3
+ if (!schema)
4
+ return '';
5
+ if ('tsType' in schema && typeof schema.tsType === 'string')
6
+ return `export type ${typeName} = ${schema.tsType};\n`;
7
+ const tsType = compileTs({ schema: { ...schema, components }, name: typeName, ...options });
8
+ return tsType;
9
+ }
@@ -0,0 +1,12 @@
1
+ import { JSONSchema7 } from 'json-schema';
2
+ import { OpenAPIObject } from 'openapi3-ts/oas31';
3
+ interface CompileOptions {
4
+ name: string;
5
+ schema: JSONSchema7 & {
6
+ components?: OpenAPIObject['components'];
7
+ };
8
+ refs?: Map<string, JSONSchema7>;
9
+ dontCreateRefTypes?: boolean;
10
+ }
11
+ export declare function compileTs(options: CompileOptions): string;
12
+ export {};
@@ -0,0 +1,261 @@
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) => collectDefinitions(s, refs));
72
+ }
73
+ function isSchema(value) {
74
+ return typeof value === 'object' && !Array.isArray(value);
75
+ }
76
+ function compileSchema(schema, name, context) {
77
+ if (typeof schema === 'boolean') {
78
+ return schema ? 'any' : 'never';
79
+ }
80
+ // Handle x-tsType extension
81
+ if ('x-tsType' in schema && typeof schema['x-tsType'] === 'string') {
82
+ return schema['x-tsType'];
83
+ }
84
+ // Handle $ref
85
+ if (schema.$ref) {
86
+ return handleRef(schema.$ref, context);
87
+ }
88
+ // Handle combinators
89
+ if (schema.allOf) {
90
+ return handleAllOf(schema.allOf, name, context);
91
+ }
92
+ if (schema.anyOf) {
93
+ return handleAnyOf(schema.anyOf, name, context);
94
+ }
95
+ if (schema.oneOf) {
96
+ return handleOneOf(schema.oneOf, name, context);
97
+ }
98
+ // Handle type-specific compilation
99
+ if (schema.enum) {
100
+ return handleEnum(schema.enum);
101
+ }
102
+ if (schema.const !== undefined) {
103
+ return handleConst(schema.const);
104
+ }
105
+ if (!schema.type) {
106
+ // No type specified, could be object
107
+ if (schema.properties || schema.additionalProperties) {
108
+ return handleObject(schema, name, context);
109
+ }
110
+ return 'any';
111
+ }
112
+ if (Array.isArray(schema.type)) {
113
+ return schema.type.map((t) => compileSchemaWithType({ ...schema, type: t }, name, context)).join(' | ');
114
+ }
115
+ return compileSchemaWithType(schema, name, context);
116
+ }
117
+ function compileSchemaWithType(schema, name, context) {
118
+ switch (schema.type) {
119
+ case 'null':
120
+ return 'null';
121
+ case 'boolean':
122
+ return 'boolean';
123
+ case 'string':
124
+ return 'string';
125
+ case 'number':
126
+ return 'number';
127
+ case 'integer':
128
+ return 'number';
129
+ case 'array':
130
+ return handleArray(schema, name, context);
131
+ case 'object':
132
+ return handleObject(schema, name, context);
133
+ default:
134
+ return 'any';
135
+ }
136
+ }
137
+ function handleRef(ref, context) {
138
+ const typeName = refToTypeName(ref);
139
+ // Check if we're already compiling this ref (circular reference)
140
+ if (context.refsInProgress.has(ref)) {
141
+ return typeName;
142
+ }
143
+ // Check if already compiled
144
+ if (context.compiledRefs.has(ref)) {
145
+ return typeName;
146
+ }
147
+ // Find the referenced schema
148
+ const referencedSchema = context.refs.get(ref);
149
+ if (!referencedSchema) {
150
+ return 'any'; // Reference not found
151
+ }
152
+ // Mark as in progress
153
+ context.refsInProgress.add(ref);
154
+ // Compile the referenced schema
155
+ const compiledType = compileSchema(referencedSchema, typeName, context);
156
+ const description = referencedSchema.description
157
+ ? `/** ${escapeJSDocComment(referencedSchema.description)} */\n`
158
+ : '';
159
+ context.compiledRefs.set(ref, `${description}export type ${typeName} = ${compiledType};`);
160
+ // Mark as completed
161
+ context.refsInProgress.delete(ref);
162
+ return typeName;
163
+ }
164
+ function handleAllOf(schemas, name, context) {
165
+ const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-all-of-${i}`), context));
166
+ // For allOf, we need to intersect types
167
+ // If they're all objects, we can merge them properly
168
+ const objectTypes = types.filter((t) => t.startsWith('{') && t.endsWith('}'));
169
+ if (objectTypes.length === types.length) {
170
+ // Merge object types
171
+ const merged = objectTypes.map((t) => t.slice(1, -1).trim()).filter((t) => t.length > 0);
172
+ return merged.length > 0 ? `{ ${merged.join('; ')} }` : '{}';
173
+ }
174
+ return types.join(' & ');
175
+ }
176
+ function handleAnyOf(schemas, name, context) {
177
+ const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-any-of-${i}`), context));
178
+ return types.join(' | ');
179
+ }
180
+ function handleOneOf(schemas, name, context) {
181
+ // For TypeScript, oneOf behaves like anyOf
182
+ const types = schemas.map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-one-of-${i}`), context));
183
+ return types.join(' | ');
184
+ }
185
+ function handleEnum(enumValues) {
186
+ return enumValues.map((v) => JSON.stringify(v)).join(' | ');
187
+ }
188
+ function handleConst(value) {
189
+ return JSON.stringify(value);
190
+ }
191
+ function handleArray(schema, name, context) {
192
+ if (!schema.items) {
193
+ return 'any[]';
194
+ }
195
+ if (Array.isArray(schema.items)) {
196
+ // Tuple (ignoring min/max as requested)
197
+ const types = schema.items.map((item, i) => compileSchema(item, `${name}Item${i}`, context));
198
+ return `[${types.join(', ')}]`;
199
+ }
200
+ const itemType = compileSchema(schema.items, `${name}Item`, context);
201
+ return `${wrapUnionType(itemType)}[]`;
202
+ }
203
+ function handleObject(schema, name, context) {
204
+ const props = [];
205
+ // Handle known properties
206
+ if (schema.properties) {
207
+ const required = new Set(schema.required || []);
208
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
209
+ if (!isSchema(propSchema))
210
+ continue;
211
+ const isRequired = required.has(propName);
212
+ // Ensure the generated type name for nested properties is valid
213
+ const nestedTypeName = sanitizeTypeName(`${name}-${propName}`);
214
+ const propType = compileSchema(propSchema, nestedTypeName, context);
215
+ const safePropName = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(propName) ? propName : `"${propName}"`;
216
+ // Add JSDoc comment if description is present
217
+ const comment = propSchema.description ? `\n/** ${escapeJSDocComment(propSchema.description)} */\n` : '';
218
+ props.push(`${comment}${safePropName}${isRequired ? '' : '?'}: ${propType}`);
219
+ }
220
+ }
221
+ // Handle additional properties
222
+ if (schema.additionalProperties === true) {
223
+ props.push('[key: string]: any');
224
+ }
225
+ else if (schema.additionalProperties && isSchema(schema.additionalProperties)) {
226
+ const additionalTypeName = sanitizeTypeName(`${name}-additional`);
227
+ const additionalType = compileSchema(schema.additionalProperties, additionalTypeName, context);
228
+ props.push(`[key: string]: ${additionalType}`);
229
+ }
230
+ // Handle pattern properties
231
+ if (schema.patternProperties) {
232
+ // For simplicity, treat pattern properties as string index signature
233
+ const patternTypes = Object.values(schema.patternProperties)
234
+ .filter(isSchema)
235
+ .map((s, i) => compileSchema(s, sanitizeTypeName(`${name}-pattern-${i}`), context));
236
+ if (patternTypes.length > 0) {
237
+ props.push(`[key: string]: ${patternTypes.join(' | ')}`);
238
+ }
239
+ }
240
+ return props.length > 0 ? `{ ${props.join('; ')} }` : '{}';
241
+ }
242
+ function refToTypeName(ref) {
243
+ // Extract the last part of the reference as the type name
244
+ const parts = ref.split('/');
245
+ const name = parts[parts.length - 1];
246
+ // Convert kebab-case to PascalCase
247
+ return upperFirst(camelCase(name));
248
+ }
249
+ function wrapUnionType(type) {
250
+ // Wrap union types in parentheses for array types
251
+ return type.includes('|') ? `(${type})` : type;
252
+ }
253
+ function sanitizeTypeName(name) {
254
+ return upperFirst(camelCase(name));
255
+ }
256
+ // Utility function to escape JSDoc comment terminators in descriptions
257
+ function escapeJSDocComment(description) {
258
+ if (!description)
259
+ return '';
260
+ return description.replace(/\*\//g, '*\\/');
261
+ }
@@ -1,2 +1,2 @@
1
- import { KnownAny } from '../types.mjs';
2
- export default function debounceWithArgs<T extends (...args: KnownAny[]) => KnownAny>(fn: T, wait: number): (...args: Parameters<T>) => void | Promise<void>;
1
+ import type { KnownAny } from 'vovk';
2
+ export default function debounceWithArgs<Callback extends (...args: KnownAny[]) => KnownAny>(callback: Callback, wait: number): (...args: Parameters<Callback>) => Promise<Awaited<ReturnType<Callback>>>;
@@ -1,14 +1,29 @@
1
- import debounce from 'lodash/debounce.js';
2
- export default function debounceWithArgs(fn, wait) {
3
- const debouncedFunctions = new Map();
1
+ export default function debounceWithArgs(callback, wait) {
2
+ // Stores timeouts keyed by the stringified arguments
3
+ const timeouts = new Map();
4
4
  return (...args) => {
5
+ // Convert arguments to a JSON string (or any other stable key generation)
5
6
  const key = JSON.stringify(args);
6
- if (!debouncedFunctions.has(key)) {
7
- debouncedFunctions.set(key, debounce(fn, wait));
8
- }
9
- const debouncedFn = debouncedFunctions.get(key);
10
- if (debouncedFn) {
11
- debouncedFn(...args);
7
+ // Clear any existing timer for this specific key
8
+ if (timeouts.has(key)) {
9
+ clearTimeout(timeouts.get(key));
12
10
  }
11
+ // Return a promise that resolves/rejects after the debounce delay
12
+ return new Promise((resolve, reject) => {
13
+ const timeoutId = setTimeout(async () => {
14
+ try {
15
+ const result = await callback(...args);
16
+ resolve(result);
17
+ }
18
+ catch (error) {
19
+ reject(error);
20
+ }
21
+ finally {
22
+ // Remove the entry once the callback is invoked
23
+ timeouts.delete(key);
24
+ }
25
+ }, wait);
26
+ timeouts.set(key, timeoutId);
27
+ });
13
28
  };
14
29
  }
@@ -1,4 +1,6 @@
1
- export default function formatLoggedSegmentName(segmentName: string, { withChalk, upperFirst }?: {
1
+ export default 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
- import chalkHighlightThing from './chalkHighlightThing.mjs';
2
1
  import upperFirstLodash from 'lodash/upperFirst.js';
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 default 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
  }
@@ -1,4 +1,5 @@
1
- import net from 'net';
1
+ import net from 'node:net';
2
+ // Created with AI
2
3
  /**
3
4
  * Checks if a port is available.
4
5
  * @param {number} port - The port to check.
@@ -34,7 +35,7 @@ function getAvailablePort(startPort, maxAttempts, attempt, onWarning) {
34
35
  getAvailablePort(startPort + 1, maxAttempts, attempt + 1, onWarning).then(resolve, reject);
35
36
  }
36
37
  else {
37
- reject('No available ports found');
38
+ reject(new Error('No available ports found'));
38
39
  }
39
40
  });
40
41
  });
@@ -1,4 +1,4 @@
1
- import fs from 'fs/promises';
1
+ import fs from 'node:fs/promises';
2
2
  export var FileSystemEntryType;
3
3
  (function (FileSystemEntryType) {
4
4
  FileSystemEntryType["FILE"] = "FILE";
@@ -0,0 +1,3 @@
1
+ import { 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,22 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ let cachedPromise;
4
+ export function getPackageJson(cwd, log) {
5
+ const pkgPath = path.join(cwd, 'package.json');
6
+ // If we have a cached promise, return it
7
+ if (cachedPromise) {
8
+ return cachedPromise;
9
+ }
10
+ const promise = fs
11
+ .readFile(pkgPath, 'utf8')
12
+ .then((content) => JSON.parse(content))
13
+ .catch(() => {
14
+ cachedPromise = undefined;
15
+ log.warn(`Failed to read package.json at ${pkgPath}. Using a fallback.`);
16
+ return {
17
+ name: 'unknown',
18
+ };
19
+ });
20
+ cachedPromise = promise;
21
+ return promise;
22
+ }
@@ -0,0 +1,4 @@
1
+ export default function getPublicModuleNameFromPath(modulePath: string): {
2
+ moduleName: string | null;
3
+ restPath: string;
4
+ };
@@ -0,0 +1,9 @@
1
+ export default 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,7 @@
1
+ import { VovkStrictConfig, type VovkConfig } from 'vovk';
2
+ import type { ProjectInfo } from '../getProjectInfo/index.mjs';
3
+ export declare function normalizeOpenAPIMixins({ mixinModules, log, cwd, }: {
4
+ mixinModules: NonNullable<VovkConfig['openApiMixins']>;
5
+ log: ProjectInfo['log'];
6
+ cwd?: string;
7
+ }): Promise<VovkStrictConfig['openApiMixins']>;
@@ -0,0 +1,67 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import * as YAML from 'yaml';
4
+ import chalkHighlightThing from './chalkHighlightThing.mjs';
5
+ async function getOpenApiSpecLocal(openApiSpecFilePath, cwd) {
6
+ const openApiSpecAbsolutePath = path.resolve(cwd, openApiSpecFilePath);
7
+ const fileName = path.basename(openApiSpecAbsolutePath);
8
+ if (!fileName.endsWith('.json') && !fileName.endsWith('.yaml') && !fileName.endsWith('.yml')) {
9
+ throw new Error(`Invalid OpenAPI spec file format: ${fileName}. Please provide a JSON or YAML file.`);
10
+ }
11
+ const openApiSpecContent = await fs.readFile(openApiSpecAbsolutePath, 'utf8');
12
+ return (fileName.endsWith('.json') ? JSON.parse(openApiSpecContent) : YAML.parse(openApiSpecContent));
13
+ }
14
+ async function getOpenApiSpecRemote({ cwd, url, fallback, log, }) {
15
+ const resp = await fetch(url);
16
+ const text = await resp.text();
17
+ if (!resp.ok) {
18
+ if (fallback) {
19
+ log.warn(`Failed to fetch OpenAPI spec from ${chalkHighlightThing(url)}: ${resp.status} ${resp.statusText}. Falling back to ${chalkHighlightThing(fallback)}`);
20
+ return getOpenApiSpecLocal(fallback, cwd);
21
+ }
22
+ throw new Error(`Failed to fetch OpenAPI spec from ${chalkHighlightThing(url)}: ${resp.status} ${resp.statusText}`);
23
+ }
24
+ if (fallback) {
25
+ const fallbackAbsolutePath = path.resolve(cwd, fallback);
26
+ const existingFallback = await fs.readFile(fallbackAbsolutePath, 'utf8').catch(() => null);
27
+ if (existingFallback !== text) {
28
+ await fs.mkdir(path.dirname(fallbackAbsolutePath), { recursive: true });
29
+ await fs.writeFile(fallbackAbsolutePath, text);
30
+ log.info(`Saved OpenAPI spec to fallback file ${chalkHighlightThing(fallbackAbsolutePath)}`);
31
+ }
32
+ else {
33
+ log.debug(`OpenAPI spec at ${chalkHighlightThing(url)} is unchanged. Skipping write to fallback file ${chalkHighlightThing(fallbackAbsolutePath)}`);
34
+ }
35
+ }
36
+ return (text.trim().startsWith('{') || text.trim().startsWith('[') ? JSON.parse(text) : YAML.parse(text));
37
+ }
38
+ export async function normalizeOpenAPIMixins({ mixinModules, log, cwd = process.cwd(), }) {
39
+ if (mixinModules) {
40
+ const modules = await Promise.all(Object.entries(mixinModules).map(async ([mixinName, { source, apiRoot, getModuleName, getMethodName, errorMessageKey, package: packageJson },]) => {
41
+ let openAPIObject;
42
+ if ('url' in source) {
43
+ openAPIObject = await getOpenApiSpecRemote({ url: source.url, fallback: source.fallback, log, cwd });
44
+ }
45
+ else if ('file' in source) {
46
+ openAPIObject = await getOpenApiSpecLocal(source.file, cwd);
47
+ }
48
+ else if ('object' in source) {
49
+ openAPIObject = source.object;
50
+ }
51
+ else {
52
+ throw new Error('Invalid source type for OpenAPI configuration');
53
+ }
54
+ return {
55
+ source: { object: openAPIObject },
56
+ apiRoot,
57
+ getModuleName,
58
+ getMethodName,
59
+ errorMessageKey,
60
+ package: packageJson,
61
+ mixinName,
62
+ };
63
+ }));
64
+ return Object.fromEntries(modules.map((module) => [module.mixinName, module]));
65
+ }
66
+ return {};
67
+ }
@@ -0,0 +1,3 @@
1
+ import { type VovkSchema } from 'vovk';
2
+ export default function pickSegmentFullSchema(schema: VovkSchema, segmentNames: string[]): VovkSchema;
3
+ export declare function omitSegmentFullSchema(schema: VovkSchema, segmentNames: string[]): VovkSchema;
@@ -0,0 +1,15 @@
1
+ import { VovkSchemaIdEnum } from 'vovk';
2
+ export default function pickSegmentFullSchema(schema, segmentNames) {
3
+ return {
4
+ $schema: VovkSchemaIdEnum.SCHEMA,
5
+ meta: schema.meta,
6
+ segments: Object.fromEntries(segmentNames.map((segmentName) => [segmentName, schema.segments[segmentName]])),
7
+ };
8
+ }
9
+ export function omitSegmentFullSchema(schema, segmentNames) {
10
+ return {
11
+ $schema: VovkSchemaIdEnum.SCHEMA,
12
+ meta: schema.meta,
13
+ segments: Object.fromEntries(Object.entries(schema.segments).filter(([segmentName]) => !segmentNames.includes(segmentName))),
14
+ };
15
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Removes all directories in a folder that aren't in the provided allowlist
3
+ * Supports nested directory paths like 'foo/bar/baz'
4
+ *
5
+ * @param folderPath - The path to the folder to process
6
+ * @param allowedDirs - Array of relative directory paths to keep
7
+ * @returns Promise that resolves when all operations are complete
8
+ */
9
+ declare function removeUnlistedDirectories(folderPath: string, allowedDirs: string[]): Promise<void>;
10
+ export default removeUnlistedDirectories;