@vrplatform/kysely 1.3.45-4658 → 1.3.45-4684
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/main/control-plane/index.d.ts +2 -0
- package/build/main/control-plane/index.js.map +1 -1
- 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/tenant/index.d.ts +1 -0
- package/build/main/query/tenant/index.js +18 -0
- package/build/main/query/tenant/index.js.map +1 -0
- package/build/main/query/tenant/status.d.ts +20 -0
- package/build/main/query/tenant/status.js +43 -0
- package/build/main/query/tenant/status.js.map +1 -0
- package/build/main/regional.d.ts +21 -0
- package/build/main/regional.js +48 -0
- package/build/main/regional.js.map +1 -1
- package/build/main/v1.generated.d.ts +40 -0
- package/build/main/v1.generated.js.map +1 -1
- package/build/migrations/0000000000064-partner-api-foundation.ts +36 -0
- package/build/module/control-plane/index.d.ts +2 -0
- package/build/module/control-plane/index.js.map +1 -1
- 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/tenant/index.d.ts +1 -0
- package/build/module/query/tenant/index.js +2 -0
- package/build/module/query/tenant/index.js.map +1 -0
- package/build/module/query/tenant/status.d.ts +20 -0
- package/build/module/query/tenant/status.js +38 -0
- package/build/module/query/tenant/status.js.map +1 -0
- package/build/module/regional.d.ts +21 -0
- package/build/module/regional.js +47 -0
- package/build/module/regional.js.map +1 -1
- package/build/module/v1.generated.d.ts +40 -0
- package/build/module/v1.generated.js.map +1 -1
- package/package.json +1 -1
|
@@ -89,6 +89,7 @@ export interface ControlPlaneTenant {
|
|
|
89
89
|
status: Generated<string | null>;
|
|
90
90
|
storageRealm: Generated<string>;
|
|
91
91
|
type: Generated<string | null>;
|
|
92
|
+
uniqueRef: string | null;
|
|
92
93
|
updatedAt: Generated<Timestamp | null>;
|
|
93
94
|
}
|
|
94
95
|
export interface ControlPlaneTenantUser {
|
|
@@ -121,6 +122,7 @@ export interface ControlPlaneUser {
|
|
|
121
122
|
lastName: string | null;
|
|
122
123
|
name: string | null;
|
|
123
124
|
partnerId: string | null;
|
|
125
|
+
phone: string | null;
|
|
124
126
|
secondaryEmails: Generated<Json>;
|
|
125
127
|
status: Generated<string | null>;
|
|
126
128
|
sub: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["control-plane/index.ts"],"names":[],"mappings":"","sourcesContent":["import type { ColumnType, Kysely as K } from 'kysely';\n\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\n\nexport type Json = JsonValue;\n\nexport type JsonArray = JsonValue[];\n\nexport type JsonObject = {\n [x: string]: JsonValue | undefined;\n};\n\nexport type JsonPrimitive = boolean | number | string | null;\n\nexport type JsonValue = JsonArray | JsonObject | JsonPrimitive;\n\nexport type Timestamp = ColumnType<Date | string, Date | string, Date | string>;\n\nexport interface ControlPlaneApp {\n authentication: Generated<Json>;\n category: string | null;\n color: string | null;\n createdAt: Generated<Timestamp>;\n icon: string | null;\n iconRound: string | null;\n id: Generated<string>;\n info: Json | null;\n name: string;\n type: string;\n version: Generated<number>;\n}\n\nexport interface ControlPlaneBookingChannel {\n channelRef: string | null;\n color: string | null;\n createdAt: Generated<Timestamp>;\n icon: string | null;\n iconProvider: string | null;\n id: Generated<string>;\n lastGenerateRun: Timestamp | null;\n logo: string | null;\n logoProvider: string | null;\n selectedBookingChannelIconCandidateId: string | null;\n selectedBookingChannelLogoCandidateId: string | null;\n uniqueRef: string;\n updatedAt: Generated<Timestamp>;\n}\n\nexport interface ControlPlaneCurrency {\n name: string;\n}\n\nexport interface ControlPlaneConnectionRoute {\n connectionId: string;\n createdAt: Generated<Timestamp>;\n tenantId: string;\n updatedAt: Generated<Timestamp>;\n}\n\nexport interface ControlPlaneFeature {\n createdAt: Generated<Timestamp>;\n description: string | null;\n id: Generated<string>;\n issueUrl: string | null;\n requiredApprovals: Generated<Json | null>;\n status: Generated<string | null>;\n tenantId: string | null;\n title: string;\n updatedAt: Generated<Timestamp>;\n url: string | null;\n}\n\nexport interface ControlPlaneFeatureEnabledTeam {\n createdAt: Generated<Timestamp>;\n featureId: string;\n id: Generated<string>;\n status: Generated<string | null>;\n tenantId: string;\n updatedAt: Generated<Timestamp>;\n}\n\nexport interface ControlPlaneOutbox {\n aggregateId: string;\n aggregateType: string;\n attempts: Generated<number>;\n createdAt: Generated<Timestamp>;\n dataRegion: string | null;\n eventType: string;\n eventVersion: number;\n id: Generated<string>;\n lastError: string | null;\n payload: Generated<Json>;\n publishedAt: Timestamp | null;\n}\n\nexport interface ControlPlaneTenant {\n billingPlan: string | null;\n createdAt: Generated<Timestamp>;\n dataRegion: Generated<string>;\n id: Generated<string>;\n name: string;\n partnerId: string | null;\n slug: string;\n status: Generated<string | null>;\n storageRealm: Generated<string>;\n type: Generated<string | null>;\n updatedAt: Generated<Timestamp | null>;\n}\n\nexport interface ControlPlaneTenantUser {\n createdAt: Generated<Timestamp | null>;\n id: Generated<string>;\n lastInvitedAt: Timestamp | null;\n role: Generated<string | null>;\n status: Generated<string | null>;\n tenantId: string;\n updatedAt: Generated<Timestamp | null>;\n userId: string;\n}\n\nexport interface ControlPlaneToken {\n createdAt: Generated<Timestamp>;\n expiresAt: Timestamp | null;\n id: Generated<string>;\n nanoId: Generated<string>;\n payload: Generated<Json>;\n tenantId: string | null;\n type: string | null;\n userId: string | null;\n}\n\nexport interface ControlPlaneUser {\n clerkId: string | null;\n createdAt: Generated<Timestamp | null>;\n email: string | null;\n firstName: string | null;\n id: Generated<string>;\n isAdmin: Generated<boolean>;\n lastName: string | null;\n name: string | null;\n partnerId: string | null;\n secondaryEmails: Generated<Json>;\n status: Generated<string | null>;\n sub: string | null;\n type: Generated<string | null>;\n updatedAt: Generated<Timestamp | null>;\n}\n\nexport interface ControlPlaneDB {\n 'public.app': ControlPlaneApp;\n 'public.bookingChannel': ControlPlaneBookingChannel;\n 'public.connectionRoute': ControlPlaneConnectionRoute;\n 'public.currency': ControlPlaneCurrency;\n 'public.feature': ControlPlaneFeature;\n 'public.featureEnabledTeam': ControlPlaneFeatureEnabledTeam;\n 'public.controlPlaneOutbox': ControlPlaneOutbox;\n 'public.tenant': ControlPlaneTenant;\n 'public.tenantUser': ControlPlaneTenantUser;\n 'public.token': ControlPlaneToken;\n 'public.user': ControlPlaneUser;\n}\n\nexport type ControlPlaneKysely = K<ControlPlaneDB>;\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["control-plane/index.ts"],"names":[],"mappings":"","sourcesContent":["import type { ColumnType, Kysely as K } from 'kysely';\n\nexport type Generated<T> =\n T extends ColumnType<infer S, infer I, infer U>\n ? ColumnType<S, I | undefined, U>\n : ColumnType<T, T | undefined, T>;\n\nexport type Json = JsonValue;\n\nexport type JsonArray = JsonValue[];\n\nexport type JsonObject = {\n [x: string]: JsonValue | undefined;\n};\n\nexport type JsonPrimitive = boolean | number | string | null;\n\nexport type JsonValue = JsonArray | JsonObject | JsonPrimitive;\n\nexport type Timestamp = ColumnType<Date | string, Date | string, Date | string>;\n\nexport interface ControlPlaneApp {\n authentication: Generated<Json>;\n category: string | null;\n color: string | null;\n createdAt: Generated<Timestamp>;\n icon: string | null;\n iconRound: string | null;\n id: Generated<string>;\n info: Json | null;\n name: string;\n type: string;\n version: Generated<number>;\n}\n\nexport interface ControlPlaneBookingChannel {\n channelRef: string | null;\n color: string | null;\n createdAt: Generated<Timestamp>;\n icon: string | null;\n iconProvider: string | null;\n id: Generated<string>;\n lastGenerateRun: Timestamp | null;\n logo: string | null;\n logoProvider: string | null;\n selectedBookingChannelIconCandidateId: string | null;\n selectedBookingChannelLogoCandidateId: string | null;\n uniqueRef: string;\n updatedAt: Generated<Timestamp>;\n}\n\nexport interface ControlPlaneCurrency {\n name: string;\n}\n\nexport interface ControlPlaneConnectionRoute {\n connectionId: string;\n createdAt: Generated<Timestamp>;\n tenantId: string;\n updatedAt: Generated<Timestamp>;\n}\n\nexport interface ControlPlaneFeature {\n createdAt: Generated<Timestamp>;\n description: string | null;\n id: Generated<string>;\n issueUrl: string | null;\n requiredApprovals: Generated<Json | null>;\n status: Generated<string | null>;\n tenantId: string | null;\n title: string;\n updatedAt: Generated<Timestamp>;\n url: string | null;\n}\n\nexport interface ControlPlaneFeatureEnabledTeam {\n createdAt: Generated<Timestamp>;\n featureId: string;\n id: Generated<string>;\n status: Generated<string | null>;\n tenantId: string;\n updatedAt: Generated<Timestamp>;\n}\n\nexport interface ControlPlaneOutbox {\n aggregateId: string;\n aggregateType: string;\n attempts: Generated<number>;\n createdAt: Generated<Timestamp>;\n dataRegion: string | null;\n eventType: string;\n eventVersion: number;\n id: Generated<string>;\n lastError: string | null;\n payload: Generated<Json>;\n publishedAt: Timestamp | null;\n}\n\nexport interface ControlPlaneTenant {\n billingPlan: string | null;\n createdAt: Generated<Timestamp>;\n dataRegion: Generated<string>;\n id: Generated<string>;\n name: string;\n partnerId: string | null;\n slug: string;\n status: Generated<string | null>;\n storageRealm: Generated<string>;\n type: Generated<string | null>;\n uniqueRef: string | null;\n updatedAt: Generated<Timestamp | null>;\n}\n\nexport interface ControlPlaneTenantUser {\n createdAt: Generated<Timestamp | null>;\n id: Generated<string>;\n lastInvitedAt: Timestamp | null;\n role: Generated<string | null>;\n status: Generated<string | null>;\n tenantId: string;\n updatedAt: Generated<Timestamp | null>;\n userId: string;\n}\n\nexport interface ControlPlaneToken {\n createdAt: Generated<Timestamp>;\n expiresAt: Timestamp | null;\n id: Generated<string>;\n nanoId: Generated<string>;\n payload: Generated<Json>;\n tenantId: string | null;\n type: string | null;\n userId: string | null;\n}\n\nexport interface ControlPlaneUser {\n clerkId: string | null;\n createdAt: Generated<Timestamp | null>;\n email: string | null;\n firstName: string | null;\n id: Generated<string>;\n isAdmin: Generated<boolean>;\n lastName: string | null;\n name: string | null;\n partnerId: string | null;\n phone: string | null;\n secondaryEmails: Generated<Json>;\n status: Generated<string | null>;\n sub: string | null;\n type: Generated<string | null>;\n updatedAt: Generated<Timestamp | null>;\n}\n\nexport interface ControlPlaneDB {\n 'public.app': ControlPlaneApp;\n 'public.bookingChannel': ControlPlaneBookingChannel;\n 'public.connectionRoute': ControlPlaneConnectionRoute;\n 'public.currency': ControlPlaneCurrency;\n 'public.feature': ControlPlaneFeature;\n 'public.featureEnabledTeam': ControlPlaneFeatureEnabledTeam;\n 'public.controlPlaneOutbox': ControlPlaneOutbox;\n 'public.tenant': ControlPlaneTenant;\n 'public.tenantUser': ControlPlaneTenantUser;\n 'public.token': ControlPlaneToken;\n 'public.user': ControlPlaneUser;\n}\n\nexport type ControlPlaneKysely = K<ControlPlaneDB>;\n"]}
|
|
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./listing"), exports);
|
|
18
|
+
__exportStar(require("./tenant"), exports);
|
|
18
19
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["query/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B","sourcesContent":["export * from './listing';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["query/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,2CAAyB","sourcesContent":["export * from './listing';\nexport * from './tenant';\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './status';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./status"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["query/tenant/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB","sourcesContent":["export * from './status';\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DB, ExpressionBuilder, PublicTenant } 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", import("kysely/dist/cjs/util/type-utils").SqlBool>;
|
|
8
|
+
export declare function whereTenantNotTestTeam(eb: TenantExpressionBuilder, { excludedTenantIds, testPartnerTenantId, }: {
|
|
9
|
+
excludedTenantIds: readonly [string, ...string[]];
|
|
10
|
+
testPartnerTenantId: string;
|
|
11
|
+
}): import("kysely").ExpressionWrapper<DB & {
|
|
12
|
+
tenant: PublicTenant;
|
|
13
|
+
}, "tenant", import("kysely/dist/cjs/util/type-utils").SqlBool>;
|
|
14
|
+
export declare function whereTenantShouldBePaid(eb: TenantExpressionBuilder, { includePartnerTeams, today, }?: {
|
|
15
|
+
includePartnerTeams?: boolean;
|
|
16
|
+
today?: Date | string;
|
|
17
|
+
}): import("kysely").ExpressionWrapper<DB & {
|
|
18
|
+
tenant: PublicTenant;
|
|
19
|
+
}, "tenant", import("kysely/dist/cjs/util/type-utils").SqlBool>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.whereTenantActive = whereTenantActive;
|
|
4
|
+
exports.whereTenantNotTestTeam = whereTenantNotTestTeam;
|
|
5
|
+
exports.whereTenantShouldBePaid = whereTenantShouldBePaid;
|
|
6
|
+
const utils_1 = require("@vrplatform/utils");
|
|
7
|
+
function whereTenantActive(eb) {
|
|
8
|
+
return eb.and([
|
|
9
|
+
eb('tenant.calculatedStatus', '=', 'active'),
|
|
10
|
+
eb.or([
|
|
11
|
+
eb('tenant.calculatedBillingPartnerId', 'is', null),
|
|
12
|
+
eb.exists(eb
|
|
13
|
+
.selectFrom('public.tenant as billingPartner')
|
|
14
|
+
.select('billingPartner.id')
|
|
15
|
+
.whereRef('billingPartner.id', '=', 'tenant.calculatedBillingPartnerId')
|
|
16
|
+
.where('billingPartner.calculatedStatus', '=', 'active')),
|
|
17
|
+
]),
|
|
18
|
+
]);
|
|
19
|
+
}
|
|
20
|
+
function whereTenantNotTestTeam(eb, { excludedTenantIds, testPartnerTenantId, }) {
|
|
21
|
+
return eb.and([
|
|
22
|
+
eb('tenant.id', 'not in', [...excludedTenantIds]),
|
|
23
|
+
eb('tenant.partnerId', '!=', testPartnerTenantId),
|
|
24
|
+
eb.or([
|
|
25
|
+
eb('tenant.billingPartnerId', 'is', null),
|
|
26
|
+
eb('tenant.billingPartnerId', '!=', testPartnerTenantId),
|
|
27
|
+
]),
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
function whereTenantShouldBePaid(eb, { includePartnerTeams = false, today = (0, utils_1.day)().yyyymmdd(), } = {}) {
|
|
31
|
+
return eb.and([
|
|
32
|
+
whereTenantActive(eb),
|
|
33
|
+
eb('tenant.isOnboarding', '=', false),
|
|
34
|
+
eb.or([
|
|
35
|
+
eb('tenant.trialUntil', '<', today),
|
|
36
|
+
eb('tenant.trialUntil', 'is', null),
|
|
37
|
+
]),
|
|
38
|
+
...(includePartnerTeams
|
|
39
|
+
? []
|
|
40
|
+
: [eb('tenant.calculatedIsBillable', '=', true)]),
|
|
41
|
+
]);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"src/","sources":["query/tenant/status.ts"],"names":[],"mappings":";;AAQA,8CAkBC;AAED,wDAkBC;AAED,0DAqBC;AArED,6CAAwC;AAQxC,SAAgB,iBAAiB,CAAC,EAA2B;IAC3D,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE,QAAQ,CAAC;QAC5C,EAAE,CAAC,EAAE,CAAC;YACJ,EAAE,CAAC,mCAAmC,EAAE,IAAI,EAAE,IAAI,CAAC;YACnD,EAAE,CAAC,MAAM,CACP,EAAE;iBACC,UAAU,CAAC,iCAAiC,CAAC;iBAC7C,MAAM,CAAC,mBAAmB,CAAC;iBAC3B,QAAQ,CACP,mBAAmB,EACnB,GAAG,EACH,mCAAmC,CACpC;iBACA,KAAK,CAAC,iCAAiC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAC3D;SACF,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,sBAAsB,CACpC,EAA2B,EAC3B,EACE,iBAAiB,EACjB,mBAAmB,GAIpB;IAED,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,GAAG,iBAAiB,CAAC,CAAC;QACjD,EAAE,CAAC,kBAAkB,EAAE,IAAI,EAAE,mBAAmB,CAAC;QACjD,EAAE,CAAC,EAAE,CAAC;YACJ,EAAE,CAAC,yBAAyB,EAAE,IAAI,EAAE,IAAI,CAAC;YACzC,EAAE,CAAC,yBAAyB,EAAE,IAAI,EAAE,mBAAmB,CAAC;SACzD,CAAC;KACH,CAAC,CAAC;AACL,CAAC;AAED,SAAgB,uBAAuB,CACrC,EAA2B,EAC3B,EACE,mBAAmB,GAAG,KAAK,EAC3B,KAAK,GAAG,IAAA,WAAG,GAAE,CAAC,QAAQ,EAAE,MAItB,EAAE;IAEN,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,iBAAiB,CAAC,EAAE,CAAC;QACrB,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE,KAAK,CAAC;QACrC,EAAE,CAAC,EAAE,CAAC;YACJ,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE,KAAK,CAAC;YACnC,EAAE,CAAC,mBAAmB,EAAE,IAAI,EAAE,IAAI,CAAC;SACpC,CAAC;QACF,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 { day } from '@vrplatform/utils';\nimport type { DB, ExpressionBuilder, PublicTenant } from '../../isomorphic';\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.or([\n eb('tenant.calculatedBillingPartnerId', 'is', null),\n eb.exists(\n eb\n .selectFrom('public.tenant as billingPartner')\n .select('billingPartner.id')\n .whereRef(\n 'billingPartner.id',\n '=',\n 'tenant.calculatedBillingPartnerId'\n )\n .where('billingPartner.calculatedStatus', '=', 'active')\n ),\n ]),\n ]);\n}\n\nexport function whereTenantNotTestTeam(\n eb: TenantExpressionBuilder,\n {\n excludedTenantIds,\n testPartnerTenantId,\n }: {\n excludedTenantIds: readonly [string, ...string[]];\n testPartnerTenantId: string;\n }\n) {\n return eb.and([\n eb('tenant.id', 'not in', [...excludedTenantIds]),\n eb('tenant.partnerId', '!=', testPartnerTenantId),\n eb.or([\n eb('tenant.billingPartnerId', 'is', null),\n eb('tenant.billingPartnerId', '!=', testPartnerTenantId),\n ]),\n ]);\n}\n\nexport function whereTenantShouldBePaid(\n eb: TenantExpressionBuilder,\n {\n includePartnerTeams = false,\n today = day().yyyymmdd(),\n }: {\n includePartnerTeams?: boolean;\n today?: Date | string;\n } = {}\n) {\n return eb.and([\n whereTenantActive(eb),\n eb('tenant.isOnboarding', '=', false),\n eb.or([\n eb('tenant.trialUntil', '<', today),\n eb('tenant.trialUntil', 'is', null),\n ]),\n ...(includePartnerTeams\n ? []\n : [eb('tenant.calculatedIsBillable', '=', true)]),\n ]);\n}\n"]}
|
package/build/main/regional.d.ts
CHANGED
|
@@ -14,6 +14,27 @@ export declare function useTenantDatabaseKysely(dataRegion: TenantDataRegion, re
|
|
|
14
14
|
useAsyncKysely?: UseAsyncRegionalKysely;
|
|
15
15
|
probe?: true;
|
|
16
16
|
}): Promise<Kysely>;
|
|
17
|
+
export type TenantKysely = {
|
|
18
|
+
id: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
dataRegion: TenantDataRegion;
|
|
21
|
+
kysely: Kysely;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a tenant's regional Kysely connection from a connection id, slug,
|
|
25
|
+
* or tenant id. The control-plane connection is opened and destroyed
|
|
26
|
+
* internally; the returned regional connection must be destroyed by the
|
|
27
|
+
* caller.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveTenantKysely({ connectionId, slug, tenantId, repositoryName, env, ...options }: {
|
|
30
|
+
connectionId?: string;
|
|
31
|
+
slug?: string;
|
|
32
|
+
tenantId?: string;
|
|
33
|
+
repositoryName: string;
|
|
34
|
+
env: RegionEnvironment;
|
|
35
|
+
useAsyncKysely?: UseAsyncRegionalKysely;
|
|
36
|
+
probe?: true;
|
|
37
|
+
}): Promise<TenantKysely>;
|
|
17
38
|
export type RegionRunError = {
|
|
18
39
|
dataRegion: TenantDataRegion;
|
|
19
40
|
error: string;
|
package/build/main/regional.js
CHANGED
|
@@ -39,6 +39,7 @@ exports.resolveTenantDatabaseUrl = resolveTenantDatabaseUrl;
|
|
|
39
39
|
exports.resolveTenantApiBaseUrl = resolveTenantApiBaseUrl;
|
|
40
40
|
exports.resolveConfiguredTenantDataRegions = resolveConfiguredTenantDataRegions;
|
|
41
41
|
exports.useTenantDatabaseKysely = useTenantDatabaseKysely;
|
|
42
|
+
exports.resolveTenantKysely = resolveTenantKysely;
|
|
42
43
|
exports.forEachConfiguredRegion = forEachConfiguredRegion;
|
|
43
44
|
// The tenant-data-region contract lives here — the region set plus how a
|
|
44
45
|
// region resolves to its database url, API base url, and how to fan a job out
|
|
@@ -115,6 +116,53 @@ async function useTenantDatabaseKysely(dataRegion, repositoryName, env, options
|
|
|
115
116
|
probe: options.probe,
|
|
116
117
|
});
|
|
117
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Resolve a tenant's regional Kysely connection from a connection id, slug,
|
|
121
|
+
* or tenant id. The control-plane connection is opened and destroyed
|
|
122
|
+
* internally; the returned regional connection must be destroyed by the
|
|
123
|
+
* caller.
|
|
124
|
+
*/
|
|
125
|
+
async function resolveTenantKysely({ connectionId, slug, tenantId, repositoryName, env, ...options }) {
|
|
126
|
+
if (!connectionId && !slug && !tenantId) {
|
|
127
|
+
throw new Error('connectionId, slug, or tenantId is required');
|
|
128
|
+
}
|
|
129
|
+
const controlPlaneDatabaseUrl = env.CONTROL_PLANE_DATABASE_URL;
|
|
130
|
+
if (!controlPlaneDatabaseUrl)
|
|
131
|
+
throw new Error('CONTROL_PLANE_DATABASE_URL is required');
|
|
132
|
+
const controlPlane = await (options.useAsyncKysely ?? defaultUseAsyncKysely)(controlPlaneDatabaseUrl, {
|
|
133
|
+
repositoryName: `${repositoryName}/control-plane`,
|
|
134
|
+
probe: options.probe,
|
|
135
|
+
});
|
|
136
|
+
let tenant;
|
|
137
|
+
try {
|
|
138
|
+
const query = controlPlane
|
|
139
|
+
.selectFrom('public.tenant')
|
|
140
|
+
.select([
|
|
141
|
+
'public.tenant.id as id',
|
|
142
|
+
'public.tenant.slug as slug',
|
|
143
|
+
'public.tenant.dataRegion as dataRegion',
|
|
144
|
+
]);
|
|
145
|
+
tenant = await (slug
|
|
146
|
+
? query.where('public.tenant.slug', '=', slug)
|
|
147
|
+
: connectionId
|
|
148
|
+
? query
|
|
149
|
+
.innerJoin('public.connectionRoute', 'public.connectionRoute.tenantId', 'public.tenant.id')
|
|
150
|
+
.where('public.connectionRoute.connectionId', '=', connectionId)
|
|
151
|
+
: query.where('public.tenant.id', '=', tenantId)).executeTakeFirst();
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
await controlPlane.destroy();
|
|
155
|
+
}
|
|
156
|
+
if (!tenant)
|
|
157
|
+
throw new Error('Tenant not found in control plane');
|
|
158
|
+
assertTenantDataRegion(tenant.dataRegion);
|
|
159
|
+
return {
|
|
160
|
+
id: tenant.id,
|
|
161
|
+
slug: tenant.slug,
|
|
162
|
+
dataRegion: tenant.dataRegion,
|
|
163
|
+
kysely: await useTenantDatabaseKysely(tenant.dataRegion, repositoryName, env, options),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
118
166
|
/**
|
|
119
167
|
* Run a job once per configured tenant data region with per-region error
|
|
120
168
|
* isolation: one region's outage must not stall the others (regions iterate
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"regional.js","sourceRoot":"src/","sources":["regional.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Kysely } from './index';\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 Error(`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 Error(`Missing database url for ${dataRegion}`);\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 Error(`Missing ${key}`);\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 Error(\n `Tenant data regions ${aliased.join(', ')} share the same database url; ` +\n 'each configured region must point at a distinct database'\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\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\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 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 Error(\n `All configured tenant data regions failed: ${errors\n .map((regionError) => `${regionError.dataRegion}: ${regionError.error}`)\n .join('; ')}`\n );\n }\n\n return { results, errors };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"regional.js","sourceRoot":"src/","sources":["regional.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Kysely } from './index';\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 Error(`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 Error(`Missing database url for ${dataRegion}`);\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 Error(`Missing ${key}`);\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 Error(\n `Tenant data regions ${aliased.join(', ')} share the same database url; ` +\n 'each configured region must point at a distinct database'\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\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\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 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 (options.useAsyncKysely ?? defaultUseAsyncKysely)(\n controlPlaneDatabaseUrl,\n {\n repositoryName: `${repositoryName}/control-plane`,\n probe: options.probe,\n }\n );\n let tenant: { id: string; slug: string; dataRegion: string } | undefined;\n try {\n const query = controlPlane\n .selectFrom('public.tenant')\n .select([\n 'public.tenant.id as id',\n 'public.tenant.slug as slug',\n 'public.tenant.dataRegion as dataRegion',\n ]);\n tenant = await (\n slug\n ? query.where('public.tenant.slug', '=', slug)\n : connectionId\n ? query\n .innerJoin(\n 'public.connectionRoute',\n 'public.connectionRoute.tenantId',\n 'public.tenant.id'\n )\n .where('public.connectionRoute.connectionId', '=', connectionId)\n : query.where('public.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 Error(\n `All configured tenant data regions failed: ${errors\n .map((regionError) => `${regionError.dataRegion}: ${regionError.error}`)\n .join('; ')}`\n );\n }\n\n return { results, errors };\n}\n"]}
|
|
@@ -332,6 +332,12 @@ export interface AccountingOwnerStatementProjection {
|
|
|
332
332
|
tenantId: string;
|
|
333
333
|
variant: string;
|
|
334
334
|
}
|
|
335
|
+
export interface AccountingOwnerStatementProjectionListingRevision {
|
|
336
|
+
listingId: string;
|
|
337
|
+
revision: Generated<Int8>;
|
|
338
|
+
tenantId: string;
|
|
339
|
+
updatedAt: Generated<Timestamp>;
|
|
340
|
+
}
|
|
335
341
|
export interface AccountingOwnerStatementProjectionRevision {
|
|
336
342
|
revision: Generated<Int8>;
|
|
337
343
|
tenantId: string;
|
|
@@ -956,6 +962,35 @@ export interface PublicConnection {
|
|
|
956
962
|
version: number | null;
|
|
957
963
|
webhookVersion: Generated<Int8>;
|
|
958
964
|
}
|
|
965
|
+
export interface PublicConnectionRoute {
|
|
966
|
+
connectionId: string;
|
|
967
|
+
createdAt: Generated<Timestamp>;
|
|
968
|
+
tenantId: string;
|
|
969
|
+
updatedAt: Generated<Timestamp>;
|
|
970
|
+
}
|
|
971
|
+
export interface PublicControlPlaneOutbox {
|
|
972
|
+
aggregateId: string;
|
|
973
|
+
aggregateType: string;
|
|
974
|
+
attempts: Generated<number>;
|
|
975
|
+
createdAt: Generated<Timestamp>;
|
|
976
|
+
dataRegion: string | null;
|
|
977
|
+
eventType: string;
|
|
978
|
+
eventVersion: number;
|
|
979
|
+
id: Generated<string>;
|
|
980
|
+
lastError: string | null;
|
|
981
|
+
payload: Generated<Json>;
|
|
982
|
+
publishedAt: Timestamp | null;
|
|
983
|
+
}
|
|
984
|
+
export interface PublicControlPlaneProjectionEvent {
|
|
985
|
+
aggregateId: string;
|
|
986
|
+
aggregateType: string;
|
|
987
|
+
appliedAt: Generated<Timestamp>;
|
|
988
|
+
dataRegion: string;
|
|
989
|
+
eventId: string;
|
|
990
|
+
eventType: string;
|
|
991
|
+
eventVersion: number;
|
|
992
|
+
payload: Generated<Json>;
|
|
993
|
+
}
|
|
959
994
|
export interface PublicPlaidConnectSession {
|
|
960
995
|
connectionId: string | null;
|
|
961
996
|
createdAt: Generated<Timestamp>;
|
|
@@ -1702,6 +1737,7 @@ export interface PublicTenant {
|
|
|
1702
1737
|
svixEndpoints: boolean | null;
|
|
1703
1738
|
trialUntil: Timestamp | null;
|
|
1704
1739
|
type: Generated<string | null>;
|
|
1740
|
+
uniqueRef: string | null;
|
|
1705
1741
|
updatedAt: Generated<Timestamp | null>;
|
|
1706
1742
|
website: string | null;
|
|
1707
1743
|
}
|
|
@@ -2016,6 +2052,7 @@ export interface DB {
|
|
|
2016
2052
|
"accounting.journalEntryTriggerType": AccountingJournalEntryTriggerType;
|
|
2017
2053
|
"accounting.journalEntryType": AccountingJournalEntryType;
|
|
2018
2054
|
"accounting.ownerStatementProjection": AccountingOwnerStatementProjection;
|
|
2055
|
+
"accounting.ownerStatementProjectionListingRevision": AccountingOwnerStatementProjectionListingRevision;
|
|
2019
2056
|
"accounting.ownerStatementProjectionRevision": AccountingOwnerStatementProjectionRevision;
|
|
2020
2057
|
"accounting.paidStatus": AccountingPaidStatus;
|
|
2021
2058
|
"accounting.party": AccountingParty;
|
|
@@ -2072,6 +2109,9 @@ export interface DB {
|
|
|
2072
2109
|
"public.bookingChannel": PublicBookingChannel;
|
|
2073
2110
|
"public.bookingChannelIconCandidate": PublicBookingChannelIconCandidate;
|
|
2074
2111
|
"public.connection": PublicConnection;
|
|
2112
|
+
"public.connectionRoute": PublicConnectionRoute;
|
|
2113
|
+
"public.controlPlaneOutbox": PublicControlPlaneOutbox;
|
|
2114
|
+
"public.controlPlaneProjectionEvent": PublicControlPlaneProjectionEvent;
|
|
2075
2115
|
"public.plaidConnectSession": PublicPlaidConnectSession;
|
|
2076
2116
|
"public.currency": PublicCurrency;
|
|
2077
2117
|
"public.duplicateEmails": PublicDuplicateEmails;
|