@vrplatform/kysely 1.3.40 → 1.3.41-stage.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.
Files changed (105) 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 +141 -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 +6 -3
  8. package/build/main/index.js +8 -9
  9. package/build/main/index.js.map +1 -1
  10. package/build/main/isomorphic.d.ts +2 -1
  11. package/build/main/isomorphic.js +2 -2
  12. package/build/main/isomorphic.js.map +1 -1
  13. package/build/main/local/digest.js.map +1 -1
  14. package/build/main/local/generate.js +12 -0
  15. package/build/main/local/generate.js.map +1 -1
  16. package/build/main/local/index.d.ts +3 -2
  17. package/build/main/local/index.js +56 -3
  18. package/build/main/local/index.js.map +1 -1
  19. package/build/main/local/prune-pulled-schema.js.map +1 -1
  20. package/build/main/pganalyze-comment-plugin/comment-transformer.js +0 -1
  21. package/build/main/pganalyze-comment-plugin/comment-transformer.js.map +1 -1
  22. package/build/main/pganalyze-comment-plugin/index.js +16 -3
  23. package/build/main/pganalyze-comment-plugin/index.js.map +1 -1
  24. package/build/main/plugins.js.map +1 -1
  25. package/build/main/query/listing/status.d.ts +4 -4
  26. package/build/main/query/listing/status.js +19 -2
  27. package/build/main/query/listing/status.js.map +1 -1
  28. package/build/main/regional.d.ts +34 -0
  29. package/build/main/regional.js +139 -0
  30. package/build/main/regional.js.map +1 -0
  31. package/build/main/test/index.js.map +1 -1
  32. package/build/main/transaction.d.ts +7 -0
  33. package/build/main/transaction.js +32 -0
  34. package/build/main/transaction.js.map +1 -0
  35. package/build/main/v1.generated.d.ts +133 -2
  36. package/build/main/v1.generated.js.map +1 -1
  37. package/build/migrations/0000000000001-init.ts +5 -0
  38. package/build/migrations/0000000000009-drop-store-schema.ts +14 -0
  39. package/build/migrations/0000000000010-bank-rules.ts +190 -0
  40. package/build/migrations/0000000000011-bank-rule-line-adjustments.ts +65 -0
  41. package/build/migrations/0000000000012-bank-rule-conditions.ts +45 -0
  42. package/build/migrations/0000000000013-bank-rule-name-unique.ts +15 -0
  43. package/build/migrations/0000000000014-bank-rule-drop-legacy-conditions.ts +41 -0
  44. package/build/migrations/0000000000015-booking-channel-logo-dev-provider.ts +70 -0
  45. package/build/migrations/0000000000016-owner-statement-draft-status.ts +22 -0
  46. package/build/migrations/0000000000017-tenant-migration-markers.ts +18 -0
  47. package/build/migrations/0000000000018-security-deposits-payable-category.ts +63 -0
  48. package/build/migrations/0000000000019-tenant-app-settings.ts +76 -0
  49. package/build/migrations/0000000000020-operating-opening-trial-balance.ts +36 -0
  50. package/build/migrations/0000000000021-audit-foreign-key-indexes.ts +77 -0
  51. package/build/migrations/0000000000022-opening-trial-balance-combined-columns.ts +30 -0
  52. package/build/migrations/0000000000023-journal-entry-currency-mismatch-reason.ts +14 -0
  53. package/build/migrations/0000000000024-listing-parent.ts +37 -0
  54. package/build/migrations/0000000000025-journal-entry-parent-listing.ts +26 -0
  55. package/build/migrations/0000000000026-booking-channel-logo-slots.ts +89 -0
  56. package/build/migrations/0000000000027-list-browse-performance-indexes.ts +52 -0
  57. package/build/migrations/0000000000028-flow-setting-status-jsonb.ts +20 -0
  58. package/build/migrations/0000000000029-tenant-slug.ts +328 -0
  59. package/build/migrations/0000000000030-pms-accounting-windows.ts +50 -0
  60. package/build/migrations/0000000000031-owner-statement-projection-listing-revision.ts +20 -0
  61. package/build/migrations/0000000000032-flow-event-listening.ts +28 -0
  62. package/build/migrations/0000000000033-opening-trial-balance-transaction.ts +272 -0
  63. package/build/migrations/0000000000034-historic-general-ledger.ts +24 -0
  64. package/build/migrations/0000000000035-tenant-regional-placement.ts +37 -0
  65. package/build/migrations/0000000000036-control-plane-projection-events.ts +43 -0
  66. package/build/migrations/0000000000037-tenant-guide-onboarding.ts +21 -0
  67. package/build/migrations/0000000000038-ramp-staging-connect-params.ts +35 -0
  68. package/build/migrations/0000000000038-tracking-event-analytics-sink.ts +31 -0
  69. package/build/migrations/0000000000039-crunchy-region.ts +86 -0
  70. package/build/migrations/0000000000039-gl-pms-accounting-window-guard.ts +125 -0
  71. package/build/module/camel-case/index.js.map +1 -1
  72. package/build/module/control-plane/index.d.ts +141 -0
  73. package/build/module/control-plane/index.js +2 -0
  74. package/build/module/control-plane/index.js.map +1 -0
  75. package/build/module/error.js.map +1 -1
  76. package/build/module/index.d.ts +6 -3
  77. package/build/module/index.js +8 -9
  78. package/build/module/index.js.map +1 -1
  79. package/build/module/isomorphic.d.ts +2 -1
  80. package/build/module/isomorphic.js.map +1 -1
  81. package/build/module/local/digest.js.map +1 -1
  82. package/build/module/local/generate.js +12 -0
  83. package/build/module/local/generate.js.map +1 -1
  84. package/build/module/local/index.d.ts +3 -2
  85. package/build/module/local/index.js +23 -3
  86. package/build/module/local/index.js.map +1 -1
  87. package/build/module/local/prune-pulled-schema.js.map +1 -1
  88. package/build/module/pganalyze-comment-plugin/comment-transformer.js.map +1 -1
  89. package/build/module/pganalyze-comment-plugin/index.js.map +1 -1
  90. package/build/module/plugins.js.map +1 -1
  91. package/build/module/query/listing/status.d.ts +4 -4
  92. package/build/module/query/listing/status.js +19 -2
  93. package/build/module/query/listing/status.js.map +1 -1
  94. package/build/module/regional.d.ts +34 -0
  95. package/build/module/regional.js +97 -0
  96. package/build/module/regional.js.map +1 -0
  97. package/build/module/test/index.js.map +1 -1
  98. package/build/module/transaction.d.ts +7 -0
  99. package/build/module/transaction.js +25 -0
  100. package/build/module/transaction.js.map +1 -0
  101. package/build/module/v1.generated.d.ts +133 -2
  102. package/build/module/v1.generated.js.map +1 -1
  103. package/package.json +13 -4
  104. package/build/main/tsconfig.main.tsbuildinfo +0 -1
  105. package/build/module/tsconfig.esm.tsbuildinfo +0 -1
@@ -0,0 +1,77 @@
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 index if not exists audit_action_parent_action_idx
7
+ on audit.action (parent_action_id)
8
+ where parent_action_id is not null;
9
+ `.execute(db);
10
+
11
+ await sql`
12
+ create index if not exists audit_journal_delta_action_idx
13
+ on audit.journal_delta (action_id);
14
+ `.execute(db);
15
+
16
+ await sql`
17
+ create index if not exists audit_journal_delta_effect_idx
18
+ on audit.journal_delta (effect_id)
19
+ where effect_id is not null;
20
+ `.execute(db);
21
+
22
+ await sql`
23
+ create index if not exists audit_journal_delta_mutation_idx
24
+ on audit.journal_delta (mutation_id)
25
+ where mutation_id is not null;
26
+ `.execute(db);
27
+
28
+ await sql`
29
+ create index if not exists audit_mutation_action_idx
30
+ on audit.mutation (action_id);
31
+ `.execute(db);
32
+
33
+ await sql`
34
+ create index if not exists audit_mutation_effect_idx
35
+ on audit.mutation (effect_id)
36
+ where effect_id is not null;
37
+ `.execute(db);
38
+
39
+ await sql`
40
+ create index if not exists audit_effect_action_idx
41
+ on audit.effect (action_id);
42
+ `.execute(db);
43
+
44
+ await sql`
45
+ create index if not exists audit_effect_attempt_action_idx
46
+ on audit.effect_attempt (action_id);
47
+ `.execute(db);
48
+
49
+ await sql`
50
+ create index if not exists audit_effect_attempt_effect_idx
51
+ on audit.effect_attempt (effect_id);
52
+ `.execute(db);
53
+ }
54
+
55
+ export async function down(db: Kysely<any>): Promise<void> {
56
+ await sql`drop index if exists audit.audit_action_parent_action_idx;`.execute(
57
+ db
58
+ );
59
+ await sql`drop index if exists audit.audit_effect_attempt_effect_idx;`.execute(
60
+ db
61
+ );
62
+ await sql`drop index if exists audit.audit_effect_attempt_action_idx;`.execute(
63
+ db
64
+ );
65
+ await sql`drop index if exists audit.audit_effect_action_idx;`.execute(db);
66
+ await sql`drop index if exists audit.audit_mutation_effect_idx;`.execute(db);
67
+ await sql`drop index if exists audit.audit_mutation_action_idx;`.execute(db);
68
+ await sql`drop index if exists audit.audit_journal_delta_mutation_idx;`.execute(
69
+ db
70
+ );
71
+ await sql`drop index if exists audit.audit_journal_delta_effect_idx;`.execute(
72
+ db
73
+ );
74
+ await sql`drop index if exists audit.audit_journal_delta_action_idx;`.execute(
75
+ db
76
+ );
77
+ }
@@ -0,0 +1,30 @@
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.account
7
+ rename column operating_opening_trial_balance_cent_total
8
+ to opening_trial_balance_combined_cent_total;
9
+ `.execute(db);
10
+
11
+ await sql`
12
+ alter table accounting.account
13
+ rename column operating_opening_trial_balance_updated_at
14
+ to opening_trial_balance_updated_at;
15
+ `.execute(db);
16
+ }
17
+
18
+ export async function down(db: Kysely<any>): Promise<void> {
19
+ await sql`
20
+ alter table accounting.account
21
+ rename column opening_trial_balance_combined_cent_total
22
+ to operating_opening_trial_balance_cent_total;
23
+ `.execute(db);
24
+
25
+ await sql`
26
+ alter table accounting.account
27
+ rename column opening_trial_balance_updated_at
28
+ to operating_opening_trial_balance_updated_at;
29
+ `.execute(db);
30
+ }
@@ -0,0 +1,14 @@
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 type accounting.journal_entry_inactive_reason
7
+ add value if not exists 'currencyMismatch'
8
+ before 'unknownInactiveReason';
9
+ `.execute(db);
10
+ }
11
+
12
+ export async function down(_db: Kysely<any>): Promise<void> {
13
+ // PostgreSQL enum values cannot be removed safely without rebuilding the type.
14
+ }
@@ -0,0 +1,37 @@
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 public.listing
7
+ add column parent_id uuid null references public.listing(id)
8
+ on update restrict on delete restrict;
9
+ `.execute(db);
10
+
11
+ await sql`
12
+ create index listing_tenant_id_parent_id_idx
13
+ on public.listing(tenant_id, parent_id);
14
+ `.execute(db);
15
+
16
+ await sql`
17
+ alter table public.listing
18
+ add constraint listing_parent_not_self_check
19
+ check (parent_id is null or parent_id <> id);
20
+ `.execute(db);
21
+ }
22
+
23
+ export async function down(db: Kysely<any>): Promise<void> {
24
+ await sql`
25
+ alter table public.listing
26
+ drop constraint if exists listing_parent_not_self_check;
27
+ `.execute(db);
28
+
29
+ await sql`
30
+ drop index if exists listing_tenant_id_parent_id_idx;
31
+ `.execute(db);
32
+
33
+ await sql`
34
+ alter table public.listing
35
+ drop column if exists parent_id;
36
+ `.execute(db);
37
+ }
@@ -0,0 +1,26 @@
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.journal_entry
7
+ add column parent_listing_id uuid null references public.listing(id)
8
+ on update restrict on delete restrict;
9
+ `.execute(db);
10
+
11
+ await sql`
12
+ create index journal_entry_tenant_id_parent_listing_id_idx
13
+ on accounting.journal_entry(tenant_id, parent_listing_id);
14
+ `.execute(db);
15
+ }
16
+
17
+ export async function down(db: Kysely<any>): Promise<void> {
18
+ await sql`
19
+ drop index if exists accounting.journal_entry_tenant_id_parent_listing_id_idx;
20
+ `.execute(db);
21
+
22
+ await sql`
23
+ alter table accounting.journal_entry
24
+ drop column if exists parent_listing_id;
25
+ `.execute(db);
26
+ }
@@ -0,0 +1,89 @@
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 public.booking_channel
7
+ add column if not exists logo text null,
8
+ add column if not exists logo_provider text null,
9
+ add column if not exists selected_booking_channel_logo_candidate_id uuid null;
10
+ `.execute(db);
11
+
12
+ await sql`
13
+ alter table public.booking_channel
14
+ drop constraint if exists booking_channel_logo_provider_check;
15
+ `.execute(db);
16
+
17
+ await sql`
18
+ alter table public.booking_channel
19
+ add constraint booking_channel_logo_provider_check
20
+ check (
21
+ logo_provider is null
22
+ or logo_provider in ('cloudflare', 'logo_dev')
23
+ );
24
+ `.execute(db);
25
+
26
+ await sql`
27
+ alter table public.booking_channel_icon_candidate
28
+ add column if not exists width integer null,
29
+ add column if not exists height integer null;
30
+ `.execute(db);
31
+
32
+ await sql`
33
+ alter table public.booking_channel_icon_candidate
34
+ drop constraint if exists booking_channel_icon_candidate_dimensions_check;
35
+ `.execute(db);
36
+
37
+ await sql`
38
+ alter table public.booking_channel_icon_candidate
39
+ add constraint booking_channel_icon_candidate_dimensions_check
40
+ check (
41
+ (width is null or width > 0)
42
+ and (height is null or height > 0)
43
+ );
44
+ `.execute(db);
45
+
46
+ await sql`
47
+ alter table public.booking_channel
48
+ drop constraint if exists booking_channel_selected_logo_candidate_same_booking_channel_fk;
49
+ `.execute(db);
50
+
51
+ await sql`
52
+ alter table public.booking_channel
53
+ add constraint booking_channel_selected_logo_candidate_same_booking_channel_fk
54
+ foreign key (id, selected_booking_channel_logo_candidate_id)
55
+ references public.booking_channel_icon_candidate(booking_channel_id, id)
56
+ on update restrict
57
+ on delete restrict;
58
+ `.execute(db);
59
+ }
60
+
61
+ export async function down(db: Kysely<any>): Promise<void> {
62
+ await sql`
63
+ alter table public.booking_channel
64
+ drop constraint if exists booking_channel_selected_logo_candidate_same_booking_channel_fk;
65
+ `.execute(db);
66
+
67
+ await sql`
68
+ alter table public.booking_channel
69
+ drop constraint if exists booking_channel_logo_provider_check;
70
+ `.execute(db);
71
+
72
+ await sql`
73
+ alter table public.booking_channel_icon_candidate
74
+ drop constraint if exists booking_channel_icon_candidate_dimensions_check;
75
+ `.execute(db);
76
+
77
+ await sql`
78
+ alter table public.booking_channel
79
+ drop column if exists selected_booking_channel_logo_candidate_id,
80
+ drop column if exists logo_provider,
81
+ drop column if exists logo;
82
+ `.execute(db);
83
+
84
+ await sql`
85
+ alter table public.booking_channel_icon_candidate
86
+ drop column if exists height,
87
+ drop column if exists width;
88
+ `.execute(db);
89
+ }
@@ -0,0 +1,52 @@
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 index if not exists reservation_tenant_gl_status_effective_check_in_idx
7
+ on public.reservation (
8
+ tenant_id,
9
+ general_ledger_status,
10
+ effective_check_in desc
11
+ )
12
+ include (id, status, connection_id, source_id, listing_id)
13
+ where status not in ('inactive', 'inquiry');
14
+ `.execute(db);
15
+
16
+ await sql`
17
+ create index if not exists listing_ownership_period_inactive_lookup_idx
18
+ on public.listing_ownership_period (
19
+ tenant_id,
20
+ listing_id,
21
+ start_at,
22
+ end_at
23
+ )
24
+ where set_listing_inactive = true;
25
+ `.execute(db);
26
+
27
+ await sql`
28
+ create index if not exists transaction_tenant_type_status_date_idx
29
+ on accounting.transaction (
30
+ tenant_id,
31
+ type,
32
+ status,
33
+ is_opening_balance,
34
+ date desc
35
+ )
36
+ include (id, account_id, connection_id, source_id, contact_id);
37
+ `.execute(db);
38
+ }
39
+
40
+ export async function down(db: Kysely<any>): Promise<void> {
41
+ await sql`
42
+ drop index if exists accounting.transaction_tenant_type_status_date_idx;
43
+ `.execute(db);
44
+
45
+ await sql`
46
+ drop index if exists public.listing_ownership_period_inactive_lookup_idx;
47
+ `.execute(db);
48
+
49
+ await sql`
50
+ drop index if exists public.reservation_tenant_gl_status_effective_check_in_idx;
51
+ `.execute(db);
52
+ }
@@ -0,0 +1,20 @@
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
+ update core.flow_setting
7
+ set value = case value #>> '{}'
8
+ when '"active"' then to_jsonb('active'::text)
9
+ when '"inactive"' then to_jsonb('inactive'::text)
10
+ else value
11
+ end
12
+ where setting_key = 'status'
13
+ and jsonb_typeof(value) = 'string'
14
+ and value #>> '{}' in ('"active"', '"inactive"');
15
+ `.execute(db);
16
+ }
17
+
18
+ export async function down(_db: Kysely<any>): Promise<void> {
19
+ // Data repair is intentionally irreversible.
20
+ }
@@ -0,0 +1,328 @@
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 public.tenant
7
+ add column slug text;
8
+ `.execute(db);
9
+
10
+ await sql`
11
+ create or replace function public.normalize_tenant_slug(value text)
12
+ returns text
13
+ language sql
14
+ immutable
15
+ as $$
16
+ select trim(
17
+ both '-'
18
+ from regexp_replace(
19
+ regexp_replace(lower(coalesce(value, '')), '[^a-z0-9]+', '-', 'g'),
20
+ '-+',
21
+ '-',
22
+ 'g'
23
+ )
24
+ );
25
+ $$;
26
+ `.execute(db);
27
+
28
+ await sql`
29
+ create or replace function public.build_unique_tenant_slug(
30
+ target_name text,
31
+ target_id uuid
32
+ )
33
+ returns text
34
+ language plpgsql
35
+ as $$
36
+ declare
37
+ reserved text[] := array[
38
+ 'admin',
39
+ 'api',
40
+ 'app',
41
+ 'assets',
42
+ 'auth',
43
+ 'billing',
44
+ 'cdn',
45
+ 'cloudflare',
46
+ 'dev',
47
+ 'docs',
48
+ 'email',
49
+ 'ftp',
50
+ 'help',
51
+ 'imap',
52
+ 'internal',
53
+ 'localhost',
54
+ 'login',
55
+ 'logout',
56
+ 'mail',
57
+ 'mx',
58
+ 'new',
59
+ 'ns1',
60
+ 'ns2',
61
+ 'payments',
62
+ 'portal',
63
+ 'pop',
64
+ 'signup',
65
+ 'smtp',
66
+ 'sftp',
67
+ 'ssh',
68
+ 'staging',
69
+ 'static',
70
+ 'status',
71
+ 'support',
72
+ 'team',
73
+ 'teams',
74
+ 'test',
75
+ 'vrplatform',
76
+ 'worker',
77
+ 'workers',
78
+ 'www'
79
+ ];
80
+ base text;
81
+ candidate text;
82
+ suffix integer := 1;
83
+ suffix_text text;
84
+ begin
85
+ base := public.normalize_tenant_slug(target_name);
86
+ if base = '' then
87
+ base := 'team';
88
+ end if;
89
+ if base = any(reserved) then
90
+ base := left(base, 58) || '-team';
91
+ end if;
92
+
93
+ base := left(base, 63);
94
+ base := trim(both '-' from base);
95
+ if base = '' then
96
+ base := 'team';
97
+ end if;
98
+
99
+ candidate := base;
100
+ while exists (
101
+ select 1
102
+ from public.tenant
103
+ where slug = candidate
104
+ and id <> target_id
105
+ ) loop
106
+ suffix := suffix + 1;
107
+ suffix_text := '-' || suffix::text;
108
+ candidate := left(base, 63 - length(suffix_text)) || suffix_text;
109
+ end loop;
110
+
111
+ return candidate;
112
+ end;
113
+ $$;
114
+ `.execute(db);
115
+
116
+ await sql`
117
+ create or replace function public.set_tenant_slug()
118
+ returns trigger
119
+ language plpgsql
120
+ as $$
121
+ declare
122
+ reserved text[] := array[
123
+ 'admin',
124
+ 'api',
125
+ 'app',
126
+ 'assets',
127
+ 'auth',
128
+ 'billing',
129
+ 'cdn',
130
+ 'cloudflare',
131
+ 'dev',
132
+ 'docs',
133
+ 'email',
134
+ 'ftp',
135
+ 'help',
136
+ 'imap',
137
+ 'internal',
138
+ 'localhost',
139
+ 'login',
140
+ 'logout',
141
+ 'mail',
142
+ 'mx',
143
+ 'new',
144
+ 'ns1',
145
+ 'ns2',
146
+ 'payments',
147
+ 'portal',
148
+ 'pop',
149
+ 'signup',
150
+ 'smtp',
151
+ 'sftp',
152
+ 'ssh',
153
+ 'staging',
154
+ 'static',
155
+ 'status',
156
+ 'support',
157
+ 'team',
158
+ 'teams',
159
+ 'test',
160
+ 'vrplatform',
161
+ 'worker',
162
+ 'workers',
163
+ 'www'
164
+ ];
165
+ begin
166
+ if new.slug is null or btrim(new.slug) = '' then
167
+ new.slug := public.build_unique_tenant_slug(new.name, new.id);
168
+ else
169
+ new.slug := public.normalize_tenant_slug(new.slug);
170
+
171
+ if new.slug = '' then
172
+ raise exception 'Tenant slug is required' using errcode = '23514';
173
+ end if;
174
+ if new.slug !~ '^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$' then
175
+ raise exception 'Tenant slug % is invalid', new.slug
176
+ using errcode = '23514';
177
+ end if;
178
+ if new.slug = any(reserved) then
179
+ raise exception 'Tenant slug % is reserved', new.slug
180
+ using errcode = '23514';
181
+ end if;
182
+ if exists (
183
+ select 1
184
+ from public.tenant
185
+ where slug = new.slug
186
+ and id <> new.id
187
+ ) then
188
+ raise exception 'Tenant slug % is already in use', new.slug
189
+ using errcode = '23505';
190
+ end if;
191
+ end if;
192
+
193
+ return new;
194
+ end;
195
+ $$;
196
+ `.execute(db);
197
+
198
+ await sql`
199
+ create trigger set_public_tenant_slug
200
+ before insert or update of slug, name on public.tenant
201
+ for each row
202
+ execute function public.set_tenant_slug();
203
+ `.execute(db);
204
+
205
+ await sql`
206
+ do $$
207
+ declare
208
+ tenant_row record;
209
+ candidate text;
210
+ begin
211
+ for tenant_row in
212
+ select id, name
213
+ from public.tenant
214
+ where slug is null or btrim(slug) = ''
215
+ order by created_at asc, id asc
216
+ loop
217
+ candidate := public.build_unique_tenant_slug(
218
+ tenant_row.name,
219
+ tenant_row.id
220
+ );
221
+
222
+ update public.tenant
223
+ set slug = candidate
224
+ where id = tenant_row.id;
225
+ end loop;
226
+ end;
227
+ $$;
228
+ `.execute(db);
229
+
230
+ await sql`
231
+ alter table public.tenant
232
+ alter column slug set not null;
233
+ `.execute(db);
234
+
235
+ await sql`
236
+ alter table public.tenant
237
+ add constraint tenant_slug_format_check
238
+ check (slug ~ '^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$');
239
+ `.execute(db);
240
+
241
+ await sql`
242
+ alter table public.tenant
243
+ add constraint tenant_slug_reserved_check
244
+ check (
245
+ slug <> all(array[
246
+ 'admin',
247
+ 'api',
248
+ 'app',
249
+ 'assets',
250
+ 'auth',
251
+ 'billing',
252
+ 'cdn',
253
+ 'cloudflare',
254
+ 'dev',
255
+ 'docs',
256
+ 'email',
257
+ 'ftp',
258
+ 'help',
259
+ 'imap',
260
+ 'internal',
261
+ 'localhost',
262
+ 'login',
263
+ 'logout',
264
+ 'mail',
265
+ 'mx',
266
+ 'new',
267
+ 'ns1',
268
+ 'ns2',
269
+ 'payments',
270
+ 'portal',
271
+ 'pop',
272
+ 'signup',
273
+ 'smtp',
274
+ 'sftp',
275
+ 'ssh',
276
+ 'staging',
277
+ 'static',
278
+ 'status',
279
+ 'support',
280
+ 'team',
281
+ 'teams',
282
+ 'test',
283
+ 'vrplatform',
284
+ 'worker',
285
+ 'workers',
286
+ 'www'
287
+ ])
288
+ );
289
+ `.execute(db);
290
+
291
+ await sql`
292
+ create unique index tenant_slug_unique_idx
293
+ on public.tenant (slug);
294
+ `.execute(db);
295
+ }
296
+
297
+ export async function down(db: Kysely<any>): Promise<void> {
298
+ await sql`
299
+ drop index if exists public.tenant_slug_unique_idx;
300
+ `.execute(db);
301
+
302
+ await sql`
303
+ alter table public.tenant
304
+ drop constraint if exists tenant_slug_reserved_check,
305
+ drop constraint if exists tenant_slug_format_check;
306
+ `.execute(db);
307
+
308
+ await sql`
309
+ drop trigger if exists set_public_tenant_slug on public.tenant;
310
+ `.execute(db);
311
+
312
+ await sql`
313
+ drop function if exists public.set_tenant_slug();
314
+ `.execute(db);
315
+
316
+ await sql`
317
+ drop function if exists public.build_unique_tenant_slug(text, uuid);
318
+ `.execute(db);
319
+
320
+ await sql`
321
+ drop function if exists public.normalize_tenant_slug(text);
322
+ `.execute(db);
323
+
324
+ await sql`
325
+ alter table public.tenant
326
+ drop column if exists slug;
327
+ `.execute(db);
328
+ }