@vrplatform/kysely 1.3.41 → 1.3.45-1
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/build/.data.tar +0 -0
- package/build/main/camel-case/index.js +1 -1
- package/build/main/camel-case/index.js.map +1 -1
- package/build/main/control-plane/index.d.ts +368 -0
- package/build/main/control-plane/index.js +64 -0
- package/build/main/control-plane/index.js.map +1 -0
- package/build/main/domain.d.ts +4 -0
- package/build/main/domain.js +3 -0
- package/build/main/domain.js.map +1 -0
- package/build/main/error.d.ts +2 -1
- package/build/main/error.js +2 -1
- package/build/main/error.js.map +1 -1
- package/build/main/index.d.ts +13 -16
- package/build/main/index.js +145 -65
- package/build/main/index.js.map +1 -1
- package/build/main/isomorphic.d.ts +5 -5
- package/build/main/isomorphic.js +4 -10
- package/build/main/isomorphic.js.map +1 -1
- package/build/main/local/digest.d.ts +3 -3
- package/build/main/local/digest.js.map +1 -1
- package/build/main/local/generate.js +10 -10
- package/build/main/local/generate.js.map +1 -1
- package/build/main/local/index.d.ts +5 -1
- package/build/main/local/index.js +215 -6
- package/build/main/local/index.js.map +1 -1
- package/build/main/local/prune-pulled-schema.js +4 -1
- package/build/main/local/prune-pulled-schema.js.map +1 -1
- package/build/main/migration.d.ts +7 -0
- package/build/main/migration.js +11 -0
- package/build/main/migration.js.map +1 -0
- package/build/main/pganalyze-comment-plugin/comment-transformer.js +0 -1
- package/build/main/pganalyze-comment-plugin/comment-transformer.js.map +1 -1
- package/build/main/pganalyze-comment-plugin/index.js +16 -3
- package/build/main/pganalyze-comment-plugin/index.js.map +1 -1
- package/build/main/plugins.d.ts +2 -2
- package/build/main/plugins.js.map +1 -1
- package/build/main/postgres-error.d.ts +8 -0
- package/build/main/postgres-error.js +21 -0
- package/build/main/postgres-error.js.map +1 -0
- package/build/main/query/index.d.ts +1 -0
- package/build/main/query/index.js +1 -0
- package/build/main/query/index.js.map +1 -1
- package/build/main/query/listing/status.d.ts +2 -12
- package/build/main/query/listing/status.js +31 -19
- package/build/main/query/listing/status.js.map +1 -1
- package/build/main/query/tenant/billing-partner.d.ts +4 -0
- package/build/main/query/tenant/billing-partner.js +8 -0
- package/build/main/query/tenant/billing-partner.js.map +1 -0
- package/build/main/query/tenant/index.d.ts +2 -0
- package/build/main/query/tenant/index.js +19 -0
- package/build/main/query/tenant/index.js.map +1 -0
- package/build/main/query/tenant/status.d.ts +14 -0
- package/build/main/query/tenant/status.js +40 -0
- package/build/main/query/tenant/status.js.map +1 -0
- package/build/main/regional.d.ts +60 -0
- package/build/main/regional.js +205 -0
- package/build/main/regional.js.map +1 -0
- package/build/main/test/index.d.ts +3 -14
- package/build/main/test/index.js +0 -23
- package/build/main/test/index.js.map +1 -1
- package/build/main/transaction.d.ts +3 -3
- package/build/main/transaction.js.map +1 -1
- package/build/main/v1.generated.d.ts +593 -23
- package/build/main/v1.generated.js.map +1 -1
- package/build/migrations-v2/0000000000000-baseline.ts +5 -0
- package/build/migrations-v2/0000000000001-public-audit-events-v2.ts +137 -0
- package/build/migrations-v2/0000000000002-audit-recovery-watermarks.ts +30 -0
- package/build/migrations-v2/0000000000003-audit-effect-recovery-watermark.ts +13 -0
- package/build/migrations-v2/0000000000004-audit-event-projected-at.ts +15 -0
- package/build/migrations-v2/0000000000005-audit-recovery-indexes.ts +63 -0
- package/build/migrations-v2/0000000000006-audit-action-public-event-projected-at.ts +22 -0
- package/build/migrations-v2/0000000000007-audit-action-public-event-projected-at-backfill.ts +35 -0
- package/build/migrations-v2/0000000000008-global-user-attribution.ts +33 -0
- package/build/migrations-v2/0000000000008-team-issue-rule-state.ts +52 -0
- package/build/migrations-v2/0000000000009-reservation-issue-projections.ts +45 -0
- package/build/migrations-v2/0000000000010-bank-rule-application-bank-record-delete-cascade.ts +26 -0
- package/build/migrations-v2/0000000000011-guest-balance-journal-entry-lookup-index.ts +25 -0
- package/build/migrations-v2/0000000000012-trial-free-analytics-billing-status.ts +72 -0
- package/build/migrations-v2/0000000000013-ui-permission-bundles.ts +45 -0
- package/build/migrations-v2/0000000000014-ui-permission-bundle-top-up.ts +32 -0
- package/build/migrations-v2/0000000000016-managed-team-access.ts +34 -0
- package/build/migrations-v2/0000000000017-drop-trial-until.ts +42 -0
- package/build/migrations-v2/0000000000018-accounting-integrity-monitoring.ts +276 -0
- package/build/migrations-v2/0000000000019-accounting-integrity-run-shards.ts +26 -0
- package/build/migrations-v2/0000000000020-accounting-integrity-case-classification.ts +52 -0
- package/build/migrations-v2/0000000000021-payment-line-classification-monitor-indexes.ts +31 -0
- package/build/migrations-v2/0000000000022-accounting-integrity-controlled-repairs.ts +281 -0
- package/build/migrations-v2/0000000000023-line-classification-usage.ts +325 -0
- package/build/migrations-v2/0000000000024-tenant-region-migration.ts +108 -0
- package/build/migrations-v2/0000000000025-partner-ach-denial-default.ts +30 -0
- package/build/migrations-v2/0000000000026-line-classification-usage-capture.ts +399 -0
- package/build/migrations-v2/0000000000027-payout-replacement-chain.ts +27 -0
- package/build/migrations-v2/0000000000028-accounting-integrity-operator-workflow.ts +108 -0
- package/build/migrations-v2/0000000000029-accounting-integrity-definition-artifacts.ts +40 -0
- package/build/migrations-v2/0000000000030-accounting-integrity-review-metadata-compatibility.ts +49 -0
- package/build/module/camel-case/index.js +1 -1
- package/build/module/camel-case/index.js.map +1 -1
- package/build/module/control-plane/index.d.ts +368 -0
- package/build/module/control-plane/index.js +50 -0
- package/build/module/control-plane/index.js.map +1 -0
- package/build/module/domain.d.ts +4 -0
- package/build/module/domain.js +2 -0
- package/build/module/domain.js.map +1 -0
- package/build/module/error.d.ts +2 -1
- package/build/module/error.js +3 -1
- package/build/module/error.js.map +1 -1
- package/build/module/index.d.ts +13 -16
- package/build/module/index.js +146 -63
- package/build/module/index.js.map +1 -1
- package/build/module/isomorphic.d.ts +5 -5
- package/build/module/isomorphic.js +1 -6
- package/build/module/isomorphic.js.map +1 -1
- package/build/module/local/digest.d.ts +3 -3
- package/build/module/local/digest.js.map +1 -1
- package/build/module/local/generate.js +10 -10
- package/build/module/local/generate.js.map +1 -1
- package/build/module/local/index.d.ts +5 -1
- package/build/module/local/index.js +216 -7
- package/build/module/local/index.js.map +1 -1
- package/build/module/local/prune-pulled-schema.js +4 -1
- package/build/module/local/prune-pulled-schema.js.map +1 -1
- package/build/module/migration.d.ts +7 -0
- package/build/module/migration.js +8 -0
- package/build/module/migration.js.map +1 -0
- package/build/module/pganalyze-comment-plugin/comment-transformer.js.map +1 -1
- package/build/module/pganalyze-comment-plugin/index.js.map +1 -1
- package/build/module/plugins.d.ts +2 -2
- package/build/module/plugins.js.map +1 -1
- package/build/module/postgres-error.d.ts +8 -0
- package/build/module/postgres-error.js +18 -0
- package/build/module/postgres-error.js.map +1 -0
- package/build/module/query/index.d.ts +1 -0
- package/build/module/query/index.js +1 -0
- package/build/module/query/index.js.map +1 -1
- package/build/module/query/listing/status.d.ts +2 -12
- package/build/module/query/listing/status.js +31 -17
- package/build/module/query/listing/status.js.map +1 -1
- package/build/module/query/tenant/billing-partner.d.ts +4 -0
- package/build/module/query/tenant/billing-partner.js +5 -0
- package/build/module/query/tenant/billing-partner.js.map +1 -0
- package/build/module/query/tenant/index.d.ts +2 -0
- package/build/module/query/tenant/index.js +3 -0
- package/build/module/query/tenant/index.js.map +1 -0
- package/build/module/query/tenant/status.d.ts +14 -0
- package/build/module/query/tenant/status.js +35 -0
- package/build/module/query/tenant/status.js.map +1 -0
- package/build/module/regional.d.ts +60 -0
- package/build/module/regional.js +162 -0
- package/build/module/regional.js.map +1 -0
- package/build/module/test/index.d.ts +3 -14
- package/build/module/test/index.js +1 -23
- package/build/module/test/index.js.map +1 -1
- package/build/module/transaction.d.ts +3 -3
- package/build/module/transaction.js.map +1 -1
- package/build/module/v1.generated.d.ts +593 -23
- package/build/module/v1.generated.js.map +1 -1
- package/package.json +18 -9
- package/build/main/tsconfig.main.tsbuildinfo +0 -1
- package/build/module/tsconfig.esm.tsbuildinfo +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-partner.js","sourceRoot":"src/","sources":["query/tenant/billing-partner.ts"],"names":[],"mappings":"AAAA,MAAM,aAAa,GAAG,sCAAsC,CAAC;AAE7D,MAAM,UAAU,6BAA6B,CAAC,EAC5C,gBAAgB,EAChB,SAAS,GAIV;IACC,OAAO,gBAAgB,IAAI,SAAS,IAAI,aAAa,CAAC;AACxD,CAAC","sourcesContent":["const VRP_TENANT_ID = '7bfec41f-aa3f-4602-95d2-f7996e4f0e59';\n\nexport function getCalculatedBillingPartnerId({\n billingPartnerId,\n partnerId,\n}: {\n billingPartnerId?: string | null;\n partnerId?: string | null;\n}) {\n return billingPartnerId ?? partnerId ?? VRP_TENANT_ID;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["query/tenant/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC","sourcesContent":["export * from './billing-partner';\nexport * from './status';\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DB, type ExpressionBuilder, type PublicTenant, type SqlBool } from '../../isomorphic';
|
|
2
|
+
type TenantExpressionBuilder = ExpressionBuilder<DB & {
|
|
3
|
+
tenant: PublicTenant;
|
|
4
|
+
}, 'tenant'>;
|
|
5
|
+
export declare function whereTenantActive(eb: TenantExpressionBuilder): import("kysely").ExpressionWrapper<DB & {
|
|
6
|
+
tenant: PublicTenant;
|
|
7
|
+
}, "tenant", SqlBool>;
|
|
8
|
+
export declare function whereTenantNotTestTeam(testTeamId: string): import("kysely").RawBuilder<SqlBool>;
|
|
9
|
+
export declare function whereTenantShouldBePaid(eb: TenantExpressionBuilder, { includePartnerTeams, }?: {
|
|
10
|
+
includePartnerTeams?: boolean;
|
|
11
|
+
}): import("kysely").ExpressionWrapper<DB & {
|
|
12
|
+
tenant: PublicTenant;
|
|
13
|
+
}, "tenant", SqlBool>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { sql, } from '../../isomorphic';
|
|
2
|
+
import { getCalculatedBillingPartnerId } from './billing-partner';
|
|
3
|
+
export function whereTenantActive(eb) {
|
|
4
|
+
return eb.and([
|
|
5
|
+
eb('tenant.calculatedStatus', '=', 'active'),
|
|
6
|
+
eb.not(eb.exists(eb
|
|
7
|
+
.selectFrom('public.tenant as billingPartner')
|
|
8
|
+
.select('billingPartner.id')
|
|
9
|
+
.where('billingPartner.id', '=', eb.fn.coalesce('tenant.calculatedBillingPartnerId', eb.val(getCalculatedBillingPartnerId({}))))
|
|
10
|
+
.where('billingPartner.calculatedStatus', 'is distinct from', 'active'))),
|
|
11
|
+
]);
|
|
12
|
+
}
|
|
13
|
+
export function whereTenantNotTestTeam(testTeamId) {
|
|
14
|
+
return sql `(
|
|
15
|
+
${sql.ref('tenant.id')} != ${testTeamId}
|
|
16
|
+
and (
|
|
17
|
+
${sql.ref('tenant.partnerId')} is null
|
|
18
|
+
or ${sql.ref('tenant.partnerId')} != ${testTeamId}
|
|
19
|
+
)
|
|
20
|
+
and (
|
|
21
|
+
${sql.ref('tenant.billingPartnerId')} is null
|
|
22
|
+
or ${sql.ref('tenant.billingPartnerId')} != ${testTeamId}
|
|
23
|
+
)
|
|
24
|
+
)`;
|
|
25
|
+
}
|
|
26
|
+
export function whereTenantShouldBePaid(eb, { includePartnerTeams = false, } = {}) {
|
|
27
|
+
return eb.and([
|
|
28
|
+
whereTenantActive(eb),
|
|
29
|
+
eb('tenant.isOnboarding', '=', false),
|
|
30
|
+
...(includePartnerTeams
|
|
31
|
+
? []
|
|
32
|
+
: [eb('tenant.calculatedIsBillable', '=', true)]),
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"src/","sources":["query/tenant/status.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,GAKJ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAOlE,MAAM,UAAU,iBAAiB,CAAC,EAA2B;IAC3D,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE,QAAQ,CAAC;QAC5C,EAAE,CAAC,GAAG,CACJ,EAAE,CAAC,MAAM,CACP,EAAE;aACC,UAAU,CAAC,iCAAiC,CAAC;aAC7C,MAAM,CAAC,mBAAmB,CAAC;aAC3B,KAAK,CACJ,mBAAmB,EACnB,GAAG,EACH,EAAE,CAAC,EAAE,CAAC,QAAQ,CACZ,mCAAmC,EACnC,EAAE,CAAC,GAAG,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAC,CAC1C,CACF;aACA,KAAK,CACJ,iCAAiC,EACjC,kBAAkB,EAClB,QAAQ,CACT,CACJ,CACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACvD,OAAO,GAAG,CAAS;MACf,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,UAAU;;QAEnC,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC;WACxB,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,OAAO,UAAU;;;QAG/C,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC;WAC/B,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC,OAAO,UAAU;;IAE1D,CAAC;AACL,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,EAA2B,EAC3B,EACE,mBAAmB,GAAG,KAAK,MAGzB,EAAE;IAEN,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,iBAAiB,CAAC,EAAE,CAAC;QACrB,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE,KAAK,CAAC;QACrC,GAAG,CAAC,mBAAmB;YACrB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;KACpD,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n sql,\n type DB,\n type ExpressionBuilder,\n type PublicTenant,\n type SqlBool,\n} from '../../isomorphic';\nimport { getCalculatedBillingPartnerId } from './billing-partner';\n\ntype TenantExpressionBuilder = ExpressionBuilder<\n DB & { tenant: PublicTenant },\n 'tenant'\n>;\n\nexport function whereTenantActive(eb: TenantExpressionBuilder) {\n return eb.and([\n eb('tenant.calculatedStatus', '=', 'active'),\n eb.not(\n eb.exists(\n eb\n .selectFrom('public.tenant as billingPartner')\n .select('billingPartner.id')\n .where(\n 'billingPartner.id',\n '=',\n eb.fn.coalesce(\n 'tenant.calculatedBillingPartnerId',\n eb.val(getCalculatedBillingPartnerId({}))\n )\n )\n .where(\n 'billingPartner.calculatedStatus',\n 'is distinct from',\n 'active'\n )\n )\n ),\n ]);\n}\n\nexport function whereTenantNotTestTeam(testTeamId: string) {\n return sql<SqlBool>`(\n ${sql.ref('tenant.id')} != ${testTeamId}\n and (\n ${sql.ref('tenant.partnerId')} is null\n or ${sql.ref('tenant.partnerId')} != ${testTeamId}\n )\n and (\n ${sql.ref('tenant.billingPartnerId')} is null\n or ${sql.ref('tenant.billingPartnerId')} != ${testTeamId}\n )\n )`;\n}\n\nexport function whereTenantShouldBePaid(\n eb: TenantExpressionBuilder,\n {\n includePartnerTeams = false,\n }: {\n includePartnerTeams?: boolean;\n } = {}\n) {\n return eb.and([\n whereTenantActive(eb),\n eb('tenant.isOnboarding', '=', false),\n ...(includePartnerTeams\n ? []\n : [eb('tenant.calculatedIsBillable', '=', true)]),\n ]);\n}\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ControlPlaneKysely, Kysely } from './index';
|
|
2
|
+
export declare const tenantDataRegions: readonly ["ap", "crunchy", "eu", "hostaway", "us"];
|
|
3
|
+
export type TenantDataRegion = (typeof tenantDataRegions)[number];
|
|
4
|
+
export type RegionEnvironment = Record<string, string | undefined>;
|
|
5
|
+
export declare function assertTenantDataRegion(value: string | undefined): asserts value is TenantDataRegion;
|
|
6
|
+
export declare function resolveTenantDatabaseUrl(dataRegion: TenantDataRegion, env: RegionEnvironment): string;
|
|
7
|
+
export declare function resolveTenantApiBaseUrl(dataRegion: TenantDataRegion, env: RegionEnvironment): string;
|
|
8
|
+
export declare function resolveConfiguredTenantDataRegions(env: RegionEnvironment): ("ap" | "crunchy" | "eu" | "hostaway" | "us")[];
|
|
9
|
+
type UseAsyncRegionalKysely = (connectionString: string, options?: {
|
|
10
|
+
repositoryName?: string;
|
|
11
|
+
probe?: true;
|
|
12
|
+
}) => Promise<Kysely>;
|
|
13
|
+
type UseAsyncControlPlaneKysely = (connectionString: string, options?: {
|
|
14
|
+
repositoryName?: string;
|
|
15
|
+
probe?: true;
|
|
16
|
+
}) => Promise<ControlPlaneKysely>;
|
|
17
|
+
export declare function useTenantDatabaseKysely(dataRegion: TenantDataRegion, repositoryName: string, env: RegionEnvironment, options?: {
|
|
18
|
+
useAsyncKysely?: UseAsyncRegionalKysely;
|
|
19
|
+
probe?: true;
|
|
20
|
+
}): Promise<Kysely>;
|
|
21
|
+
export type TenantKysely = {
|
|
22
|
+
id: string;
|
|
23
|
+
slug: string;
|
|
24
|
+
dataRegion: TenantDataRegion;
|
|
25
|
+
kysely: Kysely;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a tenant's regional Kysely connection from a connection id, slug,
|
|
29
|
+
* or tenant id. The control-plane connection is opened and destroyed
|
|
30
|
+
* internally; the returned regional connection must be destroyed by the
|
|
31
|
+
* caller.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveTenantKysely({ connectionId, slug, tenantId, repositoryName, env, ...options }: {
|
|
34
|
+
connectionId?: string;
|
|
35
|
+
slug?: string;
|
|
36
|
+
tenantId?: string;
|
|
37
|
+
repositoryName: string;
|
|
38
|
+
env: RegionEnvironment;
|
|
39
|
+
useAsyncControlPlaneKysely?: UseAsyncControlPlaneKysely;
|
|
40
|
+
useAsyncKysely?: UseAsyncRegionalKysely;
|
|
41
|
+
probe?: true;
|
|
42
|
+
}): Promise<TenantKysely>;
|
|
43
|
+
export type RegionRunError = {
|
|
44
|
+
dataRegion: TenantDataRegion;
|
|
45
|
+
error: string;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Run a job once per configured tenant data region with per-region error
|
|
49
|
+
* isolation: one region's outage must not stall the others (regions iterate
|
|
50
|
+
* in a fixed order, so a failing first region would otherwise starve the
|
|
51
|
+
* rest on every attempt). Throws only when every configured region failed.
|
|
52
|
+
*/
|
|
53
|
+
export declare function forEachConfiguredRegion<T>(run: (dataRegion: TenantDataRegion) => Promise<T>, env: RegionEnvironment): Promise<{
|
|
54
|
+
results: {
|
|
55
|
+
dataRegion: TenantDataRegion;
|
|
56
|
+
result: T;
|
|
57
|
+
}[];
|
|
58
|
+
errors: RegionRunError[];
|
|
59
|
+
}>;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { KyselyError } from './error';
|
|
2
|
+
// The tenant-data-region contract lives here — the region set plus how a
|
|
3
|
+
// region resolves to its database url, API base url, and how to fan a job out
|
|
4
|
+
// across regions. Every runtime resolves regions through these functions;
|
|
5
|
+
// @vrplatform/common re-exports them for app-facing imports, and the trigger
|
|
6
|
+
// runtime wraps them only to default `env` to process.env.
|
|
7
|
+
//
|
|
8
|
+
// Region → resource keys are derived from the region name
|
|
9
|
+
// (`DATABASE_URL_VRTRUST_<REGION>`, `API_BASE_URL_<REGION>`), so adding a
|
|
10
|
+
// region means extending this array plus its env values and migrations — not
|
|
11
|
+
// editing per-region branches.
|
|
12
|
+
export const tenantDataRegions = [
|
|
13
|
+
'ap',
|
|
14
|
+
'crunchy',
|
|
15
|
+
'eu',
|
|
16
|
+
'hostaway',
|
|
17
|
+
'us',
|
|
18
|
+
];
|
|
19
|
+
export function assertTenantDataRegion(value) {
|
|
20
|
+
if (!tenantDataRegions.includes(value)) {
|
|
21
|
+
throw new KyselyError('Missing or invalid tenant data region', { value });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function databaseUrlEnvKey(dataRegion) {
|
|
25
|
+
return `DATABASE_URL_VRTRUST_${dataRegion.toUpperCase()}`;
|
|
26
|
+
}
|
|
27
|
+
function apiBaseUrlEnvKey(dataRegion) {
|
|
28
|
+
return `API_BASE_URL_${dataRegion.toUpperCase()}`;
|
|
29
|
+
}
|
|
30
|
+
export function resolveTenantDatabaseUrl(dataRegion, env) {
|
|
31
|
+
const databaseUrl = env[databaseUrlEnvKey(dataRegion)];
|
|
32
|
+
if (!databaseUrl) {
|
|
33
|
+
throw new KyselyError('Tenant database URL is missing', {
|
|
34
|
+
dataRegion,
|
|
35
|
+
key: databaseUrlEnvKey(dataRegion),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return databaseUrl;
|
|
39
|
+
}
|
|
40
|
+
export function resolveTenantApiBaseUrl(dataRegion, env) {
|
|
41
|
+
const key = apiBaseUrlEnvKey(dataRegion);
|
|
42
|
+
const baseUrl = env[key];
|
|
43
|
+
if (!baseUrl) {
|
|
44
|
+
throw new KyselyError('Tenant API base URL is missing', {
|
|
45
|
+
dataRegion,
|
|
46
|
+
key,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return baseUrl;
|
|
50
|
+
}
|
|
51
|
+
export function resolveConfiguredTenantDataRegions(env) {
|
|
52
|
+
const configured = tenantDataRegions.filter((region) => Boolean(env[databaseUrlEnvKey(region)]));
|
|
53
|
+
if (!configured.length) {
|
|
54
|
+
throw new Error('No tenant data region database urls configured');
|
|
55
|
+
}
|
|
56
|
+
// Two regions sharing one URL means every region-fan-out job scans the
|
|
57
|
+
// same physical DB twice (e.g. a stale dashboard env var re-merged into a
|
|
58
|
+
// deploy). Regions must stay unset until they point at distinct databases.
|
|
59
|
+
const regionsByUrl = new Map();
|
|
60
|
+
for (const region of configured) {
|
|
61
|
+
const url = env[databaseUrlEnvKey(region)];
|
|
62
|
+
regionsByUrl.set(url, [...(regionsByUrl.get(url) ?? []), region]);
|
|
63
|
+
}
|
|
64
|
+
for (const aliased of regionsByUrl.values()) {
|
|
65
|
+
if (aliased.length > 1) {
|
|
66
|
+
throw new KyselyError('Tenant data regions share the same database URL', {
|
|
67
|
+
dataRegions: aliased,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return configured;
|
|
72
|
+
}
|
|
73
|
+
async function defaultUseAsyncKysely(connectionString, options) {
|
|
74
|
+
const { useAsyncKysely } = await import('./index');
|
|
75
|
+
return await useAsyncKysely(connectionString, options);
|
|
76
|
+
}
|
|
77
|
+
async function defaultUseAsyncControlPlaneKysely(connectionString, options) {
|
|
78
|
+
const { useAsyncControlPlaneKysely } = await import('./index');
|
|
79
|
+
return await useAsyncControlPlaneKysely(connectionString, options);
|
|
80
|
+
}
|
|
81
|
+
export async function useTenantDatabaseKysely(dataRegion, repositoryName, env, options = {}) {
|
|
82
|
+
const open = options.useAsyncKysely ?? defaultUseAsyncKysely;
|
|
83
|
+
return await open(resolveTenantDatabaseUrl(dataRegion, env), {
|
|
84
|
+
repositoryName,
|
|
85
|
+
probe: options.probe,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a tenant's regional Kysely connection from a connection id, slug,
|
|
90
|
+
* or tenant id. The control-plane connection is opened and destroyed
|
|
91
|
+
* internally; the returned regional connection must be destroyed by the
|
|
92
|
+
* caller.
|
|
93
|
+
*/
|
|
94
|
+
export async function resolveTenantKysely({ connectionId, slug, tenantId, repositoryName, env, ...options }) {
|
|
95
|
+
if (!connectionId && !slug && !tenantId) {
|
|
96
|
+
throw new Error('connectionId, slug, or tenantId is required');
|
|
97
|
+
}
|
|
98
|
+
const controlPlaneDatabaseUrl = env.CONTROL_PLANE_DATABASE_URL;
|
|
99
|
+
if (!controlPlaneDatabaseUrl)
|
|
100
|
+
throw new Error('CONTROL_PLANE_DATABASE_URL is required');
|
|
101
|
+
const controlPlane = await (options.useAsyncControlPlaneKysely ?? defaultUseAsyncControlPlaneKysely)(controlPlaneDatabaseUrl, {
|
|
102
|
+
repositoryName: `${repositoryName}/control-plane`,
|
|
103
|
+
probe: options.probe,
|
|
104
|
+
});
|
|
105
|
+
let tenant;
|
|
106
|
+
try {
|
|
107
|
+
const query = controlPlane
|
|
108
|
+
.selectFrom('controlPlane.tenant as tenant')
|
|
109
|
+
.select([
|
|
110
|
+
'tenant.id as id',
|
|
111
|
+
'tenant.slug as slug',
|
|
112
|
+
'tenant.dataRegion as dataRegion',
|
|
113
|
+
]);
|
|
114
|
+
tenant = await (slug
|
|
115
|
+
? query.where('tenant.slug', '=', slug)
|
|
116
|
+
: connectionId
|
|
117
|
+
? query
|
|
118
|
+
.innerJoin('controlPlane.connectionRoute as connectionRoute', 'connectionRoute.tenantId', 'tenant.id')
|
|
119
|
+
.where('connectionRoute.connectionId', '=', connectionId)
|
|
120
|
+
: query.where('tenant.id', '=', tenantId)).executeTakeFirst();
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
await controlPlane.destroy();
|
|
124
|
+
}
|
|
125
|
+
if (!tenant)
|
|
126
|
+
throw new Error('Tenant not found in control plane');
|
|
127
|
+
assertTenantDataRegion(tenant.dataRegion);
|
|
128
|
+
return {
|
|
129
|
+
id: tenant.id,
|
|
130
|
+
slug: tenant.slug,
|
|
131
|
+
dataRegion: tenant.dataRegion,
|
|
132
|
+
kysely: await useTenantDatabaseKysely(tenant.dataRegion, repositoryName, env, options),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Run a job once per configured tenant data region with per-region error
|
|
137
|
+
* isolation: one region's outage must not stall the others (regions iterate
|
|
138
|
+
* in a fixed order, so a failing first region would otherwise starve the
|
|
139
|
+
* rest on every attempt). Throws only when every configured region failed.
|
|
140
|
+
*/
|
|
141
|
+
export async function forEachConfiguredRegion(run, env) {
|
|
142
|
+
const results = [];
|
|
143
|
+
const errors = [];
|
|
144
|
+
for (const dataRegion of resolveConfiguredTenantDataRegions(env)) {
|
|
145
|
+
try {
|
|
146
|
+
results.push({ dataRegion, result: await run(dataRegion) });
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
errors.push({
|
|
150
|
+
dataRegion,
|
|
151
|
+
error: error instanceof Error ? error.message : String(error),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (!results.length && errors.length) {
|
|
156
|
+
throw new KyselyError('All configured tenant data regions failed', {
|
|
157
|
+
errors,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return { results, errors };
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=regional.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"regional.js","sourceRoot":"src/","sources":["regional.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ControlPlaneKysely, Kysely } from './index';\nimport { KyselyError } from './error';\n\n// The tenant-data-region contract lives here — the region set plus how a\n// region resolves to its database url, API base url, and how to fan a job out\n// across regions. Every runtime resolves regions through these functions;\n// @vrplatform/common re-exports them for app-facing imports, and the trigger\n// runtime wraps them only to default `env` to process.env.\n//\n// Region → resource keys are derived from the region name\n// (`DATABASE_URL_VRTRUST_<REGION>`, `API_BASE_URL_<REGION>`), so adding a\n// region means extending this array plus its env values and migrations — not\n// editing per-region branches.\nexport const tenantDataRegions = [\n 'ap',\n 'crunchy',\n 'eu',\n 'hostaway',\n 'us',\n] as const;\nexport type TenantDataRegion = (typeof tenantDataRegions)[number];\n\nexport type RegionEnvironment = Record<string, string | undefined>;\n\nexport function assertTenantDataRegion(\n value: string | undefined\n): asserts value is TenantDataRegion {\n if (!tenantDataRegions.includes(value as TenantDataRegion)) {\n throw new KyselyError('Missing or invalid tenant data region', { value });\n }\n}\n\nfunction databaseUrlEnvKey(dataRegion: TenantDataRegion) {\n return `DATABASE_URL_VRTRUST_${dataRegion.toUpperCase()}`;\n}\n\nfunction apiBaseUrlEnvKey(dataRegion: TenantDataRegion) {\n return `API_BASE_URL_${dataRegion.toUpperCase()}`;\n}\n\nexport function resolveTenantDatabaseUrl(\n dataRegion: TenantDataRegion,\n env: RegionEnvironment\n) {\n const databaseUrl = env[databaseUrlEnvKey(dataRegion)];\n if (!databaseUrl) {\n throw new KyselyError('Tenant database URL is missing', {\n dataRegion,\n key: databaseUrlEnvKey(dataRegion),\n });\n }\n return databaseUrl;\n}\n\nexport function resolveTenantApiBaseUrl(\n dataRegion: TenantDataRegion,\n env: RegionEnvironment\n) {\n const key = apiBaseUrlEnvKey(dataRegion);\n const baseUrl = env[key];\n if (!baseUrl) {\n throw new KyselyError('Tenant API base URL is missing', {\n dataRegion,\n key,\n });\n }\n return baseUrl;\n}\n\nexport function resolveConfiguredTenantDataRegions(env: RegionEnvironment) {\n const configured = tenantDataRegions.filter((region) =>\n Boolean(env[databaseUrlEnvKey(region)])\n );\n\n if (!configured.length) {\n throw new Error('No tenant data region database urls configured');\n }\n\n // Two regions sharing one URL means every region-fan-out job scans the\n // same physical DB twice (e.g. a stale dashboard env var re-merged into a\n // deploy). Regions must stay unset until they point at distinct databases.\n const regionsByUrl = new Map<string, TenantDataRegion[]>();\n for (const region of configured) {\n const url = env[databaseUrlEnvKey(region)]!;\n regionsByUrl.set(url, [...(regionsByUrl.get(url) ?? []), region]);\n }\n for (const aliased of regionsByUrl.values()) {\n if (aliased.length > 1) {\n throw new KyselyError('Tenant data regions share the same database URL', {\n dataRegions: aliased,\n });\n }\n }\n\n return configured;\n}\n\ntype UseAsyncRegionalKysely = (\n connectionString: string,\n options?: {\n repositoryName?: string;\n probe?: true;\n }\n) => Promise<Kysely>;\n\ntype UseAsyncControlPlaneKysely = (\n connectionString: string,\n options?: {\n repositoryName?: string;\n probe?: true;\n }\n) => Promise<ControlPlaneKysely>;\n\nasync function defaultUseAsyncKysely(\n connectionString: string,\n options?: Parameters<UseAsyncRegionalKysely>[1]\n) {\n const { useAsyncKysely } = await import('./index');\n return await useAsyncKysely(connectionString, options);\n}\n\nasync function defaultUseAsyncControlPlaneKysely(\n connectionString: string,\n options?: Parameters<UseAsyncControlPlaneKysely>[1]\n) {\n const { useAsyncControlPlaneKysely } = await import('./index');\n return await useAsyncControlPlaneKysely(connectionString, options);\n}\n\nexport async function useTenantDatabaseKysely(\n dataRegion: TenantDataRegion,\n repositoryName: string,\n env: RegionEnvironment,\n options: { useAsyncKysely?: UseAsyncRegionalKysely; probe?: true } = {}\n) {\n const open = options.useAsyncKysely ?? defaultUseAsyncKysely;\n return await open(resolveTenantDatabaseUrl(dataRegion, env), {\n repositoryName,\n probe: options.probe,\n });\n}\n\nexport type TenantKysely = {\n id: string;\n slug: string;\n dataRegion: TenantDataRegion;\n kysely: Kysely;\n};\n\n/**\n * Resolve a tenant's regional Kysely connection from a connection id, slug,\n * or tenant id. The control-plane connection is opened and destroyed\n * internally; the returned regional connection must be destroyed by the\n * caller.\n */\nexport async function resolveTenantKysely({\n connectionId,\n slug,\n tenantId,\n repositoryName,\n env,\n ...options\n}: {\n connectionId?: string;\n slug?: string;\n tenantId?: string;\n repositoryName: string;\n env: RegionEnvironment;\n useAsyncControlPlaneKysely?: UseAsyncControlPlaneKysely;\n useAsyncKysely?: UseAsyncRegionalKysely;\n probe?: true;\n}): Promise<TenantKysely> {\n if (!connectionId && !slug && !tenantId) {\n throw new Error('connectionId, slug, or tenantId is required');\n }\n\n const controlPlaneDatabaseUrl = env.CONTROL_PLANE_DATABASE_URL;\n if (!controlPlaneDatabaseUrl)\n throw new Error('CONTROL_PLANE_DATABASE_URL is required');\n\n const controlPlane = await (\n options.useAsyncControlPlaneKysely ?? defaultUseAsyncControlPlaneKysely\n )(controlPlaneDatabaseUrl, {\n repositoryName: `${repositoryName}/control-plane`,\n probe: options.probe,\n });\n let tenant: { id: string; slug: string; dataRegion: string } | undefined;\n try {\n const query = controlPlane\n .selectFrom('controlPlane.tenant as tenant')\n .select([\n 'tenant.id as id',\n 'tenant.slug as slug',\n 'tenant.dataRegion as dataRegion',\n ]);\n tenant = await (\n slug\n ? query.where('tenant.slug', '=', slug)\n : connectionId\n ? query\n .innerJoin(\n 'controlPlane.connectionRoute as connectionRoute',\n 'connectionRoute.tenantId',\n 'tenant.id'\n )\n .where('connectionRoute.connectionId', '=', connectionId)\n : query.where('tenant.id', '=', tenantId!)\n ).executeTakeFirst();\n } finally {\n await controlPlane.destroy();\n }\n if (!tenant) throw new Error('Tenant not found in control plane');\n\n assertTenantDataRegion(tenant.dataRegion);\n return {\n id: tenant.id,\n slug: tenant.slug,\n dataRegion: tenant.dataRegion,\n kysely: await useTenantDatabaseKysely(\n tenant.dataRegion,\n repositoryName,\n env,\n options\n ),\n };\n}\n\nexport type RegionRunError = {\n dataRegion: TenantDataRegion;\n error: string;\n};\n\n/**\n * Run a job once per configured tenant data region with per-region error\n * isolation: one region's outage must not stall the others (regions iterate\n * in a fixed order, so a failing first region would otherwise starve the\n * rest on every attempt). Throws only when every configured region failed.\n */\nexport async function forEachConfiguredRegion<T>(\n run: (dataRegion: TenantDataRegion) => Promise<T>,\n env: RegionEnvironment\n): Promise<{\n results: { dataRegion: TenantDataRegion; result: T }[];\n errors: RegionRunError[];\n}> {\n const results: { dataRegion: TenantDataRegion; result: T }[] = [];\n const errors: RegionRunError[] = [];\n\n for (const dataRegion of resolveConfiguredTenantDataRegions(env)) {\n try {\n results.push({ dataRegion, result: await run(dataRegion) });\n } catch (error) {\n errors.push({\n dataRegion,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n if (!results.length && errors.length) {\n throw new KyselyError('All configured tenant data regions failed', {\n errors,\n });\n }\n\n return { results, errors };\n}\n"]}
|
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
declare global {
|
|
5
|
-
var __vrplatform_testSharedKysely: Promise<Kysely<DB>> | Kysely<DB> | undefined;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* @deprecated Use `useLocalKyselyResource` instead.
|
|
9
|
-
*/
|
|
10
|
-
export declare function useTestKysely(cleanupStrategy: 'afterAll', args?: UseLocalKyselyOptions): Promise<{
|
|
11
|
-
kysely: Kysely<DB>;
|
|
12
|
-
}>;
|
|
13
|
-
export declare function useTestAutoCleanupKysely(args?: UseLocalKyselyOptions): Promise<Kysely<DB>>;
|
|
14
|
-
type ReturnType = Kysely<DB> & {
|
|
1
|
+
import { type LocalKysely, type UseLocalKyselyOptions } from '../local';
|
|
2
|
+
export declare function useTestAutoCleanupKysely(args?: UseLocalKyselyOptions): Promise<LocalKysely>;
|
|
3
|
+
type ReturnType = LocalKysely & {
|
|
15
4
|
[Symbol.dispose]: () => Promise<void>;
|
|
16
5
|
};
|
|
17
6
|
export declare function useLocalKyselyResource(args?: UseLocalKyselyOptions): Promise<ReturnType>;
|
|
@@ -1,28 +1,6 @@
|
|
|
1
1
|
import { afterAll } from 'bun:test';
|
|
2
|
-
import { useLocalKysely } from '../local';
|
|
3
|
-
/**
|
|
4
|
-
* @deprecated Use `useLocalKyselyResource` instead.
|
|
5
|
-
*/
|
|
6
|
-
export async function useTestKysely(cleanupStrategy, args) {
|
|
7
|
-
const kysely = await useLocalKysely(args);
|
|
8
|
-
if (cleanupStrategy === 'afterAll')
|
|
9
|
-
afterAll(async () => {
|
|
10
|
-
await kysely.destroy();
|
|
11
|
-
});
|
|
12
|
-
return {
|
|
13
|
-
kysely,
|
|
14
|
-
};
|
|
15
|
-
}
|
|
2
|
+
import { useLocalKysely, } from '../local';
|
|
16
3
|
export async function useTestAutoCleanupKysely(args) {
|
|
17
|
-
if (process.env.VR_TEST_SHARED_KYSELY === '1') {
|
|
18
|
-
const existing = globalThis.__vrplatform_testSharedKysely;
|
|
19
|
-
if (!existing) {
|
|
20
|
-
const created = useLocalKysely(args);
|
|
21
|
-
globalThis.__vrplatform_testSharedKysely = created;
|
|
22
|
-
return created;
|
|
23
|
-
}
|
|
24
|
-
return await existing;
|
|
25
|
-
}
|
|
26
4
|
const kysely = await useLocalKysely(args);
|
|
27
5
|
afterAll(async () => {
|
|
28
6
|
await kysely.destroy();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAGL,cAAc,GACf,MAAM,UAAU,CAAC;AAElB,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAA4B;IACzE,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAE1C,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAA4B;IAE5B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAE1C,OAAO,IAAI,KAAK,CACd,EAAE,EACF;QACE,GAAG,CAAC,OAAO,EAAE,IAAI;YACf,8CAA8C;YAC9C,IAAI,IAAI,KAAK,MAAM,CAAC,YAAY,EAAE,CAAC;gBACjC,OAAO,KAAK,IAAI,EAAE;oBAChB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;gBACzB,CAAC,CAAC;YACJ,CAAC;YACD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtB,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;gBACvC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;KACF,CACY,CAAC;AAClB,CAAC","sourcesContent":["import { afterAll } from 'bun:test';\nimport {\n type LocalKysely,\n type UseLocalKyselyOptions,\n useLocalKysely,\n} from '../local';\n\nexport async function useTestAutoCleanupKysely(args?: UseLocalKyselyOptions) {\n const kysely = await useLocalKysely(args);\n\n afterAll(async () => {\n await kysely.destroy();\n });\n\n return kysely;\n}\n\ntype ReturnType = LocalKysely & {\n [Symbol.dispose]: () => Promise<void>;\n};\n\nexport async function useLocalKyselyResource(\n args?: UseLocalKyselyOptions\n): Promise<ReturnType> {\n const kysely = await useLocalKysely(args);\n\n return new Proxy(\n {},\n {\n get(_target, prop) {\n // somehow not working if using Symbol.dispose\n if (prop === Symbol.asyncDispose) {\n return async () => {\n await kysely.destroy();\n };\n }\n if (prop === 'kysely') {\n return kysely;\n }\n if (typeof kysely[prop] === 'function') {\n return kysely[prop].bind(kysely);\n }\n return kysely[prop];\n },\n }\n ) as ReturnType;\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type ControlledTransaction, type
|
|
1
|
+
import { type ControlledTransaction, type DatabaseKysely, type DatabaseTransaction, type Transaction } from './isomorphic';
|
|
2
2
|
export type ActiveTransaction = Transaction | ControlledTransaction;
|
|
3
|
-
export declare function isActiveTransaction(db:
|
|
4
|
-
export declare function startControlledTransaction(db:
|
|
3
|
+
export declare function isActiveTransaction<Database>(db: DatabaseKysely<Database> | DatabaseTransaction<Database>): db is DatabaseTransaction<Database>;
|
|
4
|
+
export declare function startControlledTransaction<Database>(db: DatabaseKysely<Database>): Promise<import("kysely").ControlledTransaction<Database, []>>;
|
|
5
5
|
export declare function createTransactionSavepoint(trx: ActiveTransaction, savepointName: string): Promise<ActiveTransaction>;
|
|
6
6
|
export declare function rollbackTransactionSavepoint(trx: ActiveTransaction, savepointName: string): Promise<ActiveTransaction>;
|
|
7
7
|
export declare function releaseTransactionSavepoint(trx: ActiveTransaction, savepointName: string): Promise<ActiveTransaction>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.js","sourceRoot":"src/","sources":["transaction.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"transaction.js","sourceRoot":"src/","sources":["transaction.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,GAAG,GAEJ,MAAM,cAAc,CAAC;AAItB,MAAM,UAAU,mBAAmB,CACjC,EAA4D;IAE5D,OAAO,EAAE,EAAE,aAAa,KAAK,IAAI,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,EAA4B;IAE5B,OAAO,EAAE,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,CAAC;AACzC,CAAC;AAED,uEAAuE;AACvE,kDAAkD;AAClD,SAAS,mBAAmB,CAC1B,GAAgB,EAChB,OAAoE,EACpE,aAAqB;IAErB,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,IAAI,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,GAAsB,EACtB,aAAqB;IAErB,MAAM,mBAAmB,CAAC,GAAG,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;IAC3D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAChD,GAAsB,EACtB,aAAqB;IAErB,MAAM,mBAAmB,CAAC,GAAG,EAAE,uBAAuB,EAAE,aAAa,CAAC,CAAC;IACvE,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,GAAsB,EACtB,aAAqB;IAErB,MAAM,mBAAmB,CAAC,GAAG,EAAE,mBAAmB,EAAE,aAAa,CAAC,CAAC;IACnE,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["import {\n type ControlledTransaction,\n type DatabaseKysely,\n type DatabaseTransaction,\n sql,\n type Transaction,\n} from './isomorphic';\n\nexport type ActiveTransaction = Transaction | ControlledTransaction;\n\nexport function isActiveTransaction<Database>(\n db: DatabaseKysely<Database> | DatabaseTransaction<Database>\n): db is DatabaseTransaction<Database> {\n return db?.isTransaction === true;\n}\n\nexport async function startControlledTransaction<Database>(\n db: DatabaseKysely<Database>\n) {\n return db.startTransaction().execute();\n}\n\n// Our dialect stack supports controlled transactions, but not Kysely's\n// savepoint commands consistently across drivers.\nfunction runSavepointCommand(\n trx: Transaction,\n command: 'savepoint' | 'rollback to savepoint' | 'release savepoint',\n savepointName: string\n) {\n return sql.raw(`${command} ${savepointName}`).execute(trx);\n}\n\nexport async function createTransactionSavepoint(\n trx: ActiveTransaction,\n savepointName: string\n): Promise<ActiveTransaction> {\n await runSavepointCommand(trx, 'savepoint', savepointName);\n return trx;\n}\n\nexport async function rollbackTransactionSavepoint(\n trx: ActiveTransaction,\n savepointName: string\n): Promise<ActiveTransaction> {\n await runSavepointCommand(trx, 'rollback to savepoint', savepointName);\n return trx;\n}\n\nexport async function releaseTransactionSavepoint(\n trx: ActiveTransaction,\n savepointName: string\n): Promise<ActiveTransaction> {\n await runSavepointCommand(trx, 'release savepoint', savepointName);\n return trx;\n}\n"]}
|