@gandalan/weblibs 1.5.22 → 1.5.24

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.
@@ -11,6 +11,8 @@ const rootDtsPath = path.join(rootDir, "index.d.ts");
11
11
  const dtoIndexPath = path.join(rootDir, "api", "dtos", "index.js");
12
12
  const businessIndexPath = path.join(rootDir, "api", "business", "index.js");
13
13
  const uiIndexPath = path.join(rootDir, "ui", "index.js");
14
+ const neherApp3TypesJsPath = path.join(rootDir, "api", "neherApp3Types.js");
15
+ const neherApp3TypesDtsPath = path.join(rootDir, "api", "neherApp3Types.d.ts");
14
16
 
15
17
  const dtoLeafDirectory = path.join(rootDir, "api", "dtos");
16
18
  const businessLeafDirectory = path.join(rootDir, "api", "business");
@@ -25,6 +27,193 @@ const businessRootMarkerEnd = "// END GENERATED ROOT BUSINESS TYPEDEFS";
25
27
 
26
28
  const simpleImportTypePattern = /^import\((?:"|').+(?:"|')\)\.[A-Za-z0-9_$]+$/;
27
29
 
30
+ const rawType = (js, ts = js) => ({ kind: "raw", js, ts });
31
+ const refType = (name) => rawType(name);
32
+ const importType = (importPath, name) => rawType(`import("${importPath}").${name}`);
33
+ const arrayType = (elementType) => ({ kind: "array", elementType });
34
+ const promiseType = (valueType) => ({ kind: "promise", valueType });
35
+ const unionType = (...types) => ({ kind: "union", types });
36
+ const intersectionType = (...types) => ({ kind: "intersection", types });
37
+ const functionType = (params, returnType) => ({ kind: "function", params, returnType });
38
+ const objectLiteralType = (properties) => ({ kind: "objectLiteral", properties });
39
+
40
+ const stringType = refType("string");
41
+ const booleanType = refType("boolean");
42
+ const voidType = refType("void");
43
+ const jsObjectType = rawType("Object", "object");
44
+ const jsFunctionType = rawType("function", "Function");
45
+
46
+ const neherApp3JsImports = [
47
+ { importPath: "./fluentApi.js", name: "FluentApi" },
48
+ { importPath: "./idasFluentApi.js", name: "IDASFluentApi" },
49
+ { importPath: "./fluentAuthManager.js", name: "FluentAuthManager" }
50
+ ];
51
+
52
+ const neherApp3TypeDefinitions = [
53
+ {
54
+ kind: "alias",
55
+ name: "NeherApp3NotifyType",
56
+ type: unionType(rawType("0"), rawType("1"), rawType("2"))
57
+ },
58
+ {
59
+ kind: "object",
60
+ name: "ArtikelstammEintrag",
61
+ properties: [
62
+ { name: "KatalogArtikelGuid", type: stringType, optional: true },
63
+ { name: "KatalogNummer", type: stringType, optional: true },
64
+ { name: "Katalognummer", type: stringType, optional: true },
65
+ { name: "Nummer", type: stringType, optional: true }
66
+ ]
67
+ },
68
+ {
69
+ kind: "object",
70
+ name: "Variante",
71
+ properties: [
72
+ { name: "VarianteGuid", type: stringType, optional: true },
73
+ { name: "Name", type: stringType, optional: true },
74
+ { name: "Kuerzel", type: stringType, optional: true }
75
+ ]
76
+ },
77
+ {
78
+ kind: "object",
79
+ name: "Werteliste",
80
+ properties: [
81
+ { name: "WerteListeGuid", type: stringType, optional: true },
82
+ { name: "Name", type: stringType, optional: true }
83
+ ]
84
+ },
85
+ {
86
+ kind: "object",
87
+ name: "NeherApp3ArtikelstammCache",
88
+ properties: [
89
+ { name: "getArtikelStamm", type: functionType([], promiseType(arrayType(refType("ArtikelstammEintrag")))) },
90
+ { name: "getWarenGruppen", type: functionType([], promiseType(arrayType(jsObjectType))) },
91
+ {
92
+ name: "getArtikelByGuid",
93
+ type: functionType([{ name: "guid", type: stringType }], promiseType(unionType(refType("ArtikelstammEintrag"), rawType("undefined"))))
94
+ },
95
+ {
96
+ name: "getArtikelByKatalognummer",
97
+ type: functionType([{ name: "nummer", type: stringType }], promiseType(unionType(refType("ArtikelstammEintrag"), rawType("undefined"))))
98
+ }
99
+ ]
100
+ },
101
+ {
102
+ kind: "object",
103
+ name: "NeherApp3ErfassungCache",
104
+ properties: [
105
+ { name: "getVarianten", type: functionType([], promiseType(arrayType(refType("Variante")))) },
106
+ {
107
+ name: "getVariante",
108
+ type: functionType([{ name: "variantenNameOderKuerzel", type: stringType }], promiseType(unionType(refType("Variante"), rawType("undefined"))))
109
+ },
110
+ { name: "getWertelisten", type: functionType([], promiseType(arrayType(refType("Werteliste")))) },
111
+ {
112
+ name: "getWerteliste",
113
+ type: functionType([{ name: "name", type: stringType }], promiseType(unionType(refType("Werteliste"), rawType("undefined"))))
114
+ },
115
+ { name: "getScripts", type: functionType([], promiseType(arrayType(jsObjectType))) },
116
+ { name: "createUIMachine", type: functionType([{ name: "v", type: refType("Variante") }], voidType) }
117
+ ]
118
+ },
119
+ {
120
+ kind: "object",
121
+ name: "NeherApp3Props",
122
+ properties: [
123
+ { name: "api", type: importType("./fluentApi.js", "FluentApi") },
124
+ { name: "authManager", type: importType("./fluentAuthManager.js", "FluentAuthManager"), optional: true },
125
+ { name: "idas", type: importType("./idasFluentApi.js", "IDASFluentApi") },
126
+ { name: "mainCssPath", type: stringType, optional: true }
127
+ ]
128
+ },
129
+ {
130
+ kind: "object",
131
+ name: "NeherApp3MenuItem",
132
+ properties: [
133
+ { name: "id", type: stringType, optional: true, description: "Unique identifier for the menu item (auto-generated if not provided)" },
134
+ { name: "selected", type: booleanType, optional: true, description: "Indicates if the menu item is currently selected (managed by the menu system)" },
135
+ { name: "icon", type: stringType, optional: true, description: "URL to an icon" },
136
+ { name: "url", type: unionType(stringType, rawType("null")), optional: true, description: "Relative URL to use for routes" },
137
+ { name: "text", type: stringType, description: "Display text" },
138
+ { name: "parent", type: unionType(stringType, rawType("null")), optional: true, description: "Parent menu item (optional). If not set, the item will be added to the top level menu." },
139
+ { name: "hidden", type: booleanType, optional: true, description: "If true, the menu item will not be displayed" }
140
+ ]
141
+ },
142
+ {
143
+ kind: "alias",
144
+ name: "NeherApp3SetupContext",
145
+ type: intersectionType(
146
+ refType("NeherApp3Props"),
147
+ objectLiteralType([{ name: "neherapp3", type: refType("NeherApp3") }])
148
+ )
149
+ },
150
+ {
151
+ kind: "object",
152
+ name: "NeherApp3Module",
153
+ properties: [
154
+ { name: "moduleName", type: stringType },
155
+ {
156
+ name: "setup",
157
+ type: functionType([{ name: "context", type: refType("NeherApp3SetupContext") }], unionType(voidType, promiseType(voidType))),
158
+ optional: true
159
+ },
160
+ {
161
+ name: "mount",
162
+ type: functionType(
163
+ [
164
+ { name: "node", type: refType("HTMLElement") },
165
+ { name: "props", type: refType("NeherApp3SetupContext") }
166
+ ],
167
+ unionType(voidType, jsFunctionType)
168
+ ),
169
+ optional: true,
170
+ description: "Must return an optional unmount function"
171
+ },
172
+ { name: "embedUrl", type: stringType, optional: true },
173
+ { name: "extraCSS", type: arrayType(stringType), optional: true },
174
+ { name: "useShadowDom", type: booleanType, optional: true, description: "If true, the app will be embedded in a shadow DOM. This is required for CSS isolation." }
175
+ ]
176
+ },
177
+ {
178
+ kind: "object",
179
+ name: "NeherApp3ApiCollection",
180
+ properties: [
181
+ { name: "idas", type: importType("./idasFluentApi.js", "IDASFluentApi"), optional: true },
182
+ { name: "hostingEnvironment", type: importType("./fluentApi.js", "FluentApi"), optional: true }
183
+ ]
184
+ },
185
+ {
186
+ kind: "object",
187
+ name: "NeherApp3CacheCollection",
188
+ properties: [
189
+ { name: "artikelstamm", type: refType("NeherApp3ArtikelstammCache") },
190
+ { name: "erfassung", type: refType("NeherApp3ErfassungCache") }
191
+ ]
192
+ },
193
+ {
194
+ kind: "object",
195
+ name: "NeherApp3",
196
+ properties: [
197
+ { name: "addMenuItem", type: functionType([{ name: "menuItem", type: refType("NeherApp3MenuItem") }], voidType) },
198
+ { name: "addApp", type: functionType([{ name: "appModule", type: unionType(refType("NeherApp3Module"), stringType) }], promiseType(voidType)) },
199
+ {
200
+ name: "notify",
201
+ type: functionType(
202
+ [
203
+ { name: "message", type: stringType },
204
+ { name: "type", type: refType("NeherApp3NotifyType"), optional: true },
205
+ { name: "cb", type: jsFunctionType, optional: true }
206
+ ],
207
+ voidType
208
+ ),
209
+ description: "Shows a notification. Type defaults to 0 (info). Callback is optional."
210
+ },
211
+ { name: "api", type: refType("NeherApp3ApiCollection") },
212
+ { name: "cache", type: refType("NeherApp3CacheCollection") }
213
+ ]
214
+ }
215
+ ];
216
+
28
217
  async function collectFiles(directoryPath, allowedExtensions) {
29
218
  const entries = await readdir(directoryPath, { withFileTypes: true });
30
219
  const files = [];
@@ -54,10 +243,109 @@ function toRelativeImportPath(fromFilePath, targetFilePath) {
54
243
  return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
55
244
  }
56
245
 
246
+ function toRelativeDtsImportPath(fromFilePath, targetFilePath) {
247
+ const importPath = toRelativeImportPath(fromFilePath, targetFilePath);
248
+
249
+ if (targetFilePath === path.join(rootDir, "api", "neherApp3Types.js")) {
250
+ return importPath.replace(/\.js$/, "");
251
+ }
252
+
253
+ return importPath;
254
+ }
255
+
57
256
  function getJSDocBlocks(source) {
58
257
  return source.match(/\/\*\*[\s\S]*?\*\//g) ?? [];
59
258
  }
60
259
 
260
+ function renderTypeExpression(typeExpression, format) {
261
+ switch (typeExpression.kind) {
262
+ case "raw":
263
+ return format === "js" ? typeExpression.js : typeExpression.ts;
264
+ case "array":
265
+ return `${renderTypeExpression(typeExpression.elementType, format)}[]`;
266
+ case "promise":
267
+ return `Promise<${renderTypeExpression(typeExpression.valueType, format)}>`;
268
+ case "union":
269
+ return typeExpression.types.map((type) => renderTypeExpression(type, format)).join(" | ");
270
+ case "intersection":
271
+ return typeExpression.types.map((type) => renderTypeExpression(type, format)).join(" & ");
272
+ case "function":
273
+ return `(${typeExpression.params.map((param) => `${param.name}${param.optional ? "?" : ""}: ${renderTypeExpression(param.type, format)}`).join(", ")}) => ${renderTypeExpression(typeExpression.returnType, format)}`;
274
+ case "objectLiteral":
275
+ return `{ ${typeExpression.properties.map((property) => `${property.name}${property.optional ? "?" : ""}: ${renderTypeExpression(property.type, format)}`).join("; ")} }`;
276
+ default:
277
+ throw new Error(`Unsupported type expression kind: ${typeExpression.kind}`);
278
+ }
279
+ }
280
+
281
+ function buildNeherApp3TypesJsSource() {
282
+ const lines = [
283
+ "/**",
284
+ " * Auto-generated NeherApp3 root type definitions.",
285
+ " * Do not modify manually - changes will be overwritten by scripts/generate-root-dto-typedefs.mjs",
286
+ " */",
287
+ ""
288
+ ];
289
+
290
+ for (const jsImport of neherApp3JsImports) {
291
+ lines.push(`/** @typedef {import("${jsImport.importPath}").${jsImport.name}} ${jsImport.name} */`);
292
+ }
293
+
294
+ lines.push("");
295
+
296
+ for (const definition of neherApp3TypeDefinitions) {
297
+ if (definition.kind === "alias") {
298
+ lines.push("/**");
299
+ lines.push(` * @typedef {${renderTypeExpression(definition.type, "js")}} ${definition.name}`);
300
+ lines.push(" */");
301
+ lines.push("");
302
+ continue;
303
+ }
304
+
305
+ lines.push("/**");
306
+ lines.push(` * @typedef {Object} ${definition.name}`);
307
+
308
+ for (const property of definition.properties) {
309
+ const propertyName = property.optional ? `[${property.name}]` : property.name;
310
+ const propertyLine = ` * @property {${renderTypeExpression(property.type, "js")}} ${propertyName}`;
311
+ lines.push(property.description ? `${propertyLine} - ${property.description}` : propertyLine);
312
+ }
313
+
314
+ lines.push(" */");
315
+ lines.push("");
316
+ }
317
+
318
+ lines.push("export {};");
319
+ return `${lines.join("\n")}\n`;
320
+ }
321
+
322
+ function buildNeherApp3TypesDtsSource() {
323
+ const lines = [
324
+ "// Auto-generated NeherApp3 root type definitions.",
325
+ "// Do not modify manually - changes will be overwritten by scripts/generate-root-dto-typedefs.mjs",
326
+ ""
327
+ ];
328
+
329
+ for (const definition of neherApp3TypeDefinitions) {
330
+ if (definition.kind === "alias") {
331
+ lines.push(`export type ${definition.name} = ${renderTypeExpression(definition.type, "ts")};`);
332
+ lines.push("");
333
+ continue;
334
+ }
335
+
336
+ lines.push(`export type ${definition.name} = {`);
337
+
338
+ for (const property of definition.properties) {
339
+ lines.push(` ${property.name}${property.optional ? "?" : ""}: ${renderTypeExpression(property.type, "ts")};`);
340
+ }
341
+
342
+ lines.push("};");
343
+ lines.push("");
344
+ }
345
+
346
+ return `${lines.join("\n")}\n`;
347
+ }
348
+
61
349
  function normalizeJSDocBlock(block) {
62
350
  return block
63
351
  .replace(/^\/\*\*/, "")
@@ -68,6 +356,148 @@ function normalizeJSDocBlock(block) {
68
356
  .trim();
69
357
  }
70
358
 
359
+ function parseBracketedType(rest) {
360
+ if (!rest.startsWith("{")) {
361
+ return null;
362
+ }
363
+
364
+ let braceDepth = 0;
365
+ let closingBraceIndex = -1;
366
+
367
+ for (let index = 0; index < rest.length; index += 1) {
368
+ const character = rest[index];
369
+
370
+ if (character === "{") {
371
+ braceDepth += 1;
372
+ } else if (character === "}") {
373
+ braceDepth -= 1;
374
+
375
+ if (braceDepth === 0) {
376
+ closingBraceIndex = index;
377
+ break;
378
+ }
379
+ }
380
+ }
381
+
382
+ if (closingBraceIndex === -1) {
383
+ return null;
384
+ }
385
+
386
+ return {
387
+ typeExpression: rest.slice(1, closingBraceIndex).trim(),
388
+ remainder: rest.slice(closingBraceIndex + 1).trim()
389
+ };
390
+ }
391
+
392
+ function parseTagWithType(line, tagName) {
393
+ const marker = `@${tagName}`;
394
+ const tagIndex = line.indexOf(marker);
395
+
396
+ if (tagIndex === -1) {
397
+ return null;
398
+ }
399
+
400
+ const parsed = parseBracketedType(line.slice(tagIndex + marker.length).trimStart());
401
+
402
+ if (!parsed) {
403
+ return null;
404
+ }
405
+
406
+ return parsed;
407
+ }
408
+
409
+ function parseNameToken(remainder) {
410
+ const nameMatch = remainder.match(/^(\[[^\]]+\]|[^\s-]+)/);
411
+
412
+ if (!nameMatch) {
413
+ return null;
414
+ }
415
+
416
+ const rawName = nameMatch[1];
417
+ const optional = rawName.startsWith("[") && rawName.endsWith("]");
418
+ const normalizedName = optional ? rawName.slice(1, -1).replace(/=.*/, "") : rawName;
419
+ const description = remainder.slice(nameMatch[0].length).trim().replace(/^-/u, "").trim();
420
+
421
+ return {
422
+ name: normalizedName,
423
+ optional,
424
+ description
425
+ };
426
+ }
427
+
428
+ function extractPropertyEntries(normalizedBlock) {
429
+ const properties = [];
430
+
431
+ for (const line of normalizedBlock.split("\n")) {
432
+ if (!line.includes("@property")) {
433
+ continue;
434
+ }
435
+
436
+ const parsedTag = parseTagWithType(line, "property");
437
+
438
+ if (!parsedTag) {
439
+ continue;
440
+ }
441
+
442
+ const parsedName = parseNameToken(parsedTag.remainder);
443
+
444
+ if (!parsedName) {
445
+ continue;
446
+ }
447
+
448
+ properties.push({
449
+ name: parsedName.name,
450
+ optional: parsedName.optional,
451
+ description: parsedName.description,
452
+ typeExpression: parsedTag.typeExpression
453
+ });
454
+ }
455
+
456
+ return properties;
457
+ }
458
+
459
+ function extractParamEntries(normalizedBlock) {
460
+ const params = [];
461
+
462
+ for (const line of normalizedBlock.split("\n")) {
463
+ if (!line.includes("@param")) {
464
+ continue;
465
+ }
466
+
467
+ const parsedTag = parseTagWithType(line, "param");
468
+
469
+ if (!parsedTag) {
470
+ continue;
471
+ }
472
+
473
+ const parsedName = parseNameToken(parsedTag.remainder);
474
+
475
+ if (!parsedName) {
476
+ continue;
477
+ }
478
+
479
+ params.push({
480
+ name: parsedName.name,
481
+ optional: parsedName.optional,
482
+ typeExpression: parsedTag.typeExpression
483
+ });
484
+ }
485
+
486
+ return params;
487
+ }
488
+
489
+ function extractReturnsEntry(normalizedBlock) {
490
+ for (const line of normalizedBlock.split("\n")) {
491
+ if (!line.includes("@returns") && !line.includes("@return")) {
492
+ continue;
493
+ }
494
+
495
+ return parseTagWithType(line, line.includes("@returns") ? "returns" : "return");
496
+ }
497
+
498
+ return null;
499
+ }
500
+
71
501
  function extractTypedefEntry(normalizedBlock) {
72
502
  const typedefIndex = normalizedBlock.indexOf("@typedef");
73
503
 
@@ -111,10 +541,13 @@ function extractTypedefEntry(normalizedBlock) {
111
541
  return null;
112
542
  }
113
543
 
544
+ const properties = extractPropertyEntries(normalizedBlock);
545
+
114
546
  return {
115
- kind: "typedef",
547
+ kind: typeExpression === "Object" && properties.length > 0 ? "object" : "typedef",
116
548
  name: nameMatch[1],
117
- typeExpression
549
+ typeExpression,
550
+ properties
118
551
  };
119
552
  }
120
553
 
@@ -128,7 +561,9 @@ function extractCallbackEntry(normalizedBlock) {
128
561
  return {
129
562
  kind: "callback",
130
563
  name: callbackMatch[1],
131
- typeExpression: null
564
+ typeExpression: null,
565
+ params: extractParamEntries(normalizedBlock),
566
+ returns: extractReturnsEntry(normalizedBlock)?.typeExpression ?? "void"
132
567
  };
133
568
  }
134
569
 
@@ -160,6 +595,135 @@ function extractPublicTypeEntries(source, filePath) {
160
595
  return entries;
161
596
  }
162
597
 
598
+ function splitTopLevel(text, delimiterCharacter) {
599
+ const parts = [];
600
+ let current = "";
601
+ let angleDepth = 0;
602
+ let braceDepth = 0;
603
+ let bracketDepth = 0;
604
+ let parenDepth = 0;
605
+
606
+ for (const character of text) {
607
+ if (character === "<") {
608
+ angleDepth += 1;
609
+ } else if (character === ">") {
610
+ angleDepth = Math.max(0, angleDepth - 1);
611
+ } else if (character === "{") {
612
+ braceDepth += 1;
613
+ } else if (character === "}") {
614
+ braceDepth = Math.max(0, braceDepth - 1);
615
+ } else if (character === "[") {
616
+ bracketDepth += 1;
617
+ } else if (character === "]") {
618
+ bracketDepth = Math.max(0, bracketDepth - 1);
619
+ } else if (character === "(") {
620
+ parenDepth += 1;
621
+ } else if (character === ")") {
622
+ parenDepth = Math.max(0, parenDepth - 1);
623
+ }
624
+
625
+ if (character === delimiterCharacter && angleDepth === 0 && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
626
+ parts.push(current.trim());
627
+ current = "";
628
+ continue;
629
+ }
630
+
631
+ current += character;
632
+ }
633
+
634
+ if (current.trim()) {
635
+ parts.push(current.trim());
636
+ }
637
+
638
+ return parts;
639
+ }
640
+
641
+ function replaceObjectGenerics(typeExpression, transformTypeExpression) {
642
+ let result = "";
643
+
644
+ for (let index = 0; index < typeExpression.length; index += 1) {
645
+ if (!typeExpression.startsWith("Object<", index)) {
646
+ result += typeExpression[index];
647
+ continue;
648
+ }
649
+
650
+ let angleDepth = 0;
651
+ let closingIndex = -1;
652
+
653
+ for (let cursor = index + "Object".length; cursor < typeExpression.length; cursor += 1) {
654
+ const character = typeExpression[cursor];
655
+
656
+ if (character === "<") {
657
+ angleDepth += 1;
658
+ } else if (character === ">") {
659
+ angleDepth -= 1;
660
+
661
+ if (angleDepth === 0) {
662
+ closingIndex = cursor;
663
+ break;
664
+ }
665
+ }
666
+ }
667
+
668
+ if (closingIndex === -1) {
669
+ result += typeExpression[index];
670
+ continue;
671
+ }
672
+
673
+ const genericContent = typeExpression.slice(index + "Object<".length, closingIndex);
674
+ const genericParts = splitTopLevel(genericContent, ",");
675
+
676
+ if (genericParts.length === 2) {
677
+ result += `Record<${transformTypeExpression(genericParts[0])}, ${transformTypeExpression(genericParts[1])}>`;
678
+ } else {
679
+ result += "object";
680
+ }
681
+
682
+ index = closingIndex;
683
+ }
684
+
685
+ return result;
686
+ }
687
+
688
+ function transformTypeExpressionForDts(typeExpression, availableTypeNames) {
689
+ const normalizedExpression = typeExpression.trim();
690
+
691
+ if (normalizedExpression === "*") {
692
+ return "any";
693
+ }
694
+
695
+ let transformedExpression = normalizedExpression.replace(
696
+ /import\((?:"|')[^"']+(?:"|')\)\.([A-Za-z0-9_$]+)(\[[^\]]+\])?/g,
697
+ (fullMatch, typeName, suffix = "") => availableTypeNames.has(typeName) ? `${typeName}${suffix}` : fullMatch
698
+ );
699
+
700
+ transformedExpression = replaceObjectGenerics(transformedExpression, (innerExpression) => transformTypeExpressionForDts(innerExpression, availableTypeNames));
701
+ transformedExpression = transformedExpression.replace(/\bObject\b/g, "object");
702
+ transformedExpression = transformedExpression.replace(/\bfunction\b/g, "Function");
703
+
704
+ return transformedExpression;
705
+ }
706
+
707
+ function renderTypeEntryAsDts(entry, availableTypeNames) {
708
+ if (entry.kind === "object") {
709
+ const lines = [`export type ${entry.name} = {`];
710
+
711
+ for (const property of entry.properties) {
712
+ lines.push(` ${property.name}${property.optional ? "?" : ""}: ${transformTypeExpressionForDts(property.typeExpression, availableTypeNames)};`);
713
+ }
714
+
715
+ lines.push("};", "");
716
+ return lines;
717
+ }
718
+
719
+ if (entry.kind === "callback") {
720
+ const params = entry.params.map((param) => `${param.name}${param.optional ? "?" : ""}: ${transformTypeExpressionForDts(param.typeExpression, availableTypeNames)}`).join(", ");
721
+ return [`export type ${entry.name} = (${params}) => ${transformTypeExpressionForDts(entry.returns, availableTypeNames)};`, ""];
722
+ }
723
+
724
+ return [`export type ${entry.name} = ${transformTypeExpressionForDts(entry.typeExpression, availableTypeNames)};`, ""];
725
+ }
726
+
163
727
  function buildTypeExportMap(typeEntries) {
164
728
  const exportMap = new Map();
165
729
 
@@ -359,60 +923,20 @@ function replaceGeneratedBlock(source, startMarker, endMarker, generatedBlock, t
359
923
  return `${before}${generatedBlock}${after}`;
360
924
  }
361
925
 
362
- function buildRootDts(dtoAliases, sourceByFilePath, allJsFilesByDirectory) {
363
- const dtoAliasNames = new Set(dtoAliases.map((alias) => alias.name));
926
+ function buildRootDts(publicTypeEntries) {
364
927
  const lines = [
365
928
  "export * from \"./index.js\";",
366
929
  ""
367
930
  ];
368
931
 
369
- const sourceFiles = [rootIndexPath];
370
-
371
- for (const directory of sourceDirectories) {
372
- sourceFiles.push(...allJsFilesByDirectory.get(directory));
373
- }
374
-
375
- const discoveredTypeEntries = [];
376
-
377
- for (const filePath of sourceFiles) {
378
- const source = sourceByFilePath.get(filePath);
379
- discoveredTypeEntries.push(...extractPublicTypeEntries(source, filePath));
380
- }
381
-
382
- const canonicalizedTypeEntries = discoveredTypeEntries.filter((entry) => {
383
- const relativeFilePath = toPosixRelativePath(entry.filePath);
384
- const isDtoLeafFile = relativeFilePath.startsWith("api/dtos/") && relativeFilePath !== "api/dtos/index.js";
385
-
386
- if (isDtoLeafFile && dtoAliasNames.has(entry.name)) {
387
- return false;
388
- }
389
-
390
- return true;
391
- });
392
-
393
- const typeExportMap = buildTypeExportMap(canonicalizedTypeEntries);
932
+ const typeExportMap = buildTypeExportMap(publicTypeEntries);
394
933
  const sortedEntries = [...typeExportMap.values()].sort((left, right) => left.name.localeCompare(right.name));
934
+ const availableTypeNames = new Set(sortedEntries.map((entry) => entry.name));
395
935
 
396
936
  for (const entry of sortedEntries) {
397
- if (dtoAliasNames.has(entry.name)) {
398
- lines.push(`export type ${entry.name} = import("./api/dtos/index.js").${entry.name};`);
399
- dtoAliasNames.delete(entry.name);
400
- continue;
401
- }
402
-
403
- const relativeImportPath = `./${toPosixRelativePath(entry.filePath)}`;
404
- lines.push(`export type ${entry.name} = import("${relativeImportPath}").${entry.name};`);
405
- }
406
-
407
- for (const alias of dtoAliases) {
408
- if (!dtoAliasNames.has(alias.name)) {
409
- continue;
410
- }
411
-
412
- lines.push(`export type ${alias.name} = import("./api/dtos/index.js").${alias.name};`);
937
+ lines.push(...renderTypeEntryAsDts(entry, availableTypeNames));
413
938
  }
414
939
 
415
- lines.push("");
416
940
  return `${lines.join("\n")}\n`;
417
941
  }
418
942
 
@@ -436,18 +960,29 @@ for (const filePath of [rootIndexPath, ...dtoLeafFiles, ...businessLeafFiles, ..
436
960
  const dtoEntries = buildTypeExportMap(dtoLeafFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)));
437
961
  const dtoAliases = buildDtoAliases(dtoEntries);
438
962
  const businessAliases = buildBusinessAliases(businessLeafFiles, sourceByFilePath);
963
+ const publicTypeEntries = [
964
+ ...dtoLeafFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)),
965
+ ...businessLeafFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)),
966
+ ...otherApiJsFiles.flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath)),
967
+ ...uiLeafFiles.filter((filePath) => path.extname(filePath) === ".js").flatMap((filePath) => extractPublicTypeEntries(sourceByFilePath.get(filePath), filePath))
968
+ ];
439
969
 
440
970
  const generatedDtoIndexSource = buildDtoIndexSource(dtoAliases);
441
971
  const generatedBusinessIndexSource = buildBusinessIndexSource(businessAliases);
442
972
  const generatedUiIndexSource = buildUiIndexSource(uiLeafFiles, sourceByFilePath);
973
+ const generatedNeherApp3TypesJsSource = buildNeherApp3TypesJsSource();
974
+ const generatedNeherApp3TypesDtsSource = buildNeherApp3TypesDtsSource();
443
975
 
444
976
  await writeFile(dtoIndexPath, generatedDtoIndexSource);
445
977
  await writeFile(businessIndexPath, generatedBusinessIndexSource);
446
978
  await writeFile(uiIndexPath, generatedUiIndexSource);
979
+ await writeFile(neherApp3TypesJsPath, generatedNeherApp3TypesJsSource);
980
+ await writeFile(neherApp3TypesDtsPath, generatedNeherApp3TypesDtsSource);
447
981
 
448
982
  sourceByFilePath.set(dtoIndexPath, generatedDtoIndexSource);
449
983
  sourceByFilePath.set(businessIndexPath, generatedBusinessIndexSource);
450
984
  sourceByFilePath.set(uiIndexPath, generatedUiIndexSource);
985
+ sourceByFilePath.set(neherApp3TypesJsPath, generatedNeherApp3TypesJsSource);
451
986
 
452
987
  const dtoRootBlock = buildRootAliasBlock(
453
988
  dtoRootMarkerStart,
@@ -474,16 +1009,13 @@ const updatedRootIndexSource = replaceGeneratedBlock(rootIndexWithDtoAliases, bu
474
1009
  await writeFile(rootIndexPath, updatedRootIndexSource);
475
1010
  sourceByFilePath.set(rootIndexPath, updatedRootIndexSource);
476
1011
 
477
- const allJsFilesByDirectory = new Map([
478
- ["api", [...dtoLeafFiles, ...businessLeafFiles, ...otherApiJsFiles].sort()],
479
- ["ui", uiLeafFiles.filter((filePath) => path.extname(filePath) === ".js").sort()]
480
- ]);
481
-
482
- const generatedRootDts = buildRootDts(dtoAliases, sourceByFilePath, allJsFilesByDirectory);
1012
+ const generatedRootDts = buildRootDts(publicTypeEntries);
483
1013
  await writeFile(rootDtsPath, generatedRootDts);
484
1014
 
485
1015
  console.log(`Updated ${toPosixRelativePath(dtoIndexPath)} with ${dtoAliases.length} generated DTO aliases.`);
486
1016
  console.log(`Updated ${toPosixRelativePath(businessIndexPath)} with ${businessAliases.length} generated business API exports.`);
487
1017
  console.log(`Updated ${toPosixRelativePath(uiIndexPath)} with ${uiLeafFiles.length} generated UI exports.`);
1018
+ console.log(`Updated ${toPosixRelativePath(neherApp3TypesJsPath)} generated NeherApp3 JSDoc type exports.`);
1019
+ console.log(`Updated ${toPosixRelativePath(neherApp3TypesDtsPath)} generated NeherApp3 declaration exports.`);
488
1020
  console.log(`Updated ${toPosixRelativePath(rootIndexPath)} generated JSDoc alias blocks.`);
489
1021
  console.log(`Wrote ${toPosixRelativePath(rootDtsPath)} with full package type exports.`);