@vrplatform/kysely 1.3.41 → 1.3.45-4444

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/build/.data.tar +0 -0
  2. package/build/main/camel-case/index.js.map +1 -1
  3. package/build/main/control-plane/index.d.ts +142 -0
  4. package/build/main/control-plane/index.js +3 -0
  5. package/build/main/control-plane/index.js.map +1 -0
  6. package/build/main/error.js.map +1 -1
  7. package/build/main/index.d.ts +5 -3
  8. package/build/main/index.js +8 -10
  9. package/build/main/index.js.map +1 -1
  10. package/build/main/isomorphic.js +2 -2
  11. package/build/main/isomorphic.js.map +1 -1
  12. package/build/main/local/digest.js.map +1 -1
  13. package/build/main/local/generate.js.map +1 -1
  14. package/build/main/local/index.js.map +1 -1
  15. package/build/main/local/prune-pulled-schema.js.map +1 -1
  16. package/build/main/pganalyze-comment-plugin/comment-transformer.js +0 -1
  17. package/build/main/pganalyze-comment-plugin/comment-transformer.js.map +1 -1
  18. package/build/main/pganalyze-comment-plugin/index.js +16 -3
  19. package/build/main/pganalyze-comment-plugin/index.js.map +1 -1
  20. package/build/main/plugins.js.map +1 -1
  21. package/build/main/query/listing/status.d.ts +4 -4
  22. package/build/main/query/listing/status.js +2 -1
  23. package/build/main/query/listing/status.js.map +1 -1
  24. package/build/main/regional.d.ts +34 -0
  25. package/build/main/regional.js +145 -0
  26. package/build/main/regional.js.map +1 -0
  27. package/build/main/test/index.js.map +1 -1
  28. package/build/main/transaction.js.map +1 -1
  29. package/build/main/v1.generated.d.ts +123 -1
  30. package/build/main/v1.generated.js.map +1 -1
  31. package/build/migrations/0000000000001-init.ts +5 -0
  32. package/build/migrations/0000000000009-drop-store-schema.ts +14 -0
  33. package/build/migrations/0000000000010-bank-rules.ts +190 -0
  34. package/build/migrations/0000000000011-bank-rule-line-adjustments.ts +65 -0
  35. package/build/migrations/0000000000012-bank-rule-conditions.ts +45 -0
  36. package/build/migrations/0000000000013-bank-rule-name-unique.ts +15 -0
  37. package/build/migrations/0000000000014-bank-rule-drop-legacy-conditions.ts +41 -0
  38. package/build/migrations/0000000000015-booking-channel-logo-dev-provider.ts +70 -0
  39. package/build/migrations/0000000000016-owner-statement-draft-status.ts +22 -0
  40. package/build/migrations/0000000000017-tenant-migration-markers.ts +18 -0
  41. package/build/migrations/0000000000018-security-deposits-payable-category.ts +63 -0
  42. package/build/migrations/0000000000019-tenant-app-settings.ts +76 -0
  43. package/build/migrations/0000000000020-operating-opening-trial-balance.ts +36 -0
  44. package/build/migrations/0000000000021-audit-foreign-key-indexes.ts +77 -0
  45. package/build/migrations/0000000000022-opening-trial-balance-combined-columns.ts +30 -0
  46. package/build/migrations/0000000000023-journal-entry-currency-mismatch-reason.ts +14 -0
  47. package/build/migrations/0000000000024-listing-parent.ts +37 -0
  48. package/build/migrations/0000000000025-journal-entry-parent-listing.ts +26 -0
  49. package/build/migrations/0000000000026-booking-channel-logo-slots.ts +89 -0
  50. package/build/migrations/0000000000027-list-browse-performance-indexes.ts +52 -0
  51. package/build/migrations/0000000000028-flow-setting-status-jsonb.ts +20 -0
  52. package/build/migrations/0000000000029-tenant-slug.ts +328 -0
  53. package/build/migrations/0000000000030-pms-accounting-windows.ts +50 -0
  54. package/build/migrations/0000000000031-owner-statement-projection-listing-revision.ts +20 -0
  55. package/build/migrations/0000000000032-flow-event-listening.ts +28 -0
  56. package/build/migrations/0000000000033-opening-trial-balance-transaction.ts +272 -0
  57. package/build/migrations/0000000000034-historic-general-ledger.ts +24 -0
  58. package/build/migrations/0000000000035-tenant-regional-placement.ts +37 -0
  59. package/build/migrations/0000000000036-control-plane-projection-events.ts +43 -0
  60. package/build/migrations/0000000000037-tenant-guide-onboarding.ts +21 -0
  61. package/build/migrations/0000000000038-tracking-event-analytics-sink.ts +31 -0
  62. package/build/migrations/0000000000039-crunchy-region.ts +86 -0
  63. package/build/migrations/0000000000040-hostaway-region.ts +72 -0
  64. package/build/migrations/0000000000041-audit-tenant-fk.ts +66 -0
  65. package/build/migrations/0000000000042-ramp-staging-connect-params.ts +35 -0
  66. package/build/migrations/0000000000043-gl-pms-accounting-window-guard.ts +125 -0
  67. package/build/migrations/0000000000044-tenant-cross-region-fks.ts +45 -0
  68. package/build/migrations/0000000000045-cloudflare-region-env.ts +100 -0
  69. package/build/migrations/0000000000046-control-plane-outbox-alias.ts +46 -0
  70. package/build/migrations/0000000000047-ramp-contact-last-4-digits.ts +16 -0
  71. package/build/migrations/0000000000047-tenant-status-backfill.ts +40 -0
  72. package/build/migrations/0000000000048-tenant-status-constraint.ts +24 -0
  73. package/build/migrations/0000000000049-health-tenant-issues.ts +69 -0
  74. package/build/migrations/0000000000050-partner-webhooks.ts +135 -0
  75. package/build/migrations/0000000000051-health-webhook-runtime-grants.ts +42 -0
  76. package/build/migrations/0000000000052-feature-approval-team-enablement.ts +13 -0
  77. package/build/migrations/0000000000053-health-webhook-enums.ts +296 -0
  78. package/build/module/camel-case/index.js.map +1 -1
  79. package/build/module/control-plane/index.d.ts +142 -0
  80. package/build/module/control-plane/index.js +2 -0
  81. package/build/module/control-plane/index.js.map +1 -0
  82. package/build/module/error.js.map +1 -1
  83. package/build/module/index.d.ts +5 -3
  84. package/build/module/index.js +8 -10
  85. package/build/module/index.js.map +1 -1
  86. package/build/module/isomorphic.js.map +1 -1
  87. package/build/module/local/digest.js.map +1 -1
  88. package/build/module/local/generate.js.map +1 -1
  89. package/build/module/local/index.js.map +1 -1
  90. package/build/module/local/prune-pulled-schema.js.map +1 -1
  91. package/build/module/pganalyze-comment-plugin/comment-transformer.js.map +1 -1
  92. package/build/module/pganalyze-comment-plugin/index.js.map +1 -1
  93. package/build/module/plugins.js.map +1 -1
  94. package/build/module/query/listing/status.d.ts +4 -4
  95. package/build/module/query/listing/status.js +2 -1
  96. package/build/module/query/listing/status.js.map +1 -1
  97. package/build/module/regional.d.ts +34 -0
  98. package/build/module/regional.js +103 -0
  99. package/build/module/regional.js.map +1 -0
  100. package/build/module/test/index.js.map +1 -1
  101. package/build/module/transaction.js.map +1 -1
  102. package/build/module/v1.generated.d.ts +123 -1
  103. package/build/module/v1.generated.js.map +1 -1
  104. package/package.json +13 -4
  105. package/build/main/tsconfig.main.tsbuildinfo +0 -1
  106. package/build/module/tsconfig.esm.tsbuildinfo +0 -1
@@ -0,0 +1,125 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ export async function up(db: Kysely<any>): Promise<void> {
5
+ await sql`
6
+ create or replace function public.assert_gl_pms_accounting_window_for_tenant(
7
+ check_tenant_id uuid
8
+ )
9
+ returns void
10
+ language plpgsql
11
+ as $$
12
+ begin
13
+ if check_tenant_id is null then
14
+ return;
15
+ end if;
16
+
17
+ if exists (
18
+ select 1
19
+ from public.tenant as tenant
20
+ where tenant.id = check_tenant_id
21
+ and tenant.is_general_ledger is true
22
+ and exists (
23
+ select 1
24
+ from public.connection as connection
25
+ inner join public.app as app on app.id = connection.app_id
26
+ where connection.tenant_id = check_tenant_id
27
+ and connection.status = 'active'
28
+ and connection.accounting_start_at is null
29
+ and app.category = 'propertyManagementSystem'
30
+ )
31
+ and not exists (
32
+ select 1
33
+ from public.connection as connection
34
+ inner join public.app as app on app.id = connection.app_id
35
+ where connection.tenant_id = check_tenant_id
36
+ and connection.status = 'active'
37
+ and connection.accounting_start_at is not null
38
+ and connection.accounting_end_at is null
39
+ and app.category = 'propertyManagementSystem'
40
+ )
41
+ ) then
42
+ raise exception
43
+ 'Active GL PMS connection is missing accountingStartAt without an open PMS accounting source'
44
+ using errcode = '23514';
45
+ end if;
46
+ end;
47
+ $$;
48
+ `.execute(db);
49
+
50
+ await sql`
51
+ create or replace function public.assert_gl_pms_accounting_window_connection_trigger()
52
+ returns trigger
53
+ language plpgsql
54
+ as $$
55
+ begin
56
+ if tg_op = 'DELETE' then
57
+ perform public.assert_gl_pms_accounting_window_for_tenant(old.tenant_id);
58
+ return null;
59
+ end if;
60
+
61
+ if tg_op = 'UPDATE' and new.tenant_id is distinct from old.tenant_id then
62
+ perform public.assert_gl_pms_accounting_window_for_tenant(old.tenant_id);
63
+ end if;
64
+
65
+ perform public.assert_gl_pms_accounting_window_for_tenant(new.tenant_id);
66
+ return null;
67
+ end;
68
+ $$;
69
+ `.execute(db);
70
+
71
+ await sql`
72
+ create or replace function public.assert_gl_pms_accounting_window_tenant_trigger()
73
+ returns trigger
74
+ language plpgsql
75
+ as $$
76
+ begin
77
+ if new.is_general_ledger is true then
78
+ perform public.assert_gl_pms_accounting_window_for_tenant(new.id);
79
+ end if;
80
+
81
+ return null;
82
+ end;
83
+ $$;
84
+ `.execute(db);
85
+
86
+ await sql`
87
+ create constraint trigger connection_gl_pms_accounting_window_guard
88
+ after insert or update or delete on public.connection
89
+ deferrable initially deferred
90
+ for each row
91
+ execute function public.assert_gl_pms_accounting_window_connection_trigger();
92
+ `.execute(db);
93
+
94
+ await sql`
95
+ create constraint trigger tenant_gl_pms_accounting_window_guard
96
+ after insert or update on public.tenant
97
+ deferrable initially deferred
98
+ for each row
99
+ execute function public.assert_gl_pms_accounting_window_tenant_trigger();
100
+ `.execute(db);
101
+ }
102
+
103
+ export async function down(db: Kysely<any>): Promise<void> {
104
+ await sql`
105
+ drop trigger if exists tenant_gl_pms_accounting_window_guard
106
+ on public.tenant;
107
+ `.execute(db);
108
+
109
+ await sql`
110
+ drop trigger if exists connection_gl_pms_accounting_window_guard
111
+ on public.connection;
112
+ `.execute(db);
113
+
114
+ await sql`
115
+ drop function if exists public.assert_gl_pms_accounting_window_tenant_trigger();
116
+ `.execute(db);
117
+
118
+ await sql`
119
+ drop function if exists public.assert_gl_pms_accounting_window_connection_trigger();
120
+ `.execute(db);
121
+
122
+ await sql`
123
+ drop function if exists public.assert_gl_pms_accounting_window_for_tenant(uuid);
124
+ `.execute(db);
125
+ }
@@ -0,0 +1,45 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ // Tenant-to-tenant references (partner, billing partner, chart-of-accounts
5
+ // source) are cross-region: a partner homed in one cell manages tenants in
6
+ // another, so these self-referential FKs cannot hold within a single regional
7
+ // database. The control plane holds the authoritative tenant graph; the
8
+ // regional columns keep the ids as plain references. Drop the FKs.
9
+ export async function up(db: Kysely<any>): Promise<void> {
10
+ await sql`
11
+ alter table public.tenant
12
+ drop constraint if exists tenant_partner_id_fkey;
13
+ `.execute(db);
14
+ await sql`
15
+ alter table public.tenant
16
+ drop constraint if exists tenant_billing_partner_id_fkey;
17
+ `.execute(db);
18
+ await sql`
19
+ alter table public.tenant
20
+ drop constraint if exists tenant_chart_of_account_tenant_id_fkey;
21
+ `.execute(db);
22
+ }
23
+
24
+ export async function down(db: Kysely<any>): Promise<void> {
25
+ // NOT VALID: cross-region references may already exist; only future writes
26
+ // are constrained again.
27
+ await sql`
28
+ alter table public.tenant
29
+ add constraint tenant_partner_id_fkey
30
+ foreign key (partner_id) references public.tenant(id)
31
+ on update restrict on delete restrict not valid;
32
+ `.execute(db);
33
+ await sql`
34
+ alter table public.tenant
35
+ add constraint tenant_billing_partner_id_fkey
36
+ foreign key (billing_partner_id) references public.tenant(id)
37
+ on update restrict on delete restrict not valid;
38
+ `.execute(db);
39
+ await sql`
40
+ alter table public.tenant
41
+ add constraint tenant_chart_of_account_tenant_id_fkey
42
+ foreign key (chart_of_account_tenant_id) references public.tenant(id)
43
+ on update restrict on delete restrict not valid;
44
+ `.execute(db);
45
+ }
@@ -0,0 +1,100 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ const regionalApiEnvKeys = sql`array[
5
+ 'staging-ap',
6
+ 'staging-crunchy',
7
+ 'staging-eu',
8
+ 'staging-hostaway',
9
+ 'staging-us',
10
+ 'production-ap',
11
+ 'production-crunchy',
12
+ 'production-eu',
13
+ 'production-hostaway',
14
+ 'production-us'
15
+ ]::text[]`;
16
+
17
+ export async function up(db: Kysely<any>): Promise<void> {
18
+ await sql`
19
+ do $$
20
+ begin
21
+ if exists (
22
+ select 1
23
+ from public.app
24
+ cross join lateral jsonb_each(
25
+ coalesce(authentication -> 'defaultConnectParamsByApiEnv', '{}'::jsonb)
26
+ ) as env(key, value)
27
+ where env.key = any(${regionalApiEnvKeys})
28
+ and not (
29
+ coalesce(authentication -> 'defaultConnectParamsByApiEnv', '{}'::jsonb)
30
+ ? split_part(env.key, '-', 1)
31
+ )
32
+ group by public.app.id, split_part(env.key, '-', 1)
33
+ having count(distinct env.value::text) > 1
34
+ ) then
35
+ raise exception
36
+ 'Regional defaultConnectParamsByApiEnv values disagree; add staging/production explicitly before removing regional keys';
37
+ end if;
38
+ end $$;
39
+ `.execute(db);
40
+
41
+ await sql`
42
+ with base_values as (
43
+ select
44
+ public.app.id,
45
+ split_part(env.key, '-', 1) as base_env,
46
+ min(env.value::text)::jsonb as params
47
+ from public.app
48
+ cross join lateral jsonb_each(
49
+ coalesce(authentication -> 'defaultConnectParamsByApiEnv', '{}'::jsonb)
50
+ ) as env(key, value)
51
+ where env.key = any(${regionalApiEnvKeys})
52
+ and not (
53
+ coalesce(authentication -> 'defaultConnectParamsByApiEnv', '{}'::jsonb)
54
+ ? split_part(env.key, '-', 1)
55
+ )
56
+ group by public.app.id, split_part(env.key, '-', 1)
57
+ ),
58
+ per_app as (
59
+ select id, jsonb_object_agg(base_env, params) as params_by_env
60
+ from base_values
61
+ group by id
62
+ )
63
+ update public.app
64
+ set authentication = jsonb_set(
65
+ coalesce(authentication, '{}'::jsonb),
66
+ '{defaultConnectParamsByApiEnv}',
67
+ coalesce(authentication -> 'defaultConnectParamsByApiEnv', '{}'::jsonb)
68
+ || per_app.params_by_env,
69
+ true
70
+ )
71
+ from per_app
72
+ where public.app.id = per_app.id;
73
+ `.execute(db);
74
+
75
+ await sql`
76
+ update public.app
77
+ set authentication = jsonb_set(
78
+ coalesce(authentication, '{}'::jsonb),
79
+ '{defaultConnectParamsByApiEnv}',
80
+ coalesce(authentication -> 'defaultConnectParamsByApiEnv', '{}'::jsonb)
81
+ - 'staging-ap'
82
+ - 'staging-crunchy'
83
+ - 'staging-eu'
84
+ - 'staging-hostaway'
85
+ - 'staging-us'
86
+ - 'production-ap'
87
+ - 'production-crunchy'
88
+ - 'production-eu'
89
+ - 'production-hostaway'
90
+ - 'production-us',
91
+ true
92
+ )
93
+ where coalesce(authentication -> 'defaultConnectParamsByApiEnv', '{}'::jsonb)
94
+ ?| ${regionalApiEnvKeys};
95
+ `.execute(db);
96
+ }
97
+
98
+ export async function down(db: Kysely<any>): Promise<void> {
99
+ await sql`select 1`.execute(db);
100
+ }
@@ -0,0 +1,46 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ export async function up(db: Kysely<any>): Promise<void> {
5
+ await sql`
6
+ create table if not exists public.control_plane_outbox (
7
+ id uuid primary key default gen_random_uuid(),
8
+ aggregate_type text not null,
9
+ aggregate_id text not null,
10
+ event_type text not null,
11
+ event_version integer not null,
12
+ data_region text,
13
+ payload jsonb not null default jsonb_build_object(),
14
+ created_at timestamptz not null default now(),
15
+ published_at timestamptz,
16
+ attempts integer not null default 0,
17
+ last_error text,
18
+ constraint control_plane_outbox_region_check
19
+ check (
20
+ data_region is null
21
+ or data_region in ('ap', 'crunchy', 'eu', 'hostaway', 'us')
22
+ ),
23
+ constraint control_plane_outbox_event_version_check
24
+ check (event_version > 0),
25
+ constraint control_plane_outbox_aggregate_version_key
26
+ unique (aggregate_type, aggregate_id, event_version)
27
+ );
28
+ `.execute(db);
29
+
30
+ await sql`
31
+ create index if not exists control_plane_outbox_pending_idx
32
+ on public.control_plane_outbox (created_at, id)
33
+ where published_at is null;
34
+ `.execute(db);
35
+
36
+ await sql`
37
+ create index if not exists control_plane_outbox_aggregate_idx
38
+ on public.control_plane_outbox (aggregate_type, aggregate_id, created_at);
39
+ `.execute(db);
40
+ }
41
+
42
+ export async function down(db: Kysely<any>): Promise<void> {
43
+ await sql`
44
+ drop table if exists public.control_plane_outbox;
45
+ `.execute(db);
46
+ }
@@ -0,0 +1,16 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ export async function up(db: Kysely<any>): Promise<void> {
5
+ await sql`
6
+ alter table accounting.contact
7
+ add column if not exists ramp_last4_digits text;
8
+ `.execute(db);
9
+ }
10
+
11
+ export async function down(db: Kysely<any>): Promise<void> {
12
+ await sql`
13
+ alter table accounting.contact
14
+ drop column if exists ramp_last4_digits;
15
+ `.execute(db);
16
+ }
@@ -0,0 +1,40 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ // PRO-16537 part 1/2: tenant.status represents activity only
5
+ // (active | inactive). Onboarding state lives on is_onboarding; 'deleted'
6
+ // rows are legacy data (the delete flow hard-deletes the row) and read as
7
+ // inactive. The DDL (default + constraint) lives in the next migration —
8
+ // tenant has AFTER triggers, so DML and ALTER TABLE cannot share one
9
+ // transaction.
10
+ export async function up(db: Kysely<any>): Promise<void> {
11
+ // Hasura notify triggers on tenant leave pending trigger events that block
12
+ // the follow-up ALTER TABLE in the same migration transaction; a data-only
13
+ // backfill should not emit events anyway.
14
+ await sql`
15
+ alter table public.tenant disable trigger user;
16
+ `.execute(db);
17
+ await sql`
18
+ update public.tenant
19
+ set status = 'active', is_onboarding = true
20
+ where status = 'onboarding';
21
+ `.execute(db);
22
+ await sql`
23
+ update public.tenant
24
+ set status = 'inactive'
25
+ where status = 'deleted';
26
+ `.execute(db);
27
+ await sql`
28
+ update public.tenant
29
+ set status = 'active'
30
+ where status is null;
31
+ `.execute(db);
32
+ await sql`
33
+ alter table public.tenant enable trigger user;
34
+ `.execute(db);
35
+ }
36
+
37
+ export async function down(): Promise<void> {
38
+ // Backfill is not reversible; the legacy statuses carry no extra
39
+ // information worth restoring.
40
+ }
@@ -0,0 +1,24 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ // PRO-16537 part 2/2: default to active and reject anything outside
5
+ // active | inactive (see 0000000000047-tenant-status-backfill).
6
+ export async function up(db: Kysely<any>): Promise<void> {
7
+ await sql`
8
+ alter table public.tenant alter column status set default 'active';
9
+ `.execute(db);
10
+ await sql`
11
+ alter table public.tenant
12
+ add constraint tenant_status_check
13
+ check (status is not null and status in ('active', 'inactive'));
14
+ `.execute(db);
15
+ }
16
+
17
+ export async function down(db: Kysely<any>): Promise<void> {
18
+ await sql`
19
+ alter table public.tenant drop constraint if exists tenant_status_check;
20
+ `.execute(db);
21
+ await sql`
22
+ alter table public.tenant alter column status set default 'onboarding';
23
+ `.execute(db);
24
+ }
@@ -0,0 +1,69 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ export async function up(db: Kysely<any>): Promise<void> {
5
+ await sql`create schema if not exists health;`.execute(db);
6
+
7
+ await sql`
8
+ create table health.tenant_issue_snapshot (
9
+ tenant_id uuid primary key references public.tenant(id) on delete cascade,
10
+ version bigint not null default 0,
11
+ content_hash text,
12
+ dirty_at timestamptz not null default now(),
13
+ computed_at timestamptz,
14
+ created_at timestamptz not null default now(),
15
+ updated_at timestamptz not null default now(),
16
+ constraint tenant_issue_snapshot_version_check check (version >= 0)
17
+ );
18
+ `.execute(db);
19
+
20
+ await sql`
21
+ create index tenant_issue_snapshot_dirty_idx
22
+ on health.tenant_issue_snapshot (dirty_at, computed_at);
23
+ `.execute(db);
24
+
25
+ await sql`
26
+ create table health.tenant_issue (
27
+ id uuid primary key default gen_random_uuid(),
28
+ tenant_id uuid not null references public.tenant(id) on delete cascade,
29
+ code text not null,
30
+ status text not null,
31
+ version bigint not null,
32
+ snapshot_version bigint not null,
33
+ severity text not null,
34
+ category text not null,
35
+ message text not null,
36
+ affected integer not null,
37
+ context jsonb not null,
38
+ content_hash text not null,
39
+ first_seen_at timestamptz not null,
40
+ opened_at timestamptz not null,
41
+ last_changed_at timestamptz not null,
42
+ resolved_at timestamptz,
43
+ created_at timestamptz not null default now(),
44
+ updated_at timestamptz not null default now(),
45
+ constraint tenant_issue_tenant_code_key unique (tenant_id, code),
46
+ constraint tenant_issue_status_check check (status in ('open', 'resolved')),
47
+ constraint tenant_issue_version_check check (version > 0),
48
+ constraint tenant_issue_snapshot_version_check check (snapshot_version > 0),
49
+ constraint tenant_issue_severity_check check (severity in ('error', 'warning')),
50
+ constraint tenant_issue_category_check
51
+ check (category in ('criticalToUser', 'criticalToSystem')),
52
+ constraint tenant_issue_affected_check
53
+ check (
54
+ (status = 'open' and affected > 0)
55
+ or (status = 'resolved' and affected = 0)
56
+ )
57
+ );
58
+ `.execute(db);
59
+
60
+ await sql`
61
+ create index tenant_issue_open_idx
62
+ on health.tenant_issue (tenant_id, code)
63
+ where status = 'open';
64
+ `.execute(db);
65
+ }
66
+
67
+ export async function down(db: Kysely<any>): Promise<void> {
68
+ await sql`drop schema if exists health cascade;`.execute(db);
69
+ }
@@ -0,0 +1,135 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ export async function up(db: Kysely<any>): Promise<void> {
5
+ await sql`create schema if not exists webhook;`.execute(db);
6
+
7
+ await sql`
8
+ create table webhook.subscription (
9
+ id uuid primary key default gen_random_uuid(),
10
+ owner_tenant_id uuid not null,
11
+ scope text not null,
12
+ url text not null,
13
+ status text not null default 'pending',
14
+ api_version text not null,
15
+ event_types text[] not null,
16
+ description text,
17
+ verified_at timestamptz,
18
+ disabled_at timestamptz,
19
+ created_at timestamptz not null default now(),
20
+ updated_at timestamptz not null default now(),
21
+ constraint webhook_subscription_scope_check
22
+ check (scope in ('self', 'managedTeams')),
23
+ constraint webhook_subscription_status_check
24
+ check (status in ('pending', 'active', 'disabled')),
25
+ constraint webhook_subscription_event_types_check
26
+ check (cardinality(event_types) > 0)
27
+ );
28
+ `.execute(db);
29
+
30
+ await sql`
31
+ create index webhook_subscription_owner_idx
32
+ on webhook.subscription (owner_tenant_id, created_at desc);
33
+ `.execute(db);
34
+
35
+ await sql`
36
+ create table webhook.subscription_secret (
37
+ id uuid primary key default gen_random_uuid(),
38
+ subscription_id uuid not null
39
+ references webhook.subscription(id) on delete cascade,
40
+ encrypted_secret text not null,
41
+ encryption_key_id text not null,
42
+ active_from timestamptz not null default now(),
43
+ expires_at timestamptz,
44
+ revoked_at timestamptz,
45
+ created_at timestamptz not null default now()
46
+ );
47
+ `.execute(db);
48
+
49
+ await sql`
50
+ create index webhook_subscription_secret_active_idx
51
+ on webhook.subscription_secret (subscription_id, active_from desc);
52
+ `.execute(db);
53
+
54
+ await sql`
55
+ create table webhook.delivery (
56
+ id uuid primary key default gen_random_uuid(),
57
+ source_event_key text not null,
58
+ subscription_id uuid not null
59
+ references webhook.subscription(id) on delete cascade,
60
+ source_tenant_id uuid,
61
+ event_type text not null,
62
+ api_version text not null,
63
+ resource_type text,
64
+ resource_id text,
65
+ body text not null,
66
+ status text not null default 'queued',
67
+ attempt_count integer not null default 0,
68
+ next_attempt_at timestamptz not null default now(),
69
+ claimed_at timestamptz,
70
+ claimed_by text,
71
+ last_error_code text,
72
+ delivered_at timestamptz,
73
+ failed_at timestamptz,
74
+ created_at timestamptz not null default now(),
75
+ updated_at timestamptz not null default now(),
76
+ constraint webhook_delivery_subscription_event_key
77
+ unique (subscription_id, source_event_key),
78
+ constraint webhook_delivery_status_check
79
+ check (
80
+ status in (
81
+ 'queued',
82
+ 'delivering',
83
+ 'retrying',
84
+ 'succeeded',
85
+ 'failed',
86
+ 'canceled'
87
+ )
88
+ ),
89
+ constraint webhook_delivery_attempt_count_check check (attempt_count >= 0)
90
+ );
91
+ `.execute(db);
92
+
93
+ await sql`
94
+ create index webhook_delivery_recovery_idx
95
+ on webhook.delivery (status, next_attempt_at);
96
+ `.execute(db);
97
+ await sql`
98
+ create index webhook_delivery_subscription_created_idx
99
+ on webhook.delivery (subscription_id, created_at desc);
100
+ `.execute(db);
101
+ await sql`
102
+ create index webhook_delivery_source_tenant_created_idx
103
+ on webhook.delivery (source_tenant_id, created_at desc);
104
+ `.execute(db);
105
+
106
+ await sql`
107
+ create table webhook.delivery_attempt (
108
+ id uuid primary key default gen_random_uuid(),
109
+ delivery_id uuid not null references webhook.delivery(id) on delete cascade,
110
+ subscription_secret_id uuid not null
111
+ references webhook.subscription_secret(id) on delete restrict,
112
+ attempt_number integer not null,
113
+ started_at timestamptz not null,
114
+ finished_at timestamptz not null,
115
+ duration_ms integer not null,
116
+ response_status integer,
117
+ error_class text,
118
+ response_excerpt text,
119
+ created_at timestamptz not null default now(),
120
+ constraint webhook_delivery_attempt_number_key
121
+ unique (delivery_id, attempt_number),
122
+ constraint webhook_delivery_attempt_number_check check (attempt_number > 0),
123
+ constraint webhook_delivery_attempt_duration_check check (duration_ms >= 0)
124
+ );
125
+ `.execute(db);
126
+
127
+ await sql`
128
+ create index webhook_delivery_attempt_delivery_created_idx
129
+ on webhook.delivery_attempt (delivery_id, created_at);
130
+ `.execute(db);
131
+ }
132
+
133
+ export async function down(db: Kysely<any>): Promise<void> {
134
+ await sql`drop schema if exists webhook cascade;`.execute(db);
135
+ }
@@ -0,0 +1,42 @@
1
+ import type { Kysely } from 'kysely';
2
+ import { sql } from 'kysely';
3
+
4
+ export async function up(db: Kysely<any>): Promise<void> {
5
+ await sql`
6
+ do $$
7
+ begin
8
+ if exists (select 1 from pg_roles where rolname = 'application') then
9
+ grant usage on schema health, webhook to application;
10
+ grant select, insert, update, delete
11
+ on all tables in schema health, webhook to application;
12
+ grant usage, select, update
13
+ on all sequences in schema health, webhook to application;
14
+ alter default privileges in schema health, webhook
15
+ grant select, insert, update, delete on tables to application;
16
+ alter default privileges in schema health, webhook
17
+ grant usage, select, update on sequences to application;
18
+ end if;
19
+ end
20
+ $$;
21
+ `.execute(db);
22
+ }
23
+
24
+ export async function down(db: Kysely<any>): Promise<void> {
25
+ await sql`
26
+ do $$
27
+ begin
28
+ if exists (select 1 from pg_roles where rolname = 'application') then
29
+ alter default privileges in schema health, webhook
30
+ revoke select, insert, update, delete on tables from application;
31
+ alter default privileges in schema health, webhook
32
+ revoke usage, select, update on sequences from application;
33
+ revoke select, insert, update, delete
34
+ on all tables in schema health, webhook from application;
35
+ revoke usage, select, update
36
+ on all sequences in schema health, webhook from application;
37
+ revoke usage on schema health, webhook from application;
38
+ end if;
39
+ end
40
+ $$;
41
+ `.execute(db);
42
+ }
@@ -0,0 +1,13 @@
1
+ import type { Kysely } from 'kysely';
2
+
3
+ // CUS-2503: retained as a no-op because this migration name reached staging.
4
+ // Feature evaluation belongs in /me and must not manufacture team enablements
5
+ // from user approvals. Keeping the file lets environments with an existing
6
+ // migration-ledger entry continue to migrate safely.
7
+ export async function up(db: Kysely<unknown>): Promise<void> {
8
+ void db;
9
+ }
10
+
11
+ export async function down(db: Kysely<unknown>): Promise<void> {
12
+ void db;
13
+ }