@danceroutine/tango-orm 1.6.0 → 1.8.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 (84) hide show
  1. package/dist/InternalDialect-ClSaUNso.js +10 -0
  2. package/dist/InternalDialect-ClSaUNso.js.map +1 -0
  3. package/dist/PostgresAdapter-CXKdKBG-.js +4 -0
  4. package/dist/PostgresAdapter-DySFW6vy.js +128 -0
  5. package/dist/PostgresAdapter-DySFW6vy.js.map +1 -0
  6. package/dist/{SqliteClient-CjOK9-ki.js → SqliteAdapter-CDdOjRmW.js} +57 -3
  7. package/dist/SqliteAdapter-CDdOjRmW.js.map +1 -0
  8. package/dist/SqliteAdapter-mjtXuVTg.js +4 -0
  9. package/dist/connection/adapters/Adapter.d.ts +32 -1
  10. package/dist/connection/adapters/dialects/PostgresAdapter.d.ts +5 -6
  11. package/dist/connection/adapters/dialects/SqliteAdapter.d.ts +4 -6
  12. package/dist/connection/adapters/index.d.ts +1 -1
  13. package/dist/connection/index.d.ts +1 -1
  14. package/dist/connection/index.js +4 -5
  15. package/dist/{connection-B_K2ZAf7.js → connection-Dmhgx31M.js} +5 -7
  16. package/dist/{connection-B_K2ZAf7.js.map → connection-Dmhgx31M.js.map} +1 -1
  17. package/dist/{defaultRuntime-BPK9kWEW.js → defaultRuntime-DzqBQ9Hb.js} +63 -16
  18. package/dist/defaultRuntime-DzqBQ9Hb.js.map +1 -0
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +11 -12
  21. package/dist/manager/ManagerLike.d.ts +19 -0
  22. package/dist/manager/ModelManager.d.ts +45 -2
  23. package/dist/manager/index.d.ts +6 -0
  24. package/dist/manager/index.js +8 -7
  25. package/dist/manager/internal/MutationCompiler.d.ts +14 -6
  26. package/dist/manager/relations/ManyToManyRelatedManager.d.ts +147 -0
  27. package/dist/manager/relations/ManyToManyRelatedQuerySet.d.ts +62 -0
  28. package/dist/manager/relations/MaterializedModelRecord.d.ts +28 -0
  29. package/dist/manager/relations/index.d.ts +9 -0
  30. package/dist/manager/relations/internal/ThroughTableManager.d.ts +79 -0
  31. package/dist/manager-DrDTiCAz.js +24 -0
  32. package/dist/manager-DrDTiCAz.js.map +1 -0
  33. package/dist/query/ModelQuerySet.d.ts +20 -0
  34. package/dist/query/QBuilder.d.ts +3 -3
  35. package/dist/query/QuerySet.d.ts +58 -18
  36. package/dist/query/compiler/QueryCompiler.d.ts +13 -4
  37. package/dist/query/domain/CompiledQuery.d.ts +169 -2
  38. package/dist/query/domain/FilterInput.d.ts +1 -1
  39. package/dist/query/domain/FilterKey.d.ts +4 -2
  40. package/dist/query/domain/QNode.d.ts +4 -4
  41. package/dist/query/domain/QuerySetState.d.ts +3 -3
  42. package/dist/query/domain/RelationMeta.d.ts +9 -0
  43. package/dist/query/domain/RelationTyping.d.ts +47 -0
  44. package/dist/query/domain/TableMetaFactory.d.ts +1 -14
  45. package/dist/query/domain/index.d.ts +1 -1
  46. package/dist/query/domain/internal/InternalPrefetchQueryKind.d.ts +20 -0
  47. package/dist/query/index.d.ts +1 -0
  48. package/dist/query/index.js +3 -2
  49. package/dist/query/internal/isQNodeLike.d.ts +3 -0
  50. package/dist/query/planning/QueryPlanner.d.ts +1 -1
  51. package/dist/{query-C6So1r6H.js → query-DUZnBFhf.js} +474 -156
  52. package/dist/query-DUZnBFhf.js.map +1 -0
  53. package/dist/registerModelObjects-DxlBfuUN.js +797 -0
  54. package/dist/registerModelObjects-DxlBfuUN.js.map +1 -0
  55. package/dist/runtime/TangoRuntime.d.ts +9 -0
  56. package/dist/runtime/index.d.ts +3 -2
  57. package/dist/runtime/index.js +7 -6
  58. package/dist/runtime/internal/SqliteDBClientProvider.d.ts +3 -0
  59. package/dist/{runtime-ByXbpVBS.js → runtime-1H88J3nN.js} +3 -3
  60. package/dist/runtime-1H88J3nN.js.map +1 -0
  61. package/dist/transaction/index.js +5 -4
  62. package/dist/{transaction-Cs0Z9tbW.js → transaction-ZhfDf-f8.js} +2 -2
  63. package/dist/{transaction-Cs0Z9tbW.js.map → transaction-ZhfDf-f8.js.map} +1 -1
  64. package/dist/validation/SQLValidationEngine.d.ts +22 -5
  65. package/dist/validation/SqlValidationPlan.d.ts +5 -4
  66. package/dist/validation/internal/InternalSqlValidationPlanKind.d.ts +25 -0
  67. package/dist/validation/internal/InternalValidatedFilterDescriptorKind.d.ts +4 -0
  68. package/package.json +6 -6
  69. package/dist/PostgresAdapter-BFdo_nIt.js +0 -4
  70. package/dist/PostgresAdapter-CMiEpHya.js +0 -49
  71. package/dist/PostgresAdapter-CMiEpHya.js.map +0 -1
  72. package/dist/PostgresClient-BQJZfEOT.js +0 -68
  73. package/dist/PostgresClient-BQJZfEOT.js.map +0 -1
  74. package/dist/SqliteAdapter-A-P9zUhP.js +0 -4
  75. package/dist/SqliteAdapter-CeqhyrPC.js +0 -44
  76. package/dist/SqliteAdapter-CeqhyrPC.js.map +0 -1
  77. package/dist/SqliteClient-CjOK9-ki.js.map +0 -1
  78. package/dist/defaultRuntime-BPK9kWEW.js.map +0 -1
  79. package/dist/manager-C6oJ2tAF.js +0 -13
  80. package/dist/manager-C6oJ2tAF.js.map +0 -1
  81. package/dist/query-C6So1r6H.js.map +0 -1
  82. package/dist/registerModelObjects-BKMpfc4Z.js +0 -263
  83. package/dist/registerModelObjects-BKMpfc4Z.js.map +0 -1
  84. package/dist/runtime-ByXbpVBS.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultRuntime-DzqBQ9Hb.js","names":["engine: AsyncLocalTransactionEngine","state: TransactionState","frame: TransactionFrame","callback: () => void","options: OnCommitOptions","work: (tx: AtomicTransaction) => Promise<T> | T","options: SavepointOptions","runtime: TangoRuntime","work: (tx: AtomicTransaction) => Promise<T> | T","state: TransactionState","options: SavepointOptions","savepointName?: string","error: unknown","callbacks: readonly CallbackRecord[]","cause: unknown","value: unknown","runtime: TangoRuntime","work: (tx: AtomicTransaction) => Promise<T> | T","runtime: TangoRuntime","sql: string","params?: readonly unknown[]","_name: string","config: AdapterConfig","sql: string","params?: readonly unknown[]","config: AdapterConfig","sql: string","params?: readonly unknown[]","filename: string","work: () => Promise<T>","release: (() => void) | null","config: AdapterConfig & { adapter: string }","loadLoadedConfig: () => LoadedConfig","value: unknown","sql: string","params?: readonly unknown[]","dialect: Dialect","defaultRuntime: TangoRuntime | null","fromFile: () => unknown"],"sources":["../src/transaction/internal/context/FrameBoundTransaction.ts","../src/transaction/internal/context/AsyncLocalTransactionEngine.ts","../src/transaction/internal/context/TransactionEngine.ts","../src/manager/internal/RuntimeBoundClient.ts","../src/runtime/internal/PostgresDBClientProvider.ts","../src/runtime/internal/SqliteDBClientProvider.ts","../src/runtime/internal/createDBClientProvider.ts","../src/runtime/TangoRuntime.ts","../src/runtime/defaultRuntime.ts"],"sourcesContent":["import type { AtomicTransaction, OnCommitOptions, SavepointOptions, SavepointResult } from '../../AtomicTransaction';\nimport type { AsyncLocalTransactionEngine } from './AsyncLocalTransactionEngine';\nimport type { TransactionFrame } from './TransactionFrame';\nimport type { TransactionState } from './TransactionState';\n\nexport class FrameBoundTransaction implements AtomicTransaction {\n private active = true;\n\n constructor(\n private readonly engine: AsyncLocalTransactionEngine,\n private readonly state: TransactionState,\n private readonly frame: TransactionFrame\n ) {}\n\n onCommit(callback: () => void, options: OnCommitOptions = {}): void {\n if (!this.active) {\n throw new Error('Cannot register an on-commit callback on an inactive transaction frame.');\n }\n\n this.frame.callbacks.push({\n order: this.state.nextCallbackOrder++,\n callback,\n robust: options.robust ?? false,\n });\n }\n\n savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<SavepointResult<T>>;\n savepoint<T>(\n work: (tx: AtomicTransaction) => Promise<T> | T,\n options: { throwOnError: false }\n ): Promise<SavepointResult<T>>;\n savepoint<T>(work: (tx: AtomicTransaction) => Promise<T> | T, options: { throwOnError: true }): Promise<T>;\n async savepoint<T>(\n work: (tx: AtomicTransaction) => Promise<T> | T,\n options: SavepointOptions = {}\n ): Promise<T | SavepointResult<T>> {\n if (!this.active) {\n throw new Error('Cannot open a savepoint from an inactive transaction frame.');\n }\n\n return this.engine.runSavepoint(this.state, work, options);\n }\n\n deactivate(): void {\n this.active = false;\n }\n}\n","import { AsyncLocalStorage } from 'node:async_hooks';\nimport { getLogger } from '@danceroutine/tango-core';\nimport type { TangoRuntime } from '../../../runtime/TangoRuntime';\nimport type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';\nimport type { AtomicTransaction, SavepointOptions, SavepointResult } from '../../AtomicTransaction';\nimport type { CallbackRecord } from './CallbackRecord';\nimport { FrameBoundTransaction } from './FrameBoundTransaction';\nimport type { TransactionFrame } from './TransactionFrame';\nimport type { TransactionState } from './TransactionState';\n\nexport class AsyncLocalTransactionEngine {\n private readonly logger = getLogger('tango.orm.transaction');\n private readonly storage = new AsyncLocalStorage<TransactionState>();\n\n assertNoActiveAtomicTransaction(): void {\n if (this.storage.getStore()) {\n throw new Error('UnitOfWork is unsupported inside transaction.atomic(...).');\n }\n }\n\n getActiveTransaction(runtime: TangoRuntime): AtomicTransaction | undefined {\n const state = this.storage.getStore();\n if (!state || state.runtime !== runtime) {\n return undefined;\n }\n\n return state.frames.at(-1)?.facade;\n }\n\n getActiveLease(runtime: TangoRuntime): TransactionClientLease | undefined {\n const state = this.storage.getStore();\n if (!state || state.runtime !== runtime) {\n return undefined;\n }\n\n return state.lease;\n }\n\n async atomic<T>(runtime: TangoRuntime, work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T> {\n const existing = this.storage.getStore();\n if (existing) {\n if (existing.runtime !== runtime) {\n throw new Error(\n 'Cannot open a transaction for one Tango runtime while another runtime transaction is active.'\n );\n }\n\n return this.runNested(existing, work);\n }\n\n const lease = await runtime.leaseTransactionClient();\n const state: TransactionState = {\n runtime,\n lease,\n frames: [],\n nextCallbackOrder: 0,\n nextSavepointId: 0,\n };\n\n try {\n return await this.storage.run(state, async () => {\n await lease.client.begin();\n const frame = this.pushFrame(state);\n\n try {\n const result = await work(frame.facade);\n await lease.client.commit();\n const root = this.popFrame(state);\n root.facade.deactivate();\n await this.runCommittedCallbacks(root.callbacks);\n return result;\n } catch (error) {\n await this.rollbackOuter(state, error);\n throw error;\n }\n });\n } finally {\n this.deactivateAllFrames(state);\n await lease.release();\n }\n }\n\n async runSavepoint<T>(\n state: TransactionState,\n work: (tx: AtomicTransaction) => Promise<T> | T,\n options: SavepointOptions\n ): Promise<T | SavepointResult<T>> {\n try {\n const value = await this.runNested(state, work);\n if (options.throwOnError) {\n return value;\n }\n\n return {\n ok: true,\n value,\n };\n } catch (error) {\n if (options.throwOnError) {\n throw error;\n }\n\n return {\n ok: false,\n error,\n };\n }\n }\n\n private async runNested<T>(state: TransactionState, work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T> {\n const savepointName = `tango_sp_${state.nextSavepointId++}`;\n await state.lease.client.createSavepoint(savepointName);\n const frame = this.pushFrame(state, savepointName);\n\n try {\n const result = await work(frame.facade);\n await state.lease.client.releaseSavepoint(savepointName);\n const completed = this.popFrame(state);\n completed.facade.deactivate();\n const parent = state.frames.at(-1);\n if (!parent) {\n throw new Error('Nested transaction frame completed without a parent frame.');\n }\n\n parent.callbacks.push(...completed.callbacks);\n parent.callbacks.sort((left, right) => left.order - right.order);\n return result;\n } catch (error) {\n try {\n await state.lease.client.rollbackToSavepoint(savepointName);\n } catch (rollbackError) {\n throw this.attachCause(rollbackError, error);\n } finally {\n const discarded = this.popFrame(state);\n discarded.facade.deactivate();\n }\n\n throw error;\n }\n }\n\n private pushFrame(state: TransactionState, savepointName?: string): TransactionFrame {\n const frame = {} as TransactionFrame;\n const facade = new FrameBoundTransaction(this, state, frame);\n frame.callbacks = [];\n frame.facade = facade;\n frame.savepointName = savepointName;\n state.frames.push(frame);\n return frame;\n }\n\n private popFrame(state: TransactionState): TransactionFrame {\n const frame = state.frames.pop();\n if (!frame) {\n throw new Error('Transaction frame stack underflow.');\n }\n\n return frame;\n }\n\n private async rollbackOuter(state: TransactionState, error: unknown): Promise<void> {\n try {\n await state.lease.client.rollback();\n } catch (rollbackError) {\n throw this.attachCause(rollbackError, error);\n } finally {\n while (state.frames.length > 0) {\n const frame = this.popFrame(state);\n frame.facade.deactivate();\n }\n }\n }\n\n private async runCommittedCallbacks(callbacks: readonly CallbackRecord[]): Promise<void> {\n for (const record of callbacks) {\n try {\n await record.callback();\n } catch (error) {\n if (!record.robust) {\n throw error;\n }\n\n try {\n this.logger.error('Post-commit callback failed.', error);\n } catch {\n // A logging backend failure cannot change the already-committed outcome.\n }\n }\n }\n }\n\n private deactivateAllFrames(state: TransactionState): void {\n state.frames.length = 0;\n }\n\n private attachCause(error: unknown, cause: unknown): unknown {\n if (!this.isErrorValue(error)) {\n return error;\n }\n\n if ('cause' in error && error.cause !== undefined) {\n return error;\n }\n\n try {\n return new Error(error.message, { cause });\n } catch {\n return error;\n }\n }\n\n private isErrorValue(value: unknown): value is Error {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { message?: unknown }).message === 'string' &&\n typeof (value as { name?: unknown }).name === 'string'\n );\n }\n}\n","import type { TangoRuntime } from '../../../runtime/TangoRuntime';\nimport type { TransactionClientLease } from '../../../runtime/internal/DBClientProvider';\nimport type { AtomicTransaction } from '../../AtomicTransaction';\nimport { AsyncLocalTransactionEngine } from './AsyncLocalTransactionEngine';\n\n/**\n * Runtime-bound transaction facade used by internal ORM/runtime components.\n */\nexport class TransactionEngine {\n private static readonly engine = new AsyncLocalTransactionEngine();\n\n private constructor(private readonly runtime: TangoRuntime) {}\n\n static forRuntime(runtime: TangoRuntime): TransactionEngine {\n return new TransactionEngine(runtime);\n }\n\n static assertNoActiveAtomicTransaction(): void {\n this.engine.assertNoActiveAtomicTransaction();\n }\n\n getActiveTransaction(): AtomicTransaction | undefined {\n return TransactionEngine.engine.getActiveTransaction(this.runtime);\n }\n\n getActiveLease(): TransactionClientLease | undefined {\n return TransactionEngine.engine.getActiveLease(this.runtime);\n }\n\n async atomic<T>(work: (tx: AtomicTransaction) => Promise<T> | T): Promise<T> {\n return TransactionEngine.engine.atomic(this.runtime, work);\n }\n}\n","import type { DBClient } from '../../connection/index';\nimport type { TangoRuntime } from '../../runtime/index';\nimport { TransactionEngine } from '../../transaction/internal/context';\n\n/**\n * DB client proxy that resolves either the active transaction lease or the\n * runtime autocommit path lazily.\n */\nexport class RuntimeBoundClient implements DBClient {\n constructor(private readonly runtime: TangoRuntime) {}\n\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n const lease = TransactionEngine.forRuntime(this.runtime).getActiveLease();\n if (lease) {\n return lease.client.query<T>(sql, params);\n }\n\n const runtimeWithQuery = this.runtime as TangoRuntime & {\n query?: <TResult = unknown>(sql: string, params?: readonly unknown[]) => Promise<{ rows: TResult[] }>;\n };\n if (typeof runtimeWithQuery.query === 'function') {\n return runtimeWithQuery.query<T>(sql, params);\n }\n\n const client = await this.runtime.getClient();\n return client.query<T>(sql, params);\n }\n\n async begin(): Promise<void> {\n throw new Error('Runtime-bound clients do not support manual begin(). Use transaction.atomic(...) instead.');\n }\n\n async commit(): Promise<void> {\n throw new Error('Runtime-bound clients do not support manual commit(). Use transaction.atomic(...) instead.');\n }\n\n async rollback(): Promise<void> {\n throw new Error('Runtime-bound clients do not support manual rollback(). Use transaction.atomic(...) instead.');\n }\n\n async createSavepoint(_name: string): Promise<void> {\n throw new Error(\n 'Runtime-bound clients do not support manual savepoints. Use transaction.atomic(...) or tx.savepoint(...) instead.'\n );\n }\n\n async releaseSavepoint(_name: string): Promise<void> {\n throw new Error(\n 'Runtime-bound clients do not support manual savepoint release. Use transaction.atomic(...) or tx.savepoint(...) instead.'\n );\n }\n\n async rollbackToSavepoint(_name: string): Promise<void> {\n throw new Error(\n 'Runtime-bound clients do not support manual savepoint rollback. Use transaction.atomic(...) or tx.savepoint(...) instead.'\n );\n }\n\n async close(): Promise<void> {\n throw new Error('Runtime-bound clients do not support manual close(). Use TangoRuntime.reset() instead.');\n }\n}\n","import pg from 'pg';\nimport type { AdapterConfig } from '../../connection/adapters/Adapter';\nimport { PostgresClient } from '../../connection/clients/dialects/PostgresClient';\nimport type { DBClientProvider, TransactionClientLease } from './DBClientProvider';\n\nconst { Pool } = pg;\n\nexport class PostgresDBClientProvider implements DBClientProvider {\n private readonly pool: pg.Pool;\n private activeLeaseCount = 0;\n\n constructor(config: AdapterConfig) {\n this.pool = new Pool({\n connectionString: config.url,\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n max: config.maxConnections || 10,\n });\n }\n\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n const result = await this.pool.query(sql, params as unknown[]);\n return { rows: result.rows as T[] };\n }\n\n async leaseTransactionClient(): Promise<TransactionClientLease> {\n const client = await this.pool.connect();\n this.activeLeaseCount += 1;\n let released = false;\n\n return {\n client: new PostgresClient(client),\n release: async () => {\n if (released) {\n return;\n }\n\n released = true;\n this.activeLeaseCount -= 1;\n client.release();\n },\n };\n }\n\n async reset(): Promise<void> {\n if (this.activeLeaseCount > 0) {\n throw new Error('Cannot reset Tango runtime while transaction leases are still active.');\n }\n\n await this.pool.end();\n }\n}\n","import { createRequire } from 'node:module';\nimport type { Database as BetterSqliteDatabase } from 'better-sqlite3';\nimport type { AdapterConfig } from '../../connection/adapters/Adapter';\nimport { SqliteClient } from '../../connection/clients/dialects/SqliteClient';\nimport type { DBClientProvider, TransactionClientLease } from './DBClientProvider';\n\ntype BetterSqliteCtor = new (filename: string, options?: unknown) => BetterSqliteDatabase;\n\nexport class SqliteDBClientProvider implements DBClientProvider {\n private readonly filename: string;\n private readonly Database: BetterSqliteCtor;\n private readonly autocommitClient: SqliteClient;\n private activeLeaseCount = 0;\n private exclusiveTail: Promise<void> = Promise.resolve();\n\n constructor(config: AdapterConfig = {}) {\n this.Database = this.getDatabaseCtor();\n this.filename =\n typeof config.filename === 'string' && config.filename.length > 0 ? config.filename : ':memory:';\n this.autocommitClient = this.openClient(this.filename);\n }\n\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n return this.runExclusive(() => this.autocommitClient.query<T>(sql, params));\n }\n\n async leaseTransactionClient(): Promise<TransactionClientLease> {\n if (this.filename === ':memory:') {\n throw new Error('transaction.atomic(...) requires a file-backed SQLite database. :memory: is unsupported.');\n }\n\n const releaseExclusive = await this.acquireExclusive();\n try {\n const client = this.openClient(this.filename);\n this.activeLeaseCount += 1;\n let released = false;\n\n return {\n client,\n release: async () => {\n if (released) {\n return;\n }\n\n released = true;\n this.activeLeaseCount -= 1;\n try {\n await client.close();\n } finally {\n releaseExclusive();\n }\n },\n };\n } catch (error) {\n releaseExclusive();\n throw error;\n }\n }\n\n async reset(): Promise<void> {\n if (this.activeLeaseCount > 0) {\n throw new Error('Cannot reset Tango runtime while transaction leases are still active.');\n }\n\n await this.autocommitClient.close();\n }\n\n private openClient(filename: string): SqliteClient {\n const db = new this.Database(filename);\n db.pragma('journal_mode = WAL');\n db.pragma('foreign_keys = ON');\n db.pragma('busy_timeout = 5000');\n return new SqliteClient(db);\n }\n\n private async runExclusive<T>(work: () => Promise<T>): Promise<T> {\n const release = await this.acquireExclusive();\n try {\n return await work();\n } finally {\n release();\n }\n }\n\n private async acquireExclusive(): Promise<() => void> {\n const previous = this.exclusiveTail;\n let release: (() => void) | null = null;\n this.exclusiveTail = new Promise<void>((resolve) => {\n release = resolve;\n });\n await previous;\n return () => {\n release?.();\n };\n }\n\n private getDatabaseCtor(): BetterSqliteCtor {\n const require = createRequire(import.meta.url);\n const moduleValue = require('better-sqlite3') as unknown;\n if (typeof moduleValue === 'function') {\n return moduleValue as BetterSqliteCtor;\n }\n\n const defaultExport = (moduleValue as { default?: unknown }).default;\n if (typeof defaultExport === 'function') {\n return defaultExport as BetterSqliteCtor;\n }\n\n throw new TypeError('Failed to load better-sqlite3 constructor.');\n }\n}\n","import type { AdapterConfig } from '../../connection/adapters/Adapter';\nimport type { DBClientProvider } from './DBClientProvider';\nimport { PostgresDBClientProvider } from './PostgresDBClientProvider';\nimport { SqliteDBClientProvider } from './SqliteDBClientProvider';\n\nexport function createDBClientProvider(config: AdapterConfig & { adapter: string }): DBClientProvider {\n switch (config.adapter) {\n case 'postgres':\n return new PostgresDBClientProvider(config);\n case 'sqlite':\n return new SqliteDBClientProvider(config);\n default:\n throw new Error(`Unsupported adapter for Tango runtime provider: ${config.adapter}`);\n }\n}\n","import type { LoadedConfig } from '@danceroutine/tango-config';\nimport type { DBClient } from '../connection/index';\nimport type { Adapter } from '../connection/adapters/Adapter';\nimport { PostgresAdapter } from '../connection/adapters/dialects/PostgresAdapter';\nimport { SqliteAdapter } from '../connection/adapters/dialects/SqliteAdapter';\nimport type { Dialect } from '../query/domain/index';\nimport { InternalDialect } from '../query/domain/internal/InternalDialect';\nimport { RuntimeBoundClient } from '../manager/internal/RuntimeBoundClient';\nimport type { DBClientProvider, TransactionClientLease } from './internal/DBClientProvider';\nimport { createDBClientProvider } from './internal/createDBClientProvider';\n\n/**\n * Framework-owned database runtime that resolves Tango config and lazily\n * creates the shared connection provider used by manager-backed models.\n */\nexport class TangoRuntime {\n static readonly BRAND = 'tango.orm.runtime' as const;\n readonly __tangoBrand: typeof TangoRuntime.BRAND = TangoRuntime.BRAND;\n private readonly loadedConfig: LoadedConfig;\n private providerPromise: Promise<DBClientProvider> | null = null;\n private runtimeClientPromise: Promise<DBClient> | null = null;\n private cachedAdapter: Adapter | null = null;\n\n constructor(loadLoadedConfig: () => LoadedConfig) {\n this.loadedConfig = loadLoadedConfig();\n }\n\n /**\n * Narrow an unknown value to `TangoRuntime`.\n */\n static isTangoRuntime(value: unknown): value is TangoRuntime {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === TangoRuntime.BRAND\n );\n }\n\n /**\n * Return the loaded Tango config snapshot for the active environment.\n */\n getConfig(): LoadedConfig {\n return this.loadedConfig;\n }\n\n /**\n * Return the configured SQL dialect for the current runtime.\n */\n getDialect(): Dialect {\n return this.loadedConfig.current.db.adapter;\n }\n\n /**\n * Return the adapter backing the configured dialect. Manager-side\n * compilers use this to obtain placeholder formatters and dialect\n * capabilities without branching on the raw dialect string.\n */\n getAdapter(): Adapter {\n if (!this.cachedAdapter) {\n this.cachedAdapter = this.buildAdapterForDialect(this.getDialect());\n }\n return this.cachedAdapter;\n }\n\n /**\n * Return the runtime-bound DB client facade used by manager-backed code.\n */\n async getClient(): Promise<DBClient> {\n if (!this.runtimeClientPromise) {\n this.runtimeClientPromise = Promise.resolve(new RuntimeBoundClient(this));\n }\n\n return this.runtimeClientPromise;\n }\n\n /**\n * Execute SQL through the autocommit path owned by this runtime.\n */\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n const provider = await this.getProvider();\n return provider.query<T>(sql, params);\n }\n\n /**\n * Lease a transaction-scoped client for `transaction.atomic(...)`.\n */\n async leaseTransactionClient(): Promise<TransactionClientLease> {\n const provider = await this.getProvider();\n return provider.leaseTransactionClient();\n }\n\n /**\n * Close and clear the cached runtime resources so tests can start fresh.\n */\n async reset(): Promise<void> {\n if (!this.providerPromise) {\n this.runtimeClientPromise = null;\n return;\n }\n\n const provider = await this.providerPromise;\n this.providerPromise = null;\n this.runtimeClientPromise = null;\n await provider.reset();\n }\n\n private buildAdapterForDialect(dialect: Dialect): Adapter {\n switch (dialect) {\n case InternalDialect.POSTGRES:\n return new PostgresAdapter();\n case InternalDialect.SQLITE:\n return new SqliteAdapter();\n }\n }\n\n private async getProvider(): Promise<DBClientProvider> {\n if (!this.providerPromise) {\n const db = this.loadedConfig.current.db;\n this.providerPromise = Promise.resolve(\n createDBClientProvider({\n adapter: db.adapter,\n url: db.url,\n host: db.host,\n port: db.port,\n database: db.database,\n user: db.user,\n password: db.password,\n filename: db.filename,\n maxConnections: db.maxConnections,\n })\n );\n }\n\n return this.providerPromise;\n }\n}\n","import { loadConfig, loadConfigFromProjectRoot } from '@danceroutine/tango-config';\nimport { TangoRuntime } from './TangoRuntime';\n\nlet defaultRuntime: TangoRuntime | null = null;\n\n/**\n * Initialize the process-default Tango runtime from a Tango config loader.\n */\nexport function initializeTangoRuntime(fromFile: () => unknown): TangoRuntime {\n defaultRuntime = new TangoRuntime(() => loadConfig(fromFile));\n return defaultRuntime;\n}\n\n/**\n * Return the process-default Tango runtime, lazily loading Tango config on first access.\n */\nexport function getTangoRuntime(): TangoRuntime {\n if (!defaultRuntime) {\n defaultRuntime = new TangoRuntime(() => loadConfigFromProjectRoot());\n }\n\n return defaultRuntime;\n}\n\n/**\n * Reset the process-default Tango runtime and release any cached client.\n */\nexport async function resetTangoRuntime(): Promise<void> {\n if (!defaultRuntime) {\n return;\n }\n\n const runtime = defaultRuntime;\n defaultRuntime = null;\n await runtime.reset();\n}\n"],"mappings":";;;;;;;;;;IAKa,wBAAN,MAAyD;CAC5D,SAAiB;CAEjB,YACqBA,QACAC,OACAC,OACnB;AAAA,OAHmB,SAAA;AAAA,OACA,QAAA;AAAA,OACA,QAAA;CACjB;CAEJ,SAASC,UAAsBC,UAA2B,CAAE,GAAQ;AAChE,OAAK,KAAK,OACN,OAAM,IAAI,MAAM;AAGpB,OAAK,MAAM,UAAU,KAAK;GACtB,OAAO,KAAK,MAAM;GAClB;GACA,QAAQ,QAAQ,UAAU;EAC7B,EAAC;CACL;CAQD,MAAM,UACFC,MACAC,UAA4B,CAAE,GACC;AAC/B,OAAK,KAAK,OACN,OAAM,IAAI,MAAM;AAGpB,SAAO,KAAK,OAAO,aAAa,KAAK,OAAO,MAAM,QAAQ;CAC7D;CAED,aAAmB;AACf,OAAK,SAAS;CACjB;AACJ;;;;ICpCY,8BAAN,MAAkC;CACrC,SAA0B,UAAU,wBAAwB;CAC5D,UAA2B,IAAI;CAE/B,kCAAwC;AACpC,MAAI,KAAK,QAAQ,UAAU,CACvB,OAAM,IAAI,MAAM;CAEvB;CAED,qBAAqBC,SAAsD;EACvE,MAAM,QAAQ,KAAK,QAAQ,UAAU;AACrC,OAAK,SAAS,MAAM,YAAY,QAC5B,QAAO;AAGX,SAAO,MAAM,OAAO,GAAA,GAAM,EAAE;CAC/B;CAED,eAAeA,SAA2D;EACtE,MAAM,QAAQ,KAAK,QAAQ,UAAU;AACrC,OAAK,SAAS,MAAM,YAAY,QAC5B,QAAO;AAGX,SAAO,MAAM;CAChB;CAED,MAAM,OAAUA,SAAuBC,MAA6D;EAChG,MAAM,WAAW,KAAK,QAAQ,UAAU;AACxC,MAAI,UAAU;AACV,OAAI,SAAS,YAAY,QACrB,OAAM,IAAI,MACN;AAIR,UAAO,KAAK,UAAU,UAAU,KAAK;EACxC;EAED,MAAM,QAAQ,MAAM,QAAQ,wBAAwB;EACpD,MAAMC,QAA0B;GAC5B;GACA;GACA,QAAQ,CAAE;GACV,mBAAmB;GACnB,iBAAiB;EACpB;AAED,MAAI;AACA,UAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,YAAY;AAC7C,UAAM,MAAM,OAAO,OAAO;IAC1B,MAAM,QAAQ,KAAK,UAAU,MAAM;AAEnC,QAAI;KACA,MAAM,SAAS,MAAM,KAAK,MAAM,OAAO;AACvC,WAAM,MAAM,OAAO,QAAQ;KAC3B,MAAM,OAAO,KAAK,SAAS,MAAM;AACjC,UAAK,OAAO,YAAY;AACxB,WAAM,KAAK,sBAAsB,KAAK,UAAU;AAChD,YAAO;IACV,SAAQ,OAAO;AACZ,WAAM,KAAK,cAAc,OAAO,MAAM;AACtC,WAAM;IACT;GACJ,EAAC;EACL,UAAS;AACN,QAAK,oBAAoB,MAAM;AAC/B,SAAM,MAAM,SAAS;EACxB;CACJ;CAED,MAAM,aACFA,OACAD,MACAE,SAC+B;AAC/B,MAAI;GACA,MAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,KAAK;AAC/C,OAAI,QAAQ,aACR,QAAO;AAGX,UAAO;IACH,IAAI;IACJ;GACH;EACJ,SAAQ,OAAO;AACZ,OAAI,QAAQ,aACR,OAAM;AAGV,UAAO;IACH,IAAI;IACJ;GACH;EACJ;CACJ;CAED,MAAc,UAAaD,OAAyBD,MAA6D;EAC7G,MAAM,iBAAiB,WAAW,MAAM,kBAAkB;AAC1D,QAAM,MAAM,MAAM,OAAO,gBAAgB,cAAc;EACvD,MAAM,QAAQ,KAAK,UAAU,OAAO,cAAc;AAElD,MAAI;GACA,MAAM,SAAS,MAAM,KAAK,MAAM,OAAO;AACvC,SAAM,MAAM,MAAM,OAAO,iBAAiB,cAAc;GACxD,MAAM,YAAY,KAAK,SAAS,MAAM;AACtC,aAAU,OAAO,YAAY;GAC7B,MAAM,SAAS,MAAM,OAAO,GAAA,GAAM;AAClC,QAAK,OACD,OAAM,IAAI,MAAM;AAGpB,UAAO,UAAU,KAAK,GAAG,UAAU,UAAU;AAC7C,UAAO,UAAU,KAAK,CAAC,MAAM,UAAU,KAAK,QAAQ,MAAM,MAAM;AAChE,UAAO;EACV,SAAQ,OAAO;AACZ,OAAI;AACA,UAAM,MAAM,MAAM,OAAO,oBAAoB,cAAc;GAC9D,SAAQ,eAAe;AACpB,UAAM,KAAK,YAAY,eAAe,MAAM;GAC/C,UAAS;IACN,MAAM,YAAY,KAAK,SAAS,MAAM;AACtC,cAAU,OAAO,YAAY;GAChC;AAED,SAAM;EACT;CACJ;CAED,UAAkBC,OAAyBE,eAA0C;EACjF,MAAM,QAAQ,CAAE;EAChB,MAAM,SAAS,IAAI,sBAAsB,MAAM,OAAO;AACtD,QAAM,YAAY,CAAE;AACpB,QAAM,SAAS;AACf,QAAM,gBAAgB;AACtB,QAAM,OAAO,KAAK,MAAM;AACxB,SAAO;CACV;CAED,SAAiBF,OAA2C;EACxD,MAAM,QAAQ,MAAM,OAAO,KAAK;AAChC,OAAK,MACD,OAAM,IAAI,MAAM;AAGpB,SAAO;CACV;CAED,MAAc,cAAcA,OAAyBG,OAA+B;AAChF,MAAI;AACA,SAAM,MAAM,MAAM,OAAO,UAAU;EACtC,SAAQ,eAAe;AACpB,SAAM,KAAK,YAAY,eAAe,MAAM;EAC/C,UAAS;AACN,UAAO,MAAM,OAAO,SAAS,GAAG;IAC5B,MAAM,QAAQ,KAAK,SAAS,MAAM;AAClC,UAAM,OAAO,YAAY;GAC5B;EACJ;CACJ;CAED,MAAc,sBAAsBC,WAAqD;AACrF,OAAK,MAAM,UAAU,UACjB,KAAI;AACA,SAAM,OAAO,UAAU;EAC1B,SAAQ,OAAO;AACZ,QAAK,OAAO,OACR,OAAM;AAGV,OAAI;AACA,SAAK,OAAO,MAAM,gCAAgC,MAAM;GAC3D,QAAO,CAEP;EACJ;CAER;CAED,oBAA4BJ,OAA+B;AACvD,QAAM,OAAO,SAAS;CACzB;CAED,YAAoBG,OAAgBE,OAAyB;AACzD,OAAK,KAAK,aAAa,MAAM,CACzB,QAAO;AAGX,MAAI,WAAW,SAAS,MAAM,UAAU,UACpC,QAAO;AAGX,MAAI;AACA,UAAO,IAAI,MAAM,MAAM,SAAS,EAAE,MAAO;EAC5C,QAAO;AACJ,UAAO;EACV;CACJ;CAED,aAAqBC,OAAgC;AACjD,gBACW,UAAU,YACjB,UAAU,eACF,MAAgC,YAAY,mBAC5C,MAA6B,SAAS;CAErD;AACJ;;;;ICnNY,oBAAN,MAAM,kBAAkB;CAC3B,OAAwB,SAAS,IAAI;CAErC,YAAqCC,SAAuB;AAAA,OAAvB,UAAA;CAAyB;CAE9D,OAAO,WAAWA,SAA0C;AACxD,SAAO,IAAI,kBAAkB;CAChC;CAED,OAAO,kCAAwC;AAC3C,OAAK,OAAO,iCAAiC;CAChD;CAED,uBAAsD;AAClD,SAAO,kBAAkB,OAAO,qBAAqB,KAAK,QAAQ;CACrE;CAED,iBAAqD;AACjD,SAAO,kBAAkB,OAAO,eAAe,KAAK,QAAQ;CAC/D;CAED,MAAM,OAAUC,MAA6D;AACzE,SAAO,kBAAkB,OAAO,OAAO,KAAK,SAAS,KAAK;CAC7D;AACJ;;;;ICxBY,qBAAN,MAA6C;CAChD,YAA6BC,SAAuB;AAAA,OAAvB,UAAA;CAAyB;CAEtD,MAAM,MAAmBC,KAAaC,QAAqD;EACvF,MAAM,QAAQ,kBAAkB,WAAW,KAAK,QAAQ,CAAC,gBAAgB;AACzE,MAAI,MACA,QAAO,MAAM,OAAO,MAAS,KAAK,OAAO;EAG7C,MAAM,mBAAmB,KAAK;AAG9B,aAAW,iBAAiB,UAAU,WAClC,QAAO,iBAAiB,MAAS,KAAK,OAAO;EAGjD,MAAM,SAAS,MAAM,KAAK,QAAQ,WAAW;AAC7C,SAAO,OAAO,MAAS,KAAK,OAAO;CACtC;CAED,MAAM,QAAuB;AACzB,QAAM,IAAI,MAAM;CACnB;CAED,MAAM,SAAwB;AAC1B,QAAM,IAAI,MAAM;CACnB;CAED,MAAM,WAA0B;AAC5B,QAAM,IAAI,MAAM;CACnB;CAED,MAAM,gBAAgBC,OAA8B;AAChD,QAAM,IAAI,MACN;CAEP;CAED,MAAM,iBAAiBA,OAA8B;AACjD,QAAM,IAAI,MACN;CAEP;CAED,MAAM,oBAAoBA,OAA8B;AACpD,QAAM,IAAI,MACN;CAEP;CAED,MAAM,QAAuB;AACzB,QAAM,IAAI,MAAM;CACnB;AACJ;;;;ACxDD,MAAM,EAAE,MAAM,GAAG;IAEJ,2BAAN,MAA2D;CAC9D;CACA,mBAA2B;CAE3B,YAAYC,QAAuB;AAC/B,OAAK,OAAO,IAAI,KAAK;GACjB,kBAAkB,OAAO;GACzB,MAAM,OAAO;GACb,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,MAAM,OAAO;GACb,UAAU,OAAO;GACjB,KAAK,OAAO,kBAAkB;EACjC;CACJ;CAED,MAAM,MAAmBC,KAAaC,QAAqD;EACvF,MAAM,SAAS,MAAM,KAAK,KAAK,MAAM,KAAK,OAAoB;AAC9D,SAAO,EAAE,MAAM,OAAO,KAAa;CACtC;CAED,MAAM,yBAA0D;EAC5D,MAAM,SAAS,MAAM,KAAK,KAAK,SAAS;AACxC,OAAK,oBAAoB;EACzB,IAAI,WAAW;AAEf,SAAO;GACH,QAAQ,IAAI,eAAe;GAC3B,SAAS,YAAY;AACjB,QAAI,SACA;AAGJ,eAAW;AACX,SAAK,oBAAoB;AACzB,WAAO,SAAS;GACnB;EACJ;CACJ;CAED,MAAM,QAAuB;AACzB,MAAI,KAAK,mBAAmB,EACxB,OAAM,IAAI,MAAM;AAGpB,QAAM,KAAK,KAAK,KAAK;CACxB;AACJ;;;;IC9CY,yBAAN,MAAyD;CAC5D;CACA;CACA;CACA,mBAA2B;CAC3B,gBAAuC,QAAQ,SAAS;CAExD,YAAYC,SAAwB,CAAE,GAAE;AACpC,OAAK,WAAW,KAAK,iBAAiB;AACtC,OAAK,kBACM,OAAO,aAAa,YAAY,OAAO,SAAS,SAAS,IAAI,OAAO,WAAW;AAC1F,OAAK,mBAAmB,KAAK,WAAW,KAAK,SAAS;CACzD;CAED,MAAM,MAAmBC,KAAaC,QAAqD;AACvF,SAAO,KAAK,aAAa,MAAM,KAAK,iBAAiB,MAAS,KAAK,OAAO,CAAC;CAC9E;CAED,MAAM,yBAA0D;AAC5D,MAAI,KAAK,aAAa,WAClB,OAAM,IAAI,MAAM;EAGpB,MAAM,mBAAmB,MAAM,KAAK,kBAAkB;AACtD,MAAI;GACA,MAAM,SAAS,KAAK,WAAW,KAAK,SAAS;AAC7C,QAAK,oBAAoB;GACzB,IAAI,WAAW;AAEf,UAAO;IACH;IACA,SAAS,YAAY;AACjB,SAAI,SACA;AAGJ,gBAAW;AACX,UAAK,oBAAoB;AACzB,SAAI;AACA,YAAM,OAAO,OAAO;KACvB,UAAS;AACN,wBAAkB;KACrB;IACJ;GACJ;EACJ,SAAQ,OAAO;AACZ,qBAAkB;AAClB,SAAM;EACT;CACJ;CAED,MAAM,QAAuB;AACzB,MAAI,KAAK,mBAAmB,EACxB,OAAM,IAAI,MAAM;AAGpB,QAAM,KAAK,iBAAiB,OAAO;CACtC;CAED,WAAmBC,UAAgC;EAC/C,MAAM,KAAK,IAAI,KAAK,SAAS;AAC7B,KAAG,OAAO,qBAAqB;AAC/B,KAAG,OAAO,oBAAoB;AAC9B,KAAG,OAAO,sBAAsB;AAChC,SAAO,IAAI,aAAa;CAC3B;CAED,MAAc,aAAgBC,MAAoC;EAC9D,MAAM,UAAU,MAAM,KAAK,kBAAkB;AAC7C,MAAI;AACA,UAAO,MAAM,MAAM;EACtB,UAAS;AACN,YAAS;EACZ;CACJ;CAED,MAAc,mBAAwC;EAClD,MAAM,WAAW,KAAK;EACtB,IAAIC,UAA+B;AACnC,OAAK,gBAAgB,IAAI,QAAc,CAAC,YAAY;AAChD,aAAU;EACb;AACD,QAAM;AACN,SAAO,MAAM;AACT,cAAW;EACd;CACJ;CAED,kBAA4C;EACxC,MAAM,UAAU,cAAc,OAAO,KAAK,IAAI;EAC9C,MAAM,cAAc,QAAQ,iBAAiB;AAC7C,aAAW,gBAAgB,WACvB,QAAO;EAGX,MAAM,gBAAiB,YAAsC;AAC7D,aAAW,kBAAkB,WACzB,QAAO;AAGX,QAAM,IAAI,UAAU;CACvB;AACJ;;;;ACzGM,SAAS,uBAAuBC,QAA+D;AAClG,SAAQ,OAAO,SAAf;AACI,OAAK,WACD,QAAO,IAAI,yBAAyB;AACxC,OAAK,SACD,QAAO,IAAI,uBAAuB;AACtC,UACI,OAAM,IAAI,OAAO,kDAAkD,OAAO,QAAQ;CACzF;AACJ;;;;ICCY,eAAN,MAAM,aAAa;CACtB,OAAgB,QAAQ;CACxB,eAAmD,aAAa;CAChE;CACA,kBAA4D;CAC5D,uBAAyD;CACzD,gBAAwC;CAExC,YAAYC,kBAAsC;AAC9C,OAAK,eAAe,kBAAkB;CACzC;;;;CAKD,OAAO,eAAeC,OAAuC;AACzD,gBACW,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,aAAa;CAE3E;;;;CAKD,YAA0B;AACtB,SAAO,KAAK;CACf;;;;CAKD,aAAsB;AAClB,SAAO,KAAK,aAAa,QAAQ,GAAG;CACvC;;;;;;CAOD,aAAsB;AAClB,OAAK,KAAK,cACN,MAAK,gBAAgB,KAAK,uBAAuB,KAAK,YAAY,CAAC;AAEvE,SAAO,KAAK;CACf;;;;CAKD,MAAM,YAA+B;AACjC,OAAK,KAAK,qBACN,MAAK,uBAAuB,QAAQ,QAAQ,IAAI,mBAAmB,MAAM;AAG7E,SAAO,KAAK;CACf;;;;CAKD,MAAM,MAAmBC,KAAaC,QAAqD;EACvF,MAAM,WAAW,MAAM,KAAK,aAAa;AACzC,SAAO,SAAS,MAAS,KAAK,OAAO;CACxC;;;;CAKD,MAAM,yBAA0D;EAC5D,MAAM,WAAW,MAAM,KAAK,aAAa;AACzC,SAAO,SAAS,wBAAwB;CAC3C;;;;CAKD,MAAM,QAAuB;AACzB,OAAK,KAAK,iBAAiB;AACvB,QAAK,uBAAuB;AAC5B;EACH;EAED,MAAM,WAAW,MAAM,KAAK;AAC5B,OAAK,kBAAkB;AACvB,OAAK,uBAAuB;AAC5B,QAAM,SAAS,OAAO;CACzB;CAED,uBAA+BC,SAA2B;AACtD,UAAQ,SAAR;AACI,QAAK,gBAAgB,SACjB,QAAO,IAAI;AACf,QAAK,gBAAgB,OACjB,QAAO,IAAI;EAClB;CACJ;CAED,MAAc,cAAyC;AACnD,OAAK,KAAK,iBAAiB;GACvB,MAAM,KAAK,KAAK,aAAa,QAAQ;AACrC,QAAK,kBAAkB,QAAQ,QAC3B,uBAAuB;IACnB,SAAS,GAAG;IACZ,KAAK,GAAG;IACR,MAAM,GAAG;IACT,MAAM,GAAG;IACT,UAAU,GAAG;IACb,MAAM,GAAG;IACT,UAAU,GAAG;IACb,UAAU,GAAG;IACb,gBAAgB,GAAG;GACtB,EAAC,CACL;EACJ;AAED,SAAO,KAAK;CACf;AACJ;;;;ACpID,IAAIC,iBAAsC;AAKnC,SAAS,uBAAuBC,UAAuC;AAC1E,kBAAiB,IAAI,aAAa,MAAM,WAAW,SAAS;AAC5D,QAAO;AACV;AAKM,SAAS,kBAAgC;AAC5C,MAAK,eACD,kBAAiB,IAAI,aAAa,MAAM,2BAA2B;AAGvE,QAAO;AACV;AAKM,eAAe,oBAAmC;AACrD,MAAK,eACD;CAGJ,MAAM,UAAU;AAChB,kBAAiB;AACjB,OAAM,QAAQ,OAAO;AACxB"}
package/dist/index.d.ts CHANGED
@@ -12,9 +12,9 @@ export * as transaction from './transaction/index';
12
12
  export { AdapterRegistry, connectDB, getDefaultAdapterRegistry } from './connection/index';
13
13
  export type { Adapter, AdapterConfig, DBClient } from './connection/index';
14
14
  export { PostgresAdapter, SqliteAdapter } from './connection/index';
15
- export { ModelManager } from './manager/index';
16
- export type { ManagerLike } from './manager/index';
17
- export { Q, QBuilder, QueryCompiler, QueryResult, QuerySet } from './query/index';
15
+ export { ManyToManyRelatedManager, ModelManager } from './manager/index';
16
+ export type { ManagerLike, ManyToManyTargetRef, MaterializedModelRecord } from './manager/index';
17
+ export { Q, QBuilder, QueryCompiler, QueryResult, QuerySet, ModelQuerySet } from './query/index';
18
18
  export type { QueryExecutor } from './query/index';
19
19
  export type { CompiledQuery, Dialect, Direction, FilterInput, FilterKey, FilterValue, LookupType, OrderSpec, OrderToken, QNode, QuerySetState, RelationMeta, TableMeta, WhereClause, } from './query/domain/index';
20
20
  export { getTangoRuntime, initializeTangoRuntime, resetTangoRuntime, TangoRuntime } from './runtime/index';
package/dist/index.js CHANGED
@@ -1,13 +1,12 @@
1
- import "./PostgresClient-BQJZfEOT.js";
2
- import { PostgresAdapter } from "./PostgresAdapter-CMiEpHya.js";
3
- import "./SqliteClient-CjOK9-ki.js";
4
- import { SqliteAdapter } from "./SqliteAdapter-CeqhyrPC.js";
5
- import { AdapterRegistry, connectDB, connection_exports, getDefaultAdapterRegistry } from "./connection-B_K2ZAf7.js";
6
- import { QBuilder, QueryCompiler, QueryResult, QuerySet, query_exports } from "./query-C6So1r6H.js";
7
- import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "./defaultRuntime-BPK9kWEW.js";
8
- import { ModelManager } from "./registerModelObjects-BKMpfc4Z.js";
9
- import { manager_exports } from "./manager-C6oJ2tAF.js";
10
- import { runtime_exports } from "./runtime-ByXbpVBS.js";
11
- import { UnitOfWork, atomic, transaction_exports } from "./transaction-Cs0Z9tbW.js";
1
+ import { PostgresAdapter } from "./PostgresAdapter-DySFW6vy.js";
2
+ import "./InternalDialect-ClSaUNso.js";
3
+ import { SqliteAdapter } from "./SqliteAdapter-CDdOjRmW.js";
4
+ import { AdapterRegistry, connectDB, connection_exports, getDefaultAdapterRegistry } from "./connection-Dmhgx31M.js";
5
+ import { ModelQuerySet, QBuilder, QueryCompiler, QueryResult, QuerySet, query_exports } from "./query-DUZnBFhf.js";
6
+ import { ManyToManyRelatedManager, ModelManager } from "./registerModelObjects-DxlBfuUN.js";
7
+ import { manager_exports } from "./manager-DrDTiCAz.js";
8
+ import { TangoRuntime, getTangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "./defaultRuntime-DzqBQ9Hb.js";
9
+ import { runtime_exports } from "./runtime-1H88J3nN.js";
10
+ import { UnitOfWork, atomic, transaction_exports } from "./transaction-ZhfDf-f8.js";
12
11
 
13
- export { AdapterRegistry, ModelManager, PostgresAdapter, QBuilder as Q, QBuilder, QueryCompiler, QueryResult, QuerySet, SqliteAdapter, TangoRuntime, UnitOfWork, atomic, connectDB, connection_exports as connection, getDefaultAdapterRegistry, getTangoRuntime, initializeTangoRuntime, manager_exports as manager, query_exports as query, resetTangoRuntime, runtime_exports as runtime, transaction_exports as transaction };
12
+ export { AdapterRegistry, ManyToManyRelatedManager, ModelManager, ModelQuerySet, PostgresAdapter, QBuilder as Q, QBuilder, QueryCompiler, QueryResult, QuerySet, SqliteAdapter, TangoRuntime, UnitOfWork, atomic, connectDB, connection_exports as connection, getDefaultAdapterRegistry, getTangoRuntime, initializeTangoRuntime, manager_exports as manager, query_exports as query, resetTangoRuntime, runtime_exports as runtime, transaction_exports as transaction };
@@ -1,3 +1,5 @@
1
+ import type { QNode } from '../query/domain/QNode';
2
+ import type { FilterInput } from '../query/domain/FilterInput';
1
3
  import type { QuerySet } from '../query/index';
2
4
  import type { TableMeta } from '../query/domain/index';
3
5
  /**
@@ -6,6 +8,23 @@ import type { TableMeta } from '../query/domain/index';
6
8
  export interface ManagerLike<TModelRow extends Record<string, unknown>, TSourceModel = unknown> {
7
9
  readonly meta: TableMeta;
8
10
  query(): QuerySet<TModelRow, TModelRow, TSourceModel>;
11
+ all(): QuerySet<TModelRow, TModelRow, TSourceModel>;
12
+ getOrCreate(args: {
13
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
14
+ defaults?: Partial<TModelRow>;
15
+ }): Promise<{
16
+ record: TModelRow;
17
+ created: boolean;
18
+ }>;
19
+ updateOrCreate(args: {
20
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
21
+ defaults?: Partial<TModelRow>;
22
+ update?: Partial<TModelRow>;
23
+ }): Promise<{
24
+ record: TModelRow;
25
+ created: boolean;
26
+ updated: boolean;
27
+ }>;
9
28
  findById(id: TModelRow[keyof TModelRow]): Promise<TModelRow | null>;
10
29
  getOrThrow(id: TModelRow[keyof TModelRow]): Promise<TModelRow>;
11
30
  create(input: Partial<TModelRow>): Promise<TModelRow>;
@@ -1,9 +1,11 @@
1
1
  import type { ModelWriteHooks } from '@danceroutine/tango-schema';
2
2
  import type { Model as SchemaModel } from '@danceroutine/tango-schema/domain';
3
- import type { TableMeta } from '../query/domain/index';
3
+ import type { QNode } from '../query/domain/QNode';
4
+ import type { FilterInput, TableMeta } from '../query/domain/index';
4
5
  import type { QuerySet } from '../query/index';
5
6
  import type { TangoRuntime } from '../runtime/TangoRuntime';
6
7
  import type { ManagerLike } from './ManagerLike';
8
+ import { ManyToManyRelatedManager } from './relations/ManyToManyRelatedManager';
7
9
  type ModelMetadataLike = Omit<SchemaModel['metadata'], 'key' | 'namespace' | 'fields'> & {
8
10
  key?: string;
9
11
  namespace?: string;
@@ -30,7 +32,7 @@ export declare class ModelManager<TModelRow extends Record<string, unknown>, TSo
30
32
  private readonly mutationCompiler;
31
33
  private readonly model;
32
34
  private readonly client;
33
- private readonly dialect;
35
+ private readonly adapter;
34
36
  private readonly runtime;
35
37
  constructor(model: ModelLike<TModelRow>, runtime: TangoRuntime);
36
38
  get meta(): TableMeta;
@@ -39,13 +41,54 @@ export declare class ModelManager<TModelRow extends Record<string, unknown>, TSo
39
41
  */
40
42
  static isModelManager<TModelRow extends Record<string, unknown>>(value: unknown): value is ModelManager<TModelRow>;
41
43
  private static createTableMeta;
44
+ private static mergeCreatePayloadFromWhere;
45
+ private static countDefinedValues;
46
+ private static collectPlainFieldsFromQNode;
47
+ private static omitLookupKeysFromAtom;
48
+ private static mergeCompatiblePartials;
42
49
  query(): QuerySet<TModelRow, TModelRow, TSourceModel>;
50
+ all(): QuerySet<TModelRow, TModelRow, TSourceModel>;
43
51
  findById(id: TModelRow[keyof TModelRow]): Promise<TModelRow | null>;
44
52
  getOrThrow(id: TModelRow[keyof TModelRow]): Promise<TModelRow>;
53
+ getOrCreate(args: {
54
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
55
+ defaults?: Partial<TModelRow>;
56
+ }): Promise<{
57
+ record: TModelRow;
58
+ created: boolean;
59
+ }>;
60
+ updateOrCreate(args: {
61
+ where: FilterInput<TModelRow> | QNode<TModelRow>;
62
+ defaults?: Partial<TModelRow>;
63
+ update?: Partial<TModelRow>;
64
+ }): Promise<{
65
+ record: TModelRow;
66
+ created: boolean;
67
+ updated: boolean;
68
+ }>;
45
69
  create(input: Partial<TModelRow>): Promise<TModelRow>;
46
70
  update(id: TModelRow[keyof TModelRow], patch: Partial<TModelRow>): Promise<TModelRow>;
47
71
  delete(id: TModelRow[keyof TModelRow]): Promise<void>;
72
+ /**
73
+ * Build a {@link ManyToManyRelatedManager} bound to a single owner record
74
+ * for the supplied many-to-many relation. The returned manager performs
75
+ * its INSERT/DELETE writes through the shared runtime-bound client, so
76
+ * mutations enroll in any active `transaction.atomic(...)` boundary.
77
+ */
78
+ createManyToManyRelatedManager<TTarget extends Record<string, unknown>>(relationName: string, ownerPrimaryKey: unknown): ManyToManyRelatedManager<TTarget>;
48
79
  bulkCreate(inputs: Partial<TModelRow>[]): Promise<TModelRow[]>;
80
+ /**
81
+ * Attach a {@link ManyToManyRelatedManager} as a non-enumerable property
82
+ * for every persisted many-to-many relation declared on the supplied
83
+ * record's model. Existing properties are left untouched so that prior
84
+ * hydration writes (such as prefetched arrays) survive the attach pass
85
+ * during the incremental rollout of related-manager hydration.
86
+ */
87
+ private attachOwnRelatedManagers;
88
+ private attachManyToManyRelatedManagers;
89
+ private resolveManagerForModelKey;
90
+ private resolveTargetExecutor;
91
+ private requireManyToManyEdge;
49
92
  private runBeforeCreate;
50
93
  private runBeforeUpdate;
51
94
  private getHookTransaction;
@@ -1,6 +1,12 @@
1
1
  /**
2
2
  * Domain boundary barrel: centralizes manager-first ORM APIs.
3
3
  */
4
+ export * as relations from './relations/index';
4
5
  export type { ManagerLike } from './ManagerLike';
5
6
  export { ModelManager } from './ModelManager';
6
7
  export { registerModelObjects } from './registerModelObjects';
8
+ export { ManyToManyRelatedQuerySet } from './relations/ManyToManyRelatedQuerySet';
9
+ export type { ManyToManyRelatedQuerySetBridge } from './relations/ManyToManyRelatedQuerySet';
10
+ export { ManyToManyRelatedManager } from './relations/ManyToManyRelatedManager';
11
+ export type { ManyToManyRelatedManagerCreateInputs, ManyToManyTargetRef } from './relations/ManyToManyRelatedManager';
12
+ export type { MaterializedModelRecord } from './relations/MaterializedModelRecord';
@@ -1,8 +1,9 @@
1
- import "../PostgresClient-BQJZfEOT.js";
2
- import "../SqliteClient-CjOK9-ki.js";
3
- import "../query-C6So1r6H.js";
4
- import "../defaultRuntime-BPK9kWEW.js";
5
- import { ModelManager, registerModelObjects } from "../registerModelObjects-BKMpfc4Z.js";
6
- import "../manager-C6oJ2tAF.js";
1
+ import "../PostgresAdapter-DySFW6vy.js";
2
+ import "../InternalDialect-ClSaUNso.js";
3
+ import "../SqliteAdapter-CDdOjRmW.js";
4
+ import "../query-DUZnBFhf.js";
5
+ import { ManyToManyRelatedManager, ManyToManyRelatedQuerySet, ModelManager, registerModelObjects } from "../registerModelObjects-DxlBfuUN.js";
6
+ import { relations_exports } from "../manager-DrDTiCAz.js";
7
+ import "../defaultRuntime-DzqBQ9Hb.js";
7
8
 
8
- export { ModelManager, registerModelObjects };
9
+ export { ManyToManyRelatedManager, ManyToManyRelatedQuerySet, ModelManager, registerModelObjects, relations_exports as relations };
@@ -1,15 +1,23 @@
1
- import type { CompiledQuery, Dialect } from '../../query/domain/index';
2
- import type { ValidatedDeleteSqlPlan, ValidatedInsertSqlPlan, ValidatedUpdateSqlPlan } from '../../validation/SQLValidationEngine';
1
+ import type { CompiledQuery } from '../../query/domain/index';
2
+ import type { Adapter } from '../../connection/adapters/Adapter';
3
+ import type { ValidatedDeleteSqlPlan, ValidatedInsertSqlPlan, ValidatedSelectSqlPlan, ValidatedUpdateSqlPlan } from '../../validation/SQLValidationEngine';
4
+ export declare const InternalDuplicateInsertPolicy: {
5
+ readonly ERROR: "error";
6
+ readonly IGNORE: "ignore";
7
+ };
8
+ export type DuplicateInsertPolicy = (typeof InternalDuplicateInsertPolicy)[keyof typeof InternalDuplicateInsertPolicy];
3
9
  /**
4
10
  * Internal compiler for manager-owned INSERT/UPDATE/DELETE statements.
5
11
  */
6
12
  export declare class MutationCompiler {
7
- private readonly dialect;
8
- constructor(dialect: Dialect);
13
+ private readonly adapter;
14
+ private readonly placeholders;
15
+ constructor(adapter: Adapter);
9
16
  compileInsert(plan: ValidatedInsertSqlPlan, values: readonly unknown[]): CompiledQuery;
10
17
  compileUpdate(plan: ValidatedUpdateSqlPlan, values: readonly unknown[], id: unknown): CompiledQuery;
11
18
  compileDelete(plan: ValidatedDeleteSqlPlan, id: unknown): CompiledQuery;
19
+ compileDeleteByJoinKeys(plan: ValidatedSelectSqlPlan, leftFilterKey: string, rightFilterKey: string, leftValue: unknown, rightValue: unknown): CompiledQuery;
12
20
  compileBulkInsert(plan: ValidatedInsertSqlPlan, valueRows: ReadonlyArray<ReadonlyArray<unknown>>): CompiledQuery;
13
- private buildValuePlaceholders;
14
- private placeholder;
21
+ compileInsertJoinLinks(plan: ValidatedInsertSqlPlan, sourceKey: string, targetKey: string, ownerValue: unknown, targetValues: readonly unknown[], duplicatePolicy: DuplicateInsertPolicy): CompiledQuery;
22
+ compileDeleteJoinLinks(plan: ValidatedSelectSqlPlan, leftFilterKey: string, rightFilterKey: string, leftValue: unknown, rightValues: readonly unknown[]): CompiledQuery;
15
23
  }
@@ -0,0 +1,147 @@
1
+ import type { DBClient } from '../../connection/clients/DBClient';
2
+ import type { TableMeta } from '../../query/domain/index';
3
+ import type { Adapter } from '../../connection/adapters/Adapter';
4
+ import type { QueryExecutor, QuerySet } from '../../query/index';
5
+ import type { OrmSqlSafetyAdapter } from '../../validation/OrmSqlSafetyAdapter';
6
+ import type { MutationCompiler } from '../internal/MutationCompiler';
7
+ import { ThroughTableManager } from './internal/ThroughTableManager';
8
+ /**
9
+ * Accepted target reference shapes for {@link ManyToManyRelatedManager.add} and
10
+ * {@link ManyToManyRelatedManager.remove}.
11
+ *
12
+ * Application code may pass a target record, a primary-key carrier object, or
13
+ * a bare primary-key value.
14
+ *
15
+ * @template TTarget - The persisted target record shape.
16
+ */
17
+ export type ManyToManyTargetRef<TTarget extends Record<string, unknown>> = TTarget | {
18
+ readonly [pk: string]: unknown;
19
+ } | string | number;
20
+ /**
21
+ * Inputs accepted by the {@link ManyToManyRelatedManager.create} factory. The
22
+ * factory owns the wiring between relation metadata, the SQL safety adapter,
23
+ * and the {@link ThroughTableManager}, so callers only supply context they
24
+ * already have on hand.
25
+ */
26
+ export interface ManyToManyRelatedManagerCreateInputs<TTarget extends Record<string, unknown>> {
27
+ /** Persisted primary-key value of the owning record. */
28
+ ownerPrimaryKey: unknown;
29
+ /** Relation name on the owning model (for error messages). */
30
+ relationName: string;
31
+ /** Display name of the owning model (for error messages). */
32
+ ownerModelLabel: string;
33
+ /** Resolved relation edge metadata for the many-to-many relation. */
34
+ relation: NonNullable<TableMeta['relations']>[string];
35
+ /** Through-model column metadata used to derive the join-table descriptor. */
36
+ throughModelFields: ReadonlyArray<{
37
+ name: string;
38
+ type: string;
39
+ primaryKey?: boolean;
40
+ }>;
41
+ /** Runtime-bound database client shared with the owning manager. */
42
+ client: DBClient;
43
+ /** Shared {@link MutationCompiler} configured for the active dialect. */
44
+ mutationCompiler: MutationCompiler;
45
+ /** Active database adapter. Supplies placeholder formatting and dialect flags. */
46
+ adapter: Adapter;
47
+ /** SQL safety adapter shared with the owning manager. */
48
+ sqlSafetyAdapter: OrmSqlSafetyAdapter;
49
+ /** Lazy resolver returning the target model's {@link QueryExecutor}. */
50
+ targetExecutorProvider: () => QueryExecutor<TTarget> | null;
51
+ /** Internal transaction runner used for multi-target membership writes. */
52
+ runAtomic: <T>(work: () => Promise<T>) => Promise<T>;
53
+ }
54
+ interface ManyToManyRelatedManagerInternalInputs<TTarget extends Record<string, unknown>> {
55
+ ownerPrimaryKey: unknown;
56
+ relationName: string;
57
+ ownerModelLabel: string;
58
+ targetPrimaryKeyField: string;
59
+ throughTableManager: ThroughTableManager;
60
+ targetExecutorProvider: () => QueryExecutor<TTarget> | null;
61
+ runAtomic: <T>(work: () => Promise<T>) => Promise<T>;
62
+ }
63
+ /**
64
+ * Django-style related manager exposed on materialized model records for each
65
+ * many-to-many relation.
66
+ *
67
+ * Use the manager to add or remove join-table membership and to query the
68
+ * related target rows. The owning record's primary key and the relation name
69
+ * are bound when the manager is attached, so application code does not need
70
+ * to pass them on every call.
71
+ *
72
+ * Prefetched memberships seed an internal cache that the queryset returned
73
+ * from `all()` short-circuits to without re-querying. Mutations through
74
+ * `add` and `remove` invalidate the cache so subsequent reads observe the
75
+ * updated membership.
76
+ *
77
+ * @template TTarget - The persisted target record shape returned by `all()`.
78
+ */
79
+ export declare class ManyToManyRelatedManager<TTarget extends Record<string, unknown>> {
80
+ private readonly inputs;
81
+ private static readonly BRAND;
82
+ readonly __tangoBrand: typeof ManyToManyRelatedManager.BRAND;
83
+ private prefetchCache;
84
+ /**
85
+ * Constructor is internal. Application and ORM code must build instances
86
+ * through {@link ManyToManyRelatedManager.create}, which owns the wiring
87
+ * between relation metadata and the backing through-table mutator. The
88
+ * testing package exposes a fixture for unit tests that need a custom
89
+ * mutator.
90
+ */
91
+ constructor(inputs: ManyToManyRelatedManagerInternalInputs<TTarget>);
92
+ /**
93
+ * Narrow an unknown value to {@link ManyToManyRelatedManager}.
94
+ */
95
+ static isManyToManyRelatedManager<TTarget extends Record<string, unknown>>(value: unknown): value is ManyToManyRelatedManager<TTarget>;
96
+ /**
97
+ * Build a {@link ManyToManyRelatedManager} bound to a single owner record.
98
+ * The factory derives the join-table descriptor from the relation edge and
99
+ * through-model fields, wires a {@link ThroughTableManager} against the
100
+ * supplied runtime-bound client, and returns a manager whose `add`,
101
+ * `remove`, and `all` methods enroll in any active
102
+ * `transaction.atomic(...)` boundary.
103
+ */
104
+ static create<TTarget extends Record<string, unknown>>(inputs: ManyToManyRelatedManagerCreateInputs<TTarget>): ManyToManyRelatedManager<TTarget>;
105
+ /**
106
+ * Insert join-table rows linking the owning record to the supplied
107
+ * targets. Duplicate links are ignored so repeated `add(...)` calls are
108
+ * idempotent. When multiple targets are supplied, Tango performs the
109
+ * membership write inside one `transaction.atomic(...)` boundary.
110
+ */
111
+ add(...targets: ManyToManyTargetRef<TTarget>[]): Promise<void>;
112
+ /**
113
+ * Delete join-table rows linking the owning record to the supplied
114
+ * targets. When multiple targets are supplied, Tango performs the
115
+ * membership write inside one `transaction.atomic(...)` boundary.
116
+ */
117
+ remove(...targets: ManyToManyTargetRef<TTarget>[]): Promise<void>;
118
+ /**
119
+ * Return a {@link QuerySet} for the related target rows of this many-to-many
120
+ * relation. When the relation was already loaded by `prefetchRelated(...)`,
121
+ * the first `fetch()` resolves with the cached materialization without
122
+ * re-querying. Mutating the membership through `add`/`remove` invalidates
123
+ * that cache.
124
+ */
125
+ all(): QuerySet<TTarget>;
126
+ /**
127
+ * Replace the prefetch cache with the supplied target rows. Called by the
128
+ * many-to-many prefetch path so a follow-up `all()` resolves without an
129
+ * extra database round-trip.
130
+ */
131
+ primePrefetchCache(targets: readonly TTarget[]): void;
132
+ /**
133
+ * Drop any cached prefetch results. Mutating helpers call this so reads
134
+ * after an `add`/`remove` go back to the database.
135
+ */
136
+ invalidateCache(): void;
137
+ /**
138
+ * Snapshot of the current prefetch cache, exposed for diagnostics and
139
+ * focused unit testing. Returns a fresh array copy so callers cannot
140
+ * mutate the manager's internal state.
141
+ */
142
+ snapshotCache(): readonly TTarget[] | null;
143
+ private resolveTargetPrimaryKeys;
144
+ private resolveTargetPrimaryKey;
145
+ private canonicalizePrimaryKey;
146
+ }
147
+ export {};
@@ -0,0 +1,62 @@
1
+ import type { QueryResult } from '../../query/domain/QueryResult';
2
+ import type { QuerySetState } from '../../query/domain/QuerySetState';
3
+ import type { QueryExecutor } from '../../query/index';
4
+ import { QuerySet } from '../../query/index';
5
+ /**
6
+ * Hooks supplied by {@link ManyToManyRelatedManager} so the queryset returned
7
+ * from `all()` can short-circuit to the prefetch cache, scope the SQL query
8
+ * to the owner via the join table, and filter targets by the resolved primary
9
+ * keys.
10
+ *
11
+ * Application code does not construct this bridge directly; it is wired by
12
+ * the related manager when `all()` is called.
13
+ */
14
+ export interface ManyToManyRelatedQuerySetBridge<TTarget extends Record<string, unknown>> {
15
+ getCache(): readonly TTarget[] | null;
16
+ fetchTargetIds(): Promise<readonly (string | number)[]>;
17
+ targetPrimaryKeyField: string;
18
+ }
19
+ type ShapeFunction<TInput, Out> = (row: TInput) => Out;
20
+ type ShapeParser<TInput, Out> = {
21
+ parse: (row: TInput) => Out;
22
+ };
23
+ /**
24
+ * {@link QuerySet} returned by `post.tags.all()` on a many-to-many related
25
+ * manager.
26
+ *
27
+ * Behaves like a normal `QuerySet` over the target model from an application
28
+ * developer's perspective: you can chain `filter`, `exclude`, `orderBy`,
29
+ * `limit`, `offset`, and terminate with `fetch`, `fetchOne`, or `count`.
30
+ * Each chainable call returns another `ManyToManyRelatedQuerySet` so the
31
+ * chain keeps the membership scoping of the owning record.
32
+ *
33
+ * Two behaviors differ from a plain `QuerySet` and matter to application
34
+ * developers:
35
+ *
36
+ * - When the relation was loaded by `prefetchRelated(...)` and no chainable
37
+ * state has been added (no `filter`, `orderBy`, etc.), `fetch()` and
38
+ * `count()` resolve from the prefetch cache without issuing SQL.
39
+ * - Mutating the membership via `post.tags.add(tag)` or
40
+ * `post.tags.remove(tag)` invalidates that cache so follow-up reads go
41
+ * back to the database.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const post = await PostModel.objects.getOrThrow(postId);
46
+ * await post.tags.add(tag);
47
+ * const tags = await post.tags.all().filter({ color: 'red' }).orderBy('name').fetch();
48
+ * ```
49
+ *
50
+ * @template TTarget - The persisted target record shape (e.g. `Tag`).
51
+ */
52
+ export declare class ManyToManyRelatedQuerySet<TTarget extends Record<string, unknown>> extends QuerySet<TTarget> {
53
+ private readonly bridge;
54
+ constructor(executor: QueryExecutor<TTarget>, bridge: ManyToManyRelatedQuerySetBridge<TTarget>, state?: QuerySetState<TTarget>);
55
+ fetch<Out>(shape?: ShapeFunction<TTarget, Out> | ShapeParser<TTarget, Out>): Promise<QueryResult<TTarget | Out>>;
56
+ fetchOne<Out>(shape?: ShapeFunction<TTarget, Out> | ShapeParser<TTarget, Out>): Promise<TTarget | Out | null>;
57
+ count(): Promise<number>;
58
+ protected spawn<TNextBaseResult extends Record<string, unknown>, TNextHydrated extends Record<string, unknown>>(state: QuerySetState<TTarget>): QuerySet<TTarget, TNextBaseResult, unknown, TNextHydrated>;
59
+ private isStateTrivial;
60
+ private scopedState;
61
+ }
62
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { z } from 'zod';
2
+ import type { Model, PersistedModelOutput } from '@danceroutine/tango-schema/domain';
3
+ import type { DecoratedFieldKind, InternalDecoratedFieldKind, RelationDecoratedSchema } from '@danceroutine/tango-schema/model';
4
+ import type { ManyToManyRelatedManager } from './ManyToManyRelatedManager';
5
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
6
+ type AnyModel = Model<z.ZodObject<z.ZodRawShape>, string>;
7
+ type SchemaShape<TSchema> = TSchema extends z.ZodObject<infer TShape> ? TShape : never;
8
+ type RelationKindOf<TField> = TField extends RelationDecoratedSchema<z.ZodTypeAny, infer TKind extends DecoratedFieldKind, AnyModel, string | undefined, string | undefined> ? TKind : never;
9
+ type RelationTarget<TField> = TField extends RelationDecoratedSchema<z.ZodTypeAny, DecoratedFieldKind, infer TTarget, string | undefined, string | undefined> ? TTarget : never;
10
+ type RelationPublishedName<TField, TFallback extends string> = TField extends RelationDecoratedSchema<z.ZodTypeAny, DecoratedFieldKind, AnyModel, infer TName extends string | undefined, string | undefined> ? TName extends string ? TName : TFallback : TFallback;
11
+ type ModelRow<TTarget> = TTarget extends Model<infer TSchema extends z.ZodObject<z.ZodRawShape>, string> ? PersistedModelOutput<TSchema> : never;
12
+ type ManyToManyKeysOfShape<TShape extends z.ZodRawShape> = Extract<{
13
+ [K in keyof TShape]: IsAny<TShape[K]> extends true ? never : [RelationKindOf<TShape[K]>] extends [never] ? never : [RelationKindOf<TShape[K]>] extends [typeof InternalDecoratedFieldKind.MANY_TO_MANY] ? K : never;
14
+ }[keyof TShape], string>;
15
+ type ManyToManyManagers<TShape extends z.ZodRawShape> = {
16
+ readonly [K in ManyToManyKeysOfShape<TShape> as K extends keyof TShape ? RelationPublishedName<TShape[K], K> : never]: K extends keyof TShape ? RelationTarget<TShape[K]> extends AnyModel ? ManyToManyRelatedManager<ModelRow<RelationTarget<TShape[K]>>> : never : never;
17
+ };
18
+ /**
19
+ * Persisted model record shape returned by ORM read and write paths.
20
+ *
21
+ * Combines the column-level {@link PersistedModelOutput} with a per-relation
22
+ * {@link ManyToManyRelatedManager} for every many-to-many field declared on the
23
+ * source schema. The manager properties are attached as non-enumerable accessors
24
+ * at runtime, so `JSON.stringify` and structural equality checks only see the
25
+ * column data.
26
+ */
27
+ export type MaterializedModelRecord<TSchema extends z.ZodObject<z.ZodRawShape>> = PersistedModelOutput<TSchema> & ManyToManyManagers<SchemaShape<TSchema>>;
28
+ export {};
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Domain boundary barrel: centralizes ORM relation managers attached to
3
+ * materialized model records.
4
+ */
5
+ export { ManyToManyRelatedQuerySet } from './ManyToManyRelatedQuerySet';
6
+ export type { ManyToManyRelatedQuerySetBridge } from './ManyToManyRelatedQuerySet';
7
+ export { ManyToManyRelatedManager } from './ManyToManyRelatedManager';
8
+ export type { ManyToManyRelatedManagerCreateInputs, ManyToManyTargetRef } from './ManyToManyRelatedManager';
9
+ export type { MaterializedModelRecord } from './MaterializedModelRecord';
@@ -0,0 +1,79 @@
1
+ import type { DBClient } from '../../../connection/clients/DBClient';
2
+ import type { TableMeta } from '../../../query/domain/index';
3
+ import type { Adapter } from '../../../connection/adapters/Adapter';
4
+ import { OrmSqlSafetyAdapter } from '../../../validation/OrmSqlSafetyAdapter';
5
+ import { MutationCompiler, type DuplicateInsertPolicy } from '../../internal/MutationCompiler';
6
+ /**
7
+ * Resolved through-table descriptor used by {@link ThroughTableManager}. The
8
+ * descriptor is derived once from a relation edge plus its through-model
9
+ * metadata, then reused for every link insert, delete, and read.
10
+ */
11
+ export interface ThroughTableLinkDescriptor {
12
+ /** Physical join-table name validated against SQL identifier safety rules. */
13
+ table: string;
14
+ /** Primary-key column name on the join table. */
15
+ primaryKey: string;
16
+ /** Full column map for the join table, used by the SQL safety adapter. */
17
+ columns: Record<string, string>;
18
+ /** Join-table column that stores the owner-side primary-key value. */
19
+ sourceColumn: string;
20
+ /** Join-table column that stores the target-side primary-key value. */
21
+ targetColumn: string;
22
+ }
23
+ /**
24
+ * Inputs accepted by {@link ThroughTableManager.fromRelation}.
25
+ */
26
+ export interface ThroughTableManagerFromRelationInputs {
27
+ relation: NonNullable<TableMeta['relations']>[string];
28
+ throughModelFields: ReadonlyArray<{
29
+ name: string;
30
+ type: string;
31
+ primaryKey?: boolean;
32
+ }>;
33
+ client: DBClient;
34
+ mutationCompiler: MutationCompiler;
35
+ adapter: Adapter;
36
+ sqlSafetyAdapter: OrmSqlSafetyAdapter;
37
+ }
38
+ export interface InsertLinkOptions {
39
+ onDuplicate?: DuplicateInsertPolicy;
40
+ }
41
+ /**
42
+ * Internal helper that issues the INSERT, DELETE, and SELECT statements for
43
+ * a single many-to-many join table. Centralizes SQL safety validation and
44
+ * compilation so {@link ManyToManyRelatedManager} implementations only deal
45
+ * in primary-key values.
46
+ */
47
+ export declare class ThroughTableManager {
48
+ private readonly client;
49
+ private readonly mutationCompiler;
50
+ private readonly descriptor;
51
+ private readonly adapter;
52
+ private readonly sqlSafetyAdapter;
53
+ constructor(client: DBClient, mutationCompiler: MutationCompiler, descriptor: ThroughTableLinkDescriptor, adapter: Adapter, sqlSafetyAdapter?: OrmSqlSafetyAdapter);
54
+ /**
55
+ * Derive a {@link ThroughTableLinkDescriptor} from the through-model
56
+ * metadata exposed by a many-to-many relation edge.
57
+ */
58
+ static buildLinkDescriptor(relation: NonNullable<TableMeta['relations']>[string], throughModelFields: ReadonlyArray<{
59
+ name: string;
60
+ type: string;
61
+ primaryKey?: boolean;
62
+ }>): ThroughTableLinkDescriptor;
63
+ /**
64
+ * Convenience factory that derives the join-table descriptor from the
65
+ * relation edge and instantiates a {@link ThroughTableManager} in one
66
+ * step.
67
+ */
68
+ static fromRelation(inputs: ThroughTableManagerFromRelationInputs): ThroughTableManager;
69
+ /**
70
+ * Read every target primary-key value linked to the supplied owner via
71
+ * the join table. Used by ManyToManyRelatedManager.all to scope
72
+ * follow-up target queries to the current owner.
73
+ */
74
+ selectTargetIdsForOwner(ownerPrimaryKey: unknown): Promise<readonly (string | number)[]>;
75
+ insertLink(ownerPrimaryKey: unknown, targetPrimaryKey: unknown, options?: InsertLinkOptions): Promise<void>;
76
+ insertLinks(ownerPrimaryKey: unknown, targetPrimaryKeys: readonly unknown[], options?: InsertLinkOptions): Promise<void>;
77
+ deleteLink(ownerPrimaryKey: unknown, targetPrimaryKey: unknown): Promise<void>;
78
+ deleteLinks(ownerPrimaryKey: unknown, targetPrimaryKeys: readonly unknown[]): Promise<void>;
79
+ }