turbine-orm 0.4.0 → 0.7.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 (57) hide show
  1. package/README.md +243 -26
  2. package/dist/cjs/cli/config.js +151 -0
  3. package/dist/cjs/cli/index.js +1176 -0
  4. package/dist/cjs/cli/migrate.js +446 -0
  5. package/dist/cjs/cli/ui.js +233 -0
  6. package/dist/cjs/client.js +512 -0
  7. package/dist/cjs/errors.js +293 -0
  8. package/dist/cjs/generate.js +321 -0
  9. package/dist/cjs/index.js +94 -0
  10. package/dist/cjs/introspect.js +287 -0
  11. package/dist/cjs/package.json +1 -0
  12. package/dist/cjs/pipeline.js +78 -0
  13. package/dist/cjs/query.js +1891 -0
  14. package/dist/cjs/schema-builder.js +238 -0
  15. package/dist/cjs/schema-sql.js +509 -0
  16. package/dist/cjs/schema.js +140 -0
  17. package/dist/cjs/serverless.js +110 -0
  18. package/dist/cli/config.js +6 -16
  19. package/dist/cli/index.js +256 -49
  20. package/dist/cli/migrate.d.ts +35 -6
  21. package/dist/cli/migrate.js +124 -76
  22. package/dist/cli/ui.js +5 -9
  23. package/dist/client.d.ts +87 -3
  24. package/dist/client.js +122 -46
  25. package/dist/errors.d.ts +138 -0
  26. package/dist/errors.js +278 -0
  27. package/dist/generate.js +37 -11
  28. package/dist/index.d.ts +10 -8
  29. package/dist/index.js +15 -11
  30. package/dist/introspect.js +3 -5
  31. package/dist/pipeline.js +8 -1
  32. package/dist/query.d.ts +310 -45
  33. package/dist/query.js +565 -237
  34. package/dist/schema-builder.js +91 -23
  35. package/dist/schema-sql.d.ts +6 -2
  36. package/dist/schema-sql.js +180 -26
  37. package/dist/schema.js +4 -1
  38. package/dist/serverless.d.ts +91 -139
  39. package/dist/serverless.js +86 -173
  40. package/package.json +44 -21
  41. package/dist/cli/config.d.ts.map +0 -1
  42. package/dist/cli/index.d.ts.map +0 -1
  43. package/dist/cli/migrate.d.ts.map +0 -1
  44. package/dist/cli/ui.d.ts.map +0 -1
  45. package/dist/client.d.ts.map +0 -1
  46. package/dist/generate.d.ts.map +0 -1
  47. package/dist/index.d.ts.map +0 -1
  48. package/dist/introspect.d.ts.map +0 -1
  49. package/dist/pipeline.d.ts.map +0 -1
  50. package/dist/query.d.ts.map +0 -1
  51. package/dist/schema-builder.d.ts.map +0 -1
  52. package/dist/schema-sql.d.ts.map +0 -1
  53. package/dist/schema.d.ts.map +0 -1
  54. package/dist/serverless.d.ts.map +0 -1
  55. package/dist/types.d.ts +0 -93
  56. package/dist/types.d.ts.map +0 -1
  57. package/dist/types.js +0 -126
package/dist/generate.js CHANGED
@@ -8,9 +8,9 @@
8
8
  *
9
9
  * Output goes to the specified directory (default: ./generated/turbine/).
10
10
  */
11
- import { writeFileSync, mkdirSync } from 'node:fs';
12
- import { join } from 'node:path';
13
- import { snakeToPascal, singularize, } from './schema.js';
11
+ import { mkdirSync, writeFileSync } from 'node:fs';
12
+ import { join, relative, resolve } from 'node:path';
13
+ import { singularize, snakeToPascal } from './schema.js';
14
14
  /** Get the TypeScript type name for a table (singularized PascalCase) */
15
15
  function entityName(tableName) {
16
16
  return snakeToPascal(singularize(tableName));
@@ -24,6 +24,12 @@ function escSQ(value) {
24
24
  // ---------------------------------------------------------------------------
25
25
  export function generate(options) {
26
26
  const outDir = options.outDir ?? './generated/turbine';
27
+ // Path traversal protection — ensure output stays within project root
28
+ const resolved = resolve(outDir);
29
+ const rel = relative(process.cwd(), resolved);
30
+ if (rel.startsWith('..') || resolve(rel) !== resolved) {
31
+ throw new Error(`Output directory must be within the project root. Got: ${outDir}`);
32
+ }
27
33
  mkdirSync(outDir, { recursive: true });
28
34
  const files = [];
29
35
  // Generate types.ts
@@ -49,15 +55,13 @@ function generatedFileHeader() {
49
55
  ' * Auto-generated by turbine-orm — DO NOT EDIT',
50
56
  ' *',
51
57
  ` * Generated at: ${new Date().toISOString()}`,
52
- ' * @see https://github.com/zvndev/turbine-orm',
58
+ ' * @see https://turbineorm.dev',
53
59
  ' */',
54
60
  '',
55
61
  ];
56
62
  }
57
63
  function generateTypes(schema) {
58
- const lines = [
59
- ...generatedFileHeader(),
60
- ];
64
+ const lines = [...generatedFileHeader()];
61
65
  // Generate enum types
62
66
  for (const [enumName, labels] of Object.entries(schema.enums)) {
63
67
  const typeName = snakeToPascal(enumName);
@@ -107,9 +111,23 @@ function generateTypes(schema) {
107
111
  }
108
112
  lines.push('};');
109
113
  lines.push('');
110
- // --- Relation types ---
114
+ // --- Relations map (for type-safe `with` clauses) ---
111
115
  const hasRelations = Object.keys(table.relations).length > 0;
112
116
  if (hasRelations) {
117
+ lines.push(`/** Available relations for the \`${table.name}\` table */`);
118
+ lines.push(`export interface ${typeName}Relations {`);
119
+ for (const [relName, rel] of Object.entries(table.relations)) {
120
+ const targetType = entityName(rel.to);
121
+ if (rel.type === 'hasMany') {
122
+ lines.push(` ${relName}: ${targetType}[];`);
123
+ }
124
+ else {
125
+ lines.push(` ${relName}: ${targetType} | null;`);
126
+ }
127
+ }
128
+ lines.push('}');
129
+ lines.push('');
130
+ // --- Legacy per-relation interfaces (kept for backward compatibility) ---
113
131
  for (const [relName, rel] of Object.entries(table.relations)) {
114
132
  const targetType = entityName(rel.to);
115
133
  if (rel.type === 'hasMany') {
@@ -213,8 +231,14 @@ function generateIndex(schema) {
213
231
  "import type { TurbineConfig } from 'turbine-orm';",
214
232
  "import { SCHEMA } from './metadata.js';",
215
233
  ];
216
- // Import all entity types
217
- const typeImports = tableEntries.map((t) => entityName(t.name));
234
+ // Import all entity types and relations maps
235
+ const typeImports = [];
236
+ for (const t of tableEntries) {
237
+ typeImports.push(entityName(t.name));
238
+ if (Object.keys(t.relations).length > 0) {
239
+ typeImports.push(`${entityName(t.name)}Relations`);
240
+ }
241
+ }
218
242
  lines.push(`import type { ${typeImports.join(', ')} } from './types.js';`);
219
243
  lines.push('');
220
244
  // Generate the client class with JSDoc
@@ -240,8 +264,10 @@ function generateIndex(schema) {
240
264
  for (const table of tableEntries) {
241
265
  const typeName = entityName(table.name);
242
266
  const accessor = snakeToCamelStr(table.name);
267
+ const hasRelations = Object.keys(table.relations).length > 0;
268
+ const genericArgs = hasRelations ? `${typeName}, ${typeName}Relations` : typeName;
243
269
  lines.push(` /** Query interface for the \`${table.name}\` table */`);
244
- lines.push(` declare readonly ${accessor}: QueryInterface<${typeName}>;`);
270
+ lines.push(` declare readonly ${accessor}: QueryInterface<${genericArgs}>;`);
245
271
  }
246
272
  lines.push('');
247
273
  lines.push(' constructor(config?: TurbineConfig) {');
package/dist/index.d.ts CHANGED
@@ -32,13 +32,15 @@
32
32
  * await db.disconnect();
33
33
  * ```
34
34
  */
35
- export { TurbineClient, TransactionClient, type TurbineConfig, type TransactionOptions, type Middleware, type MiddlewareParams, type MiddlewareNext, } from './client.js';
36
- export { QueryInterface, type DeferredQuery, type FindUniqueArgs, type FindManyArgs, type CreateArgs, type CreateManyArgs, type UpdateArgs, type UpdateManyArgs, type DeleteArgs, type DeleteManyArgs, type UpsertArgs, type CountArgs, type GroupByArgs, type AggregateArgs, type AggregateResult, type RelationFilter, type JsonFilter, type ArrayFilter, type WithClause, type WithOptions, type OrderDirection, } from './query.js';
35
+ export { type Middleware, type MiddlewareNext, type MiddlewareParams, type PgCompatPool, type PgCompatPoolClient, type PgCompatQueryResult, TransactionClient, type TransactionOptions, TurbineClient, type TurbineConfig, } from './client.js';
36
+ export { CheckConstraintError, CircularRelationError, ConnectionError, ForeignKeyError, MigrationError, NotFoundError, NotNullViolationError, RelationError, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, ValidationError, wrapPgError, } from './errors.js';
37
+ export { type GenerateOptions, generate } from './generate.js';
38
+ export { type IntrospectOptions, introspect } from './introspect.js';
37
39
  export { executePipeline, type PipelineResults } from './pipeline.js';
38
- export type { SchemaMetadata, TableMetadata, ColumnMetadata, RelationDef, IndexMetadata, } from './schema.js';
39
- export { snakeToCamel, camelToSnake, snakeToPascal, singularize, pgTypeToTs, isDateType, pgArrayType, } from './schema.js';
40
- export { introspect, type IntrospectOptions } from './introspect.js';
41
- export { generate, type GenerateOptions } from './generate.js';
42
- export { defineSchema, table, column, ColumnBuilder, type ColumnDef, type ColumnTypeName, type ColumnConfig, type ColumnType, type TableDef, type SchemaDef, } from './schema-builder.js';
43
- export { schemaToSQL, schemaToSQLString, schemaDiff, schemaPush, type DiffResult, type AlterDef, type AlterColumnDef, type PushResult, } from './schema-sql.js';
40
+ export { type AggregateArgs, type AggregateResult, type ArrayFilter, type CountArgs, type CreateArgs, type CreateManyArgs, type DeferredQuery, type DeleteArgs, type DeleteManyArgs, type FindManyArgs, type FindManyStreamArgs, type FindUniqueArgs, type GroupByArgs, type JsonFilter, type OrderDirection, QueryInterface, type RelationFilter, type TypedWithClause, type UpdateArgs, type UpdateInput, type UpdateManyArgs, type UpdateOperatorInput, type UpsertArgs, type WithClause, type WithOptions, type WithResult, } from './query.js';
41
+ export type { ColumnMetadata, IndexMetadata, RelationDef, SchemaMetadata, TableMetadata, } from './schema.js';
42
+ export { camelToSnake, isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
43
+ export { ColumnBuilder, type ColumnConfig, type ColumnDef, type ColumnType, type ColumnTypeName, column, defineSchema, type SchemaDef, type TableDef, table, } from './schema-builder.js';
44
+ export { type AlterColumnDef, type AlterDef, type DiffResult, type PushResult, schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
45
+ export { type TurbineHttpOptions, turbineHttp } from './serverless.js';
44
46
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -33,21 +33,25 @@
33
33
  * ```
34
34
  */
35
35
  // Client
36
- export { TurbineClient, TransactionClient, } from './client.js';
37
- // Query builder
38
- export { QueryInterface, } from './query.js';
36
+ export { TransactionClient, TurbineClient, } from './client.js';
37
+ // Error types
38
+ export { CheckConstraintError, CircularRelationError, ConnectionError, ForeignKeyError, MigrationError, NotFoundError, NotNullViolationError, RelationError, TimeoutError, TurbineError, TurbineErrorCode, UniqueConstraintError, ValidationError, wrapPgError, } from './errors.js';
39
+ // Code generation
40
+ export { generate } from './generate.js';
41
+ // Introspection
42
+ export { introspect } from './introspect.js';
39
43
  // Pipeline
40
44
  export { executePipeline } from './pipeline.js';
45
+ // Query builder
46
+ export { QueryInterface, } from './query.js';
41
47
  // Schema utilities
42
- export { snakeToCamel, camelToSnake, snakeToPascal, singularize, pgTypeToTs, isDateType, pgArrayType, } from './schema.js';
43
- // Introspection
44
- export { introspect } from './introspect.js';
45
- // Code generation
46
- export { generate } from './generate.js';
48
+ export { camelToSnake, isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, snakeToPascal, } from './schema.js';
47
49
  // Schema builder — define schemas in TypeScript
48
- export { defineSchema,
50
+ export { ColumnBuilder, column, defineSchema,
49
51
  // Legacy compat (deprecated — use object format with defineSchema)
50
- table, column, ColumnBuilder, } from './schema-builder.js';
52
+ table, } from './schema-builder.js';
51
53
  // Schema SQL — generate DDL, diff, and push
52
- export { schemaToSQL, schemaToSQLString, schemaDiff, schemaPush, } from './schema-sql.js';
54
+ export { schemaDiff, schemaPush, schemaToSQL, schemaToSQLString, } from './schema-sql.js';
55
+ // Serverless / edge factory
56
+ export { turbineHttp } from './serverless.js';
53
57
  //# sourceMappingURL=index.js.map
@@ -8,7 +8,7 @@
8
8
  * This is the foundation of `npx turbine generate`.
9
9
  */
10
10
  import pg from 'pg';
11
- import { pgTypeToTs, isDateType, pgArrayType, snakeToCamel, singularize, } from './schema.js';
11
+ import { isDateType, pgArrayType, pgTypeToTs, singularize, snakeToCamel, } from './schema.js';
12
12
  // ---------------------------------------------------------------------------
13
13
  // SQL queries (all parameterized, no interpolation)
14
14
  // ---------------------------------------------------------------------------
@@ -99,7 +99,7 @@ export async function introspect(options) {
99
99
  });
100
100
  try {
101
101
  // Run all information_schema queries in parallel
102
- const [tablesResult, columnsResult, pkResult, fkResult, uniqueResult, indexResult, enumResult,] = await Promise.all([
102
+ const [tablesResult, columnsResult, pkResult, fkResult, uniqueResult, indexResult, enumResult] = await Promise.all([
103
103
  pool.query(SQL_TABLES, [schema]),
104
104
  pool.query(SQL_COLUMNS, [schema]),
105
105
  pool.query(SQL_PRIMARY_KEYS, [schema]),
@@ -172,9 +172,7 @@ export async function introspect(options) {
172
172
  const isUnique = row.indexdef.includes('UNIQUE');
173
173
  // Extract column names from indexdef (e.g. "CREATE INDEX idx ON tbl USING btree (col1, col2)")
174
174
  const colMatch = row.indexdef.match(/\((.+)\)/);
175
- const columns = colMatch
176
- ? colMatch[1].split(',').map((c) => c.trim().replace(/ (ASC|DESC)/i, ''))
177
- : [];
175
+ const columns = colMatch ? colMatch[1].split(',').map((c) => c.trim().replace(/ (ASC|DESC)/i, '')) : [];
178
176
  indexesByTable.get(row.tablename).push({
179
177
  name: row.indexname,
180
178
  columns,
package/dist/pipeline.js CHANGED
@@ -15,6 +15,7 @@
15
15
  * we simulate it by running queries concurrently on a single connection or via
16
16
  * a multi-statement batch.
17
17
  */
18
+ import { wrapPgError } from './errors.js';
18
19
  // ---------------------------------------------------------------------------
19
20
  // Pipeline executor
20
21
  // ---------------------------------------------------------------------------
@@ -52,7 +53,13 @@ export async function executePipeline(pool, queries) {
52
53
  // Future: use actual Postgres pipeline protocol for true pipelining.
53
54
  const results = [];
54
55
  for (const q of queries) {
55
- const raw = await client.query(q.sql, q.params);
56
+ let raw;
57
+ try {
58
+ raw = await client.query(q.sql, q.params);
59
+ }
60
+ catch (err) {
61
+ throw wrapPgError(err);
62
+ }
56
63
  results.push(q.transform(raw));
57
64
  }
58
65
  await client.query('COMMIT');