typhex 0.1.0-alpha.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.
- package/CHANGELOG.md +20 -0
- package/LICENSE +21 -0
- package/README.md +417 -0
- package/SECURITY.md +32 -0
- package/dist/arrow/aggregates.d.ts +12 -0
- package/dist/arrow/aggregates.d.ts.map +1 -0
- package/dist/arrow/aggregates.js +32 -0
- package/dist/arrow/aggregates.js.map +1 -0
- package/dist/arrow/constants.d.ts +15 -0
- package/dist/arrow/constants.d.ts.map +1 -0
- package/dist/arrow/constants.js +42 -0
- package/dist/arrow/constants.js.map +1 -0
- package/dist/arrow/index.d.ts +7 -0
- package/dist/arrow/index.d.ts.map +1 -0
- package/dist/arrow/index.js +7 -0
- package/dist/arrow/index.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/load-config.d.ts +19 -0
- package/dist/config/load-config.d.ts.map +1 -0
- package/dist/config/load-config.js +84 -0
- package/dist/config/load-config.js.map +1 -0
- package/dist/config/load-env.d.ts +7 -0
- package/dist/config/load-env.d.ts.map +1 -0
- package/dist/config/load-env.js +62 -0
- package/dist/config/load-env.js.map +1 -0
- package/dist/config/types.d.ts +20 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +10 -0
- package/dist/config/types.js.map +1 -0
- package/dist/dbs/base-migrator.d.ts +20 -0
- package/dist/dbs/base-migrator.d.ts.map +1 -0
- package/dist/dbs/base-migrator.js +112 -0
- package/dist/dbs/base-migrator.js.map +1 -0
- package/dist/dbs/index.d.ts +9 -0
- package/dist/dbs/index.d.ts.map +1 -0
- package/dist/dbs/index.js +17 -0
- package/dist/dbs/index.js.map +1 -0
- package/dist/dbs/postgres/dialect.d.ts +8 -0
- package/dist/dbs/postgres/dialect.d.ts.map +1 -0
- package/dist/dbs/postgres/dialect.js +17 -0
- package/dist/dbs/postgres/dialect.js.map +1 -0
- package/dist/dbs/postgres/driver.d.ts +42 -0
- package/dist/dbs/postgres/driver.d.ts.map +1 -0
- package/dist/dbs/postgres/driver.js +111 -0
- package/dist/dbs/postgres/driver.js.map +1 -0
- package/dist/dbs/postgres/index.d.ts +6 -0
- package/dist/dbs/postgres/index.d.ts.map +1 -0
- package/dist/dbs/postgres/index.js +5 -0
- package/dist/dbs/postgres/index.js.map +1 -0
- package/dist/dbs/postgres/migrator.d.ts +13 -0
- package/dist/dbs/postgres/migrator.d.ts.map +1 -0
- package/dist/dbs/postgres/migrator.js +22 -0
- package/dist/dbs/postgres/migrator.js.map +1 -0
- package/dist/dbs/postgres/query-compiler.d.ts +22 -0
- package/dist/dbs/postgres/query-compiler.d.ts.map +1 -0
- package/dist/dbs/postgres/query-compiler.js +140 -0
- package/dist/dbs/postgres/query-compiler.js.map +1 -0
- package/dist/dbs/postgres/trx.d.ts +16 -0
- package/dist/dbs/postgres/trx.d.ts.map +1 -0
- package/dist/dbs/postgres/trx.js +68 -0
- package/dist/dbs/postgres/trx.js.map +1 -0
- package/dist/dbs/query-compiler.d.ts +135 -0
- package/dist/dbs/query-compiler.d.ts.map +1 -0
- package/dist/dbs/query-compiler.js +775 -0
- package/dist/dbs/query-compiler.js.map +1 -0
- package/dist/dbs/sqlite/dialect.d.ts +8 -0
- package/dist/dbs/sqlite/dialect.d.ts.map +1 -0
- package/dist/dbs/sqlite/dialect.js +17 -0
- package/dist/dbs/sqlite/dialect.js.map +1 -0
- package/dist/dbs/sqlite/driver.d.ts +9 -0
- package/dist/dbs/sqlite/driver.d.ts.map +1 -0
- package/dist/dbs/sqlite/driver.js +66 -0
- package/dist/dbs/sqlite/driver.js.map +1 -0
- package/dist/dbs/sqlite/index.d.ts +6 -0
- package/dist/dbs/sqlite/index.d.ts.map +1 -0
- package/dist/dbs/sqlite/index.js +5 -0
- package/dist/dbs/sqlite/index.js.map +1 -0
- package/dist/dbs/sqlite/migrator.d.ts +13 -0
- package/dist/dbs/sqlite/migrator.d.ts.map +1 -0
- package/dist/dbs/sqlite/migrator.js +22 -0
- package/dist/dbs/sqlite/migrator.js.map +1 -0
- package/dist/dbs/sqlite/query-compiler.d.ts +26 -0
- package/dist/dbs/sqlite/query-compiler.d.ts.map +1 -0
- package/dist/dbs/sqlite/query-compiler.js +80 -0
- package/dist/dbs/sqlite/query-compiler.js.map +1 -0
- package/dist/dbs/sqlite/trx.d.ts +13 -0
- package/dist/dbs/sqlite/trx.d.ts.map +1 -0
- package/dist/dbs/sqlite/trx.js +69 -0
- package/dist/dbs/sqlite/trx.js.map +1 -0
- package/dist/dbs/types.d.ts +92 -0
- package/dist/dbs/types.d.ts.map +1 -0
- package/dist/dbs/types.js +26 -0
- package/dist/dbs/types.js.map +1 -0
- package/dist/dialect.d.ts +3 -0
- package/dist/dialect.d.ts.map +1 -0
- package/dist/dialect.js +2 -0
- package/dist/dialect.js.map +1 -0
- package/dist/driver/factory.d.ts +19 -0
- package/dist/driver/factory.d.ts.map +1 -0
- package/dist/driver/factory.js +24 -0
- package/dist/driver/factory.js.map +1 -0
- package/dist/driver/index.d.ts +6 -0
- package/dist/driver/index.d.ts.map +1 -0
- package/dist/driver/index.js +3 -0
- package/dist/driver/index.js.map +1 -0
- package/dist/driver/sqlite.d.ts +15 -0
- package/dist/driver/sqlite.d.ts.map +1 -0
- package/dist/driver/sqlite.js +74 -0
- package/dist/driver/sqlite.js.map +1 -0
- package/dist/driver/types.d.ts +75 -0
- package/dist/driver/types.d.ts.map +1 -0
- package/dist/driver/types.js +2 -0
- package/dist/driver/types.js.map +1 -0
- package/dist/entity/entity.d.ts +67 -0
- package/dist/entity/entity.d.ts.map +1 -0
- package/dist/entity/entity.js +139 -0
- package/dist/entity/entity.js.map +1 -0
- package/dist/entity/global-driver.d.ts +38 -0
- package/dist/entity/global-driver.d.ts.map +1 -0
- package/dist/entity/global-driver.js +109 -0
- package/dist/entity/global-driver.js.map +1 -0
- package/dist/entity/index.d.ts +8 -0
- package/dist/entity/index.d.ts.map +1 -0
- package/dist/entity/index.js +3 -0
- package/dist/entity/index.js.map +1 -0
- package/dist/entity/pk-columns.d.ts +5 -0
- package/dist/entity/pk-columns.d.ts.map +1 -0
- package/dist/entity/pk-columns.js +14 -0
- package/dist/entity/pk-columns.js.map +1 -0
- package/dist/entity/relations.d.ts +134 -0
- package/dist/entity/relations.d.ts.map +1 -0
- package/dist/entity/relations.js +23 -0
- package/dist/entity/relations.js.map +1 -0
- package/dist/entity/schema-inference.d.ts +77 -0
- package/dist/entity/schema-inference.d.ts.map +1 -0
- package/dist/entity/schema-inference.js +6 -0
- package/dist/entity/schema-inference.js.map +1 -0
- package/dist/entity/types.d.ts +30 -0
- package/dist/entity/types.d.ts.map +1 -0
- package/dist/entity/types.js +5 -0
- package/dist/entity/types.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/index.d.ts +2 -0
- package/dist/ir/index.d.ts.map +1 -0
- package/dist/ir/index.js +2 -0
- package/dist/ir/index.js.map +1 -0
- package/dist/ir/types.d.ts +148 -0
- package/dist/ir/types.d.ts.map +1 -0
- package/dist/ir/types.js +97 -0
- package/dist/ir/types.js.map +1 -0
- package/dist/migration/cli.d.ts +18 -0
- package/dist/migration/cli.d.ts.map +1 -0
- package/dist/migration/cli.js +236 -0
- package/dist/migration/cli.js.map +1 -0
- package/dist/migration/diff.d.ts +10 -0
- package/dist/migration/diff.d.ts.map +1 -0
- package/dist/migration/diff.js +10 -0
- package/dist/migration/diff.js.map +1 -0
- package/dist/migration/generator.d.ts +12 -0
- package/dist/migration/generator.d.ts.map +1 -0
- package/dist/migration/generator.js +98 -0
- package/dist/migration/generator.js.map +1 -0
- package/dist/migration/index.d.ts +7 -0
- package/dist/migration/index.d.ts.map +1 -0
- package/dist/migration/index.js +5 -0
- package/dist/migration/index.js.map +1 -0
- package/dist/migration/runner.d.ts +32 -0
- package/dist/migration/runner.d.ts.map +1 -0
- package/dist/migration/runner.js +279 -0
- package/dist/migration/runner.js.map +1 -0
- package/dist/migration/topo-sort.d.ts +12 -0
- package/dist/migration/topo-sort.d.ts.map +1 -0
- package/dist/migration/topo-sort.js +42 -0
- package/dist/migration/topo-sort.js.map +1 -0
- package/dist/migration/types.d.ts +45 -0
- package/dist/migration/types.d.ts.map +1 -0
- package/dist/migration/types.js +2 -0
- package/dist/migration/types.js.map +1 -0
- package/dist/orm/aggregates.d.ts +19 -0
- package/dist/orm/aggregates.d.ts.map +1 -0
- package/dist/orm/aggregates.js +32 -0
- package/dist/orm/aggregates.js.map +1 -0
- package/dist/orm/db.d.ts +105 -0
- package/dist/orm/db.d.ts.map +1 -0
- package/dist/orm/db.js +172 -0
- package/dist/orm/db.js.map +1 -0
- package/dist/orm/expr.d.ts +125 -0
- package/dist/orm/expr.d.ts.map +1 -0
- package/dist/orm/expr.js +10 -0
- package/dist/orm/expr.js.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-batch-executor.d.ts +9 -0
- package/dist/orm/helpers/insert-graph/insert-graph-batch-executor.d.ts.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-batch-executor.js +44 -0
- package/dist/orm/helpers/insert-graph/insert-graph-batch-executor.js.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-executor.d.ts +13 -0
- package/dist/orm/helpers/insert-graph/insert-graph-executor.d.ts.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-executor.js +42 -0
- package/dist/orm/helpers/insert-graph/insert-graph-executor.js.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-planner.d.ts +46 -0
- package/dist/orm/helpers/insert-graph/insert-graph-planner.d.ts.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-planner.js +258 -0
- package/dist/orm/helpers/insert-graph/insert-graph-planner.js.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-sequential-executor.d.ts +7 -0
- package/dist/orm/helpers/insert-graph/insert-graph-sequential-executor.d.ts.map +1 -0
- package/dist/orm/helpers/insert-graph/insert-graph-sequential-executor.js +8 -0
- package/dist/orm/helpers/insert-graph/insert-graph-sequential-executor.js.map +1 -0
- package/dist/orm/helpers/insert-graph/sequence-id-assigner.d.ts +15 -0
- package/dist/orm/helpers/insert-graph/sequence-id-assigner.d.ts.map +1 -0
- package/dist/orm/helpers/insert-graph/sequence-id-assigner.js +48 -0
- package/dist/orm/helpers/insert-graph/sequence-id-assigner.js.map +1 -0
- package/dist/orm/helpers/query-plan/expr-builder.d.ts +155 -0
- package/dist/orm/helpers/query-plan/expr-builder.d.ts.map +1 -0
- package/dist/orm/helpers/query-plan/expr-builder.js +297 -0
- package/dist/orm/helpers/query-plan/expr-builder.js.map +1 -0
- package/dist/orm/helpers/query-plan/query-ir-analyzer.d.ts +48 -0
- package/dist/orm/helpers/query-plan/query-ir-analyzer.d.ts.map +1 -0
- package/dist/orm/helpers/query-plan/query-ir-analyzer.js +262 -0
- package/dist/orm/helpers/query-plan/query-ir-analyzer.js.map +1 -0
- package/dist/orm/helpers/query-plan/query-plan.d.ts +164 -0
- package/dist/orm/helpers/query-plan/query-plan.d.ts.map +1 -0
- package/dist/orm/helpers/query-plan/query-plan.js +549 -0
- package/dist/orm/helpers/query-plan/query-plan.js.map +1 -0
- package/dist/orm/helpers/query-plan/select-classifier.d.ts +161 -0
- package/dist/orm/helpers/query-plan/select-classifier.d.ts.map +1 -0
- package/dist/orm/helpers/query-plan/select-classifier.js +359 -0
- package/dist/orm/helpers/query-plan/select-classifier.js.map +1 -0
- package/dist/orm/helpers/relations/relation-assembler.d.ts +21 -0
- package/dist/orm/helpers/relations/relation-assembler.d.ts.map +1 -0
- package/dist/orm/helpers/relations/relation-assembler.js +60 -0
- package/dist/orm/helpers/relations/relation-assembler.js.map +1 -0
- package/dist/orm/helpers/relations/relation-fetcher.d.ts +30 -0
- package/dist/orm/helpers/relations/relation-fetcher.d.ts.map +1 -0
- package/dist/orm/helpers/relations/relation-fetcher.js +128 -0
- package/dist/orm/helpers/relations/relation-fetcher.js.map +1 -0
- package/dist/orm/helpers/relations/relation-joins.d.ts +74 -0
- package/dist/orm/helpers/relations/relation-joins.d.ts.map +1 -0
- package/dist/orm/helpers/relations/relation-joins.js +138 -0
- package/dist/orm/helpers/relations/relation-joins.js.map +1 -0
- package/dist/orm/helpers/relations/relation-resolver.d.ts +14 -0
- package/dist/orm/helpers/relations/relation-resolver.d.ts.map +1 -0
- package/dist/orm/helpers/relations/relation-resolver.js +25 -0
- package/dist/orm/helpers/relations/relation-resolver.js.map +1 -0
- package/dist/orm/index.d.ts +7 -0
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/orm/index.js +5 -0
- package/dist/orm/index.js.map +1 -0
- package/dist/orm/query-builder.d.ts +136 -0
- package/dist/orm/query-builder.d.ts.map +1 -0
- package/dist/orm/query-builder.js +433 -0
- package/dist/orm/query-builder.js.map +1 -0
- package/dist/orm/query-helpers.d.ts +35 -0
- package/dist/orm/query-helpers.d.ts.map +1 -0
- package/dist/orm/query-helpers.js +83 -0
- package/dist/orm/query-helpers.js.map +1 -0
- package/dist/orm/query-state.d.ts +56 -0
- package/dist/orm/query-state.d.ts.map +1 -0
- package/dist/orm/query-state.js +112 -0
- package/dist/orm/query-state.js.map +1 -0
- package/dist/orm/single-row-query-builder.d.ts +18 -0
- package/dist/orm/single-row-query-builder.d.ts.map +1 -0
- package/dist/orm/single-row-query-builder.js +90 -0
- package/dist/orm/single-row-query-builder.js.map +1 -0
- package/dist/orm/trx.d.ts +40 -0
- package/dist/orm/trx.d.ts.map +1 -0
- package/dist/orm/trx.js +118 -0
- package/dist/orm/trx.js.map +1 -0
- package/dist/parser/acorn-helpers.d.ts +43 -0
- package/dist/parser/acorn-helpers.d.ts.map +1 -0
- package/dist/parser/acorn-helpers.js +104 -0
- package/dist/parser/acorn-helpers.js.map +1 -0
- package/dist/parser/acorn-member.d.ts +16 -0
- package/dist/parser/acorn-member.d.ts.map +1 -0
- package/dist/parser/acorn-member.js +31 -0
- package/dist/parser/acorn-member.js.map +1 -0
- package/dist/parser/acorn-types.d.ts +14 -0
- package/dist/parser/acorn-types.d.ts.map +1 -0
- package/dist/parser/acorn-types.js +6 -0
- package/dist/parser/acorn-types.js.map +1 -0
- package/dist/parser/arrow-source.d.ts +23 -0
- package/dist/parser/arrow-source.d.ts.map +1 -0
- package/dist/parser/arrow-source.js +70 -0
- package/dist/parser/arrow-source.js.map +1 -0
- package/dist/parser/group-by.d.ts +17 -0
- package/dist/parser/group-by.d.ts.map +1 -0
- package/dist/parser/group-by.js +62 -0
- package/dist/parser/group-by.js.map +1 -0
- package/dist/parser/index.d.ts +5 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +3 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/parse-arrow.d.ts +10 -0
- package/dist/parser/parse-arrow.d.ts.map +1 -0
- package/dist/parser/parse-arrow.js +9 -0
- package/dist/parser/parse-arrow.js.map +1 -0
- package/dist/parser/predicate-walk.d.ts +24 -0
- package/dist/parser/predicate-walk.d.ts.map +1 -0
- package/dist/parser/predicate-walk.js +247 -0
- package/dist/parser/predicate-walk.js.map +1 -0
- package/dist/parser/resolve.d.ts +25 -0
- package/dist/parser/resolve.d.ts.map +1 -0
- package/dist/parser/resolve.js +134 -0
- package/dist/parser/resolve.js.map +1 -0
- package/dist/parser/select.d.ts +7 -0
- package/dist/parser/select.d.ts.map +1 -0
- package/dist/parser/select.js +333 -0
- package/dist/parser/select.js.map +1 -0
- package/dist/parser/update.d.ts +7 -0
- package/dist/parser/update.d.ts.map +1 -0
- package/dist/parser/update.js +51 -0
- package/dist/parser/update.js.map +1 -0
- package/dist/postgres/aggregates.d.ts +12 -0
- package/dist/postgres/aggregates.d.ts.map +1 -0
- package/dist/postgres/aggregates.js +19 -0
- package/dist/postgres/aggregates.js.map +1 -0
- package/dist/postgres.d.ts +6 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +6 -0
- package/dist/postgres.js.map +1 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/types.d.ts +16 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +40 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/sqlite/aggregates.d.ts +8 -0
- package/dist/sqlite/aggregates.d.ts.map +1 -0
- package/dist/sqlite/aggregates.js +11 -0
- package/dist/sqlite/aggregates.js.map +1 -0
- package/dist/sqlite.d.ts +6 -0
- package/dist/sqlite.d.ts.map +1 -0
- package/dist/sqlite.js +6 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/transformer/bindings.d.ts +18 -0
- package/dist/transformer/bindings.d.ts.map +1 -0
- package/dist/transformer/bindings.js +52 -0
- package/dist/transformer/bindings.js.map +1 -0
- package/dist/transformer/index.d.ts +17 -0
- package/dist/transformer/index.d.ts.map +1 -0
- package/dist/transformer/index.js +75 -0
- package/dist/transformer/index.js.map +1 -0
- package/dist/transformer/ir-emit.d.ts +12 -0
- package/dist/transformer/ir-emit.d.ts.map +1 -0
- package/dist/transformer/ir-emit.js +144 -0
- package/dist/transformer/ir-emit.js.map +1 -0
- package/dist/transformer/join-transformer.d.ts +12 -0
- package/dist/transformer/join-transformer.d.ts.map +1 -0
- package/dist/transformer/join-transformer.js +102 -0
- package/dist/transformer/join-transformer.js.map +1 -0
- package/dist/transformer/orderby-transformer.d.ts +13 -0
- package/dist/transformer/orderby-transformer.d.ts.map +1 -0
- package/dist/transformer/orderby-transformer.js +84 -0
- package/dist/transformer/orderby-transformer.js.map +1 -0
- package/dist/transformer/select-transformer.d.ts +9 -0
- package/dist/transformer/select-transformer.d.ts.map +1 -0
- package/dist/transformer/select-transformer.js +222 -0
- package/dist/transformer/select-transformer.js.map +1 -0
- package/dist/transformer/shared.d.ts +12 -0
- package/dist/transformer/shared.d.ts.map +1 -0
- package/dist/transformer/shared.js +12 -0
- package/dist/transformer/shared.js.map +1 -0
- package/dist/transformer/subquery-transformer.d.ts +15 -0
- package/dist/transformer/subquery-transformer.d.ts.map +1 -0
- package/dist/transformer/subquery-transformer.js +43 -0
- package/dist/transformer/subquery-transformer.js.map +1 -0
- package/dist/transformer/ts-aggregates.d.ts +13 -0
- package/dist/transformer/ts-aggregates.d.ts.map +1 -0
- package/dist/transformer/ts-aggregates.js +83 -0
- package/dist/transformer/ts-aggregates.js.map +1 -0
- package/dist/transformer/ts-binary.d.ts +8 -0
- package/dist/transformer/ts-binary.d.ts.map +1 -0
- package/dist/transformer/ts-binary.js +32 -0
- package/dist/transformer/ts-binary.js.map +1 -0
- package/dist/transformer/ts-member.d.ts +12 -0
- package/dist/transformer/ts-member.d.ts.map +1 -0
- package/dist/transformer/ts-member.js +22 -0
- package/dist/transformer/ts-member.js.map +1 -0
- package/dist/transformer/ts-utils.d.ts +10 -0
- package/dist/transformer/ts-utils.d.ts.map +1 -0
- package/dist/transformer/ts-utils.js +38 -0
- package/dist/transformer/ts-utils.js.map +1 -0
- package/dist/transformer/typhex-type.d.ts +8 -0
- package/dist/transformer/typhex-type.d.ts.map +1 -0
- package/dist/transformer/typhex-type.js +52 -0
- package/dist/transformer/typhex-type.js.map +1 -0
- package/dist/transformer/where-transformer.d.ts +33 -0
- package/dist/transformer/where-transformer.d.ts.map +1 -0
- package/dist/transformer/where-transformer.js +400 -0
- package/dist/transformer/where-transformer.js.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +30 -0
- package/dist/utils.js.map +1 -0
- package/docs/drivers/postgres.md +54 -0
- package/docs/drivers/sqlite.md +33 -0
- package/docs/guide/aggregations.md +272 -0
- package/docs/guide/bulk-operations.md +204 -0
- package/docs/guide/cte-and-unions.md +270 -0
- package/docs/guide/entities-relations.md +208 -0
- package/docs/guide/expressions.md +98 -0
- package/docs/guide/filtering-by-relations.md +166 -0
- package/docs/guide/getting-started.md +277 -0
- package/docs/guide/querying-relations.md +133 -0
- package/docs/guide/subqueries.md +152 -0
- package/docs/guide/transactions.md +146 -0
- package/docs/guide/typescript-transformer.md +138 -0
- package/docs/index.md +33 -0
- package/docs/migrations/overview.md +143 -0
- package/docs/public-api.md +133 -0
- package/docs/reference/api.md +665 -0
- package/docs/reference/architecture.md +83 -0
- package/docs/release-checklist.md +55 -0
- package/etc/typhex.api.md +774 -0
- package/package.json +137 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Filtering by Relations
|
|
2
|
+
|
|
3
|
+
When a `where()` predicate references a relation property, Typhex generates a SQL `JOIN` (for `manyToOne`) or an `EXISTS` subquery (for `oneToMany`) rather than loading data in application code.
|
|
4
|
+
|
|
5
|
+
## Basic manyToOne WHERE
|
|
6
|
+
|
|
7
|
+
Filter rows by a property of a related entity:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
// contacts with a where on their company name
|
|
11
|
+
const acmeContacts = await Contact.query()
|
|
12
|
+
.where((c) => c.company.name === "Acme Corp") // [!code highlight]
|
|
13
|
+
.select((c) => ({ id: c.id, name: c.name }))
|
|
14
|
+
.orderBy((c) => c.id, "asc")
|
|
15
|
+
.toArray();
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```sql
|
|
19
|
+
SELECT contacts.id AS id, contacts.name AS name
|
|
20
|
+
FROM contacts
|
|
21
|
+
LEFT JOIN companies ON companies.id = contacts.companyId
|
|
22
|
+
WHERE companies.name = ?
|
|
23
|
+
ORDER BY contacts.id ASC
|
|
24
|
+
-- params: ["Acme Corp"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
::: tip Default JOIN type
|
|
28
|
+
Typhex emits a `LEFT JOIN` by default. Use [join hints](/reference/api#innerjoin-keysorfn-leftjoin-rightjoin-fulljoin-crossjoin) (`.innerJoin()`, `.rightJoin()`, etc.) to override.
|
|
29
|
+
:::
|
|
30
|
+
|
|
31
|
+
## JOIN Reuse: WHERE + SELECT on the Same Relation
|
|
32
|
+
|
|
33
|
+
When both `where()` and `select()` reference the same `manyToOne` relation, Typhex reuses the JOIN — no duplicate fetch:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const acmeContactsWithCompany = await Contact.query()
|
|
37
|
+
.where((c) => c.company.name === "Acme Corp")
|
|
38
|
+
.select((c) => ({
|
|
39
|
+
id: c.id,
|
|
40
|
+
name: c.name,
|
|
41
|
+
company: { id: c.company.id, name: c.company.name }, // [!code highlight]
|
|
42
|
+
}))
|
|
43
|
+
.orderBy((c) => c.id, "asc")
|
|
44
|
+
.toArray();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```sql
|
|
48
|
+
SELECT contacts.id AS id, contacts.name AS name,
|
|
49
|
+
companies.id AS "company.id", companies.name AS "company.name"
|
|
50
|
+
FROM contacts
|
|
51
|
+
LEFT JOIN companies ON companies.id = contacts.companyId
|
|
52
|
+
WHERE companies.name = ?
|
|
53
|
+
ORDER BY contacts.id ASC
|
|
54
|
+
-- params: ["Acme Corp"]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Different Relations in WHERE and SELECT
|
|
58
|
+
|
|
59
|
+
You can filter by one relation and load another — each is handled independently. The `where` relation generates a JOIN; the `select` relation loads via a separate `WHERE id IN (...)` fetch:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const acmeContactsWithCategory = await Contact.query()
|
|
63
|
+
.where((c) => c.company.name === "Acme Corp")
|
|
64
|
+
.select((c) => ({
|
|
65
|
+
id: c.id,
|
|
66
|
+
name: c.name,
|
|
67
|
+
category: { id: c.category.id, name: c.category.name },
|
|
68
|
+
}))
|
|
69
|
+
.toArray();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```sql
|
|
73
|
+
-- 1. Main query — JOIN on companies (for WHERE), but select category FK
|
|
74
|
+
SELECT contacts.id AS id, contacts.name AS name, contacts.categoryId AS categoryId
|
|
75
|
+
FROM contacts
|
|
76
|
+
LEFT JOIN companies ON companies.id = contacts.companyId
|
|
77
|
+
WHERE companies.name = ?
|
|
78
|
+
|
|
79
|
+
-- 2. Category fetch
|
|
80
|
+
SELECT id, name FROM categories WHERE id IN (?, ?, ...)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Spread All Columns + a Relation
|
|
84
|
+
|
|
85
|
+
Use spread syntax to include all own columns alongside a relation:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const contactsWithCompany = await Contact.query()
|
|
89
|
+
.select((c) => ({ ...c, company: c.company })) // [!code highlight]
|
|
90
|
+
.orderBy((c) => c.id, "asc")
|
|
91
|
+
.toArray();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```sql
|
|
95
|
+
SELECT id AS id, name AS name, companyId AS companyId, categoryId AS categoryId
|
|
96
|
+
FROM contacts ORDER BY id ASC
|
|
97
|
+
|
|
98
|
+
SELECT id, name FROM companies WHERE id IN (?, ?, ...)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## oneToMany EXISTS: `.some()`
|
|
102
|
+
|
|
103
|
+
For `oneToMany` relations, use `.some()` inside `where()` to generate an `EXISTS` subquery:
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// departments that have at least one employee named "Alice"
|
|
107
|
+
const deptsWithAlice = await Department.query()
|
|
108
|
+
.where((d) => d.employees.some((e) => e.name === "Alice")) // [!code highlight]
|
|
109
|
+
.select((d) => ({ id: d.id, name: d.name }))
|
|
110
|
+
.toArray();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Typhex emits:
|
|
114
|
+
|
|
115
|
+
```sql
|
|
116
|
+
SELECT departments.id, departments.name
|
|
117
|
+
FROM departments
|
|
118
|
+
WHERE EXISTS (
|
|
119
|
+
SELECT 1 FROM employees
|
|
120
|
+
WHERE employees.departmentId = departments.id
|
|
121
|
+
AND employees.name = ?
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## oneToMany NOT EXISTS: `.every()`
|
|
126
|
+
|
|
127
|
+
Use `.every()` when every related row must satisfy a predicate. Typhex compiles this to `NOT EXISTS` over rows that fail the predicate:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
// departments where every employee is active
|
|
131
|
+
const activeDepartments = await Department.query()
|
|
132
|
+
.where((d) => d.employees.every((e) => e.active === true)) // [!code highlight]
|
|
133
|
+
.select((d) => ({ id: d.id, name: d.name }))
|
|
134
|
+
.toArray();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Typhex emits:
|
|
138
|
+
|
|
139
|
+
```sql
|
|
140
|
+
SELECT departments.id, departments.name
|
|
141
|
+
FROM departments
|
|
142
|
+
WHERE NOT EXISTS (
|
|
143
|
+
SELECT 1 FROM employees
|
|
144
|
+
WHERE employees.departmentId = departments.id
|
|
145
|
+
AND NOT (employees.active = ?)
|
|
146
|
+
)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Count with Relation WHERE
|
|
150
|
+
|
|
151
|
+
`.count()` works with any `where()` predicate, including those that reference relations:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const acmeCount = await Contact.query()
|
|
155
|
+
.where((c) => c.company.name === "Acme Corp")
|
|
156
|
+
.count();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```sql
|
|
160
|
+
SELECT COUNT(*) AS c FROM (
|
|
161
|
+
SELECT ... FROM "contacts" AS "t0"
|
|
162
|
+
LEFT JOIN "companies" AS "t1" ON "t1"."id" = "t0"."companyId"
|
|
163
|
+
WHERE ("t1"."name" = ?)
|
|
164
|
+
) AS "_count"
|
|
165
|
+
-- params: ["Acme Corp"]
|
|
166
|
+
```
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
## Installation
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install typhex better-sqlite3
|
|
7
|
+
npm install --save-dev ts-patch
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
For PostgreSQL: `npm install typhex pg`
|
|
11
|
+
|
|
12
|
+
> Native addon? See the [SQLite driver](/drivers/sqlite#installation) page if `better-sqlite3` fails to build.
|
|
13
|
+
|
|
14
|
+
Then enable the [TypeScript transformer](/guide/typescript-transformer) — it auto-captures closure variables and eliminates runtime parsing overhead. Without it, Typhex falls back to runtime parsing with Acorn (you pass closure variables manually).
|
|
15
|
+
|
|
16
|
+
## Define a Schema
|
|
17
|
+
|
|
18
|
+
Use `Entity()` to define a table. Column types are SQL type strings passed through to `CREATE TABLE`.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { Db, Entity, createSqliteDriver } from "typhex";
|
|
22
|
+
|
|
23
|
+
const User = Entity("users", {
|
|
24
|
+
id: "integer primary key autoincrement",
|
|
25
|
+
name: "text not null",
|
|
26
|
+
age: "integer not null",
|
|
27
|
+
country: "text not null",
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
TypeScript infers the row type from the schema — no separate interface needed.
|
|
32
|
+
|
|
33
|
+
## Connect and Migrate
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const db = new Db(createSqliteDriver({ path: "./app.db" }));
|
|
37
|
+
await db.migrate(); // creates tables that don't exist yet
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use `":memory:"` for tests and one-off scripts.
|
|
41
|
+
|
|
42
|
+
## Insert Rows
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
await User.query().insert({ name: "Alice", age: 30, country: "US" });
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```sql
|
|
49
|
+
INSERT INTO users (name, age, country) VALUES (?, ?, ?)
|
|
50
|
+
-- params: ["Alice", 30, "US"]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`insert()` returns the inserted row with the auto-generated `id`.
|
|
54
|
+
|
|
55
|
+
## Query with Arrow Functions
|
|
56
|
+
|
|
57
|
+
With the transformer active, write predicates as plain TypeScript — closure variables are captured automatically:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const adults = await User.query()
|
|
61
|
+
.where((u) => u.age > 18)
|
|
62
|
+
.toArray();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
```sql
|
|
66
|
+
SELECT id, name, age, country FROM users WHERE age > ?
|
|
67
|
+
-- params: [18]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Closure variables work without a second argument:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const country = "US";
|
|
74
|
+
const fromUS = await User.query()
|
|
75
|
+
.where((u) => u.country === country)
|
|
76
|
+
.toArray();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```sql
|
|
80
|
+
SELECT id, name, age, country FROM users WHERE country = ?
|
|
81
|
+
-- params: ["US"]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Multiple closure variables compose into `AND`:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const minAge = 25;
|
|
88
|
+
const maxAge = 35;
|
|
89
|
+
const inRange = await User.query()
|
|
90
|
+
.where((u) => u.age >= minAge && u.age <= maxAge)
|
|
91
|
+
.toArray();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```sql
|
|
95
|
+
SELECT id, name, age, country FROM users WHERE age >= ? AND age <= ?
|
|
96
|
+
-- params: [25, 35]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Fluent Chaining
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const oldest = await User.query()
|
|
103
|
+
.where((u) => u.age >= 25)
|
|
104
|
+
.orderBy((u) => u.age, "desc")
|
|
105
|
+
.first();
|
|
106
|
+
|
|
107
|
+
const top10 = await User.query()
|
|
108
|
+
.where((u) => u.age >= 25)
|
|
109
|
+
.orderBy((u) => u.age, "desc")
|
|
110
|
+
.limit(10)
|
|
111
|
+
.toArray();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
```sql
|
|
115
|
+
SELECT id, name, age, country FROM users
|
|
116
|
+
WHERE age >= ? ORDER BY age DESC LIMIT ?
|
|
117
|
+
-- first(): params: [25, 1]
|
|
118
|
+
-- toArray(): params: [25, 10]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`.first()` returns the first matching row or `undefined` (it sets `LIMIT 1` automatically). `.toArray()` returns all rows.
|
|
122
|
+
|
|
123
|
+
## Select Columns
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
// Column-name array
|
|
127
|
+
const names = await User.query().select(["name", "country"]).toArray();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```sql
|
|
131
|
+
SELECT name AS name, country AS country FROM users
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
// Lambda projection (with aliases)
|
|
136
|
+
const projected = await User.query()
|
|
137
|
+
.select((u) => ({ userId: u.id, fullName: u.name }))
|
|
138
|
+
.toArray();
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```sql
|
|
142
|
+
SELECT id AS userId, name AS fullName FROM users
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// Shorthand: select all
|
|
147
|
+
const all = await User.query()
|
|
148
|
+
.select((u) => u)
|
|
149
|
+
.toArray();
|
|
150
|
+
|
|
151
|
+
// Shorthand: single column
|
|
152
|
+
const ages = await User.query()
|
|
153
|
+
.select((u) => u.age)
|
|
154
|
+
.toArray();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Projection fields can also be computed expressions:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const labels = await User.query()
|
|
161
|
+
.select((u) => ({
|
|
162
|
+
name: u.name,
|
|
163
|
+
decade: (u.age / 10) * 10,
|
|
164
|
+
label: u.age >= 18 ? "adult" : "minor",
|
|
165
|
+
}))
|
|
166
|
+
.toArray();
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
In runtime mode, pass closure variables used inside `.select()` as the second argument. With the transformer enabled, they are captured automatically.
|
|
170
|
+
|
|
171
|
+
## String Predicates
|
|
172
|
+
|
|
173
|
+
`.startsWith()`, `.endsWith()`, and `.includes()` compile to SQL `LIKE`:
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
const a = await User.query()
|
|
177
|
+
.where((u) => u.name.startsWith("A"))
|
|
178
|
+
.toArray();
|
|
179
|
+
const al = await User.query()
|
|
180
|
+
.where((u) => u.name.includes("al"))
|
|
181
|
+
.toArray();
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```sql
|
|
185
|
+
SELECT ... FROM users WHERE name LIKE ? || '%' -- startsWith → params: ["A"]
|
|
186
|
+
SELECT ... FROM users WHERE name LIKE '%' || ? || '%' -- includes → params: ["al"]
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Array Membership
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const selected = await User.query()
|
|
193
|
+
.where((u) => u.id in [1, 3])
|
|
194
|
+
.toArray();
|
|
195
|
+
const excluded = await User.query()
|
|
196
|
+
.where((u) => !(u.id in [2]))
|
|
197
|
+
.toArray();
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
```sql
|
|
201
|
+
SELECT ... FROM users WHERE id IN (?, ?) -- params: [1, 3]
|
|
202
|
+
SELECT ... FROM users WHERE id NOT IN (?) -- params: [2]
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Variable arrays work too — captured automatically with the transformer:
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
const ids = [1, 2];
|
|
209
|
+
const byIds = await User.query()
|
|
210
|
+
.where((u) => u.id in ids)
|
|
211
|
+
.toArray();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Find by ID and Count
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const user = await User.query().findById(1);
|
|
218
|
+
const n = await User.query()
|
|
219
|
+
.where((u) => u.country === "US")
|
|
220
|
+
.count();
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```sql
|
|
224
|
+
SELECT ... FROM users WHERE id = ? LIMIT 1 -- params: [1]
|
|
225
|
+
SELECT COUNT(*) AS c FROM (
|
|
226
|
+
SELECT ... FROM "users" AS "t0" WHERE ("t0"."country" = ?)
|
|
227
|
+
) AS "_count" -- params: ["US"]
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Update, Patch, and Delete
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
const updated = await User.query()
|
|
234
|
+
.where((u) => u.name === "Bob")
|
|
235
|
+
.update({ age: 26 });
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
```sql
|
|
239
|
+
UPDATE users SET age = ? WHERE name = ?
|
|
240
|
+
-- params: [26, "Bob"]
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
const patched = await User.query()
|
|
245
|
+
.where((u) => u.name === "Bob")
|
|
246
|
+
.patch({ age: 26 });
|
|
247
|
+
// Same UPDATE as above, then a SELECT ... WHERE name = ? LIMIT 1 to return the row
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
const deleted = await User.query()
|
|
252
|
+
.where((u) => u.country === "UK")
|
|
253
|
+
.delete();
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
```sql
|
|
257
|
+
DELETE FROM users WHERE country = ?
|
|
258
|
+
-- params: ["UK"]
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Instance Save and Delete
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
const dave = new User({ name: "Dave", age: 35, country: "US" });
|
|
265
|
+
await dave.query().save(); // INSERT — populates dave.id
|
|
266
|
+
await dave.query().delete(); // DELETE WHERE id = ?
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
`save()` inserts when the primary key is unset and updates when it's already set.
|
|
270
|
+
|
|
271
|
+
## Debug Mode
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
TYPHEX_DEBUG=1 npx tsx your-script.ts
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Logs every SQL statement and its parameters to the console — exactly the SQL shown alongside each example on this page. Accepts `1`, `true`, or `yes`.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Querying Relations
|
|
2
|
+
|
|
3
|
+
Once you've [defined relations](/guide/entities-relations) on your entities, you can include related data in query results using a lambda passed to `.select()`.
|
|
4
|
+
|
|
5
|
+
## manyToOne in `select()`
|
|
6
|
+
|
|
7
|
+
When you select a `manyToOne` relation, Typhex fetches the related rows using a single `WHERE id IN (...)` query — not N+1 individual queries.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const Post = Entity(
|
|
11
|
+
"posts",
|
|
12
|
+
{
|
|
13
|
+
id: "integer primary key autoincrement",
|
|
14
|
+
title: "text not null",
|
|
15
|
+
body: "text",
|
|
16
|
+
authorId: "integer not null",
|
|
17
|
+
published: "boolean",
|
|
18
|
+
},
|
|
19
|
+
{ author: rel.manyToOne(() => User, { foreignKey: "authorId" }) },
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const postsWithAuthor = await Post.query()
|
|
23
|
+
.select((p) => ({ id: p.id, title: p.title, author: p.author })) // [!code highlight]
|
|
24
|
+
.orderBy((p) => p.id, "asc")
|
|
25
|
+
.toArray();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```sql
|
|
29
|
+
-- 1. Main query
|
|
30
|
+
SELECT id AS id, title AS title, authorId AS authorId FROM posts ORDER BY id ASC
|
|
31
|
+
|
|
32
|
+
-- 2. Author fetch (one query for all authors, regardless of how many posts)
|
|
33
|
+
SELECT id, name FROM users WHERE id IN (?, ?, ...)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Partial Relation Select
|
|
37
|
+
|
|
38
|
+
Select only specific fields from the related entity by using an inline object literal:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const postsPartialAuthor = await Post.query()
|
|
42
|
+
.select((p) => ({
|
|
43
|
+
id: p.id,
|
|
44
|
+
title: p.title,
|
|
45
|
+
author: { id: p.author.id, name: p.author.name }, // [!code highlight]
|
|
46
|
+
}))
|
|
47
|
+
.toArray();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```sql
|
|
51
|
+
SELECT id AS id, title AS title, authorId AS authorId FROM posts
|
|
52
|
+
SELECT id, name FROM users WHERE id IN (?, ?, ...) -- only id and name fetched
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This keeps the result shape small when you don't need all columns from the related entity.
|
|
56
|
+
|
|
57
|
+
## oneToMany in `select()`
|
|
58
|
+
|
|
59
|
+
For `oneToMany` relations, call `.query()` on the relation to get a sub-query builder:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const User = Entity(
|
|
63
|
+
"users",
|
|
64
|
+
{ id: "integer primary key autoincrement", name: "text not null", email: "text" },
|
|
65
|
+
{ posts: rel.oneToMany(() => Post, { foreignKey: "authorId" }) },
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const usersWithPosts = await User.query()
|
|
69
|
+
.select((u) => ({
|
|
70
|
+
id: u.id,
|
|
71
|
+
name: u.name,
|
|
72
|
+
posts: u.posts.query().select((p) => ({ id: p.id, title: p.title })), // [!code highlight]
|
|
73
|
+
}))
|
|
74
|
+
.orderBy((p) => p.id, "asc")
|
|
75
|
+
.toArray();
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```sql
|
|
79
|
+
-- 1. Main query
|
|
80
|
+
SELECT id AS id, name AS name FROM users ORDER BY id ASC
|
|
81
|
+
|
|
82
|
+
-- 2. Posts fetched in one round-trip — all posts for all returned users
|
|
83
|
+
SELECT id AS id, title AS title, authorId AS authorId
|
|
84
|
+
FROM posts WHERE authorId IN (?, ?, ...)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Each user's `posts` field is populated in-memory by grouping the second result on `authorId`.
|
|
88
|
+
|
|
89
|
+
## Filtering + Relations Combined
|
|
90
|
+
|
|
91
|
+
Chain `.where()` before `.select()` — they compose naturally:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
const publishedWithAuthor = await Post.query()
|
|
95
|
+
.where((p) => p.published === true)
|
|
96
|
+
.select((p) => ({ id: p.id, title: p.title, author: p.author }))
|
|
97
|
+
.toArray();
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```sql
|
|
101
|
+
SELECT id AS id, title AS title, authorId AS authorId
|
|
102
|
+
FROM posts WHERE published = ?
|
|
103
|
+
-- params: [1]
|
|
104
|
+
|
|
105
|
+
SELECT id, name FROM users WHERE id IN (?, ?, ...) -- only authors of matching posts
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
::: tip No N+1 queries
|
|
109
|
+
Relation selects always use a single `WHERE foreignKey IN (...)` query to load all related rows in one round-trip — never per-row queries.
|
|
110
|
+
:::
|
|
111
|
+
|
|
112
|
+
## Circular References
|
|
113
|
+
|
|
114
|
+
The `() => Target` thunk in relation definitions handles lazy evaluation at query time, so circular relations work out of the box at runtime. When two entities reference each other across files, TypeScript needs help with the types — declare the relation property explicitly and use `createRequire` to break the import cycle:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// models/user.ts
|
|
118
|
+
import { createRequire } from "node:module";
|
|
119
|
+
import { Entity, rel, type OneToMany } from "typhex";
|
|
120
|
+
import type { Post } from "./post.js";
|
|
121
|
+
|
|
122
|
+
const _require = createRequire(import.meta.url);
|
|
123
|
+
|
|
124
|
+
export class User extends Entity(
|
|
125
|
+
"users",
|
|
126
|
+
{ id: "integer primary key autoincrement", name: "text not null" },
|
|
127
|
+
{ posts: rel.oneToMany(() => _require("./post.js").Post, { foreignKey: "authorId" }) },
|
|
128
|
+
) {
|
|
129
|
+
declare posts: OneToMany<Post>;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If your relations aren't circular, neither `declare` nor `createRequire` is needed — types flow directly from `rel.manyToOne(() => Company, ...)`.
|