react-native-sherpa-onnx 0.3.0 → 0.3.2

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 (66) hide show
  1. package/README.md +21 -7
  2. package/SherpaOnnx.podspec +1 -1
  3. package/android/build.gradle +35 -26
  4. package/android/prebuilt-download.gradle +27 -14
  5. package/android/src/main/cpp/CMakeLists.txt +51 -17
  6. package/android/src/main/cpp/jni/archive/sherpa-onnx-archive-helper.cpp +14 -0
  7. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.cpp +16 -0
  8. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-helper.h +3 -0
  9. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect-stt.cpp +19 -2
  10. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-model-detect.h +2 -1
  11. package/android/src/main/cpp/jni/model_detect/sherpa-onnx-stt-wrapper.cpp +1 -0
  12. package/android/src/main/java/com/sherpaonnx/SherpaOnnxModule.kt +114 -8
  13. package/android/src/main/java/com/sherpaonnx/SherpaOnnxOnlineSttHelper.kt +535 -0
  14. package/android/src/main/java/com/sherpaonnx/SherpaOnnxTtsHelper.kt +10 -10
  15. package/ios/SherpaOnnx+OnlineSTT.mm +365 -0
  16. package/ios/SherpaOnnx+TTS.mm +35 -9
  17. package/ios/SherpaOnnx.mm +6 -0
  18. package/ios/model_detect/sherpa-onnx-model-detect-helper.h +3 -0
  19. package/ios/model_detect/sherpa-onnx-model-detect-helper.mm +16 -0
  20. package/ios/model_detect/sherpa-onnx-model-detect-stt.mm +19 -2
  21. package/ios/model_detect/sherpa-onnx-model-detect.h +2 -1
  22. package/ios/online_stt/sherpa-onnx-online-stt-wrapper.h +85 -0
  23. package/ios/online_stt/sherpa-onnx-online-stt-wrapper.mm +270 -0
  24. package/lib/module/NativeSherpaOnnx.js.map +1 -1
  25. package/lib/module/index.js +2 -2
  26. package/lib/module/stt/index.js +4 -0
  27. package/lib/module/stt/index.js.map +1 -1
  28. package/lib/module/stt/streaming.js +257 -0
  29. package/lib/module/stt/streaming.js.map +1 -0
  30. package/lib/module/stt/streamingTypes.js +38 -0
  31. package/lib/module/stt/streamingTypes.js.map +1 -0
  32. package/lib/module/tts/index.js +4 -43
  33. package/lib/module/tts/index.js.map +1 -1
  34. package/lib/module/tts/streaming.js +220 -0
  35. package/lib/module/tts/streaming.js.map +1 -0
  36. package/lib/module/tts/streamingTypes.js +4 -0
  37. package/lib/module/tts/streamingTypes.js.map +1 -0
  38. package/lib/module/tts/types.js +8 -1
  39. package/lib/module/tts/types.js.map +1 -1
  40. package/lib/typescript/src/NativeSherpaOnnx.d.ts +66 -1
  41. package/lib/typescript/src/NativeSherpaOnnx.d.ts.map +1 -1
  42. package/lib/typescript/src/stt/index.d.ts +3 -0
  43. package/lib/typescript/src/stt/index.d.ts.map +1 -1
  44. package/lib/typescript/src/stt/streaming.d.ts +42 -0
  45. package/lib/typescript/src/stt/streaming.d.ts.map +1 -0
  46. package/lib/typescript/src/stt/streamingTypes.d.ts +122 -0
  47. package/lib/typescript/src/stt/streamingTypes.d.ts.map +1 -0
  48. package/lib/typescript/src/tts/index.d.ts +3 -1
  49. package/lib/typescript/src/tts/index.d.ts.map +1 -1
  50. package/lib/typescript/src/tts/streaming.d.ts +24 -0
  51. package/lib/typescript/src/tts/streaming.d.ts.map +1 -0
  52. package/lib/typescript/src/tts/streamingTypes.d.ts +27 -0
  53. package/lib/typescript/src/tts/streamingTypes.d.ts.map +1 -0
  54. package/lib/typescript/src/tts/types.d.ts +19 -6
  55. package/lib/typescript/src/tts/types.d.ts.map +1 -1
  56. package/package.json +1 -2
  57. package/src/NativeSherpaOnnx.ts +95 -0
  58. package/src/index.tsx +2 -2
  59. package/src/stt/index.ts +17 -0
  60. package/src/stt/streaming.ts +361 -0
  61. package/src/stt/streamingTypes.ts +151 -0
  62. package/src/tts/index.ts +6 -66
  63. package/src/tts/streaming.ts +336 -0
  64. package/src/tts/streamingTypes.ts +54 -0
  65. package/src/tts/types.ts +20 -10
  66. package/android/codegen.gradle +0 -57
@@ -0,0 +1,336 @@
1
+ import { DeviceEventEmitter } from 'react-native';
2
+ import SherpaOnnx from '../NativeSherpaOnnx';
3
+ import type {
4
+ TTSInitializeOptions,
5
+ TTSModelType,
6
+ TtsModelOptions,
7
+ TtsGenerationOptions,
8
+ TtsStreamChunk,
9
+ TtsStreamEnd,
10
+ TtsStreamError,
11
+ TtsStreamHandlers,
12
+ TtsStreamController,
13
+ TTSModelInfo,
14
+ } from './types';
15
+ import type { StreamingTtsEngine } from './streamingTypes';
16
+ import type { ModelPathConfig } from '../types';
17
+ import { resolveModelPath } from '../utils';
18
+
19
+ let streamingTtsInstanceCounter = 0;
20
+ let ttsRequestIdCounter = 0;
21
+
22
+ /**
23
+ * Flatten model-specific options for the given model type to native init params.
24
+ */
25
+ function flattenTtsModelOptionsForNative(
26
+ modelType: TTSModelType | undefined,
27
+ modelOptions: TtsModelOptions | undefined
28
+ ): {
29
+ noiseScale: number | undefined;
30
+ noiseScaleW: number | undefined;
31
+ lengthScale: number | undefined;
32
+ } {
33
+ if (
34
+ !modelOptions ||
35
+ !modelType ||
36
+ modelType === 'auto' ||
37
+ modelType === 'zipvoice'
38
+ )
39
+ return {
40
+ noiseScale: undefined,
41
+ noiseScaleW: undefined,
42
+ lengthScale: undefined,
43
+ };
44
+ const block =
45
+ modelType === 'vits'
46
+ ? modelOptions.vits
47
+ : modelType === 'matcha'
48
+ ? modelOptions.matcha
49
+ : modelType === 'kokoro'
50
+ ? modelOptions.kokoro
51
+ : modelType === 'kitten'
52
+ ? modelOptions.kitten
53
+ : modelType === 'pocket'
54
+ ? modelOptions.pocket
55
+ : undefined;
56
+ if (!block)
57
+ return {
58
+ noiseScale: undefined,
59
+ noiseScaleW: undefined,
60
+ lengthScale: undefined,
61
+ };
62
+ const n = block as {
63
+ noiseScale?: number;
64
+ noiseScaleW?: number;
65
+ lengthScale?: number;
66
+ };
67
+ return {
68
+ noiseScale:
69
+ n.noiseScale !== undefined && typeof n.noiseScale === 'number'
70
+ ? n.noiseScale
71
+ : undefined,
72
+ noiseScaleW:
73
+ n.noiseScaleW !== undefined && typeof n.noiseScaleW === 'number'
74
+ ? n.noiseScaleW
75
+ : undefined,
76
+ lengthScale:
77
+ n.lengthScale !== undefined && typeof n.lengthScale === 'number'
78
+ ? n.lengthScale
79
+ : undefined,
80
+ };
81
+ }
82
+
83
+ function toNativeTtsOptions(
84
+ options?: TtsGenerationOptions
85
+ ): Record<string, unknown> {
86
+ if (options == null) return {};
87
+ const out: Record<string, unknown> = {};
88
+ if (options.sid !== undefined) out.sid = options.sid;
89
+ if (options.speed !== undefined) out.speed = options.speed;
90
+ if (options.silenceScale !== undefined)
91
+ out.silenceScale = options.silenceScale;
92
+ if (options.referenceAudio != null) {
93
+ out.referenceAudio = options.referenceAudio.samples;
94
+ out.referenceSampleRate = options.referenceAudio.sampleRate;
95
+ }
96
+ if (options.referenceText !== undefined)
97
+ out.referenceText = options.referenceText;
98
+ if (options.numSteps !== undefined) out.numSteps = options.numSteps;
99
+ if (options.extra != null && Object.keys(options.extra).length > 0)
100
+ out.extra = options.extra;
101
+ return out;
102
+ }
103
+
104
+ /**
105
+ * Create a streaming TTS engine instance. Use for incremental generation with
106
+ * chunk callbacks and PCM playback. Call destroy() when done.
107
+ *
108
+ * @param options - TTS initialization options or model path configuration
109
+ * @returns Promise resolving to a StreamingTtsEngine instance
110
+ * @example
111
+ * ```typescript
112
+ * const tts = await createStreamingTTS({
113
+ * modelPath: { type: 'asset', path: 'models/vits-piper-en' },
114
+ * modelType: 'vits',
115
+ * });
116
+ * const controller = await tts.generateSpeechStream('Hello', undefined, {
117
+ * onChunk: (chunk) => playPcm(chunk.samples, chunk.sampleRate),
118
+ * onEnd: () => {},
119
+ * });
120
+ * await tts.destroy();
121
+ * ```
122
+ */
123
+ export async function createStreamingTTS(
124
+ options: TTSInitializeOptions | ModelPathConfig
125
+ ): Promise<StreamingTtsEngine> {
126
+ const instanceId = `streaming_tts_${++streamingTtsInstanceCounter}`;
127
+
128
+ let modelPath: ModelPathConfig;
129
+ let modelType: TTSModelType | undefined;
130
+ let provider: string | undefined;
131
+ let numThreads: number | undefined;
132
+ let debug: boolean | undefined;
133
+ let modelOptions: TtsModelOptions | undefined;
134
+ let ruleFsts: string | undefined;
135
+ let ruleFars: string | undefined;
136
+ let maxNumSentences: number | undefined;
137
+ let silenceScale: number | undefined;
138
+
139
+ if ('modelPath' in options) {
140
+ modelPath = options.modelPath;
141
+ modelType = options.modelType;
142
+ provider = options.provider;
143
+ numThreads = options.numThreads;
144
+ debug = options.debug;
145
+ modelOptions = options.modelOptions;
146
+ ruleFsts = options.ruleFsts;
147
+ ruleFars = options.ruleFars;
148
+ maxNumSentences = options.maxNumSentences;
149
+ silenceScale = options.silenceScale;
150
+ } else {
151
+ modelPath = options;
152
+ modelType = undefined;
153
+ provider = undefined;
154
+ numThreads = undefined;
155
+ debug = undefined;
156
+ modelOptions = undefined;
157
+ ruleFsts = undefined;
158
+ ruleFars = undefined;
159
+ maxNumSentences = undefined;
160
+ silenceScale = undefined;
161
+ }
162
+
163
+ const flat = flattenTtsModelOptionsForNative(modelType, modelOptions);
164
+ const resolvedPath = await resolveModelPath(modelPath);
165
+
166
+ const result = await SherpaOnnx.initializeTts(
167
+ instanceId,
168
+ resolvedPath,
169
+ modelType ?? 'auto',
170
+ numThreads ?? 2,
171
+ debug ?? false,
172
+ flat.noiseScale,
173
+ flat.noiseScaleW,
174
+ flat.lengthScale,
175
+ ruleFsts,
176
+ ruleFars,
177
+ maxNumSentences,
178
+ silenceScale,
179
+ provider
180
+ );
181
+
182
+ if (!result.success) {
183
+ throw new Error(
184
+ `TTS initialization failed: ${JSON.stringify(
185
+ result.detectedModels ?? []
186
+ )}`
187
+ );
188
+ }
189
+
190
+ let destroyed = false;
191
+
192
+ const guard = () => {
193
+ if (destroyed) {
194
+ throw new Error(
195
+ `Streaming TTS instance ${instanceId} has been destroyed; cannot call methods on it.`
196
+ );
197
+ }
198
+ };
199
+
200
+ const engine: StreamingTtsEngine = {
201
+ get instanceId() {
202
+ return instanceId;
203
+ },
204
+
205
+ async generateSpeechStream(
206
+ text: string,
207
+ opts: TtsGenerationOptions | undefined,
208
+ handlers: TtsStreamHandlers
209
+ ): Promise<TtsStreamController> {
210
+ guard();
211
+ const requestId = `tts_req_${++ttsRequestIdCounter}`;
212
+ const subscriptions: Array<{ remove: () => void }> = [];
213
+ let unsubscribed = false;
214
+
215
+ const unsubscribe = () => {
216
+ if (unsubscribed) return;
217
+ unsubscribed = true;
218
+ subscriptions.forEach((sub) => sub.remove());
219
+ };
220
+
221
+ const matchesRequest = (e: { instanceId?: string; requestId?: string }) =>
222
+ (e.instanceId == null || e.instanceId === instanceId) &&
223
+ (e.requestId == null || e.requestId === requestId);
224
+
225
+ subscriptions.push(
226
+ DeviceEventEmitter.addListener('ttsStreamChunk', (event: unknown) => {
227
+ const e = event as TtsStreamChunk;
228
+ if (!matchesRequest(e)) {
229
+ return;
230
+ }
231
+ handlers.onChunk?.(e);
232
+ }),
233
+ DeviceEventEmitter.addListener('ttsStreamEnd', (event: unknown) => {
234
+ const e = event as TtsStreamEnd;
235
+ if (!matchesRequest(e)) {
236
+ return;
237
+ }
238
+ try {
239
+ handlers.onEnd?.(e);
240
+ } finally {
241
+ unsubscribe();
242
+ }
243
+ }),
244
+ DeviceEventEmitter.addListener('ttsStreamError', (event: unknown) => {
245
+ const e = event as TtsStreamError;
246
+ if (!matchesRequest(e)) {
247
+ return;
248
+ }
249
+ try {
250
+ handlers.onError?.(e);
251
+ } finally {
252
+ unsubscribe();
253
+ }
254
+ })
255
+ );
256
+
257
+ // Yield so the bridge can register listeners before native emits (avoids "no listeners" / "already in progress")
258
+ await new Promise<void>((resolve) => {
259
+ if (typeof setImmediate === 'function') {
260
+ setImmediate(resolve);
261
+ } else {
262
+ setTimeout(resolve, 0);
263
+ }
264
+ });
265
+
266
+ try {
267
+ await SherpaOnnx.generateTtsStream(
268
+ instanceId,
269
+ requestId,
270
+ text,
271
+ toNativeTtsOptions(opts)
272
+ );
273
+ } catch (error) {
274
+ unsubscribe();
275
+ throw error;
276
+ }
277
+
278
+ const controller: TtsStreamController = {
279
+ async cancel(): Promise<void> {
280
+ guard();
281
+ await SherpaOnnx.cancelTtsStream(instanceId);
282
+ unsubscribe();
283
+ },
284
+ unsubscribe,
285
+ };
286
+ return controller;
287
+ },
288
+
289
+ async cancelSpeechStream(): Promise<void> {
290
+ guard();
291
+ return SherpaOnnx.cancelTtsStream(instanceId);
292
+ },
293
+
294
+ async startPcmPlayer(sampleRate: number, channels: number): Promise<void> {
295
+ guard();
296
+ return SherpaOnnx.startTtsPcmPlayer(instanceId, sampleRate, channels);
297
+ },
298
+
299
+ async writePcmChunk(samples: number[]): Promise<void> {
300
+ guard();
301
+ return SherpaOnnx.writeTtsPcmChunk(instanceId, samples);
302
+ },
303
+
304
+ async stopPcmPlayer(): Promise<void> {
305
+ guard();
306
+ return SherpaOnnx.stopTtsPcmPlayer(instanceId);
307
+ },
308
+
309
+ async getModelInfo(): Promise<TTSModelInfo> {
310
+ guard();
311
+ const [sampleRate, numSpeakers] = await Promise.all([
312
+ SherpaOnnx.getTtsSampleRate(instanceId),
313
+ SherpaOnnx.getTtsNumSpeakers(instanceId),
314
+ ]);
315
+ return { sampleRate, numSpeakers };
316
+ },
317
+
318
+ async getSampleRate(): Promise<number> {
319
+ guard();
320
+ return SherpaOnnx.getTtsSampleRate(instanceId);
321
+ },
322
+
323
+ async getNumSpeakers(): Promise<number> {
324
+ guard();
325
+ return SherpaOnnx.getTtsNumSpeakers(instanceId);
326
+ },
327
+
328
+ async destroy(): Promise<void> {
329
+ if (destroyed) return;
330
+ destroyed = true;
331
+ await SherpaOnnx.unloadTts(instanceId);
332
+ },
333
+ };
334
+
335
+ return engine;
336
+ }
@@ -0,0 +1,54 @@
1
+ import type {
2
+ TtsStreamHandlers,
3
+ TtsStreamController,
4
+ TtsGenerationOptions,
5
+ TTSModelInfo,
6
+ } from './types';
7
+
8
+ // Re-export streaming event types for consumers who import from streamingTypes
9
+ export type {
10
+ TtsStreamChunk,
11
+ TtsStreamEnd,
12
+ TtsStreamError,
13
+ TtsStreamHandlers,
14
+ TtsStreamController,
15
+ TtsGenerationOptions,
16
+ TTSModelInfo,
17
+ } from './types';
18
+
19
+ /**
20
+ * Streaming-only TTS engine returned by createStreamingTTS().
21
+ * Use for incremental generation with chunk callbacks and PCM playback.
22
+ * Call destroy() when done to free native resources.
23
+ */
24
+ export interface StreamingTtsEngine {
25
+ readonly instanceId: string;
26
+
27
+ /** Generate speech in streaming mode; audio delivered via handlers. */
28
+ generateSpeechStream(
29
+ text: string,
30
+ options: TtsGenerationOptions | undefined,
31
+ handlers: TtsStreamHandlers
32
+ ): Promise<TtsStreamController>;
33
+
34
+ /** Cancel the current streaming generation. */
35
+ cancelSpeechStream(): Promise<void>;
36
+
37
+ /** Start built-in PCM playback (e.g. for play-while-generating). */
38
+ startPcmPlayer(sampleRate: number, channels: number): Promise<void>;
39
+
40
+ /** Write float PCM samples to the player. Use from onChunk. */
41
+ writePcmChunk(samples: number[]): Promise<void>;
42
+
43
+ /** Stop and release the PCM player. */
44
+ stopPcmPlayer(): Promise<void>;
45
+
46
+ /** Model sample rate and number of speakers. */
47
+ getModelInfo(): Promise<TTSModelInfo>;
48
+
49
+ getSampleRate(): Promise<number>;
50
+ getNumSpeakers(): Promise<number>;
51
+
52
+ /** Release native TTS resources. Do not use the engine after this. */
53
+ destroy(): Promise<void>;
54
+ }
package/src/tts/types.ts CHANGED
@@ -289,6 +289,8 @@ export interface GeneratedAudioWithTimestamps extends GeneratedAudio {
289
289
  export interface TtsStreamChunk {
290
290
  /** Instance ID (set by native for multi-instance routing). */
291
291
  instanceId?: string;
292
+ /** Request ID for this generation (distinguishes concurrent streams on same instance). */
293
+ requestId?: string;
292
294
  samples: number[];
293
295
  sampleRate: number;
294
296
  progress: number;
@@ -301,6 +303,8 @@ export interface TtsStreamChunk {
301
303
  export interface TtsStreamEnd {
302
304
  /** Instance ID (set by native for multi-instance routing). */
303
305
  instanceId?: string;
306
+ /** Request ID for this generation. */
307
+ requestId?: string;
304
308
  cancelled: boolean;
305
309
  }
306
310
 
@@ -310,9 +314,22 @@ export interface TtsStreamEnd {
310
314
  export interface TtsStreamError {
311
315
  /** Instance ID (set by native for multi-instance routing). */
312
316
  instanceId?: string;
317
+ /** Request ID for this generation. */
318
+ requestId?: string;
313
319
  message: string;
314
320
  }
315
321
 
322
+ /**
323
+ * Controller returned by generateSpeechStream().
324
+ * Use cancel() to stop generation, unsubscribe() to remove event listeners.
325
+ */
326
+ export interface TtsStreamController {
327
+ /** Cancel the ongoing TTS generation. */
328
+ cancel(): Promise<void>;
329
+ /** Remove event listeners (called automatically on end/error, or manually). */
330
+ unsubscribe(): void;
331
+ }
332
+
316
333
  /**
317
334
  * Handlers for TTS streaming generation (chunk, end, error).
318
335
  */
@@ -323,7 +340,9 @@ export interface TtsStreamHandlers {
323
340
  }
324
341
 
325
342
  /**
326
- * Instance-based TTS engine returned by createTTS().
343
+ * Instance-based batch TTS engine returned by createTTS().
344
+ * Use for one-shot synthesis (generateSpeech, generateSpeechWithTimestamps).
345
+ * For streaming, use createStreamingTTS() and StreamingTtsEngine instead.
327
346
  * Call destroy() when done to free native resources.
328
347
  */
329
348
  export interface TtsEngine {
@@ -336,15 +355,6 @@ export interface TtsEngine {
336
355
  text: string,
337
356
  options?: TtsGenerationOptions
338
357
  ): Promise<GeneratedAudioWithTimestamps>;
339
- generateSpeechStream(
340
- text: string,
341
- options: TtsGenerationOptions | undefined,
342
- handlers: TtsStreamHandlers
343
- ): Promise<() => void>;
344
- cancelSpeechStream(): Promise<void>;
345
- startPcmPlayer(sampleRate: number, channels: number): Promise<void>;
346
- writePcmChunk(samples: number[]): Promise<void>;
347
- stopPcmPlayer(): Promise<void>;
348
358
  updateParams(options: TtsUpdateOptions): Promise<{
349
359
  success: boolean;
350
360
  detectedModels: Array<{ type: string; modelDir: string }>;
@@ -1,57 +0,0 @@
1
- // With includesGeneratedCode=false, RNGP does not run codegen for this library when it
2
- // is built as a dependency (e.g. example app). Run codegen whenever this module is built
3
- // (standalone AAR or as part of an app) so that NativeSherpaOnnxSpec exists for Kotlin compile.
4
- // Uses Exec + node (no Node Gradle plugin) to avoid classloader/DSL issues in applied script.
5
- def libraryRoot = project.projectDir.parentFile
6
- def outDir = file("${project.buildDir}/generated/source/codegen")
7
- def codegenJavaDir = file("${project.buildDir}/generated/source/codegen/java")
8
-
9
- codegenJavaDir.mkdirs()
10
-
11
- def codegenScript = file("${libraryRoot}/node_modules/react-native/scripts/generate-codegen-artifacts.js")
12
- def outDirPath = outDir.absolutePath.replace("\\", "/")
13
- def libraryRootPath = libraryRoot.absolutePath.replace("\\", "/")
14
-
15
- tasks.register("generateCodegenSpecNode", Exec) {
16
- onlyIf { !project.hasProperty('useNpx') || project.property('useNpx') != 'true' }
17
- workingDir = libraryRoot
18
- inputs.dir(file("${libraryRoot}/src"))
19
- inputs.file(file("${libraryRoot}/package.json"))
20
- outputs.dir(codegenJavaDir)
21
-
22
- doFirst {
23
- if (!file("${libraryRoot}/node_modules/react-native").exists()) {
24
- throw new RuntimeException(
25
- "Codegen requires node_modules at library root. Run 'yarn install' (or npm install) in ${libraryRoot}, then rebuild."
26
- )
27
- }
28
- outDir.mkdirs()
29
- }
30
-
31
- commandLine(
32
- 'node',
33
- codegenScript.absolutePath,
34
- '-p', libraryRootPath,
35
- '-t', 'android',
36
- '-o', outDirPath,
37
- '-s', 'library'
38
- )
39
- environment 'CI': 'true'
40
-
41
- doLast {
42
- def nestedJava = file("${outDir}/android/app/build/generated/source/codegen/java")
43
- if (nestedJava.exists()) {
44
- project.copy {
45
- from nestedJava
46
- into codegenJavaDir
47
- }
48
- println "[codegen] Normalised spec output -> ${codegenJavaDir}"
49
- } else {
50
- println "[codegen] WARNING: expected nested output at ${nestedJava} not found"
51
- }
52
- }
53
- }
54
-
55
- tasks.register('generateCodegenSpec') {
56
- dependsOn tasks.named('generateCodegenSpecNode')
57
- }