@syncular/typegen 0.1.2 → 0.2.0

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 (99) hide show
  1. package/README.md +436 -94
  2. package/dist/cli.d.ts +1 -2
  3. package/dist/cli.js +159 -73
  4. package/dist/emit-dart.d.ts +17 -0
  5. package/dist/emit-dart.js +201 -0
  6. package/dist/emit-kotlin.d.ts +16 -0
  7. package/dist/emit-kotlin.js +201 -0
  8. package/dist/emit-queries-dart.d.ts +2 -0
  9. package/dist/emit-queries-dart.js +160 -0
  10. package/dist/emit-queries-kotlin.d.ts +2 -0
  11. package/dist/emit-queries-kotlin.js +162 -0
  12. package/dist/emit-queries-swift.d.ts +2 -0
  13. package/dist/emit-queries-swift.js +146 -0
  14. package/dist/emit-queries.d.ts +2 -0
  15. package/dist/emit-queries.js +129 -0
  16. package/dist/emit-swift.d.ts +18 -0
  17. package/dist/emit-swift.js +235 -0
  18. package/dist/emit.d.ts +16 -0
  19. package/dist/emit.js +191 -0
  20. package/dist/errors.d.ts +6 -0
  21. package/dist/errors.js +10 -0
  22. package/dist/generate.d.ts +53 -18
  23. package/dist/generate.js +391 -77
  24. package/dist/index.d.ts +17 -14
  25. package/dist/index.js +16 -13
  26. package/dist/init.d.ts +12 -0
  27. package/dist/init.js +85 -0
  28. package/dist/ir.d.ts +90 -0
  29. package/dist/ir.js +94 -0
  30. package/dist/manifest.d.ts +76 -0
  31. package/dist/manifest.js +303 -0
  32. package/dist/query.d.ts +96 -0
  33. package/dist/query.js +719 -0
  34. package/dist/sql.d.ts +13 -0
  35. package/dist/sql.js +440 -0
  36. package/package.json +14 -34
  37. package/src/cli.ts +161 -82
  38. package/src/emit-dart.ts +245 -0
  39. package/src/emit-kotlin.ts +257 -0
  40. package/src/emit-queries-dart.ts +203 -0
  41. package/src/emit-queries-kotlin.ts +209 -0
  42. package/src/emit-queries-swift.ts +198 -0
  43. package/src/emit-queries.ts +183 -0
  44. package/src/emit-swift.ts +295 -0
  45. package/src/emit.ts +239 -0
  46. package/src/errors.ts +11 -0
  47. package/src/generate.ts +574 -105
  48. package/src/index.ts +16 -13
  49. package/src/init.ts +100 -0
  50. package/src/ir.ts +197 -0
  51. package/src/manifest.ts +445 -0
  52. package/src/query.ts +918 -0
  53. package/src/sql.ts +533 -0
  54. package/dist/app-contract.d.ts +0 -154
  55. package/dist/app-contract.d.ts.map +0 -1
  56. package/dist/app-contract.js +0 -250
  57. package/dist/app-contract.js.map +0 -1
  58. package/dist/checksums.d.ts +0 -6
  59. package/dist/checksums.d.ts.map +0 -1
  60. package/dist/checksums.js +0 -173
  61. package/dist/checksums.js.map +0 -1
  62. package/dist/cli.d.ts.map +0 -1
  63. package/dist/cli.js.map +0 -1
  64. package/dist/generate.d.ts.map +0 -1
  65. package/dist/generate.js.map +0 -1
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/introspect-postgres.d.ts +0 -8
  69. package/dist/introspect-postgres.d.ts.map +0 -1
  70. package/dist/introspect-postgres.js +0 -94
  71. package/dist/introspect-postgres.js.map +0 -1
  72. package/dist/introspect-sqlite.d.ts +0 -10
  73. package/dist/introspect-sqlite.d.ts.map +0 -1
  74. package/dist/introspect-sqlite.js +0 -97
  75. package/dist/introspect-sqlite.js.map +0 -1
  76. package/dist/introspect.d.ts +0 -8
  77. package/dist/introspect.d.ts.map +0 -1
  78. package/dist/introspect.js +0 -18
  79. package/dist/introspect.js.map +0 -1
  80. package/dist/map-types.d.ts +0 -20
  81. package/dist/map-types.d.ts.map +0 -1
  82. package/dist/map-types.js +0 -151
  83. package/dist/map-types.js.map +0 -1
  84. package/dist/render.d.ts +0 -23
  85. package/dist/render.d.ts.map +0 -1
  86. package/dist/render.js +0 -140
  87. package/dist/render.js.map +0 -1
  88. package/dist/types.d.ts +0 -122
  89. package/dist/types.d.ts.map +0 -1
  90. package/dist/types.js +0 -5
  91. package/dist/types.js.map +0 -1
  92. package/src/app-contract.ts +0 -531
  93. package/src/checksums.ts +0 -257
  94. package/src/introspect-postgres.ts +0 -149
  95. package/src/introspect-sqlite.ts +0 -156
  96. package/src/introspect.ts +0 -36
  97. package/src/map-types.ts +0 -189
  98. package/src/render.ts +0 -196
  99. package/src/types.ts +0 -137
package/src/map-types.ts DELETED
@@ -1,189 +0,0 @@
1
- /**
2
- * @syncular/typegen - Type mapping
3
- *
4
- * Maps SQL types to TypeScript types for each dialect,
5
- * with support for column codec type overrides.
6
- */
7
-
8
- import type { ColumnInfo, TypegenDialect, TypeOverride } from './types';
9
-
10
- export interface ResolvedType {
11
- tsType: string;
12
- imports: Array<{ name: string; from: string }>;
13
- }
14
-
15
- function mapSqliteType(sqlType: string): string {
16
- const upper = sqlType.toUpperCase();
17
-
18
- if (upper.includes('INT')) return 'number';
19
- if (
20
- upper.includes('REAL') ||
21
- upper.includes('FLOAT') ||
22
- upper.includes('DOUBLE')
23
- )
24
- return 'number';
25
- if (upper.includes('BLOB')) return 'Uint8Array';
26
- if (upper.includes('BOOL')) return 'number';
27
- // TEXT, VARCHAR, CHAR, etc.
28
- return 'string';
29
- }
30
-
31
- function mapPostgresType(sqlType: string): string {
32
- const lower = sqlType.toLowerCase().replace(/\s+/g, ' ').trim();
33
-
34
- // Array types — strip trailing [] and map the element type
35
- if (lower.endsWith('[]')) {
36
- const element = mapPostgresType(lower.slice(0, -2));
37
- return `${element}[]`;
38
- }
39
-
40
- // Integer types
41
- if (
42
- lower === 'int2' ||
43
- lower === 'int4' ||
44
- lower === 'integer' ||
45
- lower === 'smallint' ||
46
- lower === 'serial'
47
- )
48
- return 'number';
49
-
50
- // 64-bit integers — not safe in JS
51
- if (lower === 'int8' || lower === 'bigint' || lower === 'bigserial')
52
- return 'string';
53
-
54
- // Floating-point / numeric
55
- if (
56
- lower === 'float4' ||
57
- lower === 'float8' ||
58
- lower === 'real' ||
59
- lower === 'double precision'
60
- )
61
- return 'number';
62
-
63
- // Exact numeric types are string by default to match common pg driver behavior.
64
- if (lower === 'numeric' || lower === 'decimal') return 'string';
65
-
66
- // Boolean
67
- if (lower === 'bool' || lower === 'boolean') return 'boolean';
68
-
69
- // JSON
70
- if (lower === 'json' || lower === 'jsonb') return 'unknown';
71
-
72
- // Date/time
73
- if (
74
- lower === 'timestamp' ||
75
- lower === 'timestamptz' ||
76
- lower === 'timestamp with time zone' ||
77
- lower === 'timestamp without time zone' ||
78
- lower === 'date' ||
79
- lower === 'time' ||
80
- lower === 'timetz' ||
81
- lower === 'time with time zone' ||
82
- lower === 'time without time zone'
83
- )
84
- return 'string';
85
-
86
- // Binary
87
- if (lower === 'bytea') return 'Uint8Array';
88
-
89
- // Text types
90
- if (
91
- lower === 'uuid' ||
92
- lower === 'text' ||
93
- lower === 'varchar' ||
94
- lower === 'char' ||
95
- lower === 'citext' ||
96
- lower.startsWith('character varying') ||
97
- lower.startsWith('character(') ||
98
- lower.startsWith('varchar(') ||
99
- lower.startsWith('char(')
100
- )
101
- return 'string';
102
-
103
- // Interval
104
- if (lower === 'interval') return 'string';
105
-
106
- // Network types
107
- if (lower === 'inet' || lower === 'cidr' || lower === 'macaddr')
108
- return 'string';
109
-
110
- // Geometric types
111
- if (
112
- lower === 'point' ||
113
- lower === 'line' ||
114
- lower === 'box' ||
115
- lower === 'path' ||
116
- lower === 'polygon' ||
117
- lower === 'circle' ||
118
- lower === 'lseg'
119
- )
120
- return 'string';
121
-
122
- // Range types
123
- if (
124
- lower === 'int4range' ||
125
- lower === 'int8range' ||
126
- lower === 'tsrange' ||
127
- lower === 'tstzrange' ||
128
- lower === 'daterange' ||
129
- lower === 'numrange'
130
- )
131
- return 'string';
132
-
133
- // Full-text search
134
- if (lower === 'tsvector' || lower === 'tsquery') return 'string';
135
-
136
- // Other
137
- if (lower === 'xml') return 'string';
138
- if (lower === 'money') return 'string';
139
- if (
140
- lower === 'bit' ||
141
- lower === 'varbit' ||
142
- lower.startsWith('bit(') ||
143
- lower.startsWith('bit varying')
144
- )
145
- return 'string';
146
-
147
- return 'string';
148
- }
149
-
150
- function defaultMapper(dialect: TypegenDialect): (sqlType: string) => string {
151
- return dialect === 'postgres' ? mapPostgresType : mapSqliteType;
152
- }
153
-
154
- /**
155
- * Resolve the TypeScript type for a column, applying an optional override
156
- * first and falling back to the default dialect mapping.
157
- */
158
- export function resolveColumnType(
159
- col: ColumnInfo,
160
- override?: TypeOverride
161
- ): ResolvedType {
162
- const imports: Array<{ name: string; from: string }> = [];
163
-
164
- if (override !== undefined) {
165
- if (typeof override === 'string') {
166
- const baseType = override;
167
- return {
168
- tsType: col.nullable ? `${baseType} | null` : baseType,
169
- imports,
170
- };
171
- }
172
- if (override.import) {
173
- imports.push(override.import);
174
- }
175
- const baseType = override.type;
176
- return {
177
- tsType: col.nullable ? `${baseType} | null` : baseType,
178
- imports,
179
- };
180
- }
181
-
182
- // Default mapping
183
- const mapper = defaultMapper(col.dialect);
184
- const baseType = mapper(col.sqlType);
185
- return {
186
- tsType: col.nullable ? `${baseType} | null` : baseType,
187
- imports,
188
- };
189
- }
package/src/render.ts DELETED
@@ -1,196 +0,0 @@
1
- /**
2
- * @syncular/typegen - TypeScript code generation
3
- */
4
-
5
- import type { ColumnSchema, TableSchema, VersionedSchema } from './types';
6
-
7
- /**
8
- * Convert a snake_case table/column name to PascalCase.
9
- */
10
- function toPascalCase(str: string): string {
11
- return str
12
- .split(/[_-]/)
13
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
14
- .join('');
15
- }
16
-
17
- /**
18
- * Render a single column definition.
19
- */
20
- function renderColumn(column: ColumnSchema): string {
21
- const tsType = column.hasDefault
22
- ? `Generated<${column.tsType}>`
23
- : column.tsType;
24
- return ` ${column.name}: ${tsType};`;
25
- }
26
-
27
- /**
28
- * Render a table interface.
29
- */
30
- function renderTableInterface(
31
- table: TableSchema,
32
- interfaceName: string
33
- ): string {
34
- const columns = table.columns.map(renderColumn).join('\n');
35
- return `export interface ${interfaceName} {\n${columns}\n}`;
36
- }
37
-
38
- /**
39
- * Render a database interface containing all tables.
40
- */
41
- function renderDbInterface(
42
- schema: VersionedSchema,
43
- interfaceName: string,
44
- extendsType?: string
45
- ): string {
46
- const extendsClause = extendsType ? ` extends ${extendsType}` : '';
47
- const tableEntries = schema.tables
48
- .map((t) => ` ${t.name}: ${toPascalCase(t.name)}Table;`)
49
- .join('\n');
50
-
51
- return `export interface ${interfaceName}${extendsClause} {\n${tableEntries}\n}`;
52
- }
53
-
54
- /**
55
- * Options for rendering types.
56
- */
57
- export interface RenderOptions {
58
- /** Schemas at each version (for version history) */
59
- schemas: VersionedSchema[];
60
- /** Generate versioned interfaces */
61
- includeVersionHistory?: boolean;
62
- /** Custom imports collected from resolver results */
63
- customImports?: Array<{ name: string; from: string }>;
64
- }
65
-
66
- /**
67
- * Render complete TypeScript type definitions.
68
- */
69
- export function renderTypes(options: RenderOptions): string {
70
- const { schemas, includeVersionHistory, customImports } = options;
71
- const lines: string[] = [];
72
-
73
- // Header
74
- lines.push('/**');
75
- lines.push(' * Auto-generated database types from migrations.');
76
- lines.push(' * DO NOT EDIT - regenerate with @syncular/typegen');
77
- lines.push(' */');
78
- lines.push('');
79
-
80
- const usesGenerated = schemas.some((schema) =>
81
- schema.tables.some((table) =>
82
- table.columns.some((column) => column.hasDefault)
83
- )
84
- );
85
- if (usesGenerated) {
86
- lines.push("import type { Generated } from 'kysely';");
87
- lines.push('');
88
- }
89
-
90
- // Render custom imports from resolver
91
- if (customImports && customImports.length > 0) {
92
- // Group imports by source module
93
- const byModule = new Map<string, Set<string>>();
94
- for (const imp of customImports) {
95
- let names = byModule.get(imp.from);
96
- if (!names) {
97
- names = new Set();
98
- byModule.set(imp.from, names);
99
- }
100
- names.add(imp.name);
101
- }
102
- for (const [from, names] of byModule) {
103
- const sorted = [...names].sort();
104
- lines.push(`import type { ${sorted.join(', ')} } from '${from}';`);
105
- }
106
- lines.push('');
107
- }
108
-
109
- // Get the latest schema
110
- const latestSchema = schemas[schemas.length - 1];
111
- if (!latestSchema) {
112
- lines.push('// No migrations defined');
113
- return lines.join('\n');
114
- }
115
-
116
- // Generate table interfaces for latest version
117
- for (const table of latestSchema.tables) {
118
- lines.push(renderTableInterface(table, `${toPascalCase(table.name)}Table`));
119
- lines.push('');
120
- }
121
-
122
- // Generate versioned DB interfaces if requested
123
- if (includeVersionHistory && schemas.length > 0) {
124
- for (const schema of schemas) {
125
- // For each version, generate table interfaces with version suffix
126
- // if they differ from the latest
127
- const versionSuffix = `V${schema.version}`;
128
-
129
- // Generate versioned table interfaces
130
- for (const table of schema.tables) {
131
- const latestTable = latestSchema.tables.find(
132
- (t) => t.name === table.name
133
- );
134
-
135
- // Only generate versioned interface if different from latest
136
- if (latestTable && !tablesEqual(table, latestTable)) {
137
- lines.push(
138
- renderTableInterface(
139
- table,
140
- `${toPascalCase(table.name)}Table${versionSuffix}`
141
- )
142
- );
143
- lines.push('');
144
- }
145
- }
146
-
147
- // Generate versioned DB interface
148
- const tableEntries = schema.tables
149
- .map((t) => {
150
- const latestTable = latestSchema.tables.find(
151
- (lt) => lt.name === t.name
152
- );
153
- const useVersioned = latestTable && !tablesEqual(t, latestTable);
154
- const typeName = useVersioned
155
- ? `${toPascalCase(t.name)}Table${versionSuffix}`
156
- : `${toPascalCase(t.name)}Table`;
157
- return ` ${t.name}: ${typeName};`;
158
- })
159
- .join('\n');
160
-
161
- lines.push(`export interface ClientDb${versionSuffix} {`);
162
- lines.push(tableEntries);
163
- lines.push('}');
164
- lines.push('');
165
- }
166
- }
167
-
168
- // Generate main DB interface (latest version)
169
- lines.push(renderDbInterface(latestSchema, 'ClientDb'));
170
- lines.push('');
171
-
172
- return lines.join('\n');
173
- }
174
-
175
- /**
176
- * Check if two table schemas are equal.
177
- */
178
- function tablesEqual(a: TableSchema, b: TableSchema): boolean {
179
- if (a.columns.length !== b.columns.length) return false;
180
-
181
- for (let i = 0; i < a.columns.length; i++) {
182
- const colA = a.columns[i]!;
183
- const colB = b.columns[i]!;
184
-
185
- if (
186
- colA.name !== colB.name ||
187
- colA.tsType !== colB.tsType ||
188
- colA.nullable !== colB.nullable ||
189
- colA.hasDefault !== colB.hasDefault
190
- ) {
191
- return false;
192
- }
193
- }
194
-
195
- return true;
196
- }
package/src/types.ts DELETED
@@ -1,137 +0,0 @@
1
- /**
2
- * @syncular/typegen - Type definitions
3
- */
4
-
5
- import type { DefinedMigrations } from '@syncular/migrations';
6
-
7
- export type TypegenDialect = 'sqlite' | 'postgres';
8
-
9
- /**
10
- * Column information for a schema column.
11
- */
12
- export interface ColumnInfo {
13
- table: string;
14
- column: string;
15
- sqlType: string;
16
- nullable: boolean;
17
- isPrimaryKey: boolean;
18
- hasDefault: boolean;
19
- dialect: TypegenDialect;
20
- }
21
-
22
- /**
23
- * Return type from a resolver function.
24
- */
25
- export type TypeOverride =
26
- | string
27
- | { type: string; import?: { name: string; from: string } };
28
-
29
- /**
30
- * Column codec definition shared across runtime and typegen.
31
- * Typegen only consumes the `ts` field, but a full codec object can be
32
- * provided as a single source of truth.
33
- */
34
- export interface ColumnCodec<App, Db> {
35
- ts: TypeOverride;
36
- toDb(value: App): Db;
37
- fromDb(value: Db): App;
38
- dialects?: Partial<
39
- Record<
40
- TypegenDialect,
41
- {
42
- toDb?(value: App): Db;
43
- fromDb?(value: Db): App;
44
- }
45
- >
46
- >;
47
- }
48
-
49
- /**
50
- * Column codec resolver for a column.
51
- */
52
- export type ColumnCodecResolver = (
53
- column: ColumnInfo
54
- ) => ColumnCodec<unknown, unknown> | undefined;
55
-
56
- /**
57
- * Parsed table schema.
58
- */
59
- export interface TableSchema {
60
- name: string;
61
- columns: ColumnSchema[];
62
- }
63
-
64
- /**
65
- * Parsed column schema.
66
- */
67
- export interface ColumnSchema {
68
- name: string;
69
- sqlType: string;
70
- tsType: string;
71
- nullable: boolean;
72
- isPrimaryKey: boolean;
73
- hasDefault: boolean;
74
- }
75
-
76
- /**
77
- * Schema snapshot at a specific version.
78
- */
79
- export interface VersionedSchema {
80
- version: number;
81
- tables: TableSchema[];
82
- }
83
-
84
- /**
85
- * Options for generateTypes().
86
- */
87
- export interface GenerateTypesOptions<DB = unknown> {
88
- /** Defined migrations from defineMigrations() */
89
- migrations: DefinedMigrations<DB>;
90
- /** Output file path for generated types */
91
- output: string;
92
- /** Database dialect to use for introspection (default: 'sqlite') */
93
- dialect?: TypegenDialect;
94
- /** Generate versioned interfaces (ClientDbV1, ClientDbV2, etc.) */
95
- includeVersionHistory?: boolean;
96
- /** Only generate types for these tables (default: all tables) */
97
- tables?: string[];
98
- /**
99
- * Optional column codec resolver for per-column type overrides.
100
- * Receives full column metadata (table, column, sqlType, dialect, etc.).
101
- */
102
- codecs?: ColumnCodecResolver;
103
- }
104
-
105
- /**
106
- * Result of type generation.
107
- */
108
- export interface GenerateTypesResult {
109
- /** Path to the generated file */
110
- outputPath: string;
111
- /** Current schema version */
112
- currentVersion: number;
113
- /** Number of tables generated */
114
- tableCount: number;
115
- /** Generated TypeScript code */
116
- code: string;
117
- }
118
-
119
- export interface GenerateMigrationChecksumsOptions<DB = unknown> {
120
- /** Defined migrations from defineMigrations() */
121
- migrations: DefinedMigrations<DB>;
122
- /** Output file path for generated checksums */
123
- output: string;
124
- /** Database dialect to use for replay (default: 'sqlite') */
125
- dialect?: TypegenDialect;
126
- }
127
-
128
- export interface GenerateMigrationChecksumsResult {
129
- /** Path to the generated file */
130
- outputPath: string;
131
- /** Current schema version */
132
- currentVersion: number;
133
- /** Number of checksums generated */
134
- checksumCount: number;
135
- /** Generated TypeScript code */
136
- code: string;
137
- }