uql-orm 0.21.0 → 0.23.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 (214) hide show
  1. package/README.md +5 -4
  2. package/dist/browser/uql-browser.min.js.map +3 -3
  3. package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
  4. package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
  5. package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
  6. package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
  7. package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
  8. package/dist/cockroachdb/cockroachDialect.js +33 -18
  9. package/dist/d1/d1Querier.d.ts +4 -3
  10. package/dist/d1/d1Querier.js +0 -3
  11. package/dist/d1/d1SqliteDialect.d.ts +7 -0
  12. package/dist/d1/d1SqliteDialect.js +9 -0
  13. package/dist/dialect/abstractDialect.d.ts +8 -3
  14. package/dist/dialect/abstractDialect.js +9 -4
  15. package/dist/dialect/abstractSqlDialect.d.ts +49 -8
  16. package/dist/dialect/abstractSqlDialect.js +95 -47
  17. package/dist/dialect/index.d.ts +1 -0
  18. package/dist/dialect/index.js +1 -0
  19. package/dist/dialect/indexSqlDialect.d.ts +57 -0
  20. package/dist/dialect/indexSqlDialect.js +101 -0
  21. package/dist/dialect/mysqlLikeSqlDialect.d.ts +26 -1
  22. package/dist/dialect/mysqlLikeSqlDialect.js +57 -1
  23. package/dist/dialect/pgLikeSqlDialect.d.ts +37 -22
  24. package/dist/dialect/pgLikeSqlDialect.js +97 -68
  25. package/dist/dialect/vectorCast.d.ts +21 -0
  26. package/dist/dialect/vectorCast.js +28 -0
  27. package/dist/dialect/vectorSqlDialect.d.ts +10 -3
  28. package/dist/dialect/vectorSqlDialect.js +19 -8
  29. package/dist/entity/decorator/bag.d.ts +35 -0
  30. package/dist/entity/decorator/bag.js +54 -0
  31. package/dist/entity/decorator/entity.d.ts +28 -2
  32. package/dist/entity/decorator/entity.js +40 -2
  33. package/dist/entity/decorator/members.d.ts +51 -0
  34. package/dist/entity/decorator/members.js +51 -0
  35. package/dist/entity/index.d.ts +3 -1
  36. package/dist/entity/index.js +3 -1
  37. package/dist/entity/metadata/definition.d.ts +23 -8
  38. package/dist/entity/metadata/definition.js +56 -100
  39. package/dist/http/contract.js +1 -2
  40. package/dist/http/handler.js +5 -26
  41. package/dist/http/query.js +1 -1
  42. package/dist/libsql/libsqlDialect.d.ts +12 -2
  43. package/dist/libsql/libsqlDialect.js +14 -2
  44. package/dist/libsql/libsqlQuerier.d.ts +9 -22
  45. package/dist/libsql/libsqlQuerier.js +9 -85
  46. package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
  47. package/dist/libsql/libsqlQuerierPool.js +17 -15
  48. package/dist/maria/mariaDialect.d.ts +26 -4
  49. package/dist/maria/mariaDialect.js +54 -15
  50. package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
  51. package/dist/migrate/acquireQuerierForMigrations.js +28 -0
  52. package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
  53. package/dist/migrate/builder/migrationBuilder.js +58 -76
  54. package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
  55. package/dist/migrate/builder/splitSqlStatements.js +0 -2
  56. package/dist/migrate/builder/tableBuilder.d.ts +12 -2
  57. package/dist/migrate/builder/tableBuilder.js +71 -119
  58. package/dist/migrate/builder/types.d.ts +30 -41
  59. package/dist/migrate/cli-config.js +24 -8
  60. package/dist/migrate/cli.d.ts +3 -2
  61. package/dist/migrate/cli.js +26 -49
  62. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
  63. package/dist/migrate/codegen/entityCodeGenerator.js +8 -67
  64. package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
  65. package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
  66. package/dist/migrate/codegen/index.d.ts +0 -3
  67. package/dist/migrate/codegen/index.js +0 -6
  68. package/dist/migrate/drift/driftDetector.d.ts +10 -1
  69. package/dist/migrate/drift/driftDetector.js +22 -5
  70. package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
  71. package/dist/migrate/generator/indexNodeToSchema.js +14 -0
  72. package/dist/migrate/generator/mongoCommand.d.ts +57 -0
  73. package/dist/migrate/generator/mongoCommand.js +26 -0
  74. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
  75. package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
  76. package/dist/migrate/index.d.ts +0 -1
  77. package/dist/migrate/index.js +0 -1
  78. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +30 -11
  79. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +61 -57
  80. package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
  81. package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
  82. package/dist/migrate/introspection/mongoIntrospector.js +9 -15
  83. package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
  84. package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
  85. package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
  86. package/dist/migrate/introspection/postgresIntrospector.js +4 -10
  87. package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
  88. package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
  89. package/dist/migrate/migrator.d.ts +23 -3
  90. package/dist/migrate/migrator.js +101 -150
  91. package/dist/migrate/schemaGenerator.d.ts +26 -37
  92. package/dist/migrate/schemaGenerator.js +69 -174
  93. package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
  94. package/dist/migrate/schemaGeneratorAsync.js +3 -3
  95. package/dist/migrate/storage/databaseStorage.d.ts +2 -0
  96. package/dist/migrate/storage/databaseStorage.js +12 -27
  97. package/dist/mongo/mongoDialect.d.ts +2 -0
  98. package/dist/mongo/mongoDialect.js +14 -3
  99. package/dist/mongo/mongodbQuerier.js +171 -229
  100. package/dist/mysql/mysqlDialect.d.ts +8 -0
  101. package/dist/mysql/mysqlDialect.js +13 -0
  102. package/dist/nestjs/uqlContextInterceptor.js +58 -22
  103. package/dist/nestjs/uqlModule.d.ts +2 -11
  104. package/dist/nestjs/uqlModule.js +93 -44
  105. package/dist/postgres/postgresDialect.js +2 -2
  106. package/dist/querier/abstractQuerier.d.ts +28 -0
  107. package/dist/querier/abstractQuerier.js +47 -22
  108. package/dist/querier/abstractQuerierPool.d.ts +3 -0
  109. package/dist/querier/abstractQuerierPool.js +5 -3
  110. package/dist/querier/abstractSqlQuerier.d.ts +1 -3
  111. package/dist/querier/abstractSqlQuerier.js +48 -96
  112. package/dist/querier/index.d.ts +3 -1
  113. package/dist/querier/index.js +3 -1
  114. package/dist/querier/querierContext.browser.d.ts +12 -0
  115. package/dist/querier/querierContext.browser.js +18 -0
  116. package/dist/querier/querierContext.d.ts +22 -0
  117. package/dist/querier/querierContext.js +42 -0
  118. package/dist/querier/queryError.d.ts +22 -0
  119. package/dist/querier/queryError.js +20 -0
  120. package/dist/querier/transactional.d.ts +26 -0
  121. package/dist/querier/transactional.js +43 -0
  122. package/dist/schema/canonicalType.d.ts +14 -2
  123. package/dist/schema/canonicalType.js +41 -31
  124. package/dist/schema/index.d.ts +1 -1
  125. package/dist/schema/schemaASTBuilder.d.ts +9 -2
  126. package/dist/schema/schemaASTBuilder.js +58 -49
  127. package/dist/schema/types.d.ts +10 -58
  128. package/dist/sqlite/abstractSqliteQuerier.d.ts +46 -0
  129. package/dist/sqlite/abstractSqliteQuerier.js +44 -0
  130. package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
  131. package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
  132. package/dist/sqlite/hranaQuerier.d.ts +54 -0
  133. package/dist/sqlite/hranaQuerier.js +68 -0
  134. package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
  135. package/dist/sqlite/hranaQuerierPool.js +25 -0
  136. package/dist/sqlite/index.d.ts +3 -1
  137. package/dist/sqlite/index.js +3 -1
  138. package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
  139. package/dist/sqlite/localSqliteQuerierPool.js +34 -0
  140. package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
  141. package/dist/sqlite/nodeSqliteAdapter.js +28 -0
  142. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
  143. package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
  144. package/dist/sqlite/sqliteDialect.d.ts +18 -5
  145. package/dist/sqlite/sqliteDialect.js +23 -36
  146. package/dist/sqlite/sqliteQuerier.d.ts +16 -9
  147. package/dist/sqlite/sqliteQuerier.js +4 -27
  148. package/dist/sqlite/sqliteQuerierPool.d.ts +11 -14
  149. package/dist/sqlite/sqliteQuerierPool.js +16 -24
  150. package/dist/turso/index.d.ts +3 -0
  151. package/dist/turso/index.js +3 -0
  152. package/dist/turso/local.d.ts +3 -0
  153. package/dist/turso/local.js +3 -0
  154. package/dist/turso/tursoDialect.d.ts +15 -0
  155. package/dist/turso/tursoDialect.js +18 -0
  156. package/dist/turso/tursoLocalQuerier.d.ts +25 -0
  157. package/dist/turso/tursoLocalQuerier.js +20 -0
  158. package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
  159. package/dist/turso/tursoLocalQuerierPool.js +39 -0
  160. package/dist/turso/tursoQuerier.d.ts +10 -0
  161. package/dist/turso/tursoQuerier.js +10 -0
  162. package/dist/turso/tursoQuerierPool.d.ts +39 -0
  163. package/dist/turso/tursoQuerierPool.js +38 -0
  164. package/dist/type/config.d.ts +0 -6
  165. package/dist/type/dialect.d.ts +12 -4
  166. package/dist/type/dialect.js +7 -1
  167. package/dist/type/entity.d.ts +154 -26
  168. package/dist/type/migration.d.ts +32 -23
  169. package/dist/type/querier.d.ts +6 -0
  170. package/dist/type/vector.d.ts +5 -2
  171. package/dist/util/hook.util.js +1 -1
  172. package/dist/util/index.d.ts +1 -0
  173. package/dist/util/index.js +1 -0
  174. package/dist/util/indexColumn.util.d.ts +6 -0
  175. package/dist/util/indexColumn.util.js +26 -0
  176. package/package.json +21 -16
  177. package/dist/entity/decorator/field.d.ts +0 -2
  178. package/dist/entity/decorator/field.js +0 -7
  179. package/dist/entity/decorator/filter.d.ts +0 -7
  180. package/dist/entity/decorator/filter.js +0 -11
  181. package/dist/entity/decorator/hook.d.ts +0 -7
  182. package/dist/entity/decorator/hook.js +0 -14
  183. package/dist/entity/decorator/id.d.ts +0 -2
  184. package/dist/entity/decorator/id.js +0 -7
  185. package/dist/entity/decorator/index-decorator.d.ts +0 -29
  186. package/dist/entity/decorator/index-decorator.js +0 -26
  187. package/dist/entity/decorator/index.d.ts +0 -8
  188. package/dist/entity/decorator/index.js +0 -8
  189. package/dist/entity/decorator/relation.d.ts +0 -8
  190. package/dist/entity/decorator/relation.js +0 -19
  191. package/dist/migrate/codegen/entityMerger.d.ts +0 -114
  192. package/dist/migrate/codegen/entityMerger.js +0 -293
  193. package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
  194. package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
  195. package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
  196. package/dist/migrate/codegen/smartRelationDetector.js +0 -134
  197. package/dist/migrate/generator/index.d.ts +0 -2
  198. package/dist/migrate/generator/index.js +0 -2
  199. package/dist/migrate/sync/index.d.ts +0 -6
  200. package/dist/migrate/sync/index.js +0 -6
  201. package/dist/migrate/sync/schemaSync.d.ts +0 -131
  202. package/dist/migrate/sync/schemaSync.js +0 -259
  203. package/dist/querier/decorator/index.d.ts +0 -4
  204. package/dist/querier/decorator/index.js +0 -4
  205. package/dist/querier/decorator/injectQuerier.d.ts +0 -3
  206. package/dist/querier/decorator/injectQuerier.js +0 -33
  207. package/dist/querier/decorator/log.d.ts +0 -32
  208. package/dist/querier/decorator/log.js +0 -52
  209. package/dist/querier/decorator/serialized.d.ts +0 -6
  210. package/dist/querier/decorator/serialized.js +0 -13
  211. package/dist/querier/decorator/transactional.d.ts +0 -6
  212. package/dist/querier/decorator/transactional.js +0 -48
  213. package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
  214. package/dist/sqlite/betterSqlite3Dialect.js +0 -9
@@ -4,6 +4,17 @@ import { BaseSqlIntrospector } from './baseSqlIntrospector.js';
4
4
  * Referential action type for foreign key constraints.
5
5
  */
6
6
  export type ReferentialAction = 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION';
7
+ /**
8
+ * Reads the rows of one statement while introspecting a table.
9
+ *
10
+ * Identical statements share a single round trip, which is what the mappers get instead of a querier:
11
+ * describing one table needs four facts, and on SQLite three of them come out of the same two PRAGMAs
12
+ * (`table_info` is both the column list and the primary key; the column mapper walks `index_list` and
13
+ * `index_info` for single-column uniqueness while the index mapper is walking them too). That was four
14
+ * redundant statements out of ten per table - on D1 and Turso, where every PRAGMA is an HTTP round trip,
15
+ * it is four avoidable ones.
16
+ */
17
+ export type TableRowReader = <T extends RawRow>(sql: string, params?: unknown[]) => Promise<T[]>;
7
18
  /**
8
19
  * Abstract base class for SQL schema introspectors.
9
20
  *
@@ -29,12 +40,16 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
29
40
  getTableSchema(tableName: string): Promise<TableSchema | undefined>;
30
41
  getTableNames(): Promise<string[]>;
31
42
  tableExists(tableName: string): Promise<boolean>;
32
- protected getQuerier(): Promise<SqlQuerier>;
33
- protected tableExistsInternal(querier: SqlQuerier, tableName: string): Promise<boolean>;
34
- protected getColumns(querier: SqlQuerier, tableName: string): Promise<ColumnSchema[]>;
35
- protected getIndexes(querier: SqlQuerier, tableName: string): Promise<IndexSchema[]>;
36
- protected getForeignKeys(querier: SqlQuerier, tableName: string): Promise<ForeignKeySchema[]>;
37
- protected getPrimaryKey(querier: SqlQuerier, tableName: string): Promise<string[] | undefined>;
43
+ /**
44
+ * Introspection reads, so `withQuerier` rather than `transaction`: the pool owns the release either
45
+ * way, and wrapping catalogue queries in a transaction would hold one open for nothing.
46
+ */
47
+ protected withSqlQuerier<T>(task: (querier: SqlQuerier) => Promise<T>): Promise<T>;
48
+ protected tableExistsInternal(read: TableRowReader, tableName: string): Promise<boolean>;
49
+ protected getColumns(read: TableRowReader, tableName: string): Promise<ColumnSchema[]>;
50
+ protected getIndexes(read: TableRowReader, tableName: string): Promise<IndexSchema[]>;
51
+ protected getForeignKeys(read: TableRowReader, tableName: string): Promise<ForeignKeySchema[]>;
52
+ protected getPrimaryKey(read: TableRowReader, tableName: string): Promise<string[] | undefined>;
38
53
  protected tableExistsParams(tableName: string): unknown[];
39
54
  protected getColumnsParams(tableName: string): unknown[];
40
55
  protected getIndexesParams(tableName: string): unknown[];
@@ -65,13 +80,17 @@ export declare abstract class AbstractSqlSchemaIntrospector extends BaseSqlIntro
65
80
  /** Extract table name from a row returned by getTableNamesQuery. */
66
81
  protected abstract mapTableNameRow(row: RawRow): string;
67
82
  /** Map column query results to ColumnSchema array. Allows async for SQLite's unique column check. */
68
- protected abstract mapColumnsResult(querier: SqlQuerier, tableName: string, results: RawRow[]): Promise<ColumnSchema[]>;
83
+ protected abstract mapColumnsResult(read: TableRowReader, tableName: string, results: RawRow[]): Promise<ColumnSchema[]>;
69
84
  /** Map index query results to IndexSchema array. Allows async for SQLite's index_info calls. */
70
- protected abstract mapIndexesResult(querier: SqlQuerier, tableName: string, results: RawRow[]): Promise<IndexSchema[]>;
85
+ protected abstract mapIndexesResult(read: TableRowReader, tableName: string, results: RawRow[]): Promise<IndexSchema[]>;
71
86
  /** Map foreign key query results to ForeignKeySchema array. */
72
- protected abstract mapForeignKeysResult(querier: SqlQuerier, tableName: string, results: RawRow[]): Promise<ForeignKeySchema[]>;
73
- /** Map primary key query results to column names array. */
74
- protected abstract mapPrimaryKeyResult(results: RawRow[]): string[] | undefined;
87
+ protected abstract mapForeignKeysResult(read: TableRowReader, tableName: string, results: RawRow[]): Promise<ForeignKeySchema[]>;
88
+ /**
89
+ * Map primary key query results to column names, in key order. `information_schema` gives every SQL
90
+ * engine here a `column_name` per row; SQLite reads its key off `PRAGMA table_info` instead and
91
+ * overrides this.
92
+ */
93
+ protected mapPrimaryKeyResult(results: RawRow[]): string[] | undefined;
75
94
  /** Parse default value string to appropriate type. */
76
95
  protected abstract parseDefaultValue(defaultValue: string | null): unknown;
77
96
  }
@@ -29,17 +29,17 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
29
29
  // Template Methods (shared control flow)
30
30
  // ============================================================================
31
31
  async getTableSchema(tableName) {
32
- const querier = await this.getQuerier();
33
- try {
34
- const exists = await this.tableExistsInternal(querier, tableName);
32
+ return this.withSqlQuerier(async (querier) => {
33
+ const read = createTableRowReader(querier);
34
+ const exists = await this.tableExistsInternal(read, tableName);
35
35
  if (!exists) {
36
36
  return undefined;
37
37
  }
38
38
  const [columns, indexes, foreignKeys, primaryKey] = await Promise.all([
39
- this.getColumns(querier, tableName),
40
- this.getIndexes(querier, tableName),
41
- this.getForeignKeys(querier, tableName),
42
- this.getPrimaryKey(querier, tableName),
39
+ this.getColumns(read, tableName),
40
+ this.getIndexes(read, tableName),
41
+ this.getForeignKeys(read, tableName),
42
+ this.getPrimaryKey(read, tableName),
43
43
  ]);
44
44
  return {
45
45
  name: tableName,
@@ -48,66 +48,47 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
48
48
  indexes,
49
49
  foreignKeys,
50
50
  };
51
- }
52
- finally {
53
- await querier.release();
54
- }
51
+ });
55
52
  }
56
53
  async getTableNames() {
57
- const querier = await this.getQuerier();
58
- try {
54
+ return this.withSqlQuerier(async (querier) => {
59
55
  const results = await querier.all(this.getTableNamesQuery());
60
56
  return results.map((row) => this.mapTableNameRow(row));
61
- }
62
- finally {
63
- await querier.release();
64
- }
57
+ });
65
58
  }
66
59
  async tableExists(tableName) {
67
- const querier = await this.getQuerier();
68
- try {
69
- return this.tableExistsInternal(querier, tableName);
70
- }
71
- finally {
72
- await querier.release();
73
- }
60
+ return this.withSqlQuerier((querier) => this.tableExistsInternal(createTableRowReader(querier), tableName));
74
61
  }
75
- async getQuerier() {
76
- const querier = await this.pool.getQuerier();
77
- if (!isSqlQuerier(querier)) {
78
- await querier.release();
79
- throw new Error(`${this.constructor.name} requires a SQL-based querier`);
80
- }
81
- return querier;
62
+ /**
63
+ * Introspection reads, so `withQuerier` rather than `transaction`: the pool owns the release either
64
+ * way, and wrapping catalogue queries in a transaction would hold one open for nothing.
65
+ */
66
+ withSqlQuerier(task) {
67
+ return this.pool.withQuerier((querier) => {
68
+ if (!isSqlQuerier(querier)) {
69
+ throw new Error(`${this.constructor.name} requires a SQL-based querier`);
70
+ }
71
+ return task(querier);
72
+ });
82
73
  }
83
- async tableExistsInternal(querier, tableName) {
84
- const sql = this.tableExistsQuery();
85
- const params = this.tableExistsParams(tableName);
86
- const results = await querier.all(sql, params);
74
+ async tableExistsInternal(read, tableName) {
75
+ const results = await read(this.tableExistsQuery(), this.tableExistsParams(tableName));
87
76
  return this.parseTableExistsResult(results);
88
77
  }
89
- async getColumns(querier, tableName) {
90
- const sql = this.getColumnsQuery(tableName);
91
- const params = this.getColumnsParams(tableName);
92
- const results = await querier.all(sql, params);
93
- return this.mapColumnsResult(querier, tableName, results);
94
- }
95
- async getIndexes(querier, tableName) {
96
- const sql = this.getIndexesQuery(tableName);
97
- const params = this.getIndexesParams(tableName);
98
- const results = await querier.all(sql, params);
99
- return this.mapIndexesResult(querier, tableName, results);
100
- }
101
- async getForeignKeys(querier, tableName) {
102
- const sql = this.getForeignKeysQuery(tableName);
103
- const params = this.getForeignKeysParams(tableName);
104
- const results = await querier.all(sql, params);
105
- return this.mapForeignKeysResult(querier, tableName, results);
106
- }
107
- async getPrimaryKey(querier, tableName) {
108
- const sql = this.getPrimaryKeyQuery(tableName);
109
- const params = this.getPrimaryKeyParams(tableName);
110
- const results = await querier.all(sql, params);
78
+ async getColumns(read, tableName) {
79
+ const results = await read(this.getColumnsQuery(tableName), this.getColumnsParams(tableName));
80
+ return this.mapColumnsResult(read, tableName, results);
81
+ }
82
+ async getIndexes(read, tableName) {
83
+ const results = await read(this.getIndexesQuery(tableName), this.getIndexesParams(tableName));
84
+ return this.mapIndexesResult(read, tableName, results);
85
+ }
86
+ async getForeignKeys(read, tableName) {
87
+ const results = await read(this.getForeignKeysQuery(tableName), this.getForeignKeysParams(tableName));
88
+ return this.mapForeignKeysResult(read, tableName, results);
89
+ }
90
+ async getPrimaryKey(read, tableName) {
91
+ const results = await read(this.getPrimaryKeyQuery(tableName), this.getPrimaryKeyParams(tableName));
111
92
  return this.mapPrimaryKeyResult(results);
112
93
  }
113
94
  // ============================================================================
@@ -157,4 +138,27 @@ export class AbstractSqlSchemaIntrospector extends BaseSqlIntrospector {
157
138
  }
158
139
  return Number(value);
159
140
  }
141
+ /**
142
+ * Map primary key query results to column names, in key order. `information_schema` gives every SQL
143
+ * engine here a `column_name` per row; SQLite reads its key off `PRAGMA table_info` instead and
144
+ * overrides this.
145
+ */
146
+ mapPrimaryKeyResult(results) {
147
+ const columns = results.map((row) => String(row['column_name']));
148
+ return columns.length ? columns : undefined;
149
+ }
150
+ }
151
+ /** A {@link TableRowReader} over one querier: the same statement is only ever sent once. */
152
+ function createTableRowReader(querier) {
153
+ const sent = new Map();
154
+ return (sql, params) => {
155
+ const key = params?.length ? `${sql}\u0000${JSON.stringify(params)}` : sql;
156
+ let rows = sent.get(key);
157
+ if (!rows) {
158
+ // PRAGMA statements take no parameters at all, so they are sent as a bare statement.
159
+ rows = params?.length ? querier.all(sql, params) : querier.all(sql);
160
+ sent.set(key, rows);
161
+ }
162
+ return rows;
163
+ };
160
164
  }
@@ -21,5 +21,4 @@ export declare abstract class BaseSqlIntrospector {
21
21
  private buildTables;
22
22
  private buildRelationships;
23
23
  private buildIndexes;
24
- private columnSchemaToCanonical;
25
24
  }
@@ -1,4 +1,4 @@
1
- import { sqlToCanonical } from '../../schema/canonicalType.js';
1
+ import { canonicalColumnType } from '../../schema/canonicalType.js';
2
2
  import { SchemaAST } from '../../schema/schemaAST.js';
3
3
  import { escapeSqlId } from '../../util/index.js';
4
4
  /**
@@ -52,7 +52,7 @@ export class BaseSqlIntrospector {
52
52
  for (const col of schema.columns) {
53
53
  const column = {
54
54
  name: col.name,
55
- type: this.columnSchemaToCanonical(col),
55
+ type: canonicalColumnType(col.type, col),
56
56
  nullable: col.nullable,
57
57
  defaultValue: col.defaultValue,
58
58
  isPrimaryKey: col.isPrimaryKey,
@@ -106,7 +106,7 @@ export class BaseSqlIntrospector {
106
106
  if (!table)
107
107
  continue;
108
108
  for (const idx of schema.indexes) {
109
- const columns = idx.columns.map((c) => table.columns.get(c)).filter((c) => !!c);
109
+ const columns = idx.columns.map((e) => table.columns.get(e.column)).filter((c) => !!c);
110
110
  if (columns.length > 0) {
111
111
  const index = {
112
112
  name: idx.name,
@@ -119,13 +119,4 @@ export class BaseSqlIntrospector {
119
119
  }
120
120
  }
121
121
  }
122
- columnSchemaToCanonical(col) {
123
- const base = sqlToCanonical(col.type);
124
- return {
125
- ...base,
126
- length: col.length ?? base.length,
127
- precision: col.precision ?? base.precision,
128
- scale: col.scale ?? base.scale,
129
- };
130
- }
131
122
  }
@@ -27,7 +27,7 @@ export class MongoSchemaIntrospector {
27
27
  if (schema.indexes) {
28
28
  for (const idx of schema.indexes) {
29
29
  const indexColumns = [];
30
- for (const colName of idx.columns) {
30
+ for (const { column: colName } of idx.columns) {
31
31
  let column = columns.get(colName);
32
32
  if (!column) {
33
33
  column = {
@@ -58,39 +58,33 @@ export class MongoSchemaIntrospector {
58
58
  return ast;
59
59
  }
60
60
  async getTableSchema(tableName) {
61
- const querier = await this.pool.getQuerier();
62
- try {
61
+ return this.pool.withQuerier(async (querier) => {
63
62
  const { db } = querier;
64
63
  const collections = await db.listCollections({ name: tableName }).toArray();
65
64
  if (collections.length === 0) {
66
65
  return undefined;
67
66
  }
68
- // MongoDB doesn't have a fixed schema, but we can look at the indexes
67
+ // MongoDB doesn't have a fixed schema, but we can look at the indexes. Annotated rather than
68
+ // inferred: the driver's `indexes()` is overloaded and resolves to `any` on some versions, which
69
+ // silently made every field below unchecked.
69
70
  const indexes = await db.collection(tableName).indexes();
70
71
  return {
71
72
  name: tableName,
72
73
  columns: [], // We don't have columns in Mongo
73
74
  indexes: indexes.map((idx) => ({
74
75
  name: idx.name ?? Object.keys(idx.key).join('_'),
75
- columns: Object.keys(idx.key),
76
+ columns: Object.keys(idx.key).map((column) => ({ column })),
76
77
  unique: !!idx.unique,
77
78
  })),
78
79
  };
79
- }
80
- finally {
81
- await querier.release();
82
- }
80
+ });
83
81
  }
84
82
  async getTableNames() {
85
- const querier = await this.pool.getQuerier();
86
- try {
83
+ return this.pool.withQuerier(async (querier) => {
87
84
  const { db } = querier;
88
85
  const collections = await db.listCollections().toArray();
89
86
  return collections.map((c) => c.name);
90
- }
91
- finally {
92
- await querier.release();
93
- }
87
+ });
94
88
  }
95
89
  async tableExists(tableName) {
96
90
  const names = await this.getTableNames();
@@ -1,5 +1,5 @@
1
- import type { ColumnSchema, ForeignKeySchema, IndexSchema, SqlQuerier } from '../../type/index.js';
2
- import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.js';
1
+ import type { ColumnSchema, ForeignKeySchema, IndexSchema } from '../../type/index.js';
2
+ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSqlSchemaIntrospector.js';
3
3
  /**
4
4
  * MySQL/MariaDB schema introspector.
5
5
  * Works with both MySQL and MariaDB as they share the same information_schema structure.
@@ -17,13 +17,13 @@ export declare class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospect
17
17
  protected mapTableNameRow(row: {
18
18
  table_name: string;
19
19
  }): string;
20
- protected mapColumnsResult(_querier: SqlQuerier, _tableName: string, results: MysqlColumnRow[]): Promise<ColumnSchema[]>;
21
- protected mapIndexesResult(_querier: SqlQuerier, _tableName: string, results: {
20
+ protected mapColumnsResult(_read: TableRowReader, _tableName: string, results: MysqlColumnRow[]): Promise<ColumnSchema[]>;
21
+ protected mapIndexesResult(_read: TableRowReader, _tableName: string, results: {
22
22
  index_name: string;
23
23
  columns: string;
24
24
  is_unique: number;
25
25
  }[]): Promise<IndexSchema[]>;
26
- protected mapForeignKeysResult(_querier: SqlQuerier, _tableName: string, results: {
26
+ protected mapForeignKeysResult(_read: TableRowReader, _tableName: string, results: {
27
27
  constraint_name: string;
28
28
  columns: string;
29
29
  referenced_table: string;
@@ -31,9 +31,6 @@ export declare class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospect
31
31
  delete_rule: string;
32
32
  update_rule: string;
33
33
  }[]): Promise<ForeignKeySchema[]>;
34
- protected mapPrimaryKeyResult(results: {
35
- column_name: string;
36
- }[]): string[] | undefined;
37
34
  protected parseDefaultValue(defaultValue: string | null): unknown;
38
35
  }
39
36
  type MysqlColumnRow = {
@@ -101,7 +101,7 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
101
101
  mapTableNameRow(row) {
102
102
  return row.table_name;
103
103
  }
104
- async mapColumnsResult(_querier, _tableName, results) {
104
+ async mapColumnsResult(_read, _tableName, results) {
105
105
  return results.map((row) => ({
106
106
  name: row.column_name,
107
107
  type: (row.column_type || '').toUpperCase(),
@@ -116,14 +116,14 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
116
116
  comment: row.column_comment || undefined,
117
117
  }));
118
118
  }
119
- async mapIndexesResult(_querier, _tableName, results) {
119
+ async mapIndexesResult(_read, _tableName, results) {
120
120
  return results.map((row) => ({
121
121
  name: row.index_name,
122
- columns: (row.columns || '').split(','),
122
+ columns: (row.columns || '').split(',').map((column) => ({ column })),
123
123
  unique: Boolean(row.is_unique),
124
124
  }));
125
125
  }
126
- async mapForeignKeysResult(_querier, _tableName, results) {
126
+ async mapForeignKeysResult(_read, _tableName, results) {
127
127
  return results.map((row) => ({
128
128
  name: row.constraint_name,
129
129
  columns: (row.columns || '').split(','),
@@ -133,12 +133,6 @@ export class MysqlSchemaIntrospector extends AbstractSqlSchemaIntrospector {
133
133
  onUpdate: this.normalizeReferentialAction(row.update_rule),
134
134
  }));
135
135
  }
136
- mapPrimaryKeyResult(results) {
137
- if (results.length === 0) {
138
- return undefined;
139
- }
140
- return results.map((r) => r.column_name);
141
- }
142
136
  parseDefaultValue(defaultValue) {
143
137
  if (defaultValue === null) {
144
138
  return undefined;
@@ -1,5 +1,5 @@
1
- import type { ColumnSchema, ForeignKeySchema, IndexSchema, RawRow, SqlQuerier } from '../../type/index.js';
2
- import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.js';
1
+ import type { ColumnSchema, ForeignKeySchema, IndexSchema, RawRow } from '../../type/index.js';
2
+ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSqlSchemaIntrospector.js';
3
3
  /**
4
4
  * PostgreSQL schema introspector
5
5
  */
@@ -14,13 +14,13 @@ export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrosp
14
14
  protected mapTableNameRow(row: {
15
15
  table_name: string;
16
16
  }): string;
17
- protected mapColumnsResult(_querier: SqlQuerier, _tableName: string, results: PostgresColumnRow[]): Promise<ColumnSchema[]>;
18
- protected mapIndexesResult(_querier: SqlQuerier, _tableName: string, results: {
17
+ protected mapColumnsResult(_read: TableRowReader, _tableName: string, results: PostgresColumnRow[]): Promise<ColumnSchema[]>;
18
+ protected mapIndexesResult(_read: TableRowReader, _tableName: string, results: {
19
19
  index_name: string;
20
20
  columns: string[];
21
21
  is_unique: boolean;
22
22
  }[]): Promise<IndexSchema[]>;
23
- protected mapForeignKeysResult(_querier: SqlQuerier, _tableName: string, results: {
23
+ protected mapForeignKeysResult(_read: TableRowReader, _tableName: string, results: {
24
24
  constraint_name: string;
25
25
  columns: string[];
26
26
  referenced_table: string;
@@ -28,9 +28,6 @@ export declare class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrosp
28
28
  delete_rule: string;
29
29
  update_rule: string;
30
30
  }[]): Promise<ForeignKeySchema[]>;
31
- protected mapPrimaryKeyResult(results: {
32
- column_name: string;
33
- }[]): string[] | undefined;
34
31
  protected normalizeType(dataType: string, udtName: string): string;
35
32
  protected parseDefaultValue(defaultValue: string | null): unknown;
36
33
  protected isAutoIncrement(columnDefault: string | null, isIdentity: string): boolean;
@@ -134,7 +134,7 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
134
134
  mapTableNameRow(row) {
135
135
  return row.table_name;
136
136
  }
137
- async mapColumnsResult(_querier, _tableName, results) {
137
+ async mapColumnsResult(_read, _tableName, results) {
138
138
  return results.map((row) => ({
139
139
  name: row.column_name,
140
140
  type: this.normalizeType(row.data_type, row.udt_name),
@@ -149,14 +149,14 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
149
149
  comment: row.column_comment ?? undefined,
150
150
  }));
151
151
  }
152
- async mapIndexesResult(_querier, _tableName, results) {
152
+ async mapIndexesResult(_read, _tableName, results) {
153
153
  return results.map((row) => ({
154
154
  name: row.index_name,
155
- columns: row.columns,
155
+ columns: row.columns.map((column) => ({ column })),
156
156
  unique: row.is_unique,
157
157
  }));
158
158
  }
159
- async mapForeignKeysResult(_querier, _tableName, results) {
159
+ async mapForeignKeysResult(_read, _tableName, results) {
160
160
  return results.map((row) => ({
161
161
  name: row.constraint_name,
162
162
  columns: row.columns,
@@ -166,12 +166,6 @@ export class PostgresSchemaIntrospector extends AbstractSqlSchemaIntrospector {
166
166
  onUpdate: this.normalizeReferentialAction(row.update_rule),
167
167
  }));
168
168
  }
169
- mapPrimaryKeyResult(results) {
170
- if (results.length === 0) {
171
- return undefined;
172
- }
173
- return results.map((r) => r.column_name);
174
- }
175
169
  // ============================================================================
176
170
  // PostgreSQL-specific helpers
177
171
  // ============================================================================
@@ -1,5 +1,5 @@
1
- import type { ColumnSchema, ForeignKeySchema, IndexSchema, SqlQuerier } from '../../type/index.js';
2
- import { AbstractSqlSchemaIntrospector } from './abstractSqlSchemaIntrospector.js';
1
+ import type { ColumnSchema, ForeignKeySchema, IndexSchema } from '../../type/index.js';
2
+ import { AbstractSqlSchemaIntrospector, type TableRowReader } from './abstractSqlSchemaIntrospector.js';
3
3
  /**
4
4
  * SQLite schema introspector
5
5
  */
@@ -18,11 +18,12 @@ export declare class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospec
18
18
  protected mapTableNameRow(row: {
19
19
  name: string;
20
20
  }): string;
21
- protected mapColumnsResult(querier: SqlQuerier, tableName: string, results: SqliteColumnRow[]): Promise<ColumnSchema[]>;
22
- protected mapIndexesResult(querier: SqlQuerier, _tableName: string, results: SqliteIndexRow[]): Promise<IndexSchema[]>;
23
- protected mapForeignKeysResult(_querier: SqlQuerier, tableName: string, results: SqliteForeignKeyRow[]): Promise<ForeignKeySchema[]>;
21
+ protected mapColumnsResult(read: TableRowReader, tableName: string, results: SqliteColumnRow[]): Promise<ColumnSchema[]>;
22
+ protected mapIndexesResult(read: TableRowReader, _tableName: string, results: SqliteIndexRow[]): Promise<IndexSchema[]>;
23
+ protected mapForeignKeysResult(_read: TableRowReader, tableName: string, results: SqliteForeignKeyRow[]): Promise<ForeignKeySchema[]>;
24
24
  protected mapPrimaryKeyResult(results: SqliteColumnRow[]): string[] | undefined;
25
25
  private getUniqueColumns;
26
+ private getIndexColumns;
26
27
  protected normalizeType(type: string): string;
27
28
  protected extractLength(type: string): number | undefined;
28
29
  protected parseDefaultValue(defaultValue: string | null): unknown;
@@ -32,16 +32,16 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
32
32
  }
33
33
  // SQLite uses PRAGMA which doesn't use parameterized queries in the same way
34
34
  getColumnsQuery(tableName) {
35
- return `PRAGMA table_info(${this.escapeId(tableName)})`;
35
+ return /*sql*/ `PRAGMA table_info(${this.escapeId(tableName)})`;
36
36
  }
37
37
  getIndexesQuery(tableName) {
38
- return `PRAGMA index_list(${this.escapeId(tableName)})`;
38
+ return /*sql*/ `PRAGMA index_list(${this.escapeId(tableName)})`;
39
39
  }
40
40
  getForeignKeysQuery(tableName) {
41
- return `PRAGMA foreign_key_list(${this.escapeId(tableName)})`;
41
+ return /*sql*/ `PRAGMA foreign_key_list(${this.escapeId(tableName)})`;
42
42
  }
43
43
  getPrimaryKeyQuery(tableName) {
44
- return `PRAGMA table_info(${this.escapeId(tableName)})`;
44
+ return /*sql*/ `PRAGMA table_info(${this.escapeId(tableName)})`;
45
45
  }
46
46
  getColumnsParams(_tableName) {
47
47
  return [];
@@ -61,9 +61,9 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
61
61
  mapTableNameRow(row) {
62
62
  return row.name;
63
63
  }
64
- async mapColumnsResult(querier, tableName, results) {
64
+ async mapColumnsResult(read, tableName, results) {
65
65
  // Get unique columns from indexes
66
- const uniqueColumns = await this.getUniqueColumns(querier, tableName);
66
+ const uniqueColumns = await this.getUniqueColumns(read, tableName);
67
67
  return results.map((row) => ({
68
68
  name: row.name,
69
69
  type: this.normalizeType(row.type),
@@ -78,25 +78,29 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
78
78
  comment: undefined, // SQLite doesn't support column comments
79
79
  }));
80
80
  }
81
- async mapIndexesResult(querier, _tableName, results) {
81
+ async mapIndexesResult(read, _tableName, results) {
82
82
  const indexSchemas = [];
83
83
  for (const index of results) {
84
- const columns = await querier.all(`PRAGMA index_info(${this.escapeId(index.name)})`);
84
+ const columns = await this.getIndexColumns(read, index.name);
85
85
  // Include user-created indexes ('c') and multi-column unique constraints ('u')
86
86
  // Skip primary key indexes ('pk') and single-column unique constraints
87
87
  const isUserCreated = index.origin === 'c';
88
88
  const isCompositeUnique = index.origin === 'u' && columns.length > 1;
89
- if (isUserCreated || isCompositeUnique) {
89
+ // `PRAGMA index_info` names an expression entry `null` (its `cid` is -2), and the expression text
90
+ // lives only in `sqlite_master.sql`. Reporting `{ column: null }` put a column literally named
91
+ // `null` into the diff, so an index UQL cannot describe is left out entirely instead.
92
+ const named = columns.filter((column) => column.name !== null);
93
+ if (named.length === columns.length && (isUserCreated || isCompositeUnique)) {
90
94
  indexSchemas.push({
91
95
  name: index.name,
92
- columns: columns.map((c) => c.name),
96
+ columns: named.map((column) => ({ column: column.name })),
93
97
  unique: Boolean(index.unique),
94
98
  });
95
99
  }
96
100
  }
97
101
  return indexSchemas;
98
102
  }
99
- async mapForeignKeysResult(_querier, tableName, results) {
103
+ async mapForeignKeysResult(_read, tableName, results) {
100
104
  // Group by id to handle composite foreign keys
101
105
  const grouped = new Map();
102
106
  for (const row of results) {
@@ -127,20 +131,24 @@ export class SqliteSchemaIntrospector extends AbstractSqlSchemaIntrospector {
127
131
  // ============================================================================
128
132
  // SQLite-specific helpers
129
133
  // ============================================================================
130
- async getUniqueColumns(querier, tableName) {
131
- const results = await querier.all(`PRAGMA index_list(${this.escapeId(tableName)})`);
134
+ async getUniqueColumns(read, tableName) {
135
+ const indexes = await read(this.getIndexesQuery(tableName));
132
136
  const uniqueColumns = new Set();
133
- for (const index of results) {
137
+ for (const index of indexes) {
134
138
  if (index.unique) {
135
- const indexInfo = await querier.all(`PRAGMA index_info(${this.escapeId(index.name)})`);
136
- // Only single-column unique constraints
137
- if (indexInfo.length === 1) {
138
- uniqueColumns.add(indexInfo[0].name);
139
+ const columns = await this.getIndexColumns(read, index.name);
140
+ // Only single-column unique constraints, and only over a real column (not an expression)
141
+ const [column] = columns;
142
+ if (columns.length === 1 && column.name !== null) {
143
+ uniqueColumns.add(column.name);
139
144
  }
140
145
  }
141
146
  }
142
147
  return uniqueColumns;
143
148
  }
149
+ getIndexColumns(read, indexName) {
150
+ return read(/*sql*/ `PRAGMA index_info(${this.escapeId(indexName)})`);
151
+ }
144
152
  normalizeType(type) {
145
153
  // Extract base type without length/precision
146
154
  const match = type.match(/^([A-Za-z]+)/);
@@ -1,4 +1,4 @@
1
- import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, NamingStrategy, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, Type } from '../type/index.js';
1
+ import type { DialectName, LoggingOptions, Migration, MigrationDefinition, MigrationResult, MigrationStorage, MigratorOptions, MongoQuerier, Querier, QuerierPool, SchemaDiff, SchemaGenerator, SchemaIntrospector, Type } from '../type/index.js';
2
2
  import { LoggerWrapper } from '../util/index.js';
3
3
  import type { IMigrationBuilder } from './builder/types.js';
4
4
  /**
@@ -16,7 +16,7 @@ export declare class Migrator {
16
16
  readonly dialectName: DialectName;
17
17
  schemaGenerator?: SchemaGenerator;
18
18
  schemaIntrospector?: SchemaIntrospector;
19
- private readonly _namingStrategy?;
19
+ private readonly _defaultForeignKeyAction?;
20
20
  private _mongoSchemaLoadPromise?;
21
21
  constructor(pool: QuerierPool, options?: MigratorOptions);
22
22
  /** Loads MongoDB schema generator on first use; SQL generators are set in the constructor (or via {@link setSchemaGenerator}). */
@@ -26,7 +26,7 @@ export declare class Migrator {
26
26
  */
27
27
  setSchemaGenerator(generator: SchemaGenerator): void;
28
28
  protected createIntrospector(): SchemaIntrospector | undefined;
29
- protected createGenerator(namingStrategy?: NamingStrategy): SchemaGenerator | undefined;
29
+ protected createGenerator(): SchemaGenerator | undefined;
30
30
  /**
31
31
  * Get all discovered migrations from the migrations directory
32
32
  */
@@ -88,6 +88,26 @@ export declare class Migrator {
88
88
  drop?: boolean;
89
89
  logging?: boolean;
90
90
  }): Promise<void>;
91
+ /**
92
+ * The DDL {@link autoSync} would run, without running it. Separate so `--dry-run` shows the real
93
+ * statements rather than a summary of a second, differently-computed diff.
94
+ */
95
+ planSync(options?: {
96
+ safe?: boolean;
97
+ drop?: boolean;
98
+ }): Promise<string[]>;
99
+ /**
100
+ * Each pending diff with the entity it came from, since resolving that is async and every caller
101
+ * needs it. What to emit stays with the caller: a sync narrows the forward direction to what the
102
+ * caller allows and never asks for the rollback, which on SQLite cannot even be expressed (no
103
+ * `ALTER COLUMN`), so computing it eagerly for everyone would throw there.
104
+ */
105
+ private pendingDiffs;
106
+ /**
107
+ * The schema generator. A getter because MongoDB's loads lazily (see {@link ensureSchemaGenerator}),
108
+ * so every caller had to repeat the same assertion after awaiting it.
109
+ */
110
+ private get generator();
91
111
  protected filterDiff(diff: SchemaDiff, options: {
92
112
  safe?: boolean;
93
113
  drop?: boolean;