@arcteninc/core 0.0.31 → 0.0.32
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.
- package/package.json +1 -1
- package/scripts/cli-extract-types-auto.ts +125 -13
package/package.json
CHANGED
|
@@ -11,13 +11,18 @@ import { glob } from 'glob';
|
|
|
11
11
|
|
|
12
12
|
// JSON Schema compatible format
|
|
13
13
|
interface JsonSchemaProperty {
|
|
14
|
-
type
|
|
14
|
+
type?: string | string[]; // Can be string, array of strings, or omitted for anyOf/oneOf/$ref
|
|
15
15
|
description?: string;
|
|
16
16
|
default?: string;
|
|
17
17
|
enum?: string[]; // For enum types
|
|
18
18
|
items?: JsonSchemaProperty; // For array types
|
|
19
19
|
properties?: Record<string, JsonSchemaProperty>; // For object types
|
|
20
20
|
required?: string[]; // For nested objects
|
|
21
|
+
anyOf?: JsonSchemaProperty[]; // For union types
|
|
22
|
+
oneOf?: JsonSchemaProperty[]; // For discriminated unions
|
|
23
|
+
$ref?: string; // For type references
|
|
24
|
+
// Allow any additional properties for complex schemas
|
|
25
|
+
[key: string]: any;
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
interface FunctionMetadata {
|
|
@@ -451,31 +456,138 @@ function serializeType(
|
|
|
451
456
|
if (type.isUnion()) {
|
|
452
457
|
const types = type.types;
|
|
453
458
|
|
|
454
|
-
//
|
|
455
|
-
const
|
|
459
|
+
// Separate null, undefined, and other types
|
|
460
|
+
const hasNull = types.some(t => t.flags & ts.TypeFlags.Null);
|
|
461
|
+
const hasUndefined = types.some(t => t.flags & ts.TypeFlags.Undefined);
|
|
462
|
+
const nonNullUndefinedTypes = types.filter(
|
|
463
|
+
t => !(t.flags & ts.TypeFlags.Null) && !(t.flags & ts.TypeFlags.Undefined)
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
// Check if it's a string literal union (enum) - possibly with null/undefined
|
|
467
|
+
const stringLiterals = nonNullUndefinedTypes
|
|
456
468
|
.filter(t => t.flags & ts.TypeFlags.StringLiteral)
|
|
457
469
|
.map(t => (t as ts.StringLiteralType).value);
|
|
458
470
|
|
|
459
|
-
|
|
471
|
+
// If all non-null/undefined types are string literals, use enum
|
|
472
|
+
if (stringLiterals.length > 0 && stringLiterals.length === nonNullUndefinedTypes.length) {
|
|
473
|
+
const enumSchema: JsonSchemaProperty = { type: 'string', enum: stringLiterals };
|
|
474
|
+
|
|
475
|
+
// If null or undefined is also present, wrap in anyOf
|
|
476
|
+
if (hasNull || hasUndefined) {
|
|
477
|
+
return {
|
|
478
|
+
isOptional: false,
|
|
479
|
+
schema: { anyOf: [enumSchema, { type: 'null' }] }
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
460
483
|
return {
|
|
461
484
|
isOptional: false,
|
|
462
|
-
schema:
|
|
485
|
+
schema: enumSchema
|
|
463
486
|
};
|
|
464
487
|
}
|
|
465
488
|
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if (hasUndefined && nonUndefinedTypes.length === 1) {
|
|
471
|
-
const result = serializeType(nonUndefinedTypes[0], checker, visited, depth + 1);
|
|
489
|
+
// Handle optional (T | undefined) - make it optional instead of union
|
|
490
|
+
if (hasUndefined && nonNullUndefinedTypes.length === 1 && !hasNull) {
|
|
491
|
+
const result = serializeType(nonNullUndefinedTypes[0], checker, visited, depth + 1);
|
|
472
492
|
return { isOptional: true, schema: result.schema };
|
|
473
493
|
}
|
|
474
494
|
|
|
475
|
-
//
|
|
495
|
+
// Build anyOf array for proper JSON Schema union
|
|
496
|
+
const anyOf: JsonSchemaProperty[] = [];
|
|
497
|
+
|
|
498
|
+
// Add null if present
|
|
499
|
+
if (hasNull) {
|
|
500
|
+
anyOf.push({ type: 'null' });
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Add undefined as null (JSON doesn't have undefined)
|
|
504
|
+
if (hasUndefined) {
|
|
505
|
+
anyOf.push({ type: 'null' });
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Process non-null/undefined types
|
|
509
|
+
for (const unionType of nonNullUndefinedTypes) {
|
|
510
|
+
// Check if it's a primitive type
|
|
511
|
+
if (unionType.flags & ts.TypeFlags.String) {
|
|
512
|
+
anyOf.push({ type: 'string' });
|
|
513
|
+
} else if (unionType.flags & ts.TypeFlags.Number) {
|
|
514
|
+
anyOf.push({ type: 'number' });
|
|
515
|
+
} else if (unionType.flags & ts.TypeFlags.Boolean) {
|
|
516
|
+
anyOf.push({ type: 'boolean' });
|
|
517
|
+
} else if (checker.isArrayType(unionType)) {
|
|
518
|
+
// Handle array types
|
|
519
|
+
const typeArgs = (unionType as ts.TypeReference).typeArguments;
|
|
520
|
+
if (typeArgs && typeArgs.length > 0) {
|
|
521
|
+
const itemResult = serializeType(typeArgs[0], checker, visited, depth + 1);
|
|
522
|
+
anyOf.push({ type: 'array', items: itemResult.schema });
|
|
523
|
+
} else {
|
|
524
|
+
anyOf.push({ type: 'array' });
|
|
525
|
+
}
|
|
526
|
+
} else {
|
|
527
|
+
// Try to serialize the type (handles objects, type references, etc.)
|
|
528
|
+
// This will recursively resolve type aliases and interfaces
|
|
529
|
+
try {
|
|
530
|
+
const refResult = serializeType(unionType, checker, visited, depth + 1);
|
|
531
|
+
|
|
532
|
+
// Check if we got a valid schema (not just 'any' fallback)
|
|
533
|
+
const typeString = checker.typeToString(unionType);
|
|
534
|
+
const isFallbackAny = refResult.schema.type === 'any' &&
|
|
535
|
+
!refResult.schema.properties &&
|
|
536
|
+
!refResult.schema.anyOf &&
|
|
537
|
+
!refResult.schema.items &&
|
|
538
|
+
typeString.length < 100; // If type string is short, it's probably a simple reference
|
|
539
|
+
|
|
540
|
+
if (!isFallbackAny) {
|
|
541
|
+
anyOf.push(refResult.schema);
|
|
542
|
+
} else {
|
|
543
|
+
// For type references we can't fully resolve (like FilterTreeNode),
|
|
544
|
+
// check if it's a named type that might be defined elsewhere
|
|
545
|
+
// For now, we'll use 'any' but in a more advanced version,
|
|
546
|
+
// we'd track these and generate $defs or inline them
|
|
547
|
+
// Check if it looks like a type reference (starts with capital, no special chars)
|
|
548
|
+
if (/^[A-Z][a-zA-Z0-9]*$/.test(typeString)) {
|
|
549
|
+
// It's a type reference - use any for now
|
|
550
|
+
// TODO: Resolve and inline type definitions or use $ref with $defs
|
|
551
|
+
anyOf.push({ type: 'any' });
|
|
552
|
+
} else {
|
|
553
|
+
// It's something else - try to use the serialized result anyway
|
|
554
|
+
anyOf.push(refResult.schema);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
} catch (e) {
|
|
558
|
+
// Fallback for unresolvable types
|
|
559
|
+
const typeString = checker.typeToString(unionType);
|
|
560
|
+
console.warn(`Warning: Could not serialize union type: ${typeString}`, e);
|
|
561
|
+
anyOf.push({ type: 'any' });
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// If we only have one type (plus null/undefined), simplify
|
|
567
|
+
if (anyOf.length === 1) {
|
|
568
|
+
return { isOptional: hasNull || hasUndefined, schema: anyOf[0] };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// If we have multiple types, use anyOf
|
|
572
|
+
if (anyOf.length > 1) {
|
|
573
|
+
// Remove duplicate null entries
|
|
574
|
+
const uniqueAnyOf = anyOf.filter((schema, index, self) => {
|
|
575
|
+
if (schema.type === 'null') {
|
|
576
|
+
return index === self.findIndex(s => s.type === 'null');
|
|
577
|
+
}
|
|
578
|
+
return true;
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
return {
|
|
582
|
+
isOptional: false,
|
|
583
|
+
schema: { anyOf: uniqueAnyOf }
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Fallback
|
|
476
588
|
return {
|
|
477
589
|
isOptional: false,
|
|
478
|
-
schema: { type:
|
|
590
|
+
schema: { type: 'any' }
|
|
479
591
|
};
|
|
480
592
|
}
|
|
481
593
|
|