@aiquants/auth-directory-drizzle 0.7.1 → 0.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.
- package/README.md +7 -0
- package/dist/index.d.mts +32 -64
- package/dist/index.d.ts +32 -64
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -8
package/README.md
CHANGED
|
@@ -55,6 +55,13 @@ authorization admin UI uses. Removals are issued one at a time because `AdminRem
|
|
|
55
55
|
descriptor; adding a batch form to the authorization core would breach the package boundary, and the
|
|
56
56
|
count is already bounded by the reconciler's circuit breaker.
|
|
57
57
|
|
|
58
|
+
**Every transaction goes through one seam.** All nine of them are opened by `transactionRunner` in
|
|
59
|
+
`db.ts`, which runs the work again when — and only when — the server rolled it back
|
|
60
|
+
(see [`@aiquants/mssql`](../mssql/README.md) — **only the deadlock victim `1205` is run again**, and the closure is wrapped in `capture` so the ORM cannot destroy that evidence). Calling `db.transaction` directly
|
|
61
|
+
at each site means touching nine places to change the policy, and one of them eventually gets left
|
|
62
|
+
behind without saying so. Configure with `transactionRetry` on any factory's options; the closure
|
|
63
|
+
must keep its side effects inside the database, because a retry runs it from the start.
|
|
64
|
+
|
|
58
65
|
**A refusal does not abort the run.** Stopping at the first refused removal leaves the additions
|
|
59
66
|
committed, the earlier removals committed and the ledger untouched — and every later run recomputes
|
|
60
67
|
the same plan and dies at the same index, so one legitimately-guarded row blocks the whole group's
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthDirectoryCatalogGroup, AuthDirectoryCatalogSnapshot, AuthGroupExternalLink, AuthDirectoryMembershipMode, AuthDirectoryStatus, AuthAllowlistEntry } from '@aiquants/auth-core';
|
|
2
|
+
import { TransactionRetryOptions } from '@aiquants/mssql';
|
|
2
3
|
import * as drizzle_orm_mssql_core from 'drizzle-orm/mssql-core';
|
|
3
4
|
import { AnyMsSqlColumn } from 'drizzle-orm/mssql-core';
|
|
4
5
|
import { AuthzAdminStore } from '@aiquants/authz-core';
|
|
@@ -14,6 +15,7 @@ import { TenantMembershipPolicy, SyncAbort, AuthDirectoryMembershipMode as AuthD
|
|
|
14
15
|
* パラメータ (スキーマ・ロガー等) が配備ごとに違い、それを署名へ引き回すと利用側が
|
|
15
16
|
* `as any` を書く羽目になるためである。実体は実行時にこれらを満たす。
|
|
16
17
|
*/
|
|
18
|
+
|
|
17
19
|
type Rows<T> = PromiseLike<T[]>;
|
|
18
20
|
interface SelectChain<T> extends Rows<T> {
|
|
19
21
|
from(t: unknown): SelectChain<T>;
|
|
@@ -45,19 +47,35 @@ interface DirectoryDb {
|
|
|
45
47
|
}
|
|
46
48
|
/** Ensures a connection exists before the first statement. 最初の文の前に接続を確立する処理。 */
|
|
47
49
|
type EnsureConnection = () => Promise<void>;
|
|
50
|
+
/** Options every factory in this adapter shares. 本アダプタの各ファクトリが共通で受ける設定。 */
|
|
51
|
+
type TransactionalOptions = {
|
|
52
|
+
/**
|
|
53
|
+
* Retry policy for transactions SQL Server chose as the deadlock victim. See `@aiquants/mssql`.
|
|
54
|
+
* サーバがロールバックしたトランザクションの再試行方針。
|
|
55
|
+
*
|
|
56
|
+
* 鍵の範囲が重なる並行操作はデッドロックしうる。犠牲に選ばれた側はサーバによって
|
|
57
|
+
* ロールバックされる — 何もコミットされていないので、読み直して判定し直す再実行が正しい。
|
|
58
|
+
* 既定で 3 回試す。`{ attempts: 1 }` で再試行しない。
|
|
59
|
+
*/
|
|
60
|
+
transactionRetry?: TransactionRetryOptions;
|
|
61
|
+
};
|
|
48
62
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
63
|
+
* Builds the one function every transaction in this adapter goes through.
|
|
64
|
+
*
|
|
65
|
+
* 本アダプタの全トランザクションが通る、唯一の関数を組み立てる処理。
|
|
51
66
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* 9 時間ずれる — 鮮度は「常に古い」と報告され、締切の比較は 9 時間ぶん甘くなる。読む場所ごとに
|
|
55
|
-
* 対処すると必ずどこかが漏れるので、1 つの関数に閉じる。
|
|
67
|
+
* 各呼び出し箇所で `db.transaction` を直に呼ぶと、再試行を足す・方針を変えるたびに 9 箇所を
|
|
68
|
+
* 触ることになり、いずれ 1 箇所だけ取り残される。入口を 1 つにする。
|
|
56
69
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
70
|
+
* ⚠️ **閉包を DB の外へ波及させないこと。** 再実行は毎回はじめから走るため、ロールバックで元に
|
|
71
|
+
* 戻らない副作用 (外部への送信・呼び出し元の配列への追加) を閉包へ入れてはならない。これは
|
|
72
|
+
* 再試行の有無に関わらず、トランザクションに要求される性質である。
|
|
73
|
+
*
|
|
74
|
+
* @param db - 対象の DB 面。
|
|
75
|
+
* @param opts - 再試行方針を含む設定。
|
|
76
|
+
* @returns トランザクションを開いて閉包を走らせる関数。
|
|
59
77
|
*/
|
|
60
|
-
declare function
|
|
78
|
+
declare function transactionRunner(db: DirectoryDb, opts: TransactionalOptions): <T>(fn: (tx: DirectoryDb) => Promise<T>) => Promise<T>;
|
|
61
79
|
|
|
62
80
|
/** Minimal shape of a table this schema points a foreign key at. 外部キーの参照先に要る最小形。 */
|
|
63
81
|
type ReferencedTable = {
|
|
@@ -1375,7 +1393,7 @@ type DirectoryCatalogReaderOptions = {
|
|
|
1375
1393
|
* その場で `replace` を呼ぶ経路が「既に手元にあるもの」になり、上流を読めないプロセスが
|
|
1376
1394
|
* 観測時刻を詐称できてしまう。だから生成関数を 2 つに分けている。
|
|
1377
1395
|
*/
|
|
1378
|
-
type DirectoryCatalogWriterOptions = DirectoryCatalogReaderOptions & {
|
|
1396
|
+
type DirectoryCatalogWriterOptions = DirectoryCatalogReaderOptions & TransactionalOptions & {
|
|
1379
1397
|
/** Injected clock — one observation must carry one timestamp. 注入時計。 */
|
|
1380
1398
|
now: () => Date;
|
|
1381
1399
|
/** Who the audit columns record. 監査列に残す主体。 */
|
|
@@ -1625,7 +1643,7 @@ type LinkableAssertion = {
|
|
|
1625
1643
|
assertLinkableAndWrite(tenantId: string, groupId: number, write: (runner: unknown) => Promise<void>): Promise<void>;
|
|
1626
1644
|
};
|
|
1627
1645
|
/** Options for the link store. リンクストアの構成。 */
|
|
1628
|
-
type DirectoryLinkStoreOptions = {
|
|
1646
|
+
type DirectoryLinkStoreOptions = TransactionalOptions & {
|
|
1629
1647
|
/** Required — see {@link LinkableAssertion}. 必須 (忘れられる経路を作らないため)。 */
|
|
1630
1648
|
linkable: LinkableAssertion;
|
|
1631
1649
|
/** Establishes the connection before the first statement. 最初の文の前に接続を確立する処理。 */
|
|
@@ -1929,7 +1947,7 @@ declare function promoteLedgerForUser(repository: PromotionRepository, args: {
|
|
|
1929
1947
|
}): Promise<PromotionResult>;
|
|
1930
1948
|
|
|
1931
1949
|
/** Options for {@link createPromotionRepository}. 昇格リポジトリの生成オプション。 */
|
|
1932
|
-
type PromotionRepositoryOptions = {
|
|
1950
|
+
type PromotionRepositoryOptions = TransactionalOptions & {
|
|
1933
1951
|
/** Establishes the connection before the first statement. 最初の文の前に接続を確立する処理。 */
|
|
1934
1952
|
ensureConnection?: EnsureConnection;
|
|
1935
1953
|
/** Written to the audit columns of inserted membership rows. 挿入行の監査列に書く実行者。 */
|
|
@@ -1949,56 +1967,6 @@ type PromotionRepositoryOptions = {
|
|
|
1949
1967
|
*/
|
|
1950
1968
|
declare function createPromotionRepository(dbUnknown: unknown, tables: AuthDirectoryTables, authzTables: AuthzTables, opts: PromotionRepositoryOptions): PromotionRepository;
|
|
1951
1969
|
|
|
1952
|
-
/**
|
|
1953
|
-
* SQL Server parameter-limit helpers shared by every batched statement.
|
|
1954
|
-
* 一括文が共有する SQL Server のパラメータ上限まわりのヘルパー。
|
|
1955
|
-
*
|
|
1956
|
-
* ⚠️ 上限は **行数ではなくパラメータ数** である。1 行あたり何個の値を束ねるかは文ごとに違うので、
|
|
1957
|
-
* 行数だけで分割すると、列の多い `VALUES` が静かに上限を越える。しかも越えるのは件数が閾値を
|
|
1958
|
-
* 跨いだ日だけで、それまでは同じコードが問題なく動いて見える。
|
|
1959
|
-
*/
|
|
1960
|
-
/**
|
|
1961
|
-
* Hard limit of parameters per statement. 1 文あたりのパラメータ数の上限。
|
|
1962
|
-
*
|
|
1963
|
-
* SQL Server の RPC 1 回あたりのパラメータ上限は 2100。数個は文自身の値 (`SET` 句・スカラー
|
|
1964
|
-
* 述語) が使うため、分割の基準にはここから余裕を引いた値を用いる。
|
|
1965
|
-
*/
|
|
1966
|
-
declare const MAX_PARAMETERS = 2100;
|
|
1967
|
-
/** Parameters reserved for the statement's own scalar values. 文自身のスカラー値のための予約枠。 */
|
|
1968
|
-
declare const RESERVED_PARAMETERS = 100;
|
|
1969
|
-
/**
|
|
1970
|
-
* Maximum ids per `IN` list of single-column predicates. 単列述語の `IN` 句 1 回あたりの最大 ID 数。
|
|
1971
|
-
*
|
|
1972
|
-
* 1 行 1 パラメータの用途 (`inArray(col, ids)`) 専用の既定値である。複数列を束ねる `VALUES` や、
|
|
1973
|
-
* 1 文に `IN` を 2 つ描画する `UPDATE` にそのまま使ってはならない — {@link chunkByParameters} を使う。
|
|
1974
|
-
*/
|
|
1975
|
-
declare const MAX_IN_LIST = 500;
|
|
1976
|
-
/**
|
|
1977
|
-
* Splits a list into chunks that fit one statement.
|
|
1978
|
-
* 1 文に収まる大きさへリストを分割する処理。
|
|
1979
|
-
*
|
|
1980
|
-
* @param items - Items to split. 分割対象。
|
|
1981
|
-
* @param size - Maximum chunk size. 1 チャンクの最大数。
|
|
1982
|
-
* @returns Chunks in the original order. 元の順序を保ったチャンク列。
|
|
1983
|
-
* @throws {TypeError} When the size is not a positive integer. 正の整数でない場合。
|
|
1984
|
-
*/
|
|
1985
|
-
declare function chunk<T>(items: readonly T[], size?: number): T[][];
|
|
1986
|
-
/**
|
|
1987
|
-
* Splits a list so each chunk fits the parameter budget of one statement.
|
|
1988
|
-
* 各チャンクが 1 文のパラメータ枠に収まるようリストを分割する処理。
|
|
1989
|
-
*
|
|
1990
|
-
* `VALUES` は 1 行につき列数ぶんのパラメータを束ねる。7 列の台帳行なら 500 行で 3500 個になり、
|
|
1991
|
-
* 上限 2100 を越えて **実行される前に** 拒否される。しかもその失敗は再試行しても同じ場所で起き、
|
|
1992
|
-
* そのグループは永久に台帳を持てない。
|
|
1993
|
-
*
|
|
1994
|
-
* @param items - Items to split. 分割対象。
|
|
1995
|
-
* @param parametersPerItem - Parameters each item binds. 1 件が束ねるパラメータ数。
|
|
1996
|
-
* @param reservedParameters - Parameters the statement itself binds. 文自身が束ねる数。
|
|
1997
|
-
* @returns Chunks in the original order. 元の順序を保ったチャンク列。
|
|
1998
|
-
* @throws {TypeError} When a single item cannot fit the budget. 1 件すら収まらない場合。
|
|
1999
|
-
*/
|
|
2000
|
-
declare function chunkByParameters<T>(items: readonly T[], parametersPerItem: number, reservedParameters?: number): T[][];
|
|
2001
|
-
|
|
2002
1970
|
/**
|
|
2003
1971
|
* @module sync-messages
|
|
2004
1972
|
* @description Operator-facing text the synchronization records against a link.
|
|
@@ -2521,7 +2489,7 @@ type MemberResolverPort = {
|
|
|
2521
2489
|
resolve(members: readonly DirectoryMember[]): Promise<ResolvedDirectoryMember[]>;
|
|
2522
2490
|
};
|
|
2523
2491
|
/** Options for the synchronization repository. 同期リポジトリの構成。 */
|
|
2524
|
-
type SyncRepositoryOptions = {
|
|
2492
|
+
type SyncRepositoryOptions = TransactionalOptions & {
|
|
2525
2493
|
ensureConnection?: EnsureConnection;
|
|
2526
2494
|
/**
|
|
2527
2495
|
* Wall-clock supplier. 実行の時刻を供給する処理。
|
|
@@ -2556,4 +2524,4 @@ type SyncRepositoryOptions = {
|
|
|
2556
2524
|
*/
|
|
2557
2525
|
declare function createDirectorySyncRepository(dbUnknown: unknown, tables: AuthDirectoryTables, authzTables: AuthzTables, opts: SyncRepositoryOptions): DirectorySyncRepository;
|
|
2558
2526
|
|
|
2559
|
-
export { AUTH_DIRECTORY_PROVIDER_COLUMN, AUTH_DIRECTORY_TENANT_SCOPED_TABLES, type ApplyReport, type AuthDirectoryTables, type ClaimedSyncRequest, type DeleteChain, type DirectoryCatalogEntry, type DirectoryCatalogReaderOptions, type DirectoryCatalogSnapshot, type DirectoryCatalogWriterOptions, type DirectoryDb, type DirectoryLinkRow, type DirectoryLinkStoreOptions, type DirectorySyncConfig, type DirectorySyncRepository, type DirectorySyncRunResult, type EnsureConnection, type ExternalGroupGuardOptions, type ExternalLinkLookup, type InsertChain, type LedgerIdentityRow, type LedgerIdentityTables, type LinkGroupInput, type LinkableAssertion,
|
|
2527
|
+
export { AUTH_DIRECTORY_PROVIDER_COLUMN, AUTH_DIRECTORY_TENANT_SCOPED_TABLES, type ApplyReport, type AuthDirectoryTables, type ClaimedSyncRequest, type DeleteChain, type DirectoryCatalogEntry, type DirectoryCatalogReaderOptions, type DirectoryCatalogSnapshot, type DirectoryCatalogWriterOptions, type DirectoryDb, type DirectoryLinkRow, type DirectoryLinkStoreOptions, type DirectorySyncConfig, type DirectorySyncRepository, type DirectorySyncRunResult, type EnsureConnection, type ExternalGroupGuardOptions, type ExternalLinkLookup, type InsertChain, type LedgerIdentityRow, type LedgerIdentityTables, type LinkGroupInput, type LinkableAssertion, MEMBERSHIP_MODES, type MemberResolverPort, NEWLY_UNHEALTHY_SYNC_OUTCOMES, type PromotionRepository, type PromotionRepositoryOptions, type PromotionResult, type ReferencedTable, type RemovalFailure, type RemovalOutcome, STRANDED_REASON, type SelectChain, type StrandedRequests, type SyncFailureCause, type SyncLink, type SyncLoggerPort, type SyncMessages, type SyncOutcome, type SyncRepositoryOptions, type SyncRequestOutcome, type SyncRequestQueueOptions, type SyncRequestResult, type SyncScope, type TransactionalOptions, UNHEALTHY_SYNC_OUTCOMES, type UpdateChain, assertGroupMayBeLinked, assertMembershipMode, classifySyncFailure, createDirectoryCatalogReader, createDirectoryCatalogWriter, createDirectoryLinkStore, createDirectorySync, createDirectorySyncRepository, createPromotionRepository, createSyncRequestQueue, defaultExternalGroupGuardMessage, defaultSyncMessages, defineAuthDirectorySchema, findLedgerRowsForIdentity, promoteLedgerForUser, transactionRunner, withExternalGroupGuards };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AuthDirectoryCatalogGroup, AuthDirectoryCatalogSnapshot, AuthGroupExternalLink, AuthDirectoryMembershipMode, AuthDirectoryStatus, AuthAllowlistEntry } from '@aiquants/auth-core';
|
|
2
|
+
import { TransactionRetryOptions } from '@aiquants/mssql';
|
|
2
3
|
import * as drizzle_orm_mssql_core from 'drizzle-orm/mssql-core';
|
|
3
4
|
import { AnyMsSqlColumn } from 'drizzle-orm/mssql-core';
|
|
4
5
|
import { AuthzAdminStore } from '@aiquants/authz-core';
|
|
@@ -14,6 +15,7 @@ import { TenantMembershipPolicy, SyncAbort, AuthDirectoryMembershipMode as AuthD
|
|
|
14
15
|
* パラメータ (スキーマ・ロガー等) が配備ごとに違い、それを署名へ引き回すと利用側が
|
|
15
16
|
* `as any` を書く羽目になるためである。実体は実行時にこれらを満たす。
|
|
16
17
|
*/
|
|
18
|
+
|
|
17
19
|
type Rows<T> = PromiseLike<T[]>;
|
|
18
20
|
interface SelectChain<T> extends Rows<T> {
|
|
19
21
|
from(t: unknown): SelectChain<T>;
|
|
@@ -45,19 +47,35 @@ interface DirectoryDb {
|
|
|
45
47
|
}
|
|
46
48
|
/** Ensures a connection exists before the first statement. 最初の文の前に接続を確立する処理。 */
|
|
47
49
|
type EnsureConnection = () => Promise<void>;
|
|
50
|
+
/** Options every factory in this adapter shares. 本アダプタの各ファクトリが共通で受ける設定。 */
|
|
51
|
+
type TransactionalOptions = {
|
|
52
|
+
/**
|
|
53
|
+
* Retry policy for transactions SQL Server chose as the deadlock victim. See `@aiquants/mssql`.
|
|
54
|
+
* サーバがロールバックしたトランザクションの再試行方針。
|
|
55
|
+
*
|
|
56
|
+
* 鍵の範囲が重なる並行操作はデッドロックしうる。犠牲に選ばれた側はサーバによって
|
|
57
|
+
* ロールバックされる — 何もコミットされていないので、読み直して判定し直す再実行が正しい。
|
|
58
|
+
* 既定で 3 回試す。`{ attempts: 1 }` で再試行しない。
|
|
59
|
+
*/
|
|
60
|
+
transactionRetry?: TransactionRetryOptions;
|
|
61
|
+
};
|
|
48
62
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
63
|
+
* Builds the one function every transaction in this adapter goes through.
|
|
64
|
+
*
|
|
65
|
+
* 本アダプタの全トランザクションが通る、唯一の関数を組み立てる処理。
|
|
51
66
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* 9 時間ずれる — 鮮度は「常に古い」と報告され、締切の比較は 9 時間ぶん甘くなる。読む場所ごとに
|
|
55
|
-
* 対処すると必ずどこかが漏れるので、1 つの関数に閉じる。
|
|
67
|
+
* 各呼び出し箇所で `db.transaction` を直に呼ぶと、再試行を足す・方針を変えるたびに 9 箇所を
|
|
68
|
+
* 触ることになり、いずれ 1 箇所だけ取り残される。入口を 1 つにする。
|
|
56
69
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
70
|
+
* ⚠️ **閉包を DB の外へ波及させないこと。** 再実行は毎回はじめから走るため、ロールバックで元に
|
|
71
|
+
* 戻らない副作用 (外部への送信・呼び出し元の配列への追加) を閉包へ入れてはならない。これは
|
|
72
|
+
* 再試行の有無に関わらず、トランザクションに要求される性質である。
|
|
73
|
+
*
|
|
74
|
+
* @param db - 対象の DB 面。
|
|
75
|
+
* @param opts - 再試行方針を含む設定。
|
|
76
|
+
* @returns トランザクションを開いて閉包を走らせる関数。
|
|
59
77
|
*/
|
|
60
|
-
declare function
|
|
78
|
+
declare function transactionRunner(db: DirectoryDb, opts: TransactionalOptions): <T>(fn: (tx: DirectoryDb) => Promise<T>) => Promise<T>;
|
|
61
79
|
|
|
62
80
|
/** Minimal shape of a table this schema points a foreign key at. 外部キーの参照先に要る最小形。 */
|
|
63
81
|
type ReferencedTable = {
|
|
@@ -1375,7 +1393,7 @@ type DirectoryCatalogReaderOptions = {
|
|
|
1375
1393
|
* その場で `replace` を呼ぶ経路が「既に手元にあるもの」になり、上流を読めないプロセスが
|
|
1376
1394
|
* 観測時刻を詐称できてしまう。だから生成関数を 2 つに分けている。
|
|
1377
1395
|
*/
|
|
1378
|
-
type DirectoryCatalogWriterOptions = DirectoryCatalogReaderOptions & {
|
|
1396
|
+
type DirectoryCatalogWriterOptions = DirectoryCatalogReaderOptions & TransactionalOptions & {
|
|
1379
1397
|
/** Injected clock — one observation must carry one timestamp. 注入時計。 */
|
|
1380
1398
|
now: () => Date;
|
|
1381
1399
|
/** Who the audit columns record. 監査列に残す主体。 */
|
|
@@ -1625,7 +1643,7 @@ type LinkableAssertion = {
|
|
|
1625
1643
|
assertLinkableAndWrite(tenantId: string, groupId: number, write: (runner: unknown) => Promise<void>): Promise<void>;
|
|
1626
1644
|
};
|
|
1627
1645
|
/** Options for the link store. リンクストアの構成。 */
|
|
1628
|
-
type DirectoryLinkStoreOptions = {
|
|
1646
|
+
type DirectoryLinkStoreOptions = TransactionalOptions & {
|
|
1629
1647
|
/** Required — see {@link LinkableAssertion}. 必須 (忘れられる経路を作らないため)。 */
|
|
1630
1648
|
linkable: LinkableAssertion;
|
|
1631
1649
|
/** Establishes the connection before the first statement. 最初の文の前に接続を確立する処理。 */
|
|
@@ -1929,7 +1947,7 @@ declare function promoteLedgerForUser(repository: PromotionRepository, args: {
|
|
|
1929
1947
|
}): Promise<PromotionResult>;
|
|
1930
1948
|
|
|
1931
1949
|
/** Options for {@link createPromotionRepository}. 昇格リポジトリの生成オプション。 */
|
|
1932
|
-
type PromotionRepositoryOptions = {
|
|
1950
|
+
type PromotionRepositoryOptions = TransactionalOptions & {
|
|
1933
1951
|
/** Establishes the connection before the first statement. 最初の文の前に接続を確立する処理。 */
|
|
1934
1952
|
ensureConnection?: EnsureConnection;
|
|
1935
1953
|
/** Written to the audit columns of inserted membership rows. 挿入行の監査列に書く実行者。 */
|
|
@@ -1949,56 +1967,6 @@ type PromotionRepositoryOptions = {
|
|
|
1949
1967
|
*/
|
|
1950
1968
|
declare function createPromotionRepository(dbUnknown: unknown, tables: AuthDirectoryTables, authzTables: AuthzTables, opts: PromotionRepositoryOptions): PromotionRepository;
|
|
1951
1969
|
|
|
1952
|
-
/**
|
|
1953
|
-
* SQL Server parameter-limit helpers shared by every batched statement.
|
|
1954
|
-
* 一括文が共有する SQL Server のパラメータ上限まわりのヘルパー。
|
|
1955
|
-
*
|
|
1956
|
-
* ⚠️ 上限は **行数ではなくパラメータ数** である。1 行あたり何個の値を束ねるかは文ごとに違うので、
|
|
1957
|
-
* 行数だけで分割すると、列の多い `VALUES` が静かに上限を越える。しかも越えるのは件数が閾値を
|
|
1958
|
-
* 跨いだ日だけで、それまでは同じコードが問題なく動いて見える。
|
|
1959
|
-
*/
|
|
1960
|
-
/**
|
|
1961
|
-
* Hard limit of parameters per statement. 1 文あたりのパラメータ数の上限。
|
|
1962
|
-
*
|
|
1963
|
-
* SQL Server の RPC 1 回あたりのパラメータ上限は 2100。数個は文自身の値 (`SET` 句・スカラー
|
|
1964
|
-
* 述語) が使うため、分割の基準にはここから余裕を引いた値を用いる。
|
|
1965
|
-
*/
|
|
1966
|
-
declare const MAX_PARAMETERS = 2100;
|
|
1967
|
-
/** Parameters reserved for the statement's own scalar values. 文自身のスカラー値のための予約枠。 */
|
|
1968
|
-
declare const RESERVED_PARAMETERS = 100;
|
|
1969
|
-
/**
|
|
1970
|
-
* Maximum ids per `IN` list of single-column predicates. 単列述語の `IN` 句 1 回あたりの最大 ID 数。
|
|
1971
|
-
*
|
|
1972
|
-
* 1 行 1 パラメータの用途 (`inArray(col, ids)`) 専用の既定値である。複数列を束ねる `VALUES` や、
|
|
1973
|
-
* 1 文に `IN` を 2 つ描画する `UPDATE` にそのまま使ってはならない — {@link chunkByParameters} を使う。
|
|
1974
|
-
*/
|
|
1975
|
-
declare const MAX_IN_LIST = 500;
|
|
1976
|
-
/**
|
|
1977
|
-
* Splits a list into chunks that fit one statement.
|
|
1978
|
-
* 1 文に収まる大きさへリストを分割する処理。
|
|
1979
|
-
*
|
|
1980
|
-
* @param items - Items to split. 分割対象。
|
|
1981
|
-
* @param size - Maximum chunk size. 1 チャンクの最大数。
|
|
1982
|
-
* @returns Chunks in the original order. 元の順序を保ったチャンク列。
|
|
1983
|
-
* @throws {TypeError} When the size is not a positive integer. 正の整数でない場合。
|
|
1984
|
-
*/
|
|
1985
|
-
declare function chunk<T>(items: readonly T[], size?: number): T[][];
|
|
1986
|
-
/**
|
|
1987
|
-
* Splits a list so each chunk fits the parameter budget of one statement.
|
|
1988
|
-
* 各チャンクが 1 文のパラメータ枠に収まるようリストを分割する処理。
|
|
1989
|
-
*
|
|
1990
|
-
* `VALUES` は 1 行につき列数ぶんのパラメータを束ねる。7 列の台帳行なら 500 行で 3500 個になり、
|
|
1991
|
-
* 上限 2100 を越えて **実行される前に** 拒否される。しかもその失敗は再試行しても同じ場所で起き、
|
|
1992
|
-
* そのグループは永久に台帳を持てない。
|
|
1993
|
-
*
|
|
1994
|
-
* @param items - Items to split. 分割対象。
|
|
1995
|
-
* @param parametersPerItem - Parameters each item binds. 1 件が束ねるパラメータ数。
|
|
1996
|
-
* @param reservedParameters - Parameters the statement itself binds. 文自身が束ねる数。
|
|
1997
|
-
* @returns Chunks in the original order. 元の順序を保ったチャンク列。
|
|
1998
|
-
* @throws {TypeError} When a single item cannot fit the budget. 1 件すら収まらない場合。
|
|
1999
|
-
*/
|
|
2000
|
-
declare function chunkByParameters<T>(items: readonly T[], parametersPerItem: number, reservedParameters?: number): T[][];
|
|
2001
|
-
|
|
2002
1970
|
/**
|
|
2003
1971
|
* @module sync-messages
|
|
2004
1972
|
* @description Operator-facing text the synchronization records against a link.
|
|
@@ -2521,7 +2489,7 @@ type MemberResolverPort = {
|
|
|
2521
2489
|
resolve(members: readonly DirectoryMember[]): Promise<ResolvedDirectoryMember[]>;
|
|
2522
2490
|
};
|
|
2523
2491
|
/** Options for the synchronization repository. 同期リポジトリの構成。 */
|
|
2524
|
-
type SyncRepositoryOptions = {
|
|
2492
|
+
type SyncRepositoryOptions = TransactionalOptions & {
|
|
2525
2493
|
ensureConnection?: EnsureConnection;
|
|
2526
2494
|
/**
|
|
2527
2495
|
* Wall-clock supplier. 実行の時刻を供給する処理。
|
|
@@ -2556,4 +2524,4 @@ type SyncRepositoryOptions = {
|
|
|
2556
2524
|
*/
|
|
2557
2525
|
declare function createDirectorySyncRepository(dbUnknown: unknown, tables: AuthDirectoryTables, authzTables: AuthzTables, opts: SyncRepositoryOptions): DirectorySyncRepository;
|
|
2558
2526
|
|
|
2559
|
-
export { AUTH_DIRECTORY_PROVIDER_COLUMN, AUTH_DIRECTORY_TENANT_SCOPED_TABLES, type ApplyReport, type AuthDirectoryTables, type ClaimedSyncRequest, type DeleteChain, type DirectoryCatalogEntry, type DirectoryCatalogReaderOptions, type DirectoryCatalogSnapshot, type DirectoryCatalogWriterOptions, type DirectoryDb, type DirectoryLinkRow, type DirectoryLinkStoreOptions, type DirectorySyncConfig, type DirectorySyncRepository, type DirectorySyncRunResult, type EnsureConnection, type ExternalGroupGuardOptions, type ExternalLinkLookup, type InsertChain, type LedgerIdentityRow, type LedgerIdentityTables, type LinkGroupInput, type LinkableAssertion,
|
|
2527
|
+
export { AUTH_DIRECTORY_PROVIDER_COLUMN, AUTH_DIRECTORY_TENANT_SCOPED_TABLES, type ApplyReport, type AuthDirectoryTables, type ClaimedSyncRequest, type DeleteChain, type DirectoryCatalogEntry, type DirectoryCatalogReaderOptions, type DirectoryCatalogSnapshot, type DirectoryCatalogWriterOptions, type DirectoryDb, type DirectoryLinkRow, type DirectoryLinkStoreOptions, type DirectorySyncConfig, type DirectorySyncRepository, type DirectorySyncRunResult, type EnsureConnection, type ExternalGroupGuardOptions, type ExternalLinkLookup, type InsertChain, type LedgerIdentityRow, type LedgerIdentityTables, type LinkGroupInput, type LinkableAssertion, MEMBERSHIP_MODES, type MemberResolverPort, NEWLY_UNHEALTHY_SYNC_OUTCOMES, type PromotionRepository, type PromotionRepositoryOptions, type PromotionResult, type ReferencedTable, type RemovalFailure, type RemovalOutcome, STRANDED_REASON, type SelectChain, type StrandedRequests, type SyncFailureCause, type SyncLink, type SyncLoggerPort, type SyncMessages, type SyncOutcome, type SyncRepositoryOptions, type SyncRequestOutcome, type SyncRequestQueueOptions, type SyncRequestResult, type SyncScope, type TransactionalOptions, UNHEALTHY_SYNC_OUTCOMES, type UpdateChain, assertGroupMayBeLinked, assertMembershipMode, classifySyncFailure, createDirectoryCatalogReader, createDirectoryCatalogWriter, createDirectoryLinkStore, createDirectorySync, createDirectorySyncRepository, createPromotionRepository, createSyncRequestQueue, defaultExternalGroupGuardMessage, defaultSyncMessages, defineAuthDirectorySchema, findLedgerRowsForIdentity, promoteLedgerForUser, transactionRunner, withExternalGroupGuards };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var ie=Object.defineProperty;var De=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var ke=Object.prototype.hasOwnProperty;var Re=(e,n)=>{for(var m in n)ie(e,m,{get:n[m],enumerable:!0})},Ee=(e,n,m,d)=>{if(n&&typeof n=="object"||typeof n=="function")for(let u of xe(n))!ke.call(e,u)&&u!==m&&ie(e,u,{get:()=>n[u],enumerable:!(d=De(n,u))||d.enumerable});return e};var Me=e=>Ee(ie({},"__esModule",{value:!0}),e);var Qe={};Re(Qe,{AUTH_DIRECTORY_PROVIDER_COLUMN:()=>te,AUTH_DIRECTORY_TENANT_SCOPED_TABLES:()=>Fe,MAX_IN_LIST:()=>ye,MAX_PARAMETERS:()=>V,MEMBERSHIP_MODES:()=>se,NEWLY_UNHEALTHY_SYNC_OUTCOMES:()=>ne,RESERVED_PARAMETERS:()=>me,STRANDED_REASON:()=>ve,UNHEALTHY_SYNC_OUTCOMES:()=>ce,assertGroupMayBeLinked:()=>Ke,assertMembershipMode:()=>z,chunk:()=>L,chunkByParameters:()=>N,classifySyncFailure:()=>W,createDirectoryCatalogReader:()=>Pe,createDirectoryCatalogWriter:()=>_e,createDirectoryLinkStore:()=>qe,createDirectorySync:()=>We,createDirectorySyncRepository:()=>Xe,createPromotionRepository:()=>Be,createSyncRequestQueue:()=>He,defaultExternalGroupGuardMessage:()=>Le,defaultSyncMessages:()=>le,defineAuthDirectorySchema:()=>je,findLedgerRowsForIdentity:()=>j,promoteLedgerForUser:()=>Oe,toInstant:()=>K,withExternalGroupGuards:()=>$e});module.exports=Me(Qe);var ae=require("@aiquants/authz-core"),_=require("drizzle-orm");function K(e){return e instanceof Date?e:new Date(/(?:Z|[+-]\d{2}:?\d{2})$/.test(e)?e:`${e.replace(" ","T")}Z`)}var V=2100,me=100,ye=500;function L(e,n=500){if(!Number.isInteger(n)||n<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] chunk size must be a positive integer, received ${String(n)}`);let m=[];for(let d=0;d<e.length;d+=n)m.push(e.slice(d,d+n));return m}function N(e,n,m=100){if(!Number.isInteger(n)||n<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] parametersPerItem must be a positive integer, received ${String(n)}`);if(!Number.isInteger(m)||m<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] reservedParameters must be a non-negative integer, received ${String(m)}`);let d=2100-m,u=Math.floor(d/n);if(u<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] a single item binds ${n} parameters, which exceeds the ${d} available in one statement`);return L(e,u)}var Ge=10,$={tenantId:64,provider:32,externalId:256,externalKey:320,name:256,description:1e3,actor:50},ge=2147483647;function Ce(e){if(e===void 0||!Number.isFinite(e))return null;let n=Math.trunc(e);return n<0?null:n>ge?ge:n}function B(e,n,m){let d=e.trim();if(d==="")throw new TypeError(`[@aiquants/auth-directory-drizzle] ${m} must not be blank`);if(d.length>n)throw new TypeError(`[@aiquants/auth-directory-drizzle] ${m} exceeds its column length (${n}); truncating it would change which rows it owns`);return d}function _e(e,n,m){let d=e,u=m.ensureConnection??(async()=>{}),s=n.TDDirectoryCatalog,b=n.TMDirectoryCatalogObservation,f=S=>{let a=new Map;for(let g of S)a.set(g.externalId,g);return[...a.values()]};return{async replace(S,a,g){await u();let r=B((0,ae.assertTenantId)(S,"replace(tenantId)"),$.tenantId,"tenantId"),t=B(a,$.provider,"provider"),i=B(m.actor,$.actor,"actor"),l=m.now(),I=f(g.map(w=>({tenantId:r,provider:t,externalId:w.externalId.slice(0,$.externalId),externalKey:w.externalKey.slice(0,$.externalKey),name:w.name.slice(0,$.name),description:w.description===void 0?null:w.description.slice(0,$.description),memberCount:Ce(w.memberCount),observedAt:l,createdAt:l,createdBy:i})));return await d.transaction(async w=>{await w.delete(s).where((0,_.and)((0,_.eq)(s.tenantId,r),(0,_.eq)(s.provider,t)));for(let R of N(I,Ge))await w.insert(s).values(R);await w.delete(b).where((0,_.and)((0,_.eq)(b.tenantId,r),(0,_.eq)(b.provider,t))),await w.insert(b).values([{tenantId:r,provider:t,observedAt:l,entryCount:I.length,createdAt:l,createdBy:i,updatedAt:l,updatedBy:i}])}),I.length}}}function Pe(e,n,m={}){let d=e,u=m.ensureConnection??(async()=>{}),s=n.TDDirectoryCatalog,b=n.TMDirectoryCatalogObservation;return{async read(f,S){await u();let a=B((0,ae.assertTenantId)(f,"read(tenantId)"),$.tenantId,"tenantId"),g=B(S,$.provider,"provider"),r=await d.select({observedAt:b.observedAt,entryCount:b.entryCount,externalId:s.externalId,externalKey:s.externalKey,name:s.name,description:s.description,memberCount:s.memberCount}).from(b).leftJoin(s,(0,_.and)((0,_.eq)(s.tenantId,b.tenantId),(0,_.eq)(s.provider,b.provider))).where((0,_.and)((0,_.eq)(b.tenantId,a),(0,_.eq)(b.provider,g))).orderBy((0,_.asc)(s.name)),t=r.filter(I=>I.externalId!==null).map(I=>({externalId:String(I.externalId),externalKey:String(I.externalKey),name:String(I.name),...I.description===null?{}:{description:I.description},...I.memberCount===null?{}:{memberCount:Number(I.memberCount)}})),i=r[0];if(i===void 0)return{groups:t,observedAt:null};if(Number(i.entryCount)!==t.length)throw new Error(`[@aiquants/auth-directory-drizzle] catalog is inconsistent for (${a}, ${g}): the observation records ${i.entryCount} entries but ${t.length} rows are present`);let l=K(i.observedAt);if(!Number.isFinite(l.getTime()))throw new Error(`[@aiquants/auth-directory-drizzle] catalog observation for (${a}, ${g}) has an unreadable observed_at`);return{groups:t,observedAt:l}}}}var he=require("@aiquants/authz-core"),Le=(e,n)=>`\u5916\u90E8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9023\u643A\u30B0\u30EB\u30FC\u30D7 (${n}) \u306B\u306F\u7BA1\u7406\u30ED\u30FC\u30EB (${e}) \u3092\u6301\u305F\u305B\u3089\u308C\u307E\u305B\u3093\u3002\u4E0A\u6D41\u5074\u306E\u6240\u5C5E\u5909\u66F4\u304C\u305D\u306E\u307E\u307E\u7BA1\u7406\u8005\u306E\u5897\u6E1B\u306B\u306A\u308B\u305F\u3081\u3001\u7BA1\u7406\u8005\u7D4C\u8DEF\u306F\u30ED\u30FC\u30AB\u30EB\u7BA1\u7406\u306E\u30B0\u30EB\u30FC\u30D7\u306B\u9650\u5B9A\u3057\u3066\u3044\u307E\u3059\u3002`;function U(e,n,m,d){return new Set(e.filter(u=>n.has(u.roleId)&&u.appKey===m&&u.resourceKey===d).map(u=>u.action))}function X(e,n){return new Set(e.filter(m=>m.groupId===n).map(m=>m.roleId))}var F=e=>he.AUTHZ_ACTIONS.every(n=>e.has(n)),Z=(e,n)=>e.find(m=>m.roleId===n)?.roleKey??`#${n}`;function Q(e,n,m,d){let u=[...n].find(b=>F(U(e,new Set([b]),m,d)));return u!==void 0?Z(e,u):[...n].filter(b=>U(e,new Set([b]),m,d).size>0).map(b=>Z(e,b)).join(" + ")}function $e(e,n){return be(e,n)}function be(e,n){let m=e;return{...e,withTransaction:(d,u)=>m.withTransaction((s,b)=>d(be(s,n),b),u),async assignGroupRole(d,u,s,b){await m.withTransaction(async(f,S)=>{let a=await f.listGrants(d),g=X(await f.listGroupRoles(d),u);if(g.add(s),F(U(a,g,n.adminAppKey,n.adminResourceKey))){let r=await n.linkLookup.externalGroupKey(d,u,S);if(r!==null)throw new Error(n.message(Q(a,g,n.adminAppKey,n.adminResourceKey),r))}await f.assignGroupRole(d,u,s,b)})},async updateGrant(d,u,s,b){let f=s.action;if(f===void 0)return m.updateGrant(d,u,s,b);await m.withTransaction(async(S,a)=>{let g=await S.listGrants(d),r=g.find(l=>l.id===u);if(r===void 0)return S.updateGrant(d,u,s,b);let i=(await S.listResources(d)).find(l=>l.id===r.resourceId);if(i?.appKey===n.adminAppKey&&i?.resourceKey===n.adminResourceKey){let l=await S.listGroupRoles(d);for(let I of new Set(l.filter(w=>w.roleId===r.roleId).map(w=>w.groupId))){let w=X(l,I),R=U(g.filter(k=>k.id!==u),w,n.adminAppKey,n.adminResourceKey);if(w.has(r.roleId)&&R.add(f),!F(R))continue;let T=await n.linkLookup.externalGroupKey(d,I,a);if(T!==null)throw new Error(n.message(Q(g,w,n.adminAppKey,n.adminResourceKey)||Z(g,r.roleId),T))}}await S.updateGrant(d,u,s,b)})},async createGrant(d,u,s){await m.withTransaction(async(b,f)=>{let a=(await b.listResources(d)).find(g=>g.id===u.resourceId);if(a?.appKey===n.adminAppKey&&a?.resourceKey===n.adminResourceKey){let g=await b.listGrants(d),r=await b.listGroupRoles(d);for(let t of new Set(r.filter(i=>i.roleId===u.roleId).map(i=>i.groupId))){let i=U(g,X(r,t),n.adminAppKey,n.adminResourceKey);if(i.add(u.action),!F(i))continue;let l=await n.linkLookup.externalGroupKey(d,t,f);if(l!==null)throw new Error(n.message(Q(g,X(r,t),n.adminAppKey,n.adminResourceKey)||Z(g,u.roleId),l))}}await b.createGrant(d,u,s)})}}}async function Ke(e,n,m,d){let u=(await e.listGroupRoles(n)).filter(S=>S.groupId===m);if(u.length===0)return;let s=await e.listGrants(n),b=new Set(u.map(S=>S.roleId));if(!F(U(s,b,d.adminAppKey,d.adminResourceKey)))return;let f=u[0];if(f)throw new Error(d.message(Q(s,b,d.adminAppKey,d.adminResourceKey),f.groupKey))}var q=require("drizzle-orm");async function j(e,n,m){let{ledger:d,group:u}=n,{tenantId:s,externalUserId:b,email:f}=m;if(b===null&&f==="")return[];let S={ledgerId:d.id,groupId:d.groupId,tenantId:u.tenantId,externalUserId:d.externalUserId},a=async l=>(await e.select(S).from(d).innerJoin(u,(0,q.and)((0,q.eq)(u.id,d.groupId),(0,q.eq)(u.tenantId,s),(0,q.eq)(u.isActive,!0))).where(l)).map(w=>({ledgerId:Number(w.ledgerId),groupId:Number(w.groupId),tenantId:String(w.tenantId),externalUserId:w.externalUserId===null?null:String(w.externalUserId)})),g=l=>{let I=new Map;for(let w of l)I.set(w.ledgerId,{ledgerId:w.ledgerId,groupId:w.groupId,tenantId:w.tenantId});return[...I.values()].sort((w,R)=>w.ledgerId-R.ledgerId)},r=b===null?[]:await a((0,q.eq)(d.externalUserId,b));if(f==="")return g(r);let t=await a((0,q.eq)(d.email,f)),i=l=>l.externalUserId!==null&&l.externalUserId!==b;return g([...r,...t.filter(l=>!i(l))])}var ee=require("@aiquants/auth-core"),fe=require("@aiquants/auth-directory-core"),G=require("@aiquants/authz-core"),c=require("drizzle-orm");var se=["direct","transitive"];function z(e){if(!se.includes(e))throw new TypeError(`[@aiquants/auth-directory-drizzle] membershipMode must be one of ${se.join(" | ")}, received ${JSON.stringify(e)}`);return e}function Ne(e){let n=typeof e=="object"&&e!==null&&"number"in e?e.number:void 0;return n===2601||n===2627}function qe(e,n,m,d){let u=e,s=d.ensureConnection??(async()=>{}),b=d.policyCacheTtlMs??0;if(!Number.isFinite(b)||b<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(d.policyCacheTtlMs)}`);let f=new Map,S=o=>f.delete(o),{TDGroupExternalLink:a,TDGroupMemberEmail:g,TDTenantMembershipGroup:r,TDAllowlistGroup:t,TDDirectorySyncRequest:i}=n,{TMGroup:l,TDGroupMember:I}=m;async function w(o,y,h){if((await o.select({groupId:r.groupId}).from(r).where((0,c.and)((0,c.eq)(r.tenantId,y),(0,c.eq)(r.groupId,h),(0,c.eq)(r.isBreakGlass,!0)))).length>0)throw new Error(`Group ${h} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function R(o,y,h){let[v]=await o.select({id:l.id}).from(l).where((0,c.and)((0,c.eq)(l.id,h),(0,c.eq)(l.tenantId,y)));if(!v)throw new Error(`Group not found in tenant: ${h}`)}function T(o,y){return{linkId:Number(o.linkId),groupId:Number(o.groupId),tenantId:String(o.tenantId),provider:String(o.provider),externalId:String(o.externalId),externalKey:String(o.externalKey),membershipMode:z(String(o.membershipMode)),isSyncEnabled:!!o.isSyncEnabled,...o.syncedAt?{syncedAt:K(o.syncedAt).toISOString()}:{},...o.syncError?{syncError:String(o.syncError)}:{},unresolvedCount:y}}async function k(o,y){let h=await o.select({groupId:g.groupId,n:(0,c.count)()}).from(g).innerJoin(l,(0,c.and)((0,c.eq)(l.id,g.groupId),(0,c.eq)(l.tenantId,y))).where((0,c.isNull)(g.resolvedUserId)).groupBy(g.groupId);return new Map(h.map(v=>[Number(v.groupId),Number(v.n)]))}return{async listLinks(o){(0,G.assertTenantId)(o,"listLinks(tenantId)"),await s();let y=await u.select({linkId:a.id,groupId:a.groupId,tenantId:a.tenantId,provider:a.provider,externalId:a.externalId,externalKey:a.externalKey,membershipMode:a.membershipMode,isSyncEnabled:a.isSyncEnabled,syncedAt:a.syncedAt,syncError:a.syncError}).from(a).where((0,c.eq)(a.tenantId,o)).orderBy((0,c.asc)(a.groupId)),h=await k(u,o);return y.map(v=>((0,G.assertTenantMatch)(o,String(v.tenantId),"listLinks(row.tenantId)"),T(v,h.get(Number(v.groupId))??0)))},async externalGroupKey(o,y,h){(0,G.assertTenantId)(o,"externalGroupKey(tenantId)"),await s();let[v]=await(h??u).select({externalKey:a.externalKey}).from(a).where((0,c.and)((0,c.eq)(a.groupId,y),(0,c.eq)(a.tenantId,o)));return v?String(v.externalKey):null},async linkGroup(o,y,h,v){(0,G.assertTenantId)(o,"linkGroup(tenantId)");let D=z(h.membershipMode),x=h.externalId.trim(),C=(0,ee.normalizeAuthEmail)(h.externalKey);if(x===""||C==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await s(),await d.linkable.assertLinkableAndWrite(o,y,async Y=>{let E=Y;await R(E,o,y),await w(E,o,y),await E.insert(a).output().values({groupId:y,tenantId:o,provider:h.provider,externalId:x,externalKey:C,membershipMode:D,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:v})}),S(o)},async unlinkGroup(o,y){(0,G.assertTenantId)(o,"unlinkGroup(tenantId)"),await s(),await u.transaction(async h=>{await R(h,o,y),await h.delete(g).where((0,c.eq)(g.groupId,y)),await h.delete(t).where((0,c.and)((0,c.eq)(t.groupId,y),(0,c.eq)(t.tenantId,o))),await h.delete(a).where((0,c.and)((0,c.eq)(a.groupId,y),(0,c.eq)(a.tenantId,o)))}),S(o)},async setSyncEnabled(o,y,h,v){(0,G.assertTenantId)(o,"setSyncEnabled(tenantId)"),await s(),await u.update(a).set({isSyncEnabled:h,updatedAt:new Date,updatedBy:v}).output().where((0,c.and)((0,c.eq)(a.groupId,y),(0,c.eq)(a.tenantId,o)))},async requestSync(o,y,h){return(0,G.assertTenantId)(o,"requestSync(tenantId)"),await s(),u.transaction(async v=>{if(y!==null&&await R(v,o,y),(await v.select({id:i.id}).from(i).where((0,c.and)((0,c.eq)(i.tenantId,o),y===null?(0,c.isNull)(i.groupId):(0,c.eq)(i.groupId,y),(0,c.isNull)(i.pickedAt)))).length>0)return"already-pending";try{await v.insert(i).output().values({tenantId:o,groupId:y,requestedBy:h,requestedAt:new Date,createdAt:new Date,createdBy:h})}catch(x){if(!Ne(x))throw x;return"already-pending"}return"queued"})},async getStatus(o){(0,G.assertTenantId)(o,"getStatus(tenantId)"),await s();let y=await u.select({groupId:a.groupId,isSyncEnabled:a.isSyncEnabled,syncedAt:a.syncedAt,syncError:a.syncError}).from(a).where((0,c.eq)(a.tenantId,o)),[h]=await u.select({n:(0,c.count)()}).from(i).where((0,c.and)((0,c.eq)(i.tenantId,o),(0,c.isNull)(i.finishedAt))),v=y.filter(E=>!!E.isSyncEnabled),D=v.map(E=>E.syncedAt?K(E.syncedAt).getTime():null),x=D.length>0&&D.every(E=>E!==null)?Math.min(...D):null,Y=y.length>0&&v.length===0||v.length>0&&(x===null||Date.now()-x>d.stalenessThresholdSec*1e3);return{isEnabled:d.isEnabled,provider:d.provider,linkedGroupCount:y.length,...x!==null?{oldestSyncedAt:new Date(x).toISOString()}:{},isStale:Y,pausedGroupCount:y.length-v.length,stalenessThresholdSec:d.stalenessThresholdSec,erroredGroupIds:y.filter(E=>!!E.syncError).map(E=>Number(E.groupId)).sort((E,Te)=>E-Te),pendingRequestCount:Number(h?.n??0)}},async loadTenantMembershipPolicy(o){(0,G.assertTenantId)(o,"loadTenantMembershipPolicy(tenantId)");let y=b>0?f.get(o):void 0;if(y&&Date.now()-y.loadedAt<b)return y.policy;await s();let h=await u.select({groupId:r.groupId,isBreakGlass:r.isBreakGlass,externalLinkId:a.id}).from(r).innerJoin(l,(0,c.and)((0,c.eq)(l.id,r.groupId),(0,c.eq)(l.tenantId,o))).leftJoin(a,(0,c.and)((0,c.eq)(a.groupId,r.groupId),(0,c.eq)(a.tenantId,o))).where((0,c.eq)(r.tenantId,o));if(h.length===0){let C={kind:"no-proof-required"};return b>0&&f.set(o,{policy:C,loadedAt:Date.now()}),C}let v=h.map(C=>Number(C.groupId)),D=h.filter(C=>!!C.isBreakGlass);for(let C of D)if(C.externalLinkId!==null)throw new Error(`Break-glass group ${Number(C.groupId)} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`);let x={kind:"require-proof",proofGroupIds:v,breakGlassGroupIds:D.map(C=>Number(C.groupId))};return(0,fe.assertTenantMembershipPolicy)(x,`tenant ${o}`),b>0&&f.set(o,{policy:x,loadedAt:Date.now()}),x},async setTenantMembershipGroup(o,y,h,v){(0,G.assertTenantId)(o,"setTenantMembershipGroup(tenantId)"),await s(),await u.transaction(async D=>{if(await R(D,o,y),h){let[C]=await D.select({id:a.id}).from(a).where((0,c.and)((0,c.eq)(a.tenantId,o),(0,c.eq)(a.groupId,y)));if(C)throw new Error(`Group ${y} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`)}let x=await D.select({groupId:r.groupId}).from(r).where((0,c.and)((0,c.eq)(r.tenantId,o),(0,c.eq)(r.groupId,y)));if(!h&&!(await D.select({groupId:r.groupId,isBreakGlass:r.isBreakGlass}).from(r).where((0,c.eq)(r.tenantId,o))).filter(E=>Number(E.groupId)!==y).some(E=>!!E.isBreakGlass))throw new Error(`Declaring group ${y} as ordinary proof would leave tenant ${o} with a membership proof but no break-glass path; declare a locally-managed break-glass group first.`);if(x.length>0){await D.update(r).set({isBreakGlass:h,updatedAt:new Date,updatedBy:v}).output().where((0,c.and)((0,c.eq)(r.tenantId,o),(0,c.eq)(r.groupId,y)));return}await D.insert(r).output().values({tenantId:o,groupId:y,isBreakGlass:h,createdAt:new Date,createdBy:v})}),S(o)},async removeTenantMembershipGroup(o,y){(0,G.assertTenantId)(o,"removeTenantMembershipGroup(tenantId)"),await s(),await u.transaction(async h=>{let D=(await h.select({groupId:r.groupId,isBreakGlass:r.isBreakGlass}).from(r).where((0,c.eq)(r.tenantId,o))).filter(x=>Number(x.groupId)!==y);if(D.length>0&&!D.some(x=>!!x.isBreakGlass))throw new Error(`Removing group ${y} would leave tenant ${o} with a membership proof but no break-glass path.`);await h.delete(r).where((0,c.and)((0,c.eq)(r.tenantId,o),(0,c.eq)(r.groupId,y)))}),S(o)},async setGroupActive(o,y,h,v){(0,G.assertTenantId)(o,"setGroupActive(tenantId)"),await s(),await u.update(l).set({isActive:h,updatedAt:new Date,updatedBy:v}).output().where((0,c.and)((0,c.eq)(l.id,y),(0,c.eq)(l.tenantId,o)))},async listLedgerGroupIds(o,y){if((0,G.assertTenantId)(o,"listLedgerGroupIds(tenantId)"),!d.isEnabled)return[];await s();let h=await j(u,{ledger:g,group:l},{tenantId:o,externalUserId:y.externalUserId?.trim()||null,email:(0,ee.normalizeAuthEmail)(y.email)});return[...new Set(h.map(v=>v.groupId))].sort((v,D)=>v-D)},async isEmailAllowedByGroup(o){if(!d.isEnabled)return!1;let y=(0,ee.normalizeAuthEmail)(o);if(y==="")return!1;await s();let[h]=await u.select({id:g.id}).from(g).innerJoin(t,(0,c.eq)(t.groupId,g.groupId)).innerJoin(l,(0,c.and)((0,c.eq)(l.id,g.groupId),(0,c.eq)(l.isActive,!0))).where((0,c.eq)(g.email,y));return!!h},async listAllowlistGroups(o){return(0,G.assertTenantId)(o,"listAllowlistGroups(tenantId)"),await s(),(await u.select({id:t.id,groupId:t.groupId,groupKey:l.groupKey,groupName:l.name,description:t.description}).from(t).innerJoin(l,(0,c.and)((0,c.eq)(l.id,t.groupId),(0,c.eq)(l.tenantId,o))).where((0,c.eq)(t.tenantId,o)).orderBy((0,c.asc)(l.name))).map(h=>({id:Number(h.id),type:"group",groupId:Number(h.groupId),groupKey:String(h.groupKey),groupName:String(h.groupName),...h.description?{description:String(h.description)}:{},owner:{kind:"tenant",tenantId:o},readOnly:!1}))},async addAllowlistGroup(o,y,h,v){(0,G.assertTenantId)(o,"addAllowlistGroup(tenantId)"),await s(),await u.transaction(async D=>{await R(D,o,y);let[x]=await D.select({id:a.id}).from(a).where((0,c.and)((0,c.eq)(a.groupId,y),(0,c.eq)(a.tenantId,o)));if(!x)throw new Error(`Group ${y} is locally managed; only externally-sourced groups can gate sign-in, because the decision reads the upstream member ledger.`);await D.insert(t).output().values({tenantId:o,groupId:y,description:h??null,createdAt:new Date,createdBy:v})})},async removeAllowlistGroup(o,y){(0,G.assertTenantId)(o,"removeAllowlistGroup(tenantId)"),await s(),await u.delete(t).where((0,c.and)((0,c.eq)(t.id,y),(0,c.eq)(t.tenantId,o)))},async countProjectedMembers(o,y){(0,G.assertTenantId)(o,"countProjectedMembers(tenantId)"),await s();let[h]=await u.select({n:(0,c.count)()}).from(I).innerJoin(l,(0,c.and)((0,c.eq)(l.id,I.groupId),(0,c.eq)(l.tenantId,o))).where((0,c.eq)(I.groupId,y));return Number(h?.n??0)}}}var we=require("@aiquants/auth-core"),re=require("@aiquants/authz-core");async function Oe(e,n){let m=(0,re.assertTenantId)(n.tenantId,"promoteLedgerForUser(tenantId)");if(!Number.isInteger(n.userId)||n.userId<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] userId must be a positive integer, received ${String(n.userId)}`);let d=(0,we.normalizeAuthEmail)(n.email),u=n.externalUserId?.trim()||null;if(u===null&&d==="")return{addedGroupIds:[],matchedLedgerIds:[]};let s=await e.findLedgerMatches({tenantId:m,externalUserId:u,email:d});if(s.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let a of s)(0,re.assertTenantMatch)(m,a.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let b=[...new Set(s.map(a=>a.ledgerId))].sort((a,g)=>a-g),f=[...new Set(s.map(a=>a.groupId))].sort((a,g)=>a-g);return{addedGroupIds:[...await e.attachUserToGroups({tenantId:m,userId:n.userId,ledgerIds:b,groupIds:f})].sort((a,g)=>a-g),matchedLedgerIds:b}}var ue=require("@aiquants/authz-core"),P=require("drizzle-orm");var Ue=4,ze=100;function Be(e,n,m,d){let u=e,s=d.ensureConnection??(async()=>{}),b=d.now??(()=>new Date),f=n.TDGroupMemberEmail,{TMGroup:S,TDGroupMember:a}=m;return{async findLedgerMatches(g){return(0,ue.assertTenantId)(g.tenantId,"findLedgerMatches(tenantId)"),await s(),j(u,{ledger:f,group:S},g)},async attachUserToGroups({tenantId:g,userId:r,ledgerIds:t,groupIds:i}){return(0,ue.assertTenantId)(g,"attachUserToGroups(tenantId)"),await s(),t.length===0?[]:u.transaction(async l=>{let I=new Set;for(let o of L(i)){let y=await l.select({id:S.id}).from(S).where((0,P.and)((0,P.inArray)(S.id,o),(0,P.eq)(S.tenantId,g),(0,P.eq)(S.isActive,!0)));for(let h of y)I.add(Number(h.id))}let w=i.filter(o=>I.has(o));if(w.length===0)return[];let R=new Set;for(let o of L(w)){let y=await l.select({groupId:a.groupId}).from(a).where((0,P.and)((0,P.eq)(a.userId,r),(0,P.inArray)(a.groupId,o)));for(let h of y)R.add(Number(h.groupId))}let T=w.filter(o=>!R.has(o)),k=Math.max(1,2100-ze-w.length);for(let o of L(t,k))await l.update(f).set({resolvedUserId:r}).output().where((0,P.and)((0,P.inArray)(f.id,o),(0,P.inArray)(f.groupId,w)));if(T.length>0){let o=b();for(let y of N(T,Ue))await l.insert(a).output().values(y.map(h=>({groupId:h,userId:r,createdAt:o,createdBy:d.actor})))}return T})}}}var O=require("@aiquants/authz-drizzle"),p=require("drizzle-orm/mssql-core"),te="provider",Fe=["TDGroupExternalLink","TDTenantMembershipGroup","TDAllowlistGroup","TDDirectorySyncRequest","TDDirectoryCatalog","TMDirectoryCatalogObservation","TMAllowlist"];function je(e,n){let m=(0,p.mssqlSchema)(e),d=()=>({createdAt:(0,p.datetime2)("created_at"),createdBy:(0,p.nvarchar)("created_by",{length:50}),updatedAt:(0,p.datetime2)("updated_at"),updatedBy:(0,p.nvarchar)("updated_by",{length:50})}),u=()=>({createdAt:(0,p.datetime2)("created_at"),createdBy:(0,p.nvarchar)("created_by",{length:50})}),s=m.table("TDGroupExternalLink",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),tenantId:(0,p.nvarchar)(O.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,p.nvarchar)(te,{length:32}).notNull(),externalId:(0,p.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,p.nvarchar)("external_key",{length:320}).notNull(),membershipMode:(0,p.nvarchar)("membership_mode",{length:16}).notNull(),isSyncEnabled:(0,p.bit)("is_sync_enabled").notNull(),syncedAt:(0,p.datetime2)("synced_at"),syncError:(0,p.nvarchar)("sync_error",{length:1e3}),vanishedObservations:(0,p.int)("vanished_observations").notNull(),...d()},i=>[(0,p.primaryKey)({name:`${e}_TDGroupExternalLink_pk`,columns:[i.id]}),(0,p.uniqueIndex)(`${e}_TDGroupExternalLink_group_uq`).on(i.groupId),(0,p.uniqueIndex)(`${e}_TDGroupExternalLink_external_uq`).on(i.tenantId,i.provider,i.externalId),(0,p.foreignKey)({name:`${e}_TDGroupExternalLink_group_id_fk`,columns:[i.groupId],foreignColumns:[n.groupTable.id]})]),b=m.table("TDGroupMemberEmail",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),email:(0,p.nvarchar)("email",{length:320}).notNull(),externalUserId:(0,p.nvarchar)("external_user_id",{length:128}),resolvedUserId:(0,p.bigint)("resolved_user_id",{mode:"number"}),observedAt:(0,p.datetime2)("observed_at").notNull(),...d()},i=>[(0,p.primaryKey)({name:`${e}_TDGroupMemberEmail_pk`,columns:[i.id]}),(0,p.uniqueIndex)(`${e}_TDGroupMemberEmail_group_email_uq`).on(i.groupId,i.email),(0,p.foreignKey)({name:`${e}_TDGroupMemberEmail_group_id_fk`,columns:[i.groupId],foreignColumns:[n.groupTable.id]})]),f=m.table("TDTenantMembershipGroup",{tenantId:(0,p.nvarchar)(O.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),isBreakGlass:(0,p.bit)("is_break_glass").notNull(),...u()},i=>[(0,p.primaryKey)({name:`${e}_TDTenantMembershipGroup_pk`,columns:[i.tenantId,i.groupId]}),(0,p.foreignKey)({name:`${e}_TDTenantMembershipGroup_group_id_fk`,columns:[i.groupId],foreignColumns:[n.groupTable.id]})]),S=m.table("TDAllowlistGroup",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(O.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),description:(0,p.nvarchar)("description",{length:512}),...u()},i=>[(0,p.primaryKey)({name:`${e}_TDAllowlistGroup_pk`,columns:[i.id]}),(0,p.uniqueIndex)(`${e}_TDAllowlistGroup_tenant_group_uq`).on(i.tenantId,i.groupId),(0,p.foreignKey)({name:`${e}_TDAllowlistGroup_group_id_fk`,columns:[i.groupId],foreignColumns:[n.groupTable.id]})]),a=m.table("TDDirectorySyncRequest",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(O.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}),requestedBy:(0,p.nvarchar)("requested_by",{length:320}).notNull(),requestedAt:(0,p.datetime2)("requested_at").notNull(),pickedAt:(0,p.datetime2)("picked_at"),finishedAt:(0,p.datetime2)("finished_at"),result:(0,p.nvarchar)("result",{length:32}),error:(0,p.nvarchar)("error",{length:2e3}),...u()},i=>[(0,p.primaryKey)({name:`${e}_TDDirectorySyncRequest_pk`,columns:[i.id]})]),g=m.table("TMAllowlist",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(O.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),pattern:(0,p.nvarchar)("pattern",{length:256}).notNull(),type:(0,p.nvarchar)("type",{length:32}).notNull(),description:(0,p.nvarchar)("description",{length:512}),...u()},i=>[(0,p.primaryKey)({name:`${e}_TMAllowlist_pk`,columns:[i.id]}),(0,p.uniqueIndex)(`${e}_TMAllowlist_pattern_uq`).on(i.tenantId,i.pattern)]),r=m.table("TDDirectoryCatalog",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(O.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,p.nvarchar)(te,{length:32}).notNull(),externalId:(0,p.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,p.nvarchar)("external_key",{length:320}).notNull(),name:(0,p.nvarchar)("name",{length:256}).notNull(),description:(0,p.nvarchar)("description",{length:1e3}),memberCount:(0,p.int)("member_count"),observedAt:(0,p.datetime2)("observed_at").notNull(),...u()},i=>[(0,p.primaryKey)({name:`${e}_TDDirectoryCatalog_pk`,columns:[i.id]}),(0,p.uniqueIndex)(`${e}_TDDirectoryCatalog_external_uq`).on(i.tenantId,i.provider,i.externalId)]),t=m.table("TMDirectoryCatalogObservation",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(O.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,p.nvarchar)(te,{length:32}).notNull(),observedAt:(0,p.datetime2)("observed_at").notNull(),entryCount:(0,p.int)("entry_count").notNull(),...d()},i=>[(0,p.primaryKey)({name:`${e}_TMDirectoryCatalogObservation_pk`,columns:[i.id]}),(0,p.uniqueIndex)(`${e}_TMDirectoryCatalogObservation_target_uq`).on(i.tenantId,i.provider)]);return{TDGroupExternalLink:s,TDGroupMemberEmail:b,TDTenantMembershipGroup:f,TDAllowlistGroup:S,TDDirectorySyncRequest:a,TDDirectoryCatalog:r,TMDirectoryCatalogObservation:t,TMAllowlist:g}}var oe=require("@aiquants/auth-directory-core");function W(e){if(e instanceof Error&&(e.name==="AbortError"||e.name==="TimeoutError"))return"timeout";let n=typeof e=="object"&&e!==null&&"status"in e?e.status:void 0;return typeof n!="number"?"unknown":n===401||n===403?"denied":n===429||n===408?"rateLimited":n>=500?"upstreamError":"unknown"}var Ie=e=>`${e.length} \u4EF6`,de={denied:"\u8CC7\u683C\u60C5\u5831\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",rateLimited:"\u547C\u3073\u51FA\u3057\u304C\u5236\u9650\u3055\u308C\u3066\u3044\u307E\u3059",upstreamError:"\u76F8\u624B\u5074\u304C\u5185\u90E8\u30A8\u30E9\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F",timeout:"\u5FDC\u7B54\u304C\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F",localWriteError:"\u66F8\u304D\u8FBC\u307F\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",unknown:"\u8A73\u7D30\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u30ED\u30B0\u306B\u51FA\u3066\u3044\u307E\u3059"},le={upstreamUnreadable:e=>`\u4E0A\u6D41\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F (${de[e]})\u3002`,localWriteFailed:e=>`\u30ED\u30FC\u30AB\u30EB\u3078\u306E\u66F8\u304D\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F (${de[e]})\u3002`,resolverIncomplete:(e,n)=>`\u4E0A\u6D41\u304C ${e} \u4EF6\u306E\u30E1\u30F3\u30D0\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F\u304C\u3001\u7A81\u5408\u7D50\u679C\u306F ${n} \u4EF6\u3067\u3057\u305F\u3002\u4EF6\u6570\u304C\u6E1B\u308B\u7A81\u5408\u306F\u3001\u89E3\u6C7A\u3067\u304D\u306A\u304B\u3063\u305F\u4EBA\u7269\u3092\u300C\u4E0A\u6D41\u304B\u3089\u6D88\u3048\u305F\u300D\u3068\u3057\u3066\u524A\u9664\u3057\u3066\u3057\u307E\u3046\u305F\u3081\u4E2D\u65AD\u3057\u307E\u3057\u305F\u3002\u89E3\u6C7A\u3067\u304D\u306A\u3044\u30E1\u30F3\u30D0\u30FC\u306F\u7701\u7565\u305B\u305A resolvedUserId: null \u3067\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`,groupMissing:(e,n,m)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 (${e})\u3002\u9023\u7D9A ${n} \u56DE\u76EE\u3067\u3059\u3002${m} \u56DE\u306B\u9054\u3057\u305F\u6642\u70B9\u3067\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u3002`,groupVanished:(e,n)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C ${n} \u56DE\u9023\u7D9A\u3067\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F (${e})\u3002\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\u3002\u4E0A\u6D41\u304C\u5FA9\u65E7\u3059\u308C\u3070\u6B21\u56DE\u306E\u540C\u671F\u3067\u81EA\u52D5\u7684\u306B\u518D\u6709\u52B9\u5316\u3055\u308C\u307E\u3059\u3002`,planRefused:e=>{if(e.reason==="invalid-upstream-data")return`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: \u4E0A\u6D41\u306E\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\u306B\u6271\u3048\u306A\u3044\u30A2\u30C9\u30EC\u30B9\u304C ${Ie(e.offendingEmails)} \u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002\u5BFE\u8C61\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u30ED\u30B0\u306B\u51FA\u3066\u3044\u307E\u3059\u3002`;let n=e.axis==="membership"?"\u6A29\u9650\u3092\u5931\u3046\u4EBA\u6570":"\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u304D\u306A\u304F\u306A\u308B\u4EBA\u6570",m=e.axis==="membership"?e.withheldProjectionRemoves.map(String):e.withheldLedgerDeletes;return[`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: ${n}\u304C ${e.population} \u4EBA\u4E2D ${e.removalCount} \u4EBA\u306B\u306A\u308A\u307E\u3059`,`(\u4E0A\u9650: ${e.limits.maxRemovalCount} \u4EF6 / \u6BD4\u7387 ${e.limits.maxRemovalRatio})\u3002`,`\u5DEE\u3057\u6B62\u3081\u305F\u5BFE\u8C61: ${Ie(m)} (\u4E00\u89A7\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u30ED\u30B0\u306B\u51FA\u3066\u3044\u307E\u3059)\u3002`,"\u4E0A\u6D41\u5074\u306E\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u610F\u56F3\u3057\u305F\u5909\u66F4\u3067\u3042\u308C\u3070\u3001\u3053\u306E\u8EF8\u306E\u4E0A\u9650\u3060\u3051\u3092\u4E00\u6642\u7684\u306B\u5F15\u304D\u4E0A\u3052\u3066\u518D\u5B9F\u884C\u3057\u307E\u3059\u3002"].join(" ")},removalWritesFailed:e=>`${e.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u66F8\u304D\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F (${[...new Set(e.map(n=>de[n.cause]))].join(" / ")})\u3002\u6B21\u56DE\u306E\u5B9F\u884C\u3067\u518D\u8A66\u884C\u3057\u307E\u3059\u3002`,removalsRefused:e=>`${e.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u6700\u5F8C\u306E\u7BA1\u7406\u8005\u30AC\u30FC\u30C9\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F (\u30E6\u30FC\u30B6\u30FC ${e.map(n=>n.userId).join(", ")})\u3002\u6B8B\u308A\u306E\u5909\u66F4\u306F\u9069\u7528\u6E08\u307F\u3067\u3059\u3002`,foreignProvider:(e,n)=>`\u3053\u306E\u30EA\u30F3\u30AF\u306E\u4F9B\u7D66\u5143\u306F ${e} \u3067\u3059\u304C\u3001\u5B9F\u884C\u4E2D\u306E\u540C\u671F\u306F ${n} \u7528\u3067\u3059\u3002\u5BFE\u5FDC\u3059\u308B\u540C\u671F\u30B8\u30E7\u30D6\u304B\u3089\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`};var ne=["aborted","missing","vanished","upstream-failed","local-failed","foreign-provider","not-converged"],ce=[...ne,"already-vanished"];function We(e){let{directory:n,repository:m,policy:d,now:u,logger:s}=e,b=e.messages??le;if((0,oe.assertBlastRadiusPolicy)(d),!Number.isInteger(e.vanishedThreshold)||e.vanishedThreshold<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] vanishedThreshold must be a positive integer, received ${String(e.vanishedThreshold)}`);if(typeof e.provider!="string"||e.provider.trim()==="")throw new TypeError("[@aiquants/auth-directory-drizzle] provider must be a non-empty string so links of other providers can be told apart");async function f(r,t){try{await m.recordFailure(r,t)}catch(i){s.error(`[auth-directory] could not record the failure for ${r.externalKey}:`,i)}}async function S(r){try{let t=await n.getGroup(r.externalId);return t===null?{kind:"missing"}:{kind:"ok",group:t,members:await n.listGroupMembers(t,r.membershipMode)}}catch(t){return{kind:"failed",error:t instanceof Error?t:new Error(String(t))}}}async function a(r){if(r.vanishedObservations>=e.vanishedThreshold&&!r.isGroupActive)return s.warn(`[auth-directory] ${r.externalKey} still missing upstream (already deactivated after ${r.vanishedObservations} absences)`),{kind:"already-vanished",link:r};let t=await m.recordVanishedObservation(r,b.groupMissing(r.externalKey,r.vanishedObservations+1,e.vanishedThreshold));return t<e.vanishedThreshold?(s.warn(`[auth-directory] ${r.externalKey} missing upstream (${t}/${e.vanishedThreshold})`),{kind:"missing",link:r,observations:t}):(await m.markGroupInactive(r),await f(r,b.groupVanished(r.externalKey,t)),s.warn(`[auth-directory] ${r.externalKey} deactivated after ${t} consecutive absences`),{kind:"vanished",link:r})}async function g(r){if(r.provider!==e.provider)return await f(r,b.foreignProvider(r.provider,e.provider)),s.warn(`[auth-directory] skipping ${r.externalKey}: belongs to provider ${r.provider}`),{kind:"foreign-provider",link:r};let t=await S(r);if(t.kind==="missing")return a(r);if(t.kind==="failed")return await f(r,b.upstreamUnreadable(W(t.error))),s.error(`[auth-directory] upstream read failed for ${r.externalKey}:`,t.error),{kind:"upstream-failed",link:r,error:t.error};try{await m.resolveExternalIdentity(r,t.group);let[i,l]=await Promise.all([m.loadGroupSnapshot(r),m.resolveMembers(t.members)]);if(l.length!==t.members.length){let k=b.resolverIncomplete(t.members.length,l.length);return await f(r,k),s.error(`[auth-directory] ${k}`),{kind:"local-failed",link:r,error:new Error(k)}}let I=(0,oe.reconcile)(i,l,d);if(I.aborted){await f(r,b.planRefused(I.aborted));let k=I.aborted.reason==="invalid-upstream-data"?I.aborted.offendingEmails:I.aborted.axis==="membership"?I.aborted.withheldProjectionRemoves.map(String):I.aborted.withheldLedgerDeletes;return s.warn(`[auth-directory] plan refused for ${r.externalKey}: ${I.aborted.reason}; withheld: ${k.join(", ")}`),{kind:"aborted",link:r,plan:I}}let w=I.ledgerUpserts.length>0||I.ledgerDeletes.length>0||I.projectionAdds.length>0||I.projectionRemoves.length>0,R=u(),T={refusals:[],failures:[]};return w&&(T=await m.applyPlan(r,I,R)),await m.recordSuccess(r,t.group,R),T.refusals.length>0&&(await f(r,b.removalsRefused(T.refusals)),s.warn(`[auth-directory] ${T.refusals.length} removal(s) refused for ${r.externalKey}: users ${T.refusals.map(k=>k.userId).join(", ")}`)),T.failures.length>0&&(await f(r,b.removalWritesFailed(T.failures)),s.warn(`[auth-directory] ${T.failures.length} removal(s) could not be written for ${r.externalKey}: users ${T.failures.map(k=>k.userId).join(", ")}`)),I.unresolvedEmails.length>0&&s.info(`[auth-directory] ${r.externalKey}: ${I.unresolvedEmails.length} upstream member(s) have no local user yet`),T.refusals.length>0||T.failures.length>0?{kind:"not-converged",link:r,plan:I,refusals:T.refusals,failures:T.failures}:w?{kind:"applied",link:r,plan:I}:{kind:"unchanged",link:r}}catch(i){let l=i instanceof Error?i:new Error(String(i));return await f(r,b.localWriteFailed("localWriteError")),s.error(`[auth-directory] local write failed for ${r.externalKey}:`,l),{kind:"local-failed",link:r,error:l}}}return{syncOne:g,async run(r){let t=await m.listSyncableLinks(r),i=[];for(let w of t)try{i.push(await g(w))}catch(R){let T=R instanceof Error?R:new Error(String(R));s.error(`[auth-directory] ${w.externalKey} ended abnormally:`,T),i.push({kind:"local-failed",link:w,error:T})}let l=i.filter(w=>ce.includes(w.kind)).length,I=i.filter(w=>ne.includes(w.kind)).length;return s.info(`[auth-directory] run finished: ${i.length} link(s), ${l} unhealthy (${I} new)`),{outcomes:i,unhealthyCount:l,newlyUnhealthyCount:I}}}}var Se=require("@aiquants/authz-core"),M=require("drizzle-orm");var ve="The job that claimed this request ended without recording an outcome; whether the synchronization ran is unknown.",pe=2e3;function He(e,n,m){let d=e,u=m.ensureConnection??(async()=>{}),s=n.TDDirectorySyncRequest,b=(0,Se.assertTenantId)(m.tenantId,"createSyncRequestQueue(tenantId)"),f=m.maxErrorLength??pe;if(!Number.isInteger(f)||f<1||f>pe)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${pe}, received ${String(m.maxErrorLength)}`);let S={async claim(){await u();let a=await d.select({id:s.id}).from(s).where((0,M.and)((0,M.eq)(s.tenantId,b),(0,M.isNull)(s.pickedAt))).orderBy((0,M.asc)(s.id));for(let g of a){let t=(await d.update(s).set({pickedAt:m.now()}).output().where((0,M.and)((0,M.eq)(s.tenantId,b),(0,M.eq)(s.id,Number(g.id)),(0,M.isNull)(s.pickedAt))))?.[0];if(!t)continue;if(t.requestedBy===void 0||t.requestedBy===null)throw new TypeError(`[@aiquants/auth-directory-drizzle] the driver returned a row without requestedBy for request ${String(t.id)}`);let i=t.groupId??null;return{id:Number(t.id),tenantId:b,groupId:i===null?null:Number(i),requestedBy:String(t.requestedBy)}}return null},async finish(a,g){await u();let r=g.error?g.error.slice(0,f):null;return!!(await d.update(s).set({finishedAt:m.now(),result:g.result,error:r}).output().where((0,M.and)((0,M.eq)(s.tenantId,b),(0,M.eq)(s.id,a),(0,M.isNull)(s.finishedAt))))?.length},async listStranded(a){await u();let g=await d.select({id:s.id,pickedAt:s.pickedAt,requestedAt:s.requestedAt}).from(s).where((0,M.and)((0,M.eq)(s.tenantId,b),(0,M.isNull)(s.finishedAt))).orderBy((0,M.asc)(s.id)),r=t=>{let i=K(t);return Number.isNaN(i.getTime())||i<a};return{stranded:g.filter(t=>t.pickedAt!==null&&r(t.pickedAt)).map(t=>Number(t.id)),unclaimed:g.filter(t=>t.pickedAt===null&&r(t.requestedAt)).map(t=>Number(t.id))}},async abandonStranded(a){let{stranded:g}=await S.listStranded(a),r=[];for(let t of g)try{await S.finish(t,{result:"failed",error:ve})&&r.push(t)}catch(i){m.onCloseFailed?.(t,i)}return r}};return S}var H=require("@aiquants/auth-core"),J=require("@aiquants/authz-core"),A=require("drizzle-orm");var Je=7,Ye=4,Ae=1e3;function Ve(e){if(!(e instanceof Error))return!1;let n=e;return n.number===void 0&&n.code===void 0&&e.name==="Error"}function Xe(e,n,m,d){let u=e,s=d.ensureConnection??(async()=>{}),b=d.now??(()=>new Date),{TDGroupExternalLink:f,TDGroupMemberEmail:S}=n,{TMGroup:a,TDGroupMember:g}=m,r=d.actor;return{async listSyncableLinks(t){await s();let i=(0,J.assertTenantId)(t.tenantId,"listSyncableLinks(tenantId)"),l=[(0,A.eq)(f.isSyncEnabled,!0),(0,A.eq)(f.tenantId,i)];return t.groupId!==void 0&&l.push((0,A.eq)(f.groupId,t.groupId)),(await u.select({linkId:f.id,groupId:f.groupId,linkTenantId:f.tenantId,groupTenantId:a.tenantId,groupKey:a.groupKey,isGroupActive:a.isActive,provider:f.provider,externalId:f.externalId,externalKey:f.externalKey,membershipMode:f.membershipMode,vanishedObservations:f.vanishedObservations}).from(f).innerJoin(a,(0,A.and)((0,A.eq)(a.id,f.groupId),(0,A.eq)(a.tenantId,i))).where((0,A.and)(...l))).map(w=>((0,J.assertTenantMatch)(String(w.groupTenantId),String(w.linkTenantId),"listSyncableLinks(link.tenantId vs TMGroup.tenant_id)"),{linkId:Number(w.linkId),groupId:Number(w.groupId),groupKey:String(w.groupKey),tenantId:String(w.groupTenantId),provider:String(w.provider),externalId:String(w.externalId),externalKey:String(w.externalKey),membershipMode:z(String(w.membershipMode)),isGroupActive:!!w.isGroupActive,vanishedObservations:Number(w.vanishedObservations??0)}))},async loadGroupSnapshot(t){await s();let[i,l]=await Promise.all([u.select({email:S.email,externalUserId:S.externalUserId,resolvedUserId:S.resolvedUserId}).from(S).where((0,A.eq)(S.groupId,t.groupId)),u.select({userId:g.userId}).from(g).where((0,A.eq)(g.groupId,t.groupId))]);return{ledger:i.map(I=>({email:String(I.email),...I.externalUserId?{externalUserId:String(I.externalUserId)}:{},resolvedUserId:I.resolvedUserId===null?null:Number(I.resolvedUserId)})),projectedUserIds:l.map(I=>Number(I.userId))}},resolveMembers(t){return d.memberResolver.resolve(t)},async applyPlan(t,i,l){(0,J.assertTenantId)(t.tenantId,"applyPlan(link.tenantId)"),await s(),(i.ledgerUpserts.length>0||i.projectionAdds.length>0)&&await u.transaction(async T=>{let k=i.ledgerUpserts.map(h=>({...h,email:(0,H.normalizeAuthEmail)(h.email)})),o=new Map;for(let h of L(k.map(v=>v.email))){let v=await T.select({id:S.id,email:S.email}).from(S).where((0,A.and)((0,A.eq)(S.groupId,t.groupId),(0,A.inArray)(S.email,h)));for(let D of v)o.set(String(D.email),Number(D.id))}let y=k.filter(h=>!o.has(h.email));for(let h of N(y,Je))await T.insert(S).output().values(h.map(v=>({groupId:t.groupId,email:v.email,externalUserId:v.externalUserId??null,resolvedUserId:v.resolvedUserId,observedAt:l,createdAt:l,createdBy:r})));for(let h of k){let v=o.get(h.email);v!==void 0&&await T.update(S).set({externalUserId:h.externalUserId??null,resolvedUserId:h.resolvedUserId,observedAt:l,updatedAt:l,updatedBy:r}).output().where((0,A.eq)(S.id,v))}for(let h of N(i.projectionAdds,Ye)){let v=new Set((await T.select({userId:g.userId}).from(g).where((0,A.and)((0,A.eq)(g.groupId,t.groupId),(0,A.inArray)(g.userId,h)))).map(x=>Number(x.userId))),D=h.filter(x=>!v.has(x));D.length>0&&await T.insert(g).output().values(D.map(x=>({groupId:t.groupId,userId:x,createdAt:l,createdBy:r})))}});let I=[],w=[];for(let T of i.projectionRemoves)try{await d.lockoutGuard.guardedWrite(t.tenantId,{removeGroupMembership:{groupId:t.groupId,userId:T}},"remove-group-member",k=>k.delete(g).where((0,A.and)((0,A.eq)(g.groupId,t.groupId),(0,A.eq)(g.userId,T))))}catch(k){Ve(k)?I.push({userId:T,applied:!1,refusal:k.message}):w.push({userId:T,cause:W(k)})}let R=I.map(T=>T.userId);if(i.ledgerDeletes.length>0){let T=Math.max(1,1999-R.length);await u.transaction(async k=>{for(let o of L(i.ledgerDeletes,Math.min(500,T))){let y=R.length>0?(0,A.or)((0,A.isNull)(S.resolvedUserId),(0,A.notInArray)(S.resolvedUserId,R)):void 0;await k.delete(S).where((0,A.and)((0,A.eq)(S.groupId,t.groupId),(0,A.inArray)(S.email,o),...y?[y]:[]))}})}return{refusals:I,failures:w}},async resolveExternalIdentity(t,i){await s(),!(t.externalId===i.externalId&&t.externalKey===(0,H.normalizeAuthEmail)(i.externalKey))&&await u.update(f).set({externalId:i.externalId,externalKey:(0,H.normalizeAuthEmail)(i.externalKey)}).output().where((0,A.and)((0,A.eq)(f.id,t.linkId),(0,A.eq)(f.tenantId,t.tenantId)))},async recordSuccess(t,i,l){await s(),await u.transaction(async I=>{await I.update(f).set({syncedAt:l,syncError:null,vanishedObservations:0,externalId:i.externalId,externalKey:(0,H.normalizeAuthEmail)(i.externalKey),updatedAt:l,updatedBy:r}).output().where((0,A.and)((0,A.eq)(f.id,t.linkId),(0,A.eq)(f.tenantId,t.tenantId))),t.isGroupActive||await I.update(a).set({isActive:!0,updatedAt:l,updatedBy:r}).output().where((0,A.and)((0,A.eq)(a.id,t.groupId),(0,A.eq)(a.tenantId,t.tenantId)))})},async recordFailure(t,i){await s(),await u.update(f).set({syncError:i.slice(0,Ae),updatedAt:b(),updatedBy:r}).output().where((0,A.and)((0,A.eq)(f.id,t.linkId),(0,A.eq)(f.tenantId,t.tenantId)))},async recordVanishedObservation(t,i){await s();let l=t.vanishedObservations+1;return await u.update(f).set({vanishedObservations:l,syncError:i.slice(0,Ae),updatedAt:b(),updatedBy:r}).output().where((0,A.and)((0,A.eq)(f.id,t.linkId),(0,A.eq)(f.tenantId,t.tenantId))),l},async markGroupInactive(t){await s(),await u.update(a).set({isActive:!1,updatedAt:b(),updatedBy:r}).output().where((0,A.and)((0,A.eq)(a.id,t.groupId),(0,A.eq)(a.tenantId,t.tenantId),(0,A.eq)(a.isActive,!0)))}}}0&&(module.exports={AUTH_DIRECTORY_PROVIDER_COLUMN,AUTH_DIRECTORY_TENANT_SCOPED_TABLES,MAX_IN_LIST,MAX_PARAMETERS,MEMBERSHIP_MODES,NEWLY_UNHEALTHY_SYNC_OUTCOMES,RESERVED_PARAMETERS,STRANDED_REASON,UNHEALTHY_SYNC_OUTCOMES,assertGroupMayBeLinked,assertMembershipMode,chunk,chunkByParameters,classifySyncFailure,createDirectoryCatalogReader,createDirectoryCatalogWriter,createDirectoryLinkStore,createDirectorySync,createDirectorySyncRepository,createPromotionRepository,createSyncRequestQueue,defaultExternalGroupGuardMessage,defaultSyncMessages,defineAuthDirectorySchema,findLedgerRowsForIdentity,promoteLedgerForUser,toInstant,withExternalGroupGuards});
|
|
1
|
+
"use strict";var ae=Object.defineProperty;var ke=Object.getOwnPropertyDescriptor;var Re=Object.getOwnPropertyNames;var Ee=Object.prototype.hasOwnProperty;var Ge=(e,n)=>{for(var g in n)ae(e,g,{get:n[g],enumerable:!0})},Me=(e,n,g,d)=>{if(n&&typeof n=="object"||typeof n=="function")for(let l of Re(n))!Ee.call(e,l)&&l!==g&&ae(e,l,{get:()=>n[l],enumerable:!(d=ke(n,l))||d.enumerable});return e};var Ce=e=>Me(ae({},"__esModule",{value:!0}),e);var er={};Ge(er,{AUTH_DIRECTORY_PROVIDER_COLUMN:()=>ne,AUTH_DIRECTORY_TENANT_SCOPED_TABLES:()=>We,MEMBERSHIP_MODES:()=>ue,NEWLY_UNHEALTHY_SYNC_OUTCOMES:()=>oe,STRANDED_REASON:()=>Ae,UNHEALTHY_SYNC_OUTCOMES:()=>me,assertGroupMayBeLinked:()=>$e,assertMembershipMode:()=>B,classifySyncFailure:()=>H,createDirectoryCatalogReader:()=>Ke,createDirectoryCatalogWriter:()=>Le,createDirectoryLinkStore:()=>Ue,createDirectorySync:()=>Je,createDirectorySyncRepository:()=>Ze,createPromotionRepository:()=>je,createSyncRequestQueue:()=>Ye,defaultExternalGroupGuardMessage:()=>Oe,defaultSyncMessages:()=>pe,defineAuthDirectorySchema:()=>He,findLedgerRowsForIdentity:()=>W,promoteLedgerForUser:()=>ze,transactionRunner:()=>O,withExternalGroupGuards:()=>Ne});module.exports=Ce(er);var se=require("@aiquants/authz-core"),Q=require("@aiquants/mssql"),_=require("drizzle-orm");var ge=require("@aiquants/mssql");function O(e,n){return g=>(0,ge.runWithTransactionRetry)(d=>e.transaction(l=>d(()=>g(l))),n.transactionRetry)}var _e=10,N={tenantId:64,provider:32,externalId:256,externalKey:320,name:256,description:1e3,actor:50},he=2147483647;function Pe(e){if(e===void 0||!Number.isFinite(e))return null;let n=Math.trunc(e);return n<0?null:n>he?he:n}function F(e,n,g){let d=e.trim();if(d==="")throw new TypeError(`[@aiquants/auth-directory-drizzle] ${g} must not be blank`);if(d.length>n)throw new TypeError(`[@aiquants/auth-directory-drizzle] ${g} exceeds its column length (${n}); truncating it would change which rows it owns`);return d}function Le(e,n,g){let d=e,l=g.ensureConnection??(async()=>{}),s=O(d,g),f=n.TDDirectoryCatalog,v=n.TMDirectoryCatalogObservation,w=b=>{let a=new Map;for(let t of b)a.set(t.externalId,t);return[...a.values()]};return{async replace(b,a,t){await l();let i=F((0,se.assertTenantId)(b,"replace(tenantId)"),N.tenantId,"tenantId"),r=F(a,N.provider,"provider"),h=F(g.actor,N.actor,"actor"),u=g.now(),I=w(t.map(A=>({tenantId:i,provider:r,externalId:A.externalId.slice(0,N.externalId),externalKey:A.externalKey.slice(0,N.externalKey),name:A.name.slice(0,N.name),description:A.description===void 0?null:A.description.slice(0,N.description),memberCount:Pe(A.memberCount),observedAt:u,createdAt:u,createdBy:h})));return await s(async A=>{await A.delete(f).where((0,_.and)((0,_.eq)(f.tenantId,i),(0,_.eq)(f.provider,r)));for(let D of(0,Q.chunkByParameters)(I,_e))await A.insert(f).values(D);await A.delete(v).where((0,_.and)((0,_.eq)(v.tenantId,i),(0,_.eq)(v.provider,r))),await A.insert(v).values([{tenantId:i,provider:r,observedAt:u,entryCount:I.length,createdAt:u,createdBy:h,updatedAt:u,updatedBy:h}])}),I.length}}}function Ke(e,n,g={}){let d=e,l=g.ensureConnection??(async()=>{}),s=n.TDDirectoryCatalog,f=n.TMDirectoryCatalogObservation;return{async read(v,w){await l();let b=F((0,se.assertTenantId)(v,"read(tenantId)"),N.tenantId,"tenantId"),a=F(w,N.provider,"provider"),t=await d.select({observedAt:f.observedAt,entryCount:f.entryCount,externalId:s.externalId,externalKey:s.externalKey,name:s.name,description:s.description,memberCount:s.memberCount}).from(f).leftJoin(s,(0,_.and)((0,_.eq)(s.tenantId,f.tenantId),(0,_.eq)(s.provider,f.provider))).where((0,_.and)((0,_.eq)(f.tenantId,b),(0,_.eq)(f.provider,a))).orderBy((0,_.asc)(s.name)),i=t.filter(u=>u.externalId!==null).map(u=>({externalId:String(u.externalId),externalKey:String(u.externalKey),name:String(u.name),...u.description===null?{}:{description:u.description},...u.memberCount===null?{}:{memberCount:Number(u.memberCount)}})),r=t[0];if(r===void 0)return{groups:i,observedAt:null};if(Number(r.entryCount)!==i.length)throw new Error(`[@aiquants/auth-directory-drizzle] catalog is inconsistent for (${b}, ${a}): the observation records ${r.entryCount} entries but ${i.length} rows are present`);let h=(0,Q.toInstant)(r.observedAt);if(!Number.isFinite(h.getTime()))throw new Error(`[@aiquants/auth-directory-drizzle] catalog observation for (${b}, ${a}) has an unreadable observed_at`);return{groups:i,observedAt:h}}}}var be=require("@aiquants/authz-core"),Oe=(e,n)=>`\u5916\u90E8\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9023\u643A\u30B0\u30EB\u30FC\u30D7 (${n}) \u306B\u306F\u7BA1\u7406\u30ED\u30FC\u30EB (${e}) \u3092\u6301\u305F\u305B\u3089\u308C\u307E\u305B\u3093\u3002\u4E0A\u6D41\u5074\u306E\u6240\u5C5E\u5909\u66F4\u304C\u305D\u306E\u307E\u307E\u7BA1\u7406\u8005\u306E\u5897\u6E1B\u306B\u306A\u308B\u305F\u3081\u3001\u7BA1\u7406\u8005\u7D4C\u8DEF\u306F\u30ED\u30FC\u30AB\u30EB\u7BA1\u7406\u306E\u30B0\u30EB\u30FC\u30D7\u306B\u9650\u5B9A\u3057\u3066\u3044\u307E\u3059\u3002`;function z(e,n,g,d){return new Set(e.filter(l=>n.has(l.roleId)&&l.appKey===g&&l.resourceKey===d).map(l=>l.action))}function X(e,n){return new Set(e.filter(g=>g.groupId===n).map(g=>g.roleId))}var j=e=>be.AUTHZ_ACTIONS.every(n=>e.has(n)),ee=(e,n)=>e.find(g=>g.roleId===n)?.roleKey??`#${n}`;function Z(e,n,g,d){let l=[...n].find(f=>j(z(e,new Set([f]),g,d)));return l!==void 0?ee(e,l):[...n].filter(f=>z(e,new Set([f]),g,d).size>0).map(f=>ee(e,f)).join(" + ")}function Ne(e,n){return fe(e,n)}function fe(e,n){let g=e;return{...e,withTransaction:(d,l)=>g.withTransaction((s,f)=>d(fe(s,n),f),l),async assignGroupRole(d,l,s,f){await g.withTransaction(async(v,w)=>{let b=await v.listGrants(d),a=X(await v.listGroupRoles(d),l);if(a.add(s),j(z(b,a,n.adminAppKey,n.adminResourceKey))){let t=await n.linkLookup.externalGroupKey(d,l,w);if(t!==null)throw new Error(n.message(Z(b,a,n.adminAppKey,n.adminResourceKey),t))}await v.assignGroupRole(d,l,s,f)})},async updateGrant(d,l,s,f){let v=s.action;if(v===void 0)return g.updateGrant(d,l,s,f);await g.withTransaction(async(w,b)=>{let a=await w.listGrants(d),t=a.find(h=>h.id===l);if(t===void 0)return w.updateGrant(d,l,s,f);let r=(await w.listResources(d)).find(h=>h.id===t.resourceId);if(r?.appKey===n.adminAppKey&&r?.resourceKey===n.adminResourceKey){let h=await w.listGroupRoles(d);for(let u of new Set(h.filter(I=>I.roleId===t.roleId).map(I=>I.groupId))){let I=X(h,u),A=z(a.filter(x=>x.id!==l),I,n.adminAppKey,n.adminResourceKey);if(I.has(t.roleId)&&A.add(v),!j(A))continue;let D=await n.linkLookup.externalGroupKey(d,u,b);if(D!==null)throw new Error(n.message(Z(a,I,n.adminAppKey,n.adminResourceKey)||ee(a,t.roleId),D))}}await w.updateGrant(d,l,s,f)})},async createGrant(d,l,s){await g.withTransaction(async(f,v)=>{let b=(await f.listResources(d)).find(a=>a.id===l.resourceId);if(b?.appKey===n.adminAppKey&&b?.resourceKey===n.adminResourceKey){let a=await f.listGrants(d),t=await f.listGroupRoles(d);for(let i of new Set(t.filter(r=>r.roleId===l.roleId).map(r=>r.groupId))){let r=z(a,X(t,i),n.adminAppKey,n.adminResourceKey);if(r.add(l.action),!j(r))continue;let h=await n.linkLookup.externalGroupKey(d,i,v);if(h!==null)throw new Error(n.message(Z(a,X(t,i),n.adminAppKey,n.adminResourceKey)||ee(a,l.roleId),h))}}await f.createGrant(d,l,s)})}}}async function $e(e,n,g,d){let l=(await e.listGroupRoles(n)).filter(w=>w.groupId===g);if(l.length===0)return;let s=await e.listGrants(n),f=new Set(l.map(w=>w.roleId));if(!j(z(s,f,d.adminAppKey,d.adminResourceKey)))return;let v=l[0];if(v)throw new Error(d.message(Z(s,f,d.adminAppKey,d.adminResourceKey),v.groupKey))}var $=require("drizzle-orm");async function W(e,n,g){let{ledger:d,group:l}=n,{tenantId:s,externalUserId:f,email:v}=g;if(f===null&&v==="")return[];let w={ledgerId:d.id,groupId:d.groupId,tenantId:l.tenantId,externalUserId:d.externalUserId},b=async h=>(await e.select(w).from(d).innerJoin(l,(0,$.and)((0,$.eq)(l.id,d.groupId),(0,$.eq)(l.tenantId,s),(0,$.eq)(l.isActive,!0))).where(h)).map(I=>({ledgerId:Number(I.ledgerId),groupId:Number(I.groupId),tenantId:String(I.tenantId),externalUserId:I.externalUserId===null?null:String(I.externalUserId)})),a=h=>{let u=new Map;for(let I of h)u.set(I.ledgerId,{ledgerId:I.ledgerId,groupId:I.groupId,tenantId:I.tenantId});return[...u.values()].sort((I,A)=>I.ledgerId-A.ledgerId)},t=f===null?[]:await b((0,$.eq)(d.externalUserId,f));if(v==="")return a(t);let i=await b((0,$.eq)(d.email,v)),r=h=>h.externalUserId!==null&&h.externalUserId!==f;return a([...t,...i.filter(h=>!r(h))])}var re=require("@aiquants/auth-core"),we=require("@aiquants/auth-directory-core"),M=require("@aiquants/authz-core"),de=require("@aiquants/mssql"),c=require("drizzle-orm");var ue=["direct","transitive"];function B(e){if(!ue.includes(e))throw new TypeError(`[@aiquants/auth-directory-drizzle] membershipMode must be one of ${ue.join(" | ")}, received ${JSON.stringify(e)}`);return e}function qe(e){let n=typeof e=="object"&&e!==null&&"number"in e?e.number:void 0;return n===2601||n===2627}function Ue(e,n,g,d){let l=e,s=d.ensureConnection??(async()=>{}),f=O(l,d),v=d.policyCacheTtlMs??0;if(!Number.isFinite(v)||v<0)throw new TypeError(`[@aiquants/auth-directory-drizzle] policyCacheTtlMs must be a non-negative number, received ${String(d.policyCacheTtlMs)}`);let w=new Map,b=o=>w.delete(o),{TDGroupExternalLink:a,TDGroupMemberEmail:t,TDTenantMembershipGroup:i,TDAllowlistGroup:r,TDDirectorySyncRequest:h}=n,{TMGroup:u,TDGroupMember:I}=g;async function A(o,m,y){if((await o.select({groupId:i.groupId}).from(i).where((0,c.and)((0,c.eq)(i.tenantId,m),(0,c.eq)(i.groupId,y),(0,c.eq)(i.isBreakGlass,!0)))).length>0)throw new Error(`Group ${y} is a break-glass proof group; a break-glass proof path must be one the synchronization can never touch.`)}async function D(o,m,y){let[S]=await o.select({id:u.id}).from(u).where((0,c.and)((0,c.eq)(u.id,y),(0,c.eq)(u.tenantId,m)));if(!S)throw new Error(`Group not found in tenant: ${y}`)}function x(o,m){return{linkId:Number(o.linkId),groupId:Number(o.groupId),tenantId:String(o.tenantId),provider:String(o.provider),externalId:String(o.externalId),externalKey:String(o.externalKey),membershipMode:B(String(o.membershipMode)),isSyncEnabled:!!o.isSyncEnabled,...o.syncedAt?{syncedAt:(0,de.toInstant)(o.syncedAt).toISOString()}:{},...o.syncError?{syncError:String(o.syncError)}:{},unresolvedCount:m}}async function P(o,m){let y=await o.select({groupId:t.groupId,n:(0,c.count)()}).from(t).innerJoin(u,(0,c.and)((0,c.eq)(u.id,t.groupId),(0,c.eq)(u.tenantId,m))).where((0,c.isNull)(t.resolvedUserId)).groupBy(t.groupId);return new Map(y.map(S=>[Number(S.groupId),Number(S.n)]))}return{async listLinks(o){(0,M.assertTenantId)(o,"listLinks(tenantId)"),await s();let m=await l.select({linkId:a.id,groupId:a.groupId,tenantId:a.tenantId,provider:a.provider,externalId:a.externalId,externalKey:a.externalKey,membershipMode:a.membershipMode,isSyncEnabled:a.isSyncEnabled,syncedAt:a.syncedAt,syncError:a.syncError}).from(a).where((0,c.eq)(a.tenantId,o)).orderBy((0,c.asc)(a.groupId)),y=await P(l,o);return m.map(S=>((0,M.assertTenantMatch)(o,String(S.tenantId),"listLinks(row.tenantId)"),x(S,y.get(Number(S.groupId))??0)))},async externalGroupKey(o,m,y){(0,M.assertTenantId)(o,"externalGroupKey(tenantId)"),await s();let[S]=await(y??l).select({externalKey:a.externalKey}).from(a).where((0,c.and)((0,c.eq)(a.groupId,m),(0,c.eq)(a.tenantId,o)));return S?String(S.externalKey):null},async linkGroup(o,m,y,S){(0,M.assertTenantId)(o,"linkGroup(tenantId)");let k=B(y.membershipMode),R=y.externalId.trim(),C=(0,re.normalizeAuthEmail)(y.externalKey);if(R===""||C==="")throw new TypeError("[@aiquants/auth-directory-drizzle] externalId and externalKey must be non-empty");await s(),await d.linkable.assertLinkableAndWrite(o,m,async V=>{let E=V;await D(E,o,m),await A(E,o,m),await E.insert(a).output().values({groupId:m,tenantId:o,provider:y.provider,externalId:R,externalKey:C,membershipMode:k,isSyncEnabled:!0,vanishedObservations:0,createdAt:new Date,createdBy:S})}),b(o)},async unlinkGroup(o,m){(0,M.assertTenantId)(o,"unlinkGroup(tenantId)"),await s(),await f(async y=>{await D(y,o,m),await y.delete(t).where((0,c.eq)(t.groupId,m)),await y.delete(r).where((0,c.and)((0,c.eq)(r.groupId,m),(0,c.eq)(r.tenantId,o))),await y.delete(a).where((0,c.and)((0,c.eq)(a.groupId,m),(0,c.eq)(a.tenantId,o)))}),b(o)},async setSyncEnabled(o,m,y,S){(0,M.assertTenantId)(o,"setSyncEnabled(tenantId)"),await s(),await l.update(a).set({isSyncEnabled:y,updatedAt:new Date,updatedBy:S}).output().where((0,c.and)((0,c.eq)(a.groupId,m),(0,c.eq)(a.tenantId,o)))},async requestSync(o,m,y){return(0,M.assertTenantId)(o,"requestSync(tenantId)"),await s(),l.transaction(async S=>{if(m!==null&&await D(S,o,m),(await S.select({id:h.id}).from(h).where((0,c.and)((0,c.eq)(h.tenantId,o),m===null?(0,c.isNull)(h.groupId):(0,c.eq)(h.groupId,m),(0,c.isNull)(h.pickedAt)))).length>0)return"already-pending";try{await S.insert(h).output().values({tenantId:o,groupId:m,requestedBy:y,requestedAt:new Date,createdAt:new Date,createdBy:y})}catch(R){if(!qe(R))throw R;return"already-pending"}return"queued"})},async getStatus(o){(0,M.assertTenantId)(o,"getStatus(tenantId)"),await s();let m=await l.select({groupId:a.groupId,isSyncEnabled:a.isSyncEnabled,syncedAt:a.syncedAt,syncError:a.syncError}).from(a).where((0,c.eq)(a.tenantId,o)),[y]=await l.select({n:(0,c.count)()}).from(h).where((0,c.and)((0,c.eq)(h.tenantId,o),(0,c.isNull)(h.finishedAt))),S=m.filter(E=>!!E.isSyncEnabled),k=S.map(E=>E.syncedAt?(0,de.toInstant)(E.syncedAt).getTime():null),R=k.length>0&&k.every(E=>E!==null)?Math.min(...k):null,V=m.length>0&&S.length===0||S.length>0&&(R===null||Date.now()-R>d.stalenessThresholdSec*1e3);return{isEnabled:d.isEnabled,provider:d.provider,linkedGroupCount:m.length,...R!==null?{oldestSyncedAt:new Date(R).toISOString()}:{},isStale:V,pausedGroupCount:m.length-S.length,stalenessThresholdSec:d.stalenessThresholdSec,erroredGroupIds:m.filter(E=>!!E.syncError).map(E=>Number(E.groupId)).sort((E,xe)=>E-xe),pendingRequestCount:Number(y?.n??0)}},async loadTenantMembershipPolicy(o){(0,M.assertTenantId)(o,"loadTenantMembershipPolicy(tenantId)");let m=v>0?w.get(o):void 0;if(m&&Date.now()-m.loadedAt<v)return m.policy;await s();let y=await l.select({groupId:i.groupId,isBreakGlass:i.isBreakGlass,externalLinkId:a.id}).from(i).innerJoin(u,(0,c.and)((0,c.eq)(u.id,i.groupId),(0,c.eq)(u.tenantId,o))).leftJoin(a,(0,c.and)((0,c.eq)(a.groupId,i.groupId),(0,c.eq)(a.tenantId,o))).where((0,c.eq)(i.tenantId,o));if(y.length===0){let C={kind:"no-proof-required"};return v>0&&w.set(o,{policy:C,loadedAt:Date.now()}),C}let S=y.map(C=>Number(C.groupId)),k=y.filter(C=>!!C.isBreakGlass);for(let C of k)if(C.externalLinkId!==null)throw new Error(`Break-glass group ${Number(C.groupId)} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`);let R={kind:"require-proof",proofGroupIds:S,breakGlassGroupIds:k.map(C=>Number(C.groupId))};return(0,we.assertTenantMembershipPolicy)(R,`tenant ${o}`),v>0&&w.set(o,{policy:R,loadedAt:Date.now()}),R},async setTenantMembershipGroup(o,m,y,S){(0,M.assertTenantId)(o,"setTenantMembershipGroup(tenantId)"),await s(),await f(async k=>{if(await D(k,o,m),y){let[C]=await k.select({id:a.id}).from(a).where((0,c.and)((0,c.eq)(a.tenantId,o),(0,c.eq)(a.groupId,m)));if(C)throw new Error(`Group ${m} is externally sourced; a break-glass proof path must be one the synchronization can never touch.`)}let R=await k.select({groupId:i.groupId}).from(i).where((0,c.and)((0,c.eq)(i.tenantId,o),(0,c.eq)(i.groupId,m)));if(!y&&!(await k.select({groupId:i.groupId,isBreakGlass:i.isBreakGlass}).from(i).where((0,c.eq)(i.tenantId,o))).filter(E=>Number(E.groupId)!==m).some(E=>!!E.isBreakGlass))throw new Error(`Declaring group ${m} as ordinary proof would leave tenant ${o} with a membership proof but no break-glass path; declare a locally-managed break-glass group first.`);if(R.length>0){await k.update(i).set({isBreakGlass:y,updatedAt:new Date,updatedBy:S}).output().where((0,c.and)((0,c.eq)(i.tenantId,o),(0,c.eq)(i.groupId,m)));return}await k.insert(i).output().values({tenantId:o,groupId:m,isBreakGlass:y,createdAt:new Date,createdBy:S})}),b(o)},async removeTenantMembershipGroup(o,m){(0,M.assertTenantId)(o,"removeTenantMembershipGroup(tenantId)"),await s(),await f(async y=>{let k=(await y.select({groupId:i.groupId,isBreakGlass:i.isBreakGlass}).from(i).where((0,c.eq)(i.tenantId,o))).filter(R=>Number(R.groupId)!==m);if(k.length>0&&!k.some(R=>!!R.isBreakGlass))throw new Error(`Removing group ${m} would leave tenant ${o} with a membership proof but no break-glass path.`);await y.delete(i).where((0,c.and)((0,c.eq)(i.tenantId,o),(0,c.eq)(i.groupId,m)))}),b(o)},async setGroupActive(o,m,y,S){(0,M.assertTenantId)(o,"setGroupActive(tenantId)"),await s(),await l.update(u).set({isActive:y,updatedAt:new Date,updatedBy:S}).output().where((0,c.and)((0,c.eq)(u.id,m),(0,c.eq)(u.tenantId,o)))},async listLedgerGroupIds(o,m){if((0,M.assertTenantId)(o,"listLedgerGroupIds(tenantId)"),!d.isEnabled)return[];await s();let y=await W(l,{ledger:t,group:u},{tenantId:o,externalUserId:m.externalUserId?.trim()||null,email:(0,re.normalizeAuthEmail)(m.email)});return[...new Set(y.map(S=>S.groupId))].sort((S,k)=>S-k)},async isEmailAllowedByGroup(o){if(!d.isEnabled)return!1;let m=(0,re.normalizeAuthEmail)(o);if(m==="")return!1;await s();let[y]=await l.select({id:t.id}).from(t).innerJoin(r,(0,c.eq)(r.groupId,t.groupId)).innerJoin(u,(0,c.and)((0,c.eq)(u.id,t.groupId),(0,c.eq)(u.isActive,!0))).where((0,c.eq)(t.email,m));return!!y},async listAllowlistGroups(o){return(0,M.assertTenantId)(o,"listAllowlistGroups(tenantId)"),await s(),(await l.select({id:r.id,groupId:r.groupId,groupKey:u.groupKey,groupName:u.name,description:r.description}).from(r).innerJoin(u,(0,c.and)((0,c.eq)(u.id,r.groupId),(0,c.eq)(u.tenantId,o))).where((0,c.eq)(r.tenantId,o)).orderBy((0,c.asc)(u.name))).map(y=>({id:Number(y.id),type:"group",groupId:Number(y.groupId),groupKey:String(y.groupKey),groupName:String(y.groupName),...y.description?{description:String(y.description)}:{},owner:{kind:"tenant",tenantId:o},readOnly:!1}))},async addAllowlistGroup(o,m,y,S){(0,M.assertTenantId)(o,"addAllowlistGroup(tenantId)"),await s(),await f(async k=>{await D(k,o,m);let[R]=await k.select({id:a.id}).from(a).where((0,c.and)((0,c.eq)(a.groupId,m),(0,c.eq)(a.tenantId,o)));if(!R)throw new Error(`Group ${m} is locally managed; only externally-sourced groups can gate sign-in, because the decision reads the upstream member ledger.`);await k.insert(r).output().values({tenantId:o,groupId:m,description:y??null,createdAt:new Date,createdBy:S})})},async removeAllowlistGroup(o,m){(0,M.assertTenantId)(o,"removeAllowlistGroup(tenantId)"),await s(),await l.delete(r).where((0,c.and)((0,c.eq)(r.id,m),(0,c.eq)(r.tenantId,o)))},async countProjectedMembers(o,m){(0,M.assertTenantId)(o,"countProjectedMembers(tenantId)"),await s();let[y]=await l.select({n:(0,c.count)()}).from(I).innerJoin(u,(0,c.and)((0,c.eq)(u.id,I.groupId),(0,c.eq)(u.tenantId,o))).where((0,c.eq)(I.groupId,m));return Number(y?.n??0)}}}var Ie=require("@aiquants/auth-core"),te=require("@aiquants/authz-core");async function ze(e,n){let g=(0,te.assertTenantId)(n.tenantId,"promoteLedgerForUser(tenantId)");if(!Number.isInteger(n.userId)||n.userId<=0)throw new TypeError(`[@aiquants/auth-directory-drizzle] userId must be a positive integer, received ${String(n.userId)}`);let d=(0,Ie.normalizeAuthEmail)(n.email),l=n.externalUserId?.trim()||null;if(l===null&&d==="")return{addedGroupIds:[],matchedLedgerIds:[]};let s=await e.findLedgerMatches({tenantId:g,externalUserId:l,email:d});if(s.length===0)return{addedGroupIds:[],matchedLedgerIds:[]};for(let b of s)(0,te.assertTenantMatch)(g,b.tenantId,"promoteLedgerForUser(findLedgerMatches row)");let f=[...new Set(s.map(b=>b.ledgerId))].sort((b,a)=>b-a),v=[...new Set(s.map(b=>b.groupId))].sort((b,a)=>b-a);return{addedGroupIds:[...await e.attachUserToGroups({tenantId:g,userId:n.userId,ledgerIds:f,groupIds:v})].sort((b,a)=>b-a),matchedLedgerIds:f}}var le=require("@aiquants/authz-core"),q=require("@aiquants/mssql"),L=require("drizzle-orm");var Be=4,Fe=100;function je(e,n,g,d){let l=e,s=d.ensureConnection??(async()=>{}),f=O(l,d),v=d.now??(()=>new Date),w=n.TDGroupMemberEmail,{TMGroup:b,TDGroupMember:a}=g;return{async findLedgerMatches(t){return(0,le.assertTenantId)(t.tenantId,"findLedgerMatches(tenantId)"),await s(),W(l,{ledger:w,group:b},t)},async attachUserToGroups({tenantId:t,userId:i,ledgerIds:r,groupIds:h}){return(0,le.assertTenantId)(t,"attachUserToGroups(tenantId)"),await s(),r.length===0?[]:f(async u=>{let I=new Set;for(let o of(0,q.chunk)(h)){let m=await u.select({id:b.id}).from(b).where((0,L.and)((0,L.inArray)(b.id,o),(0,L.eq)(b.tenantId,t),(0,L.eq)(b.isActive,!0)));for(let y of m)I.add(Number(y.id))}let A=h.filter(o=>I.has(o));if(A.length===0)return[];let D=new Set;for(let o of(0,q.chunk)(A)){let m=await u.select({groupId:a.groupId}).from(a).where((0,L.and)((0,L.eq)(a.userId,i),(0,L.inArray)(a.groupId,o)));for(let y of m)D.add(Number(y.groupId))}let x=A.filter(o=>!D.has(o)),P=Math.max(1,q.MAX_PARAMETERS-Fe-A.length);for(let o of(0,q.chunk)(r,P))await u.update(w).set({resolvedUserId:i}).output().where((0,L.and)((0,L.inArray)(w.id,o),(0,L.inArray)(w.groupId,A)));if(x.length>0){let o=v();for(let m of(0,q.chunkByParameters)(x,Be))await u.insert(a).output().values(m.map(y=>({groupId:y,userId:i,createdAt:o,createdBy:d.actor})))}return x})}}}var U=require("@aiquants/authz-drizzle"),p=require("drizzle-orm/mssql-core"),ne="provider",We=["TDGroupExternalLink","TDTenantMembershipGroup","TDAllowlistGroup","TDDirectorySyncRequest","TDDirectoryCatalog","TMDirectoryCatalogObservation","TMAllowlist"];function He(e,n){let g=(0,p.mssqlSchema)(e),d=()=>({createdAt:(0,p.datetime2)("created_at"),createdBy:(0,p.nvarchar)("created_by",{length:50}),updatedAt:(0,p.datetime2)("updated_at"),updatedBy:(0,p.nvarchar)("updated_by",{length:50})}),l=()=>({createdAt:(0,p.datetime2)("created_at"),createdBy:(0,p.nvarchar)("created_by",{length:50})}),s=g.table("TDGroupExternalLink",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),tenantId:(0,p.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,p.nvarchar)(ne,{length:32}).notNull(),externalId:(0,p.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,p.nvarchar)("external_key",{length:320}).notNull(),membershipMode:(0,p.nvarchar)("membership_mode",{length:16}).notNull(),isSyncEnabled:(0,p.bit)("is_sync_enabled").notNull(),syncedAt:(0,p.datetime2)("synced_at"),syncError:(0,p.nvarchar)("sync_error",{length:1e3}),vanishedObservations:(0,p.int)("vanished_observations").notNull(),...d()},r=>[(0,p.primaryKey)({name:`${e}_TDGroupExternalLink_pk`,columns:[r.id]}),(0,p.uniqueIndex)(`${e}_TDGroupExternalLink_group_uq`).on(r.groupId),(0,p.uniqueIndex)(`${e}_TDGroupExternalLink_external_uq`).on(r.tenantId,r.provider,r.externalId),(0,p.foreignKey)({name:`${e}_TDGroupExternalLink_group_id_fk`,columns:[r.groupId],foreignColumns:[n.groupTable.id]})]),f=g.table("TDGroupMemberEmail",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),email:(0,p.nvarchar)("email",{length:320}).notNull(),externalUserId:(0,p.nvarchar)("external_user_id",{length:128}),resolvedUserId:(0,p.bigint)("resolved_user_id",{mode:"number"}),observedAt:(0,p.datetime2)("observed_at").notNull(),...d()},r=>[(0,p.primaryKey)({name:`${e}_TDGroupMemberEmail_pk`,columns:[r.id]}),(0,p.uniqueIndex)(`${e}_TDGroupMemberEmail_group_email_uq`).on(r.groupId,r.email),(0,p.foreignKey)({name:`${e}_TDGroupMemberEmail_group_id_fk`,columns:[r.groupId],foreignColumns:[n.groupTable.id]})]),v=g.table("TDTenantMembershipGroup",{tenantId:(0,p.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),isBreakGlass:(0,p.bit)("is_break_glass").notNull(),...l()},r=>[(0,p.primaryKey)({name:`${e}_TDTenantMembershipGroup_pk`,columns:[r.tenantId,r.groupId]}),(0,p.foreignKey)({name:`${e}_TDTenantMembershipGroup_group_id_fk`,columns:[r.groupId],foreignColumns:[n.groupTable.id]})]),w=g.table("TDAllowlistGroup",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}).notNull(),description:(0,p.nvarchar)("description",{length:512}),...l()},r=>[(0,p.primaryKey)({name:`${e}_TDAllowlistGroup_pk`,columns:[r.id]}),(0,p.uniqueIndex)(`${e}_TDAllowlistGroup_tenant_group_uq`).on(r.tenantId,r.groupId),(0,p.foreignKey)({name:`${e}_TDAllowlistGroup_group_id_fk`,columns:[r.groupId],foreignColumns:[n.groupTable.id]})]),b=g.table("TDDirectorySyncRequest",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),groupId:(0,p.bigint)("group_id",{mode:"number"}),requestedBy:(0,p.nvarchar)("requested_by",{length:320}).notNull(),requestedAt:(0,p.datetime2)("requested_at").notNull(),pickedAt:(0,p.datetime2)("picked_at"),finishedAt:(0,p.datetime2)("finished_at"),result:(0,p.nvarchar)("result",{length:32}),error:(0,p.nvarchar)("error",{length:2e3}),...l()},r=>[(0,p.primaryKey)({name:`${e}_TDDirectorySyncRequest_pk`,columns:[r.id]})]),a=g.table("TMAllowlist",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),pattern:(0,p.nvarchar)("pattern",{length:256}).notNull(),type:(0,p.nvarchar)("type",{length:32}).notNull(),description:(0,p.nvarchar)("description",{length:512}),...l()},r=>[(0,p.primaryKey)({name:`${e}_TMAllowlist_pk`,columns:[r.id]}),(0,p.uniqueIndex)(`${e}_TMAllowlist_pattern_uq`).on(r.tenantId,r.pattern)]),t=g.table("TDDirectoryCatalog",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,p.nvarchar)(ne,{length:32}).notNull(),externalId:(0,p.nvarchar)("external_id",{length:256}).notNull(),externalKey:(0,p.nvarchar)("external_key",{length:320}).notNull(),name:(0,p.nvarchar)("name",{length:256}).notNull(),description:(0,p.nvarchar)("description",{length:1e3}),memberCount:(0,p.int)("member_count"),observedAt:(0,p.datetime2)("observed_at").notNull(),...l()},r=>[(0,p.primaryKey)({name:`${e}_TDDirectoryCatalog_pk`,columns:[r.id]}),(0,p.uniqueIndex)(`${e}_TDDirectoryCatalog_external_uq`).on(r.tenantId,r.provider,r.externalId)]),i=g.table("TMDirectoryCatalogObservation",{id:(0,p.bigint)("id",{mode:"number"}).identity().notNull(),tenantId:(0,p.nvarchar)(U.AUTHZ_TENANT_COLUMN,{length:64}).notNull(),provider:(0,p.nvarchar)(ne,{length:32}).notNull(),observedAt:(0,p.datetime2)("observed_at").notNull(),entryCount:(0,p.int)("entry_count").notNull(),...d()},r=>[(0,p.primaryKey)({name:`${e}_TMDirectoryCatalogObservation_pk`,columns:[r.id]}),(0,p.uniqueIndex)(`${e}_TMDirectoryCatalogObservation_target_uq`).on(r.tenantId,r.provider)]);return{TDGroupExternalLink:s,TDGroupMemberEmail:f,TDTenantMembershipGroup:v,TDAllowlistGroup:w,TDDirectorySyncRequest:b,TDDirectoryCatalog:t,TMDirectoryCatalogObservation:i,TMAllowlist:a}}var ie=require("@aiquants/auth-directory-core");function H(e){if(e instanceof Error&&(e.name==="AbortError"||e.name==="TimeoutError"))return"timeout";let n=typeof e=="object"&&e!==null&&"status"in e?e.status:void 0;return typeof n!="number"?"unknown":n===401||n===403?"denied":n===429||n===408?"rateLimited":n>=500?"upstreamError":"unknown"}var ve=e=>`${e.length} \u4EF6`,ce={denied:"\u8CC7\u683C\u60C5\u5831\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",rateLimited:"\u547C\u3073\u51FA\u3057\u304C\u5236\u9650\u3055\u308C\u3066\u3044\u307E\u3059",upstreamError:"\u76F8\u624B\u5074\u304C\u5185\u90E8\u30A8\u30E9\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F",timeout:"\u5FDC\u7B54\u304C\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F",localWriteError:"\u66F8\u304D\u8FBC\u307F\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",unknown:"\u8A73\u7D30\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u30ED\u30B0\u306B\u51FA\u3066\u3044\u307E\u3059"},pe={upstreamUnreadable:e=>`\u4E0A\u6D41\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F (${ce[e]})\u3002`,localWriteFailed:e=>`\u30ED\u30FC\u30AB\u30EB\u3078\u306E\u66F8\u304D\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F (${ce[e]})\u3002`,resolverIncomplete:(e,n)=>`\u4E0A\u6D41\u304C ${e} \u4EF6\u306E\u30E1\u30F3\u30D0\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F\u304C\u3001\u7A81\u5408\u7D50\u679C\u306F ${n} \u4EF6\u3067\u3057\u305F\u3002\u4EF6\u6570\u304C\u6E1B\u308B\u7A81\u5408\u306F\u3001\u89E3\u6C7A\u3067\u304D\u306A\u304B\u3063\u305F\u4EBA\u7269\u3092\u300C\u4E0A\u6D41\u304B\u3089\u6D88\u3048\u305F\u300D\u3068\u3057\u3066\u524A\u9664\u3057\u3066\u3057\u307E\u3046\u305F\u3081\u4E2D\u65AD\u3057\u307E\u3057\u305F\u3002\u89E3\u6C7A\u3067\u304D\u306A\u3044\u30E1\u30F3\u30D0\u30FC\u306F\u7701\u7565\u305B\u305A resolvedUserId: null \u3067\u8FD4\u3059\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002`,groupMissing:(e,n,g)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093 (${e})\u3002\u9023\u7D9A ${n} \u56DE\u76EE\u3067\u3059\u3002${g} \u56DE\u306B\u9054\u3057\u305F\u6642\u70B9\u3067\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3059\u3002`,groupVanished:(e,n)=>`\u4E0A\u6D41\u3067\u30B0\u30EB\u30FC\u30D7\u304C ${n} \u56DE\u9023\u7D9A\u3067\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3067\u3057\u305F (${e})\u3002\u30B0\u30EB\u30FC\u30D7\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\u3002\u4E0A\u6D41\u304C\u5FA9\u65E7\u3059\u308C\u3070\u6B21\u56DE\u306E\u540C\u671F\u3067\u81EA\u52D5\u7684\u306B\u518D\u6709\u52B9\u5316\u3055\u308C\u307E\u3059\u3002`,planRefused:e=>{if(e.reason==="invalid-upstream-data")return`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: \u4E0A\u6D41\u306E\u30E1\u30F3\u30D0\u30FC\u4E00\u89A7\u306B\u6271\u3048\u306A\u3044\u30A2\u30C9\u30EC\u30B9\u304C ${ve(e.offendingEmails)} \u542B\u307E\u308C\u3066\u3044\u307E\u3059\u3002\u5BFE\u8C61\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u30ED\u30B0\u306B\u51FA\u3066\u3044\u307E\u3059\u3002`;let n=e.axis==="membership"?"\u6A29\u9650\u3092\u5931\u3046\u4EBA\u6570":"\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u304D\u306A\u304F\u306A\u308B\u4EBA\u6570",g=e.axis==="membership"?e.withheldProjectionRemoves.map(String):e.withheldLedgerDeletes;return[`\u540C\u671F\u3092\u4E2D\u65AD\u3057\u307E\u3057\u305F: ${n}\u304C ${e.population} \u4EBA\u4E2D ${e.removalCount} \u4EBA\u306B\u306A\u308A\u307E\u3059`,`(\u4E0A\u9650: ${e.limits.maxRemovalCount} \u4EF6 / \u6BD4\u7387 ${e.limits.maxRemovalRatio})\u3002`,`\u5DEE\u3057\u6B62\u3081\u305F\u5BFE\u8C61: ${ve(g)} (\u4E00\u89A7\u306F\u30B5\u30FC\u30D0\u30FC\u306E\u30ED\u30B0\u306B\u51FA\u3066\u3044\u307E\u3059)\u3002`,"\u4E0A\u6D41\u5074\u306E\u8A2D\u5B9A\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u610F\u56F3\u3057\u305F\u5909\u66F4\u3067\u3042\u308C\u3070\u3001\u3053\u306E\u8EF8\u306E\u4E0A\u9650\u3060\u3051\u3092\u4E00\u6642\u7684\u306B\u5F15\u304D\u4E0A\u3052\u3066\u518D\u5B9F\u884C\u3057\u307E\u3059\u3002"].join(" ")},removalWritesFailed:e=>`${e.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u66F8\u304D\u8FBC\u3081\u307E\u305B\u3093\u3067\u3057\u305F (${[...new Set(e.map(n=>ce[n.cause]))].join(" / ")})\u3002\u6B21\u56DE\u306E\u5B9F\u884C\u3067\u518D\u8A66\u884C\u3057\u307E\u3059\u3002`,removalsRefused:e=>`${e.length} \u4EF6\u306E\u6240\u5C5E\u89E3\u9664\u304C\u6700\u5F8C\u306E\u7BA1\u7406\u8005\u30AC\u30FC\u30C9\u306B\u3088\u308A\u62D2\u5426\u3055\u308C\u307E\u3057\u305F (\u30E6\u30FC\u30B6\u30FC ${e.map(n=>n.userId).join(", ")})\u3002\u6B8B\u308A\u306E\u5909\u66F4\u306F\u9069\u7528\u6E08\u307F\u3067\u3059\u3002`,foreignProvider:(e,n)=>`\u3053\u306E\u30EA\u30F3\u30AF\u306E\u4F9B\u7D66\u5143\u306F ${e} \u3067\u3059\u304C\u3001\u5B9F\u884C\u4E2D\u306E\u540C\u671F\u306F ${n} \u7528\u3067\u3059\u3002\u5BFE\u5FDC\u3059\u308B\u540C\u671F\u30B8\u30E7\u30D6\u304B\u3089\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002`};var oe=["aborted","missing","vanished","upstream-failed","local-failed","foreign-provider","not-converged"],me=[...oe,"already-vanished"];function Je(e){let{directory:n,repository:g,policy:d,now:l,logger:s}=e,f=e.messages??pe;if((0,ie.assertBlastRadiusPolicy)(d),!Number.isInteger(e.vanishedThreshold)||e.vanishedThreshold<1)throw new TypeError(`[@aiquants/auth-directory-drizzle] vanishedThreshold must be a positive integer, received ${String(e.vanishedThreshold)}`);if(typeof e.provider!="string"||e.provider.trim()==="")throw new TypeError("[@aiquants/auth-directory-drizzle] provider must be a non-empty string so links of other providers can be told apart");async function v(t,i){try{await g.recordFailure(t,i)}catch(r){s.error(`[auth-directory] could not record the failure for ${t.externalKey}:`,r)}}async function w(t){try{let i=await n.getGroup(t.externalId);return i===null?{kind:"missing"}:{kind:"ok",group:i,members:await n.listGroupMembers(i,t.membershipMode)}}catch(i){return{kind:"failed",error:i instanceof Error?i:new Error(String(i))}}}async function b(t){if(t.vanishedObservations>=e.vanishedThreshold&&!t.isGroupActive)return s.warn(`[auth-directory] ${t.externalKey} still missing upstream (already deactivated after ${t.vanishedObservations} absences)`),{kind:"already-vanished",link:t};let i=await g.recordVanishedObservation(t,f.groupMissing(t.externalKey,t.vanishedObservations+1,e.vanishedThreshold));return i<e.vanishedThreshold?(s.warn(`[auth-directory] ${t.externalKey} missing upstream (${i}/${e.vanishedThreshold})`),{kind:"missing",link:t,observations:i}):(await g.markGroupInactive(t),await v(t,f.groupVanished(t.externalKey,i)),s.warn(`[auth-directory] ${t.externalKey} deactivated after ${i} consecutive absences`),{kind:"vanished",link:t})}async function a(t){if(t.provider!==e.provider)return await v(t,f.foreignProvider(t.provider,e.provider)),s.warn(`[auth-directory] skipping ${t.externalKey}: belongs to provider ${t.provider}`),{kind:"foreign-provider",link:t};let i=await w(t);if(i.kind==="missing")return b(t);if(i.kind==="failed")return await v(t,f.upstreamUnreadable(H(i.error))),s.error(`[auth-directory] upstream read failed for ${t.externalKey}:`,i.error),{kind:"upstream-failed",link:t,error:i.error};try{await g.resolveExternalIdentity(t,i.group);let[r,h]=await Promise.all([g.loadGroupSnapshot(t),g.resolveMembers(i.members)]);if(h.length!==i.members.length){let x=f.resolverIncomplete(i.members.length,h.length);return await v(t,x),s.error(`[auth-directory] ${x}`),{kind:"local-failed",link:t,error:new Error(x)}}let u=(0,ie.reconcile)(r,h,d);if(u.aborted){await v(t,f.planRefused(u.aborted));let x=u.aborted.reason==="invalid-upstream-data"?u.aborted.offendingEmails:u.aborted.axis==="membership"?u.aborted.withheldProjectionRemoves.map(String):u.aborted.withheldLedgerDeletes;return s.warn(`[auth-directory] plan refused for ${t.externalKey}: ${u.aborted.reason}; withheld: ${x.join(", ")}`),{kind:"aborted",link:t,plan:u}}let I=u.ledgerUpserts.length>0||u.ledgerDeletes.length>0||u.projectionAdds.length>0||u.projectionRemoves.length>0,A=l(),D={refusals:[],failures:[]};return I&&(D=await g.applyPlan(t,u,A)),await g.recordSuccess(t,i.group,A),D.refusals.length>0&&(await v(t,f.removalsRefused(D.refusals)),s.warn(`[auth-directory] ${D.refusals.length} removal(s) refused for ${t.externalKey}: users ${D.refusals.map(x=>x.userId).join(", ")}`)),D.failures.length>0&&(await v(t,f.removalWritesFailed(D.failures)),s.warn(`[auth-directory] ${D.failures.length} removal(s) could not be written for ${t.externalKey}: users ${D.failures.map(x=>x.userId).join(", ")}`)),u.unresolvedEmails.length>0&&s.info(`[auth-directory] ${t.externalKey}: ${u.unresolvedEmails.length} upstream member(s) have no local user yet`),D.refusals.length>0||D.failures.length>0?{kind:"not-converged",link:t,plan:u,refusals:D.refusals,failures:D.failures}:I?{kind:"applied",link:t,plan:u}:{kind:"unchanged",link:t}}catch(r){let h=r instanceof Error?r:new Error(String(r));return await v(t,f.localWriteFailed("localWriteError")),s.error(`[auth-directory] local write failed for ${t.externalKey}:`,h),{kind:"local-failed",link:t,error:h}}}return{syncOne:a,async run(t){let i=await g.listSyncableLinks(t),r=[];for(let I of i)try{r.push(await a(I))}catch(A){let D=A instanceof Error?A:new Error(String(A));s.error(`[auth-directory] ${I.externalKey} ended abnormally:`,D),r.push({kind:"local-failed",link:I,error:D})}let h=r.filter(I=>me.includes(I.kind)).length,u=r.filter(I=>oe.includes(I.kind)).length;return s.info(`[auth-directory] run finished: ${r.length} link(s), ${h} unhealthy (${u} new)`),{outcomes:r,unhealthyCount:h,newlyUnhealthyCount:u}}}}var Se=require("@aiquants/authz-core"),Te=require("@aiquants/mssql"),G=require("drizzle-orm"),Ae="The job that claimed this request ended without recording an outcome; whether the synchronization ran is unknown.",ye=2e3;function Ye(e,n,g){let d=e,l=g.ensureConnection??(async()=>{}),s=n.TDDirectorySyncRequest,f=(0,Se.assertTenantId)(g.tenantId,"createSyncRequestQueue(tenantId)"),v=g.maxErrorLength??ye;if(!Number.isInteger(v)||v<1||v>ye)throw new TypeError(`[@aiquants/auth-directory-drizzle] maxErrorLength must be an integer within 1..${ye}, received ${String(g.maxErrorLength)}`);let w={async claim(){await l();let b=await d.select({id:s.id}).from(s).where((0,G.and)((0,G.eq)(s.tenantId,f),(0,G.isNull)(s.pickedAt))).orderBy((0,G.asc)(s.id));for(let a of b){let i=(await d.update(s).set({pickedAt:g.now()}).output().where((0,G.and)((0,G.eq)(s.tenantId,f),(0,G.eq)(s.id,Number(a.id)),(0,G.isNull)(s.pickedAt))))?.[0];if(!i)continue;if(i.requestedBy===void 0||i.requestedBy===null)throw new TypeError(`[@aiquants/auth-directory-drizzle] the driver returned a row without requestedBy for request ${String(i.id)}`);let r=i.groupId??null;return{id:Number(i.id),tenantId:f,groupId:r===null?null:Number(r),requestedBy:String(i.requestedBy)}}return null},async finish(b,a){await l();let t=a.error?a.error.slice(0,v):null;return!!(await d.update(s).set({finishedAt:g.now(),result:a.result,error:t}).output().where((0,G.and)((0,G.eq)(s.tenantId,f),(0,G.eq)(s.id,b),(0,G.isNull)(s.finishedAt))))?.length},async listStranded(b){await l();let a=await d.select({id:s.id,pickedAt:s.pickedAt,requestedAt:s.requestedAt}).from(s).where((0,G.and)((0,G.eq)(s.tenantId,f),(0,G.isNull)(s.finishedAt))).orderBy((0,G.asc)(s.id)),t=i=>{let r=(0,Te.toInstant)(i);return Number.isNaN(r.getTime())||r<b};return{stranded:a.filter(i=>i.pickedAt!==null&&t(i.pickedAt)).map(i=>Number(i.id)),unclaimed:a.filter(i=>i.pickedAt===null&&t(i.requestedAt)).map(i=>Number(i.id))}},async abandonStranded(b){let{stranded:a}=await w.listStranded(b),t=[];for(let i of a)try{await w.finish(i,{result:"failed",error:Ae})&&t.push(i)}catch(r){g.onCloseFailed?.(i,r)}return t}};return w}var J=require("@aiquants/auth-core"),Y=require("@aiquants/authz-core"),K=require("@aiquants/mssql"),T=require("drizzle-orm");var Ve=7,Qe=4,De=1e3;function Xe(e){if(!(e instanceof Error))return!1;let n=e;return n.number===void 0&&n.code===void 0&&e.name==="Error"}function Ze(e,n,g,d){let l=e,s=d.ensureConnection??(async()=>{}),f=O(l,d),v=d.now??(()=>new Date),{TDGroupExternalLink:w,TDGroupMemberEmail:b}=n,{TMGroup:a,TDGroupMember:t}=g,i=d.actor;return{async listSyncableLinks(r){await s();let h=(0,Y.assertTenantId)(r.tenantId,"listSyncableLinks(tenantId)"),u=[(0,T.eq)(w.isSyncEnabled,!0),(0,T.eq)(w.tenantId,h)];return r.groupId!==void 0&&u.push((0,T.eq)(w.groupId,r.groupId)),(await l.select({linkId:w.id,groupId:w.groupId,linkTenantId:w.tenantId,groupTenantId:a.tenantId,groupKey:a.groupKey,isGroupActive:a.isActive,provider:w.provider,externalId:w.externalId,externalKey:w.externalKey,membershipMode:w.membershipMode,vanishedObservations:w.vanishedObservations}).from(w).innerJoin(a,(0,T.and)((0,T.eq)(a.id,w.groupId),(0,T.eq)(a.tenantId,h))).where((0,T.and)(...u))).map(A=>((0,Y.assertTenantMatch)(String(A.groupTenantId),String(A.linkTenantId),"listSyncableLinks(link.tenantId vs TMGroup.tenant_id)"),{linkId:Number(A.linkId),groupId:Number(A.groupId),groupKey:String(A.groupKey),tenantId:String(A.groupTenantId),provider:String(A.provider),externalId:String(A.externalId),externalKey:String(A.externalKey),membershipMode:B(String(A.membershipMode)),isGroupActive:!!A.isGroupActive,vanishedObservations:Number(A.vanishedObservations??0)}))},async loadGroupSnapshot(r){await s();let[h,u]=await Promise.all([l.select({email:b.email,externalUserId:b.externalUserId,resolvedUserId:b.resolvedUserId}).from(b).where((0,T.eq)(b.groupId,r.groupId)),l.select({userId:t.userId}).from(t).where((0,T.eq)(t.groupId,r.groupId))]);return{ledger:h.map(I=>({email:String(I.email),...I.externalUserId?{externalUserId:String(I.externalUserId)}:{},resolvedUserId:I.resolvedUserId===null?null:Number(I.resolvedUserId)})),projectedUserIds:u.map(I=>Number(I.userId))}},resolveMembers(r){return d.memberResolver.resolve(r)},async applyPlan(r,h,u){(0,Y.assertTenantId)(r.tenantId,"applyPlan(link.tenantId)"),await s(),(h.ledgerUpserts.length>0||h.projectionAdds.length>0)&&await f(async x=>{let P=h.ledgerUpserts.map(y=>({...y,email:(0,J.normalizeAuthEmail)(y.email)})),o=new Map;for(let y of(0,K.chunk)(P.map(S=>S.email))){let S=await x.select({id:b.id,email:b.email}).from(b).where((0,T.and)((0,T.eq)(b.groupId,r.groupId),(0,T.inArray)(b.email,y)));for(let k of S)o.set(String(k.email),Number(k.id))}let m=P.filter(y=>!o.has(y.email));for(let y of(0,K.chunkByParameters)(m,Ve))await x.insert(b).output().values(y.map(S=>({groupId:r.groupId,email:S.email,externalUserId:S.externalUserId??null,resolvedUserId:S.resolvedUserId,observedAt:u,createdAt:u,createdBy:i})));for(let y of P){let S=o.get(y.email);S!==void 0&&await x.update(b).set({externalUserId:y.externalUserId??null,resolvedUserId:y.resolvedUserId,observedAt:u,updatedAt:u,updatedBy:i}).output().where((0,T.eq)(b.id,S))}for(let y of(0,K.chunkByParameters)(h.projectionAdds,Qe)){let S=new Set((await x.select({userId:t.userId}).from(t).where((0,T.and)((0,T.eq)(t.groupId,r.groupId),(0,T.inArray)(t.userId,y)))).map(R=>Number(R.userId))),k=y.filter(R=>!S.has(R));k.length>0&&await x.insert(t).output().values(k.map(R=>({groupId:r.groupId,userId:R,createdAt:u,createdBy:i})))}});let I=[],A=[];for(let x of h.projectionRemoves)try{await d.lockoutGuard.guardedWrite(r.tenantId,{removeGroupMembership:{groupId:r.groupId,userId:x}},"remove-group-member",P=>P.delete(t).where((0,T.and)((0,T.eq)(t.groupId,r.groupId),(0,T.eq)(t.userId,x))))}catch(P){Xe(P)?I.push({userId:x,applied:!1,refusal:P.message}):A.push({userId:x,cause:H(P)})}let D=I.map(x=>x.userId);if(h.ledgerDeletes.length>0){let x=Math.max(1,K.MAX_PARAMETERS-K.RESERVED_PARAMETERS-1-D.length);await f(async P=>{for(let o of(0,K.chunk)(h.ledgerDeletes,Math.min(K.MAX_IN_LIST,x))){let m=D.length>0?(0,T.or)((0,T.isNull)(b.resolvedUserId),(0,T.notInArray)(b.resolvedUserId,D)):void 0;await P.delete(b).where((0,T.and)((0,T.eq)(b.groupId,r.groupId),(0,T.inArray)(b.email,o),...m?[m]:[]))}})}return{refusals:I,failures:A}},async resolveExternalIdentity(r,h){await s(),!(r.externalId===h.externalId&&r.externalKey===(0,J.normalizeAuthEmail)(h.externalKey))&&await l.update(w).set({externalId:h.externalId,externalKey:(0,J.normalizeAuthEmail)(h.externalKey)}).output().where((0,T.and)((0,T.eq)(w.id,r.linkId),(0,T.eq)(w.tenantId,r.tenantId)))},async recordSuccess(r,h,u){await s(),await f(async I=>{await I.update(w).set({syncedAt:u,syncError:null,vanishedObservations:0,externalId:h.externalId,externalKey:(0,J.normalizeAuthEmail)(h.externalKey),updatedAt:u,updatedBy:i}).output().where((0,T.and)((0,T.eq)(w.id,r.linkId),(0,T.eq)(w.tenantId,r.tenantId))),r.isGroupActive||await I.update(a).set({isActive:!0,updatedAt:u,updatedBy:i}).output().where((0,T.and)((0,T.eq)(a.id,r.groupId),(0,T.eq)(a.tenantId,r.tenantId)))})},async recordFailure(r,h){await s(),await l.update(w).set({syncError:h.slice(0,De),updatedAt:v(),updatedBy:i}).output().where((0,T.and)((0,T.eq)(w.id,r.linkId),(0,T.eq)(w.tenantId,r.tenantId)))},async recordVanishedObservation(r,h){await s();let u=r.vanishedObservations+1;return await l.update(w).set({vanishedObservations:u,syncError:h.slice(0,De),updatedAt:v(),updatedBy:i}).output().where((0,T.and)((0,T.eq)(w.id,r.linkId),(0,T.eq)(w.tenantId,r.tenantId))),u},async markGroupInactive(r){await s(),await l.update(a).set({isActive:!1,updatedAt:v(),updatedBy:i}).output().where((0,T.and)((0,T.eq)(a.id,r.groupId),(0,T.eq)(a.tenantId,r.tenantId),(0,T.eq)(a.isActive,!0)))}}}0&&(module.exports={AUTH_DIRECTORY_PROVIDER_COLUMN,AUTH_DIRECTORY_TENANT_SCOPED_TABLES,MEMBERSHIP_MODES,NEWLY_UNHEALTHY_SYNC_OUTCOMES,STRANDED_REASON,UNHEALTHY_SYNC_OUTCOMES,assertGroupMayBeLinked,assertMembershipMode,classifySyncFailure,createDirectoryCatalogReader,createDirectoryCatalogWriter,createDirectoryLinkStore,createDirectorySync,createDirectorySyncRepository,createPromotionRepository,createSyncRequestQueue,defaultExternalGroupGuardMessage,defaultSyncMessages,defineAuthDirectorySchema,findLedgerRowsForIdentity,promoteLedgerForUser,transactionRunner,withExternalGroupGuards});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|