@stonecrop/schema 0.8.6 → 0.8.8

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/dist/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { GraphQLField } from 'graphql';
2
+ import { GraphQLObjectType } from 'graphql';
3
+ import { IntrospectionQuery } from 'graphql';
1
4
  import { z } from 'zod';
2
5
 
3
6
  /**
@@ -39,6 +42,8 @@ export declare const ActionDefinition: z.ZodObject<{
39
42
  */
40
43
  export declare type ActionDefinition = z.infer<typeof ActionDefinition>;
41
44
 
45
+ /* Excluded from this release type: buildScalarMap */
46
+
42
47
  /**
43
48
  * Converts camelCase to Title Case label
44
49
  * @param camelCase - Camel case string
@@ -66,88 +71,98 @@ export declare function camelToLabel(camelCase: string): string;
66
71
  export declare function camelToSnake(camelCase: string): string;
67
72
 
68
73
  /**
69
- * Extended field with conversion metadata (only used during schema-tools output)
70
- * @public
71
- */
72
- export declare interface ConversionFieldMeta extends FieldMeta {
73
- /** Original PostgreSQL type (for debugging/reference) */
74
- _pgType?: string;
75
- /** Marks fields that couldn't be automatically mapped */
76
- _unmapped?: boolean;
77
- }
78
-
79
- /**
80
- * Options for DDL to doctype conversion
74
+ * Classify a single GraphQL field into a Stonecrop field definition.
75
+ *
76
+ * Classification rules (in order):
77
+ * 1. Scalar types look up in merged scalar map
78
+ * 2. Enum types `Select` with enum values as options
79
+ * 3. Object types that are entities → `Link` with slug as options
80
+ * 4. Object types that are Connections `Doctype` with node type slug as options
81
+ * 5. List of entity type → `Doctype` with item type slug as options
82
+ * 6. Anything else → `Data` with `_unmapped: true`
83
+ *
84
+ * @param fieldName - The GraphQL field name
85
+ * @param field - The GraphQL field definition
86
+ * @param entityTypes - Set of type names classified as entities
87
+ * @param options - Conversion options (for custom scalars, unmapped meta, etc.)
88
+ * @returns The Stonecrop field definition
81
89
  * @public
82
90
  */
83
- export declare interface ConversionOptions {
84
- /** Schema to filter tables by */
85
- schema?: string;
86
- /** Tables to exclude */
87
- exclude?: string[];
88
- /** Override type mappings */
89
- typeOverrides?: Record<string, Partial<FieldMeta>>;
90
- /** How to handle inherited fields */
91
- inheritanceMode: 'flatten' | 'reference';
92
- /** Include unmapped type metadata in output */
93
- includeUnmappedMeta?: boolean;
94
- /** Use camelCase for field names (default: false, keeps snake_case) */
95
- useCamelCase?: boolean;
96
- }
91
+ export declare function classifyFieldType(fieldName: string, field: GraphQLField<unknown, unknown>, entityTypes: Set<string>, options?: GraphQLConversionOptions): GraphQLConversionFieldMeta;
97
92
 
98
93
  /**
99
- * Output of schema conversion - uses DoctypeMeta but with optional conversion metadata
94
+ * Output of GraphQL schema conversion one per entity type.
95
+ *
100
96
  * @public
101
97
  */
102
- export declare interface ConvertedDoctype extends Omit<DoctypeMeta, 'fields'> {
103
- /** Field definitions with optional conversion metadata */
104
- fields: ConversionFieldMeta[];
98
+ export declare interface ConvertedGraphQLDoctype extends Omit<DoctypeMeta, 'fields'> {
99
+ /** Field definitions with optional GraphQL conversion metadata */
100
+ fields: GraphQLConversionFieldMeta[];
101
+ /** Original GraphQL type name (for debugging/reference) */
102
+ _graphqlTypeName?: string;
105
103
  }
106
104
 
107
105
  /**
108
- * Convert PostgreSQL DDL to Stonecrop doctype schemas
109
- * @param sql - PostgreSQL DDL statements to convert
110
- * @param options - Conversion options for controlling output format
111
- * @public
112
- */
113
- export declare function convertSchema(sql: string, options?: ConversionOptions): ConvertedDoctype[];
114
-
115
- /**
116
- * Converts SQL column name to Stonecrop field naming convention
117
- * Handles special cases like ID suffixes
118
- * @param sqlName - SQL column name (snake_case)
119
- * @returns Field name and label
120
- * @public
106
+ * Convert a GraphQL schema to Stonecrop doctype schemas.
107
+ *
108
+ * Accepts either an `IntrospectionQuery` result object or an SDL string.
109
+ * Entity types are identified using heuristics (or a custom `isEntityType` function)
110
+ * and converted to `DoctypeMeta`-compatible JSON objects.
111
+ *
112
+ * @param source - GraphQL introspection result or SDL string
113
+ * @param options - Conversion options for controlling output format and behavior
114
+ * @returns Array of converted Stonecrop doctype definitions
115
+ *
121
116
  * @example
122
117
  * ```typescript
123
- * convertSQLName('user_email')
124
- * // { fieldname: 'userEmail', label: 'User Email', originalName: 'user_email' }
118
+ * // From introspection result (fetched from any GraphQL server)
119
+ * const introspection = await fetchIntrospection('http://localhost:5000/graphql')
120
+ * const doctypes = convertGraphQLSchema(introspection)
121
+ *
122
+ * // From SDL string
123
+ * const sdl = fs.readFileSync('schema.graphql', 'utf-8')
124
+ * const doctypes = convertGraphQLSchema(sdl)
125
125
  *
126
- * convertSQLName('user_id')
127
- * // { fieldname: 'userId', label: 'User', originalName: 'user_id' }
126
+ * // With PostGraphile custom scalars
127
+ * const doctypes = convertGraphQLSchema(introspection, {
128
+ * customScalars: {
129
+ * BigFloat: { component: 'ADecimalInput', fieldtype: 'Decimal' }
130
+ * }
131
+ * })
128
132
  * ```
133
+ *
134
+ * @public
129
135
  */
130
- export declare function convertSQLName(sqlName: string): NameConversion;
136
+ export declare function convertGraphQLSchema(source: IntrospectionSource, options?: GraphQLConversionOptions): ConvertedGraphQLDoctype[];
131
137
 
132
138
  /**
133
- * Batch converts multiple SQL column names
134
- * @param sqlNames - Array of SQL column names
135
- * @returns Array of name conversions
139
+ * Default heuristic to filter fields on entity types.
140
+ * Skips internal fields that don't represent meaningful data.
141
+ *
142
+ * @param fieldName - The GraphQL field name
143
+ * @param _field - The GraphQL field definition (unused in default implementation)
144
+ * @param _parentType - The parent entity type (unused in default implementation)
145
+ * @returns `true` if this field should be included
136
146
  * @public
137
147
  */
138
- export declare function convertSQLNames(sqlNames: string[]): NameConversion[];
148
+ export declare function defaultIsEntityField(fieldName: string, _field: GraphQLField<unknown, unknown>, _parentType: GraphQLObjectType): boolean;
139
149
 
140
150
  /**
141
- * Creates a bidirectional mapping between SQL and Stonecrop names
142
- * @param sqlNames - Array of SQL column names
143
- * @returns Mapping object with both directions
151
+ * Default heuristic to determine if a GraphQL object type represents an entity.
152
+ * An entity type becomes a Stonecrop doctype.
153
+ *
154
+ * This heuristic excludes:
155
+ * - Introspection types (`__*`)
156
+ * - Root operation types (`Query`, `Mutation`, `Subscription`)
157
+ * - Types with synthetic suffixes (e.g., `*Connection`, `*Edge`, `*Input`)
158
+ * - Types starting with `Node` interface marker (exact match only)
159
+ *
160
+ * @param typeName - The GraphQL type name
161
+ * @param type - The GraphQL object type definition
162
+ * @returns `true` if this type should become a Stonecrop doctype
144
163
  * @public
145
164
  */
146
- export declare function createNameMapping(sqlNames: string[]): {
147
- sqlToFieldname: Map<string, string>;
148
- fieldnameToSQL: Map<string, string>;
149
- conversions: NameConversion[];
150
- };
165
+ export declare function defaultIsEntityType(typeName: string, type: GraphQLObjectType): boolean;
151
166
 
152
167
  /**
153
168
  * Doctype metadata - complete definition of a doctype
@@ -489,18 +504,21 @@ export { FieldOptions }
489
504
  export { FieldOptions as FieldOptionsType }
490
505
 
491
506
  /**
492
- * Field template for TYPE_MAP entries
507
+ * Field template for TYPE_MAP entries.
508
+ * Defines the default component and semantic field type for a field.
493
509
  * @public
494
510
  */
495
- declare interface FieldTemplate {
511
+ export declare interface FieldTemplate {
512
+ /**
513
+ * The Vue component name to render this field (e.g., 'ATextInput', 'ADropdown')
514
+ */
496
515
  component: string;
516
+ /**
517
+ * The semantic field type (e.g., 'Data', 'Int', 'Select')
518
+ */
497
519
  fieldtype: StonecropFieldType;
498
520
  }
499
521
 
500
- declare interface FieldTemplate_2 extends FieldTemplate {
501
- _unmapped?: boolean;
502
- }
503
-
504
522
  /**
505
523
  * Validation configuration for form fields
506
524
  * @public
@@ -531,89 +549,146 @@ export declare type FieldValidation = z.infer<typeof FieldValidation>;
531
549
  export declare function getDefaultComponent(fieldtype: StonecropFieldType): string;
532
550
 
533
551
  /**
534
- * Options for column to field mapping
535
- * @public
536
- */
537
- export declare interface MapColumnOptions {
538
- /** Include unmapped type metadata in output */
539
- includeUnmappedMeta?: boolean;
540
- /** Use camelCase for field names (default: false, keeps snake_case) */
541
- useCamelCase?: boolean;
542
- }
543
-
544
- /**
545
- * Map a parsed column to a Stonecrop field definition
546
- * @param column - Parsed PostgreSQL column information
547
- * @param _tableRegistry - Map of table names to parsed table definitions (for reference resolution)
548
- * @param options - Mapping options for field naming and metadata
552
+ * Mapping from standard GraphQL scalar types to Stonecrop field types.
553
+ * These are defined by the GraphQL specification and are always available.
554
+ *
549
555
  * @public
550
556
  */
551
- export declare function mapColumnToField(column: ParsedColumn, _tableRegistry: Map<string, ParsedTable>, options?: MapColumnOptions): ConversionFieldMeta;
557
+ export declare const GQL_SCALAR_MAP: Record<string, FieldTemplate>;
552
558
 
553
559
  /**
554
- * Result of name conversion
560
+ * Extended field metadata with optional GraphQL conversion metadata.
561
+ * Only present when `includeUnmappedMeta` is enabled.
562
+ *
555
563
  * @public
556
564
  */
557
- export declare interface NameConversion {
558
- /** Converted fieldname (camelCase) */
559
- fieldname: string;
560
- /** Human-readable label */
561
- label: string;
562
- /** Original SQL name */
563
- originalName: string;
565
+ export declare interface GraphQLConversionFieldMeta extends FieldMeta {
566
+ /** Original GraphQL type name (for debugging/reference) */
567
+ _graphqlType?: string;
568
+ /** Marks fields that couldn't be automatically mapped */
569
+ _unmapped?: boolean;
564
570
  }
565
571
 
566
572
  /**
567
- * Normalize raw PostgreSQL type string to canonical PostgresType
568
- * @param rawType - Raw PostgreSQL type string (e.g., 'character varying', 'int4')
573
+ * Options for converting a GraphQL schema to Stonecrop doctype schemas.
574
+ * All hooks are optional sensible defaults are provided for common GraphQL patterns.
575
+ *
569
576
  * @public
570
577
  */
571
- export declare function normalizeType(rawType: string): PostgresType;
578
+ export declare interface GraphQLConversionOptions {
579
+ /**
580
+ * GraphQL type names to exclude from conversion.
581
+ * Applied after `isEntityType` filtering.
582
+ */
583
+ exclude?: string[];
584
+ /**
585
+ * Whitelist of GraphQL type names to convert.
586
+ * When provided, only these types are considered (after `isEntityType` filtering).
587
+ */
588
+ include?: string[];
589
+ /**
590
+ * Per-type, per-field overrides for the converted field definitions.
591
+ * Outer key is the GraphQL type name, inner key is the field name.
592
+ *
593
+ * @example
594
+ * ```typescript
595
+ * {
596
+ * SalesOrder: {
597
+ * totalAmount: { fieldtype: 'Currency', component: 'ACurrencyInput' }
598
+ * }
599
+ * }
600
+ * ```
601
+ */
602
+ typeOverrides?: Record<string, Record<string, Partial<FieldMeta>>>;
603
+ /**
604
+ * Map custom or non-standard GraphQL scalar types to Stonecrop field types.
605
+ * Merged with the built-in scalar maps (GQL_SCALAR_MAP + WELL_KNOWN_SCALARS).
606
+ * User-provided entries take highest precedence.
607
+ *
608
+ * @example
609
+ * ```typescript
610
+ * {
611
+ * MyCustomMoney: { component: 'ACurrencyInput', fieldtype: 'Currency' },
612
+ * PostGISPoint: { component: 'ATextInput', fieldtype: 'Data' }
613
+ * }
614
+ * ```
615
+ */
616
+ customScalars?: Record<string, Partial<FieldTemplate>>;
617
+ /**
618
+ * Custom function to determine if a GraphQL object type represents an entity (→ doctype).
619
+ * When provided, replaces the default heuristic entirely.
620
+ *
621
+ * The default heuristic excludes types matching synthetic patterns:
622
+ * `*Connection`, `*Edge`, `*Input`, `*Patch`, `*Payload`, `*Condition`,
623
+ * `*Filter`, `*OrderBy`, `*Aggregate`, `Query`, `Mutation`, `Subscription`, `__*`.
624
+ *
625
+ * @param typeName - The GraphQL type name
626
+ * @param type - The full GraphQL object type definition
627
+ * @returns `true` if this type should become a Stonecrop doctype
628
+ */
629
+ isEntityType?: (typeName: string, type: GraphQLObjectType) => boolean;
630
+ /**
631
+ * Custom function to filter which fields on an entity type are included.
632
+ * When provided, replaces the default field filter.
633
+ *
634
+ * The default filter excludes `nodeId`, `__typename`, and `clientMutationId`.
635
+ *
636
+ * @param fieldName - The GraphQL field name
637
+ * @param field - The full GraphQL field definition
638
+ * @param parentType - The parent entity type
639
+ * @returns `true` if this field should be included
640
+ */
641
+ isEntityField?: (fieldName: string, field: GraphQLField<unknown, unknown>, parentType: GraphQLObjectType) => boolean;
642
+ /**
643
+ * Escape hatch: fully override the classification of a specific field.
644
+ * When this returns a non-null value, it is used as the field definition
645
+ * (merged with the field name). Return `null` to fall through to default classification.
646
+ *
647
+ * @param fieldName - The GraphQL field name
648
+ * @param field - The full GraphQL field definition
649
+ * @param parentType - The parent entity type
650
+ * @returns Partial field meta to use, or `null` for default behavior
651
+ */
652
+ classifyField?: (fieldName: string, field: GraphQLField<unknown, unknown>, parentType: GraphQLObjectType) => Partial<FieldMeta> | null;
653
+ /**
654
+ * Custom function to derive the database table name from a GraphQL type name.
655
+ * The default converts PascalCase to snake_case (e.g., `SalesOrder` → `sales_order`).
656
+ *
657
+ * Return `undefined` to omit `tableName` from the output.
658
+ *
659
+ * @param typeName - The GraphQL type name
660
+ * @returns The derived table name, or `undefined`
661
+ */
662
+ deriveTableName?: (typeName: string) => string | undefined;
663
+ /**
664
+ * Include `_graphqlType` and `_unmapped` metadata on converted fields.
665
+ * Useful for debugging conversions. Defaults to `false`.
666
+ */
667
+ includeUnmappedMeta?: boolean;
668
+ }
572
669
 
573
670
  /**
574
- * Intermediate representation of a parsed column (from DDL)
671
+ * Set of scalar type names that are internal to GraphQL servers and should be skipped
672
+ * during field conversion (they don't represent meaningful data fields).
673
+ *
575
674
  * @public
576
675
  */
577
- export declare interface ParsedColumn {
578
- /** Column name (from SQL definition) */
579
- name: string;
580
- /** Raw PostgreSQL data type string */
581
- dataType: string;
582
- /** Normalized PostgreSQL type (mapped to standard types) */
583
- normalizedType: PostgresType;
584
- /** Whether the column allows NULL values */
585
- nullable: boolean;
586
- /** Whether the column is auto-generated (GENERATED ALWAYS) */
587
- isGenerated: boolean;
588
- /** Default value expression (if specified) */
589
- defaultValue?: string;
590
- /** Number of array dimensions (0 for non-array types) */
591
- arrayDimensions: number;
592
- /** Foreign key reference information (if column references another table) */
593
- reference?: {
594
- /** Referenced schema name */
595
- schema?: string;
596
- /** Referenced table name */
597
- table: string;
598
- /** Referenced column name */
599
- column: string;
600
- /** Foreign key ON DELETE action */
601
- onDelete?: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION';
602
- };
603
- /** Numeric precision (for NUMERIC/DECIMAL types) */
604
- precision?: number;
605
- /** Numeric scale (for NUMERIC/DECIMAL types) */
606
- scale?: number;
607
- /** Character/binary length constraint (for VARCHAR, CHAR, BIT types) */
608
- length?: number;
609
- }
676
+ export declare const INTERNAL_SCALARS: Set<string>;
610
677
 
611
678
  /**
612
- * Parse PostgreSQL DDL and extract table definitions
613
- * @param sql - PostgreSQL DDL statements to parse
679
+ * Input source for the GraphQL schema converter.
680
+ * Accepts either a standard GraphQL introspection result or an SDL string.
681
+ *
682
+ * - `IntrospectionQuery`: The raw result of a GraphQL introspection query (from any server)
683
+ * - `string`: An SDL (Schema Definition Language) string
684
+ *
685
+ * Note: URL fetching is intentionally not supported in the library API.
686
+ * Use the CLI (`stonecrop-schema generate --endpoint <url>`) for endpoint fetching,
687
+ * or fetch the introspection result yourself and pass it in.
688
+ *
614
689
  * @public
615
690
  */
616
- export declare function parseDDL(sql: string): ParsedTable[];
691
+ export declare type IntrospectionSource = IntrospectionQuery | string;
617
692
 
618
693
  /**
619
694
  * Parse and validate a doctype, throwing on failure
@@ -624,25 +699,6 @@ export declare function parseDDL(sql: string): ParsedTable[];
624
699
  */
625
700
  export declare function parseDoctype(data: unknown): DoctypeMeta;
626
701
 
627
- /**
628
- * Intermediate representation of a parsed table (from DDL)
629
- * @public
630
- */
631
- export declare interface ParsedTable {
632
- /** Table name (from CREATE TABLE statement) */
633
- name: string;
634
- /** Schema name (if specified, defaults to 'public') */
635
- schema?: string;
636
- /** Column definitions parsed from the table */
637
- columns: ParsedColumn[];
638
- /** Parent table names (for PostgreSQL table inheritance) */
639
- inherits?: string[];
640
- /** Table comment from COMMENT ON TABLE statement */
641
- comment?: string;
642
- /** Doctype name extracted from comment (if using \@doctype convention) */
643
- doctypeName?: string;
644
- }
645
-
646
702
  /**
647
703
  * Parse and validate a field, throwing on failure
648
704
  * @param data - Data to parse
@@ -653,22 +709,17 @@ export declare interface ParsedTable {
653
709
  export declare function parseField(data: unknown): FieldMeta;
654
710
 
655
711
  /**
656
- * Mapping from PostgreSQL types to Stonecrop field types
657
- * @public
658
- */
659
- export declare const PG_TYPE_MAP: Record<PostgresType, FieldTemplate_2>;
660
-
661
- /**
662
- * PostgreSQL types we handle during conversion
663
- * @public
664
- */
665
- export declare const PostgresType: z.ZodEnum<["text", "varchar", "char", "citext", "smallint", "integer", "bigint", "serial", "bigserial", "smallserial", "numeric", "decimal", "real", "double precision", "money", "boolean", "date", "time", "timetz", "timestamp", "timestamptz", "interval", "int4range", "int8range", "numrange", "daterange", "tsrange", "tstzrange", "bytea", "uuid", "json", "jsonb", "bit", "varbit", "xml", "unit", "cube", "unknown"]>;
666
-
667
- /**
668
- * PostgreSQL type enum inferred from Zod schema
712
+ * Convert PascalCase to snake_case (e.g., for deriving table names from type names)
713
+ * @param pascal - PascalCase string
714
+ * @returns snake_case string
669
715
  * @public
716
+ * @example
717
+ * ```typescript
718
+ * pascalToSnake('SalesOrder') // 'sales_order'
719
+ * pascalToSnake('SalesOrderItem') // 'sales_order_item'
720
+ * ```
670
721
  */
671
- export declare type PostgresType = z.infer<typeof PostgresType>;
722
+ export declare function pascalToSnake(pascal: string): string;
672
723
 
673
724
  /**
674
725
  * Converts snake_case to camelCase
@@ -727,12 +778,6 @@ export declare function toPascalCase(tableName: string): string;
727
778
  */
728
779
  export declare function toSlug(name: string): string;
729
780
 
730
- /**
731
- * PostgreSQL type aliases to canonical types
732
- * @public
733
- */
734
- export declare const TYPE_ALIASES: Record<string, PostgresType>;
735
-
736
781
  /**
737
782
  * Mapping from StonecropFieldType to default Vue component.
738
783
  * Components can be overridden in the field definition.
@@ -778,6 +823,18 @@ export declare interface ValidationResult {
778
823
  errors: ValidationError[];
779
824
  }
780
825
 
826
+ /**
827
+ * Mapping from well-known custom GraphQL scalars to Stonecrop field types.
828
+ * These cover scalars commonly used across GraphQL servers (PostGraphile, Hasura, etc.)
829
+ * without baking in knowledge of any specific server.
830
+ *
831
+ * Entries here have lower precedence than `customScalars` from options, but higher
832
+ * precedence than unknown/unmapped scalars.
833
+ *
834
+ * @public
835
+ */
836
+ export declare const WELL_KNOWN_SCALARS: Record<string, FieldTemplate>;
837
+
781
838
  /**
782
839
  * Workflow metadata - states and actions for a doctype
783
840
  * @public