turbine-orm 0.19.2 → 0.21.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 (46) hide show
  1. package/README.md +82 -18
  2. package/dist/adapters/cockroachdb.js +4 -9
  3. package/dist/cjs/adapters/cockroachdb.js +4 -9
  4. package/dist/cjs/cli/index.js +6 -3
  5. package/dist/cjs/cli/observe-ui.js +12 -2
  6. package/dist/cjs/cli/observe.js +6 -7
  7. package/dist/cjs/cli/studio.js +6 -7
  8. package/dist/cjs/client.js +33 -20
  9. package/dist/cjs/dialect.js +116 -0
  10. package/dist/cjs/errors.js +19 -1
  11. package/dist/cjs/generate.js +4 -0
  12. package/dist/cjs/index.js +3 -2
  13. package/dist/cjs/introspect.js +20 -0
  14. package/dist/cjs/mssql.js +1338 -0
  15. package/dist/cjs/mysql.js +1052 -0
  16. package/dist/cjs/query/builder.js +293 -73
  17. package/dist/cjs/sqlite.js +849 -0
  18. package/dist/cjs/typed-sql.js +9 -7
  19. package/dist/cli/index.js +6 -3
  20. package/dist/cli/observe-ui.d.ts +1 -1
  21. package/dist/cli/observe-ui.js +12 -2
  22. package/dist/cli/observe.js +7 -8
  23. package/dist/cli/studio.js +7 -8
  24. package/dist/client.d.ts +23 -1
  25. package/dist/client.js +34 -21
  26. package/dist/dialect.d.ts +258 -1
  27. package/dist/dialect.js +83 -0
  28. package/dist/errors.d.ts +12 -0
  29. package/dist/errors.js +17 -0
  30. package/dist/generate.js +4 -0
  31. package/dist/index.d.ts +3 -3
  32. package/dist/index.js +1 -1
  33. package/dist/introspect.d.ts +18 -0
  34. package/dist/introspect.js +19 -0
  35. package/dist/mssql.d.ts +233 -0
  36. package/dist/mssql.js +1298 -0
  37. package/dist/mysql.d.ts +174 -0
  38. package/dist/mysql.js +1012 -0
  39. package/dist/query/builder.d.ts +97 -0
  40. package/dist/query/builder.js +294 -74
  41. package/dist/query/index.d.ts +1 -1
  42. package/dist/sqlite.d.ts +144 -0
  43. package/dist/sqlite.js +842 -0
  44. package/dist/typed-sql.d.ts +7 -5
  45. package/dist/typed-sql.js +9 -7
  46. package/package.json +30 -1
@@ -0,0 +1,174 @@
1
+ /**
2
+ * turbine-orm/mysql — MySQL 8 engine (driver-injected, optional peer)
3
+ *
4
+ * Binds Turbine to MySQL 8 via the `mysql2` driver. `mysql2` is **not** a root
5
+ * dependency — it is an **optional peer**: `npm i turbine-orm` pulls nothing
6
+ * extra, and only consumers who `import 'turbine-orm/mysql'` install `mysql2`
7
+ * themselves. The factory loads it through a dynamic `import('mysql2/promise')`
8
+ * so importing this module never crashes when `mysql2` is absent for a consumer
9
+ * who does not use it. Turbine's root runtime dependency stays exactly `pg`.
10
+ *
11
+ * ## The two hard MySQL realities this engine solves
12
+ *
13
+ * 1. **No `RETURNING`.** `INSERT`/`UPDATE`/`DELETE` cannot return the affected
14
+ * rows. `mysqlDialect.resultStrategy = 'reselect'`: the executor runs the
15
+ * write, then issues a follow-up `SELECT` by primary key (using mysql2's
16
+ * `insertId` for auto-increment PKs) / by the `where` predicate, so
17
+ * `create`/`update`/`upsert`/`delete` still return real rows. This is the
18
+ * first shipped engine to exercise the Phase-0 `reselect` path.
19
+ * 2. **Positional `?` is broken for this builder.** Turbine pushes params in
20
+ * 1-indexed generation order but EMITS them in a different SQL-text position
21
+ * (e.g. a `with`-relation `LIMIT` lands in the SELECT list, ahead of the
22
+ * outer `WHERE`). Postgres reconciles this via numbered `$N`; positional `?`
23
+ * silently mis-binds. So `mysqlDialect` uses **mysql2 named placeholders**
24
+ * (`:p1`, `:p2`, …) and the driver shim binds via a `{ p1, p2, … }` object —
25
+ * exactly mirroring `$N` semantics regardless of text order. (See
26
+ * `turbine-orm/sqlite` for the same fix.)
27
+ *
28
+ * ## Capabilities & limits (vs PostgreSQL)
29
+ *
30
+ * - **Single query nested relations preserved** via MySQL 8 `JSON_OBJECT` /
31
+ * `JSON_ARRAYAGG` with `COALESCE(…, JSON_ARRAY())`. `JSON_ARRAYAGG` has no
32
+ * inline `ORDER BY` argument, so every ordered to-many relation is forced
33
+ * through the inner-subquery rewrite (`aggSupportsInlineOrderBy = false`).
34
+ * Nested subresults are `CAST(… AS JSON)`-wrapped so MySQL embeds them as real
35
+ * nested JSON instead of double-encoding a scalar subquery result as a string.
36
+ * - **`createMany` returns an empty array** (count-not-rows). MySQL has no
37
+ * `RETURNING` and the bulk-insert `insertId` is only the first generated id, so
38
+ * re-selecting N rows reliably is unsafe. The rows ARE inserted; re-query if you
39
+ * need them back. (Plan §4 documented divergence.)
40
+ * - **Unsupported (throw `UnsupportedFeatureError`):** pgvector distance ops,
41
+ * LISTEN/NOTIFY (`$listen`/`$notify`), RLS `sessionContext` (no GUCs).
42
+ * - **Advisory-lock migration locking** is available in principle via
43
+ * `GET_LOCK`/`RELEASE_LOCK` (`supportsAdvisoryLock = true`); the migrate CLI is
44
+ * still PostgreSQL-only, so this flag documents intent for a future adapter.
45
+ * - **Case-insensitive matching** uses `LOWER(col) LIKE LOWER(ref)` — note this
46
+ * can defeat indexes unless a functional/generated index exists.
47
+ * - **bignum:** mysql2 is configured `supportBigNumbers:true, bigNumberStrings:false`
48
+ * — the same safe-int policy Turbine uses for Postgres `int8` (number when it
49
+ * fits, decimal string otherwise). `DECIMAL` comes back as a string; `TINYINT(1)`
50
+ * binds booleans as 1/0. No global parser state is mutated.
51
+ * - **Version:** MySQL **8.0+** required (5.7 lacks `JSON_ARRAYAGG`); MariaDB is
52
+ * unsupported. The factory probes `SELECT VERSION()` and fails fast otherwise.
53
+ *
54
+ * ## Example
55
+ *
56
+ * ```ts
57
+ * import { turbineMysql } from 'turbine-orm/mysql';
58
+ * import { SCHEMA } from './generated/turbine/metadata.js';
59
+ *
60
+ * const db = await turbineMysql('mysql://user:pass@localhost:3306/app', SCHEMA);
61
+ * const users = await db.users.findMany({ with: { posts: true }, limit: 10 });
62
+ * await db.disconnect();
63
+ * ```
64
+ */
65
+ import { type PgCompatPool, type PgCompatPoolClient, TurbineClient, type TurbineConfig } from './client.js';
66
+ import { type Dialect, type IntrospectOptions } from './dialect.js';
67
+ import { type SchemaMetadata } from './schema.js';
68
+ /** mysql2's `[result, fields]` tuple. `result` is rows (SELECT) or a header (write). */
69
+ type Mysql2Result = [unknown, unknown];
70
+ interface Mysql2Queryable {
71
+ query(sql: string, values?: unknown): Promise<Mysql2Result>;
72
+ execute(sql: string, values?: unknown): Promise<Mysql2Result>;
73
+ }
74
+ interface Mysql2Connection extends Mysql2Queryable {
75
+ release(): void;
76
+ }
77
+ interface Mysql2Pool extends Mysql2Queryable {
78
+ getConnection(): Promise<Mysql2Connection>;
79
+ end(): Promise<void>;
80
+ on?(event: string, listener: (...args: any[]) => void): unknown;
81
+ }
82
+ /** pg-style query argument: a SQL string or a `{ text, values }` config object. */
83
+ type QueryArg = string | {
84
+ name?: string;
85
+ text: string;
86
+ values?: unknown[];
87
+ };
88
+ /**
89
+ * A {@link PgCompatPool} backed by a `mysql2` pool. Non-transaction queries run
90
+ * on the pool directly (any connection); `connect()` checks out a dedicated
91
+ * connection so a transaction's `START TRANSACTION` / `COMMIT` / `ROLLBACK` /
92
+ * `SAVEPOINT` nesting all run on the same physical connection.
93
+ */
94
+ export declare class MysqlPool implements PgCompatPool {
95
+ /** The underlying mysql2 pool — exposed as an escape hatch (seed / DDL / advanced ops). */
96
+ readonly pool: Mysql2Pool;
97
+ private closed;
98
+ constructor(pool: Mysql2Pool);
99
+ query(text: QueryArg, values?: unknown[]): Promise<any>;
100
+ connect(): Promise<PgCompatPoolClient>;
101
+ end(): Promise<void>;
102
+ }
103
+ /**
104
+ * Map a MySQL column type to a TypeScript type. `dialectType` is the
105
+ * `information_schema.COLUMNS.DATA_TYPE` (e.g. `bigint`, `varchar`, `datetime`);
106
+ * `columnType` is the full `COLUMN_TYPE` (e.g. `tinyint(1)`, `enum('a','b')`)
107
+ * used to detect `TINYINT(1)` booleans.
108
+ */
109
+ export declare function mysqlTypeToTs(dialectType: string, nullable: boolean, columnType?: string): string;
110
+ /**
111
+ * MySQL 8 implementation of the {@link Dialect} contract. Backtick identifier
112
+ * quoting, named `:pN` placeholders (NOT positional `?` — see the module
113
+ * docstring), `JSON_OBJECT` / `JSON_ARRAYAGG` for the single-query nested
114
+ * relation engine (`CAST(… AS JSON)`-wrapped nested subresults), no `RETURNING`
115
+ * (`resultStrategy = 'reselect'`), `INSERT … ON DUPLICATE KEY UPDATE` upserts,
116
+ * and the Postgres-only capabilities disabled (vector / LISTEN-NOTIFY / RLS).
117
+ */
118
+ export declare const mysqlDialect: Dialect;
119
+ /** Async executor that returns plain row objects for a parameterized query. */
120
+ export type MysqlRowExecutor = (sql: string, params: unknown[]) => Promise<Record<string, unknown>[]>;
121
+ /**
122
+ * Introspect a MySQL database into the same {@link SchemaMetadata} shape the
123
+ * Postgres catalog introspector produces, using a caller-supplied query
124
+ * executor (so tests can dogfood an already-open mysql2 pool/connection).
125
+ *
126
+ * @param exec Runs a parameterized (`:p1`, `:p2`, …) query and returns rows.
127
+ * @param schema The MySQL database (schema) name to introspect.
128
+ * @param options Optional include / exclude table-name filters.
129
+ */
130
+ export declare function introspectMysqlWith(exec: MysqlRowExecutor, schema: string, options?: {
131
+ include?: string[];
132
+ exclude?: string[];
133
+ }): Promise<SchemaMetadata>;
134
+ /**
135
+ * Open a short-lived mysql2 connection from `options.connectionString`,
136
+ * introspect the database (schema = `options.schema` or the connection's current
137
+ * database), and close it. Wraps {@link introspectMysqlWith} for the
138
+ * {@link DialectIntrospector} seam used by `introspect()` / `npx turbine generate`.
139
+ */
140
+ export declare function introspectMysql(options: IntrospectOptions): Promise<SchemaMetadata>;
141
+ interface MysqlConnectionConfig {
142
+ host?: string;
143
+ port?: number;
144
+ user?: string;
145
+ password?: string;
146
+ database?: string;
147
+ }
148
+ /** Options for {@link turbineMysql}. Mirrors the relevant {@link TurbineConfig} fields. */
149
+ export interface TurbineMysqlOptions extends Pick<TurbineConfig, 'logging' | 'defaultLimit' | 'warnOnUnlimited'> {
150
+ /** Maximum number of pooled connections (when Turbine builds the pool). Default: 10. */
151
+ connectionLimit?: number;
152
+ }
153
+ /**
154
+ * Create a {@link TurbineClient} bound to MySQL 8 via `mysql2`.
155
+ *
156
+ * Pass one of:
157
+ * - a connection string (`'mysql://user:pass@host:3306/db'`),
158
+ * - a mysql2 connection config object (`{ host, user, password, database }`), or
159
+ * - an existing mysql2 pool / {@link MysqlPool} (injection — you own its lifecycle,
160
+ * `disconnect()` is a no-op, advanced config like SSL lives here).
161
+ *
162
+ * When Turbine builds the pool (string/config), it pins the correct mysql2 flags
163
+ * (named placeholders, bignum, UTC dates, JSON-as-string), probes `SELECT VERSION()`
164
+ * to reject MySQL < 8.0 / MariaDB, and `disconnect()` closes the pool it created.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * import { turbineMysql } from 'turbine-orm/mysql';
169
+ * const db = await turbineMysql('mysql://root:root@localhost:3306/app', SCHEMA);
170
+ * ```
171
+ */
172
+ export declare function turbineMysql(target: string | MysqlConnectionConfig | Mysql2Pool | MysqlPool, schema: SchemaMetadata, options?: TurbineMysqlOptions): Promise<TurbineClient>;
173
+ export {};
174
+ //# sourceMappingURL=mysql.d.ts.map