@getstrata/core 2.2.36 → 2.2.38

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @getstrata/core changelog
2
2
 
3
+ ## 2.2.38
4
+
5
+ Qualify packed OIDC and SAML callbacks across Postgres, SQLite and MySQL. Fix explicitly non-migrating local bootstrap provisioning, dialect-safe transactional SAML replay, verified bearer ACS recipient binding, and independent SAML Response Destination validation through a maintained parser. Fix owned listener/client address mismatches with typed generated native options and optional CLI HOST, retaining default production binding. Preserve existing generated auth, production guards, compiler/runtime assertions and coverage gates; live provider and deployment qualification remain separate.
6
+
7
+ ## 2.2.37
8
+
9
+ Fix MySQL repository entity-returning mutations with transaction-pinned readback, persisted keys/defaults, honest null and deletion outcomes, complete model event payloads and injected connection ownership. Preserve native Postgres/SQLite RETURNING and conditional affected-row metadata. Close owned generated OpenAPI and failed-job command applications on success and failure before exit; retain both command and cleanup errors. Add mandatory packaged MySQL production column-tenancy CLI and HTTP auth/SCIM acceptance with an owned DML-only account, unchanged route/spec/SDK contracts, native ORM regressions and cleanup tests. Records dogfood issues #232 and #233 without application SQL workarounds.
10
+
3
11
  ## 2.2.36
4
12
 
5
13
  Add a mandatory installed-package SQLite column-tenancy CLI/OpenAPI/HTTP profile, including cookie/token/JWT/Basic and repeated SCIM isolation, while preserving existing Postgres security, route inventories, compiler combinations and Docker gates. Reconcile observability/rotation trackers against shipped capabilities and remaining operational qualification.
@@ -10,6 +10,27 @@ interface SamlServiceProviderOptions {
10
10
  wantAuthnResponseSigned?: boolean;
11
11
  disableRequestedAuthnContext?: boolean;
12
12
  }
13
+ interface VerifiedSamlAssertion {
14
+ Assertion?: {
15
+ $?: {
16
+ ID?: string;
17
+ };
18
+ Subject?: Array<{
19
+ SubjectConfirmation?: Array<{
20
+ $?: {
21
+ Method?: string;
22
+ };
23
+ SubjectConfirmationData?: Array<{
24
+ $?: {
25
+ Recipient?: string;
26
+ NotBefore?: string;
27
+ NotOnOrAfter?: string;
28
+ };
29
+ }>;
30
+ }>;
31
+ }>;
32
+ };
33
+ }
13
34
  interface NodeSamlProfile {
14
35
  ID?: string;
15
36
  nameID?: string;
@@ -17,13 +38,8 @@ interface NodeSamlProfile {
17
38
  name?: string;
18
39
  issuer?: string;
19
40
  sessionIndex?: string | string[];
20
- getAssertion?: () => {
21
- Assertion?: {
22
- $?: {
23
- ID?: string;
24
- };
25
- };
26
- };
41
+ getAssertion?: () => VerifiedSamlAssertion;
42
+ getSamlResponseXml?: () => string;
27
43
  [key: string]: unknown;
28
44
  }
29
45
  type NodeSamlClient = {
@@ -35,6 +35,10 @@ declare class BaseRepository<TEntity extends object, PrimaryKey extends keyof TE
35
35
  constructor(table: TableDefinition<TEntity, PrimaryKey>, connection?: DatabaseConnection);
36
36
  private assertTransactionalEvent;
37
37
  private runModelMutation;
38
+ private mysqlMutation;
39
+ private mysqlRow;
40
+ private mysqlAffected;
41
+ private mysqlWriteById;
38
42
  count(options?: ExtendedQueryOptions<TEntity>): Promise<number>;
39
43
  findAll(options?: ExtendedQueryOptions<TEntity>): Promise<TEntity[]>;
40
44
  paginate(options: {
@@ -24,7 +24,7 @@ export { Blueprint, ColumnDefinition, compileBlueprint, createSchemaBuilder, For
24
24
  export { createSqliteConnection } from "./sqliteConnection.ts";
25
25
  export type { TableDefinition } from "./table.ts";
26
26
  export { defineTable } from "./table.ts";
27
- export { runInTransaction } from "./transaction.ts";
27
+ export { isActiveTransactionConnection, runInTransaction } from "./transaction.ts";
28
28
  export type { DatabaseComparable, DatabaseScalar, MutationValues, QueryFilterValue, QueryJoin, QueryJoinOn, QueryOperator, QueryOptions, QueryOrder, QuerySelectItem, QueryWhere, UpdateValues, } from "./types.ts";
29
29
  export type { WhereNode } from "./whereBuilder.ts";
30
30
  export { WhereBuilder } from "./whereBuilder.ts";
@@ -1,7 +1,11 @@
1
1
  import { type TransactionAcquisitionMetrics } from "./acquisitionMetrics.ts";
2
2
  import type { DatabaseConnection } from "./baseRepository.ts";
3
3
  import { hasActiveTransaction, requestTransactionRollback } from "./transactionControl.ts";
4
+ /** Recognizes the callback handle without admitting an unrelated pool. */
5
+ declare function isActiveTransactionConnection(connection: DatabaseConnection): boolean;
4
6
  interface TransactionOptions {
7
+ /** Explicit root pool for injected repositories; cannot switch an active transaction's owner. */
8
+ connection?: DatabaseConnection;
5
9
  /** Cancel admission/pool checkout only. Does not cancel SQL or an admitted business callback. */
6
10
  acquisitionSignal?: AbortSignal;
7
11
  /** Observe native checkout only; requires a reserve-capable adapter. */
@@ -11,4 +15,4 @@ declare function runInTransaction<TValue>(operation: (connection: DatabaseConnec
11
15
  export type { TransactionAcquisitionMetrics, TransactionAcquisitionSnapshot, } from "./acquisitionMetrics.ts";
12
16
  export { createTransactionAcquisitionMetrics, renderTransactionAcquisitionMetrics, } from "./acquisitionMetrics.ts";
13
17
  export type { TransactionOptions };
14
- export { hasActiveTransaction, requestTransactionRollback, runInTransaction };
18
+ export { hasActiveTransaction, isActiveTransactionConnection, requestTransactionRollback, runInTransaction, };
@@ -733,6 +733,17 @@ var init_repositoryConnection = __esm(() => {
733
733
 
734
734
  // ../../src/core/database/transaction.ts
735
735
  import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
736
+ function scopedConnection(source) {
737
+ const connection = createDatabaseConnection(source);
738
+ transactionAliases.set(connection, source);
739
+ return connection;
740
+ }
741
+ function isActiveTransactionConnection(connection) {
742
+ if (!hasActiveTransaction() || !hasActiveDatabaseConnection())
743
+ return false;
744
+ const active = getActiveDatabaseConnection(connection);
745
+ return connection === active || transactionAliases.get(connection) === active;
746
+ }
736
747
  async function runInSavepoint(connection, operation) {
737
748
  const inherited = transactionFrame.getStore();
738
749
  let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
@@ -749,7 +760,7 @@ async function runInSavepoint(connection, operation) {
749
760
  return await settleTransaction(() => runWithDeferredModelEvents(async () => {
750
761
  await connection.unsafe(`SAVEPOINT ${name}`);
751
762
  try {
752
- const result = await transactionFrame.run({ connection, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(createDatabaseConnection(connection)))));
763
+ const result = await transactionFrame.run({ connection, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(scopedConnection(connection)))));
753
764
  await connection.unsafe(`RELEASE SAVEPOINT ${name}`);
754
765
  return result;
755
766
  } catch (error) {
@@ -772,9 +783,12 @@ async function runInTransaction(operation, options = {}) {
772
783
  signal?.throwIfAborted();
773
784
  return operation(connection);
774
785
  };
775
- const pool = resolveRepositoryConnection();
786
+ const pool = options.connection ?? resolveRepositoryConnection();
776
787
  if (hasActiveDatabaseConnection()) {
777
- return await runInSavepoint(getActiveDatabaseConnection(pool), admittedOperation);
788
+ const active = getActiveDatabaseConnection(pool);
789
+ if (options.connection && options.connection !== active && options.connection !== transactionRoots.get(active) && transactionAliases.get(options.connection) !== active)
790
+ throw new Error("Cannot switch database connections within an active transaction.");
791
+ return await runInSavepoint(active, admittedOperation);
778
792
  }
779
793
  if (!supportsTransactions(pool)) {
780
794
  throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
@@ -793,14 +807,15 @@ async function runInTransaction(operation, options = {}) {
793
807
  if (!supportsTransactions(connection))
794
808
  throw new Error("Reserved database connection does not support transactions.");
795
809
  return await connection.begin(AsyncLocalStorage3.bind(async (transaction) => {
796
- return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
810
+ transactionRoots.set(transaction, pool);
811
+ return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(scopedConnection(transaction))))));
797
812
  }));
798
813
  } finally {
799
814
  await reserved?.release();
800
815
  }
801
816
  }));
802
817
  }
803
- var transactionFrame, outerFrames;
818
+ var transactionFrame, outerFrames, transactionRoots, transactionAliases;
804
819
  var init_transaction = __esm(() => {
805
820
  init_deferredModelEvents();
806
821
  init_asyncContextStore();
@@ -812,6 +827,8 @@ var init_transaction = __esm(() => {
812
827
  init_acquisitionMetrics();
813
828
  transactionFrame = createAsyncContextStore("@getstrata/transactionFrame");
814
829
  outerFrames = new WeakMap;
830
+ transactionRoots = new WeakMap;
831
+ transactionAliases = new WeakMap;
815
832
  });
816
833
 
817
834
  // ../../src/core/tenant/databaseTenantContext.ts
@@ -733,6 +733,17 @@ var init_repositoryConnection = __esm(() => {
733
733
 
734
734
  // ../../src/core/database/transaction.ts
735
735
  import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
736
+ function scopedConnection(source) {
737
+ const connection = createDatabaseConnection(source);
738
+ transactionAliases.set(connection, source);
739
+ return connection;
740
+ }
741
+ function isActiveTransactionConnection(connection) {
742
+ if (!hasActiveTransaction() || !hasActiveDatabaseConnection())
743
+ return false;
744
+ const active = getActiveDatabaseConnection(connection);
745
+ return connection === active || transactionAliases.get(connection) === active;
746
+ }
736
747
  async function runInSavepoint(connection, operation) {
737
748
  const inherited = transactionFrame.getStore();
738
749
  let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
@@ -749,7 +760,7 @@ async function runInSavepoint(connection, operation) {
749
760
  return await settleTransaction(() => runWithDeferredModelEvents(async () => {
750
761
  await connection.unsafe(`SAVEPOINT ${name}`);
751
762
  try {
752
- const result = await transactionFrame.run({ connection, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(createDatabaseConnection(connection)))));
763
+ const result = await transactionFrame.run({ connection, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(scopedConnection(connection)))));
753
764
  await connection.unsafe(`RELEASE SAVEPOINT ${name}`);
754
765
  return result;
755
766
  } catch (error) {
@@ -772,9 +783,12 @@ async function runInTransaction(operation, options = {}) {
772
783
  signal?.throwIfAborted();
773
784
  return operation(connection);
774
785
  };
775
- const pool = resolveRepositoryConnection();
786
+ const pool = options.connection ?? resolveRepositoryConnection();
776
787
  if (hasActiveDatabaseConnection()) {
777
- return await runInSavepoint(getActiveDatabaseConnection(pool), admittedOperation);
788
+ const active = getActiveDatabaseConnection(pool);
789
+ if (options.connection && options.connection !== active && options.connection !== transactionRoots.get(active) && transactionAliases.get(options.connection) !== active)
790
+ throw new Error("Cannot switch database connections within an active transaction.");
791
+ return await runInSavepoint(active, admittedOperation);
778
792
  }
779
793
  if (!supportsTransactions(pool)) {
780
794
  throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
@@ -793,14 +807,15 @@ async function runInTransaction(operation, options = {}) {
793
807
  if (!supportsTransactions(connection))
794
808
  throw new Error("Reserved database connection does not support transactions.");
795
809
  return await connection.begin(AsyncLocalStorage3.bind(async (transaction) => {
796
- return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
810
+ transactionRoots.set(transaction, pool);
811
+ return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(scopedConnection(transaction))))));
797
812
  }));
798
813
  } finally {
799
814
  await reserved?.release();
800
815
  }
801
816
  }));
802
817
  }
803
- var transactionFrame, outerFrames;
818
+ var transactionFrame, outerFrames, transactionRoots, transactionAliases;
804
819
  var init_transaction = __esm(() => {
805
820
  init_deferredModelEvents();
806
821
  init_asyncContextStore();
@@ -812,6 +827,8 @@ var init_transaction = __esm(() => {
812
827
  init_acquisitionMetrics();
813
828
  transactionFrame = createAsyncContextStore("@getstrata/transactionFrame");
814
829
  outerFrames = new WeakMap;
830
+ transactionRoots = new WeakMap;
831
+ transactionAliases = new WeakMap;
815
832
  });
816
833
 
817
834
  // ../../src/core/tenant/databaseTenantContext.ts