@vrplatform/kysely 1.3.45-6561 → 1.3.45-736

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 (78) hide show
  1. package/build/.data.tar +2 -2
  2. package/build/main/control-plane/index.d.ts +137 -0
  3. package/build/main/control-plane/index.js.map +1 -1
  4. package/build/main/index.d.ts +11 -0
  5. package/build/main/index.js +131 -26
  6. package/build/main/index.js.map +1 -1
  7. package/build/main/local/generate.js +1 -0
  8. package/build/main/local/generate.js.map +1 -1
  9. package/build/main/local/index.js +109 -8
  10. package/build/main/local/index.js.map +1 -1
  11. package/build/main/postgres-error.d.ts +3 -3
  12. package/build/main/postgres-error.js +4 -3
  13. package/build/main/postgres-error.js.map +1 -1
  14. package/build/main/query/tenant/status.d.ts +1 -1
  15. package/build/main/query/tenant/status.js +4 -2
  16. package/build/main/query/tenant/status.js.map +1 -1
  17. package/build/main/regional.d.ts +1 -0
  18. package/build/main/regional.js +1 -0
  19. package/build/main/regional.js.map +1 -1
  20. package/build/main/test/index.d.ts +23 -3
  21. package/build/main/test/index.js +35 -13
  22. package/build/main/test/index.js.map +1 -1
  23. package/build/main/v1.generated.d.ts +338 -37
  24. package/build/main/v1.generated.js.map +1 -1
  25. package/build/migrations-v2/0000000000015-durable-flow-events.ts +163 -0
  26. package/build/migrations-v2/0000000000016-billing-listing-facts.ts +539 -0
  27. package/build/migrations-v2/0000000000017-webhook-secret-reencryption-failures.ts +18 -0
  28. package/build/migrations-v2/0000000000018-durable-flow-event-sensitive-input.ts +28 -0
  29. package/build/migrations-v2/0000000000019-asynchronous-operations.ts +736 -0
  30. package/build/migrations-v2/0000000000031-ui-permission-role-simplification.ts +70 -0
  31. package/build/migrations-v2/0000000000032-tenant-delete-ownership-cascades.ts +44 -0
  32. package/build/migrations-v2/0000000000033-reservation-issue-seed-bucket-index.ts +19 -0
  33. package/build/migrations-v2/0000000000034-retire-recent-classification-indexes.ts +29 -0
  34. package/build/migrations-v2/0000000000035-core-change-autovacuum.ts +24 -0
  35. package/build/migrations-v2/0000000000036-journal-entry-inactive-reason-constraint.ts +33 -0
  36. package/build/migrations-v2/0000000000037-journal-entry-inactive-reasons.ts +19 -0
  37. package/build/migrations-v2/0000000000038-journal-entry-group-inactive-reason.ts +14 -0
  38. package/build/migrations-v2/0000000000039-reservation-guest-breakdown.ts +19 -0
  39. package/build/migrations-v2/0000000000040-csv-import-source-identity-index.ts +23 -0
  40. package/build/migrations-v2/0000000000041-payment-returns.ts +38 -0
  41. package/build/migrations-v2/0000000000042-operation-resource-types.ts +609 -0
  42. package/build/migrations-v2/0000000000043-reservation-journal-freshness.ts +149 -0
  43. package/build/migrations-v2/0000000000044-reservation-journal-freshness-backfill.ts +254 -0
  44. package/build/migrations-v2/0000000000045-reservation-journal-freshness-revision-repair.ts +83 -0
  45. package/build/migrations-v2/0000000000046-materialized-reservation-journal-operations.ts +709 -0
  46. package/build/migrations-v2/0000000000047-public-audit-completion.ts +77 -0
  47. package/build/migrations-v2/0000000000048-reservation-journal-full-refresh-repair.ts +136 -0
  48. package/build/migrations-v2/0000000000049-queued-journal-operation-failures.ts +60 -0
  49. package/build/migrations-v2/0000000000050-connection-oauth-state.ts +43 -0
  50. package/build/migrations-v2/0000000000051-owner-statement-email-delivery.ts +134 -0
  51. package/build/migrations-v2/0000000000052-fee-journal-lock-skip-operations.ts +59 -0
  52. package/build/migrations-v2/0000000000053-fee-journal-lock-skip-null-attempt.ts +44 -0
  53. package/build/migrations-v2/0000000000054-journal-refresh-issue-codes.ts +16 -0
  54. package/build/migrations-v2/0000000000055-tenant-billing-subscription.ts +8 -0
  55. package/build/module/control-plane/index.d.ts +137 -0
  56. package/build/module/control-plane/index.js.map +1 -1
  57. package/build/module/index.d.ts +11 -0
  58. package/build/module/index.js +131 -26
  59. package/build/module/index.js.map +1 -1
  60. package/build/module/local/generate.js +1 -0
  61. package/build/module/local/generate.js.map +1 -1
  62. package/build/module/local/index.js +109 -8
  63. package/build/module/local/index.js.map +1 -1
  64. package/build/module/postgres-error.d.ts +3 -3
  65. package/build/module/postgres-error.js +4 -3
  66. package/build/module/postgres-error.js.map +1 -1
  67. package/build/module/query/tenant/status.d.ts +1 -1
  68. package/build/module/query/tenant/status.js +4 -2
  69. package/build/module/query/tenant/status.js.map +1 -1
  70. package/build/module/regional.d.ts +1 -0
  71. package/build/module/regional.js +1 -0
  72. package/build/module/regional.js.map +1 -1
  73. package/build/module/test/index.d.ts +23 -3
  74. package/build/module/test/index.js +34 -13
  75. package/build/module/test/index.js.map +1 -1
  76. package/build/module/v1.generated.d.ts +338 -37
  77. package/build/module/v1.generated.js.map +1 -1
  78. package/package.json +4 -2
@@ -33,6 +33,7 @@ async function migrate(root, client) {
33
33
  async function migrateKysely(root, kysely) {
34
34
  console.log('init migrating kysely ....');
35
35
  const migrator = new Migrator({
36
+ allowUnorderedMigrations: true,
36
37
  db: kysely,
37
38
  migrationTableSchema: appMigrationEpoch.tableSchema,
38
39
  migrationTableName: appMigrationEpoch.tableName,
@@ -1 +1 @@
1
- {"version":3,"file":"generate.js","sourceRoot":"src/","sources":["local/generate.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport { exists } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { join } from 'node:path';\nimport { PGlite } from '@electric-sql/pglite';\nimport { FileMigrationProvider, type Kysely, Migrator } from 'kysely';\nimport codegen from 'kysely-codegen';\nimport { PGliteDialect } from 'kysely-pglite-dialect';\nimport { appMigrationEpoch } from '../migration';\nimport { getKysely } from '../plugins';\nimport type { DB } from '../v1.generated';\n\nasync function normalizeGeneratedTypes(root: string) {\n const outFile = path.join(root, 'src/v1.generated.ts');\n const current = await Bun.file(outFile).text();\n const next = current\n .replace(\n 'export type Timestamp = ColumnType<Date, Date | string, Date | string>;',\n 'export type Timestamp = ColumnType<Date | string, Date | string, Date | string>;'\n )\n .replace(\n ' managedTeamAccess: Generated<string>;\\n',\n ' managedTeamAccess: Generated<\"all\" | \"assigned\">;\\n'\n )\n .replace(' slug: string;\\n', ' slug: Generated<string>;\\n');\n\n if (next !== current) {\n await Bun.write(outFile, next);\n }\n}\n\nasync function migrate(root: string, client: PGlite) {\n console.log('migrating pglite ....');\n const glob = new Bun.Glob('initial/*.sql');\n for await (const migration of glob.scan(root)) {\n const raw = await Bun.file(join(root, migration)).text();\n // `pg_dump` (v17+) prepends `\\restrict`/`\\unrestrict` psql meta commands\n // which PostgreSQL-compatible parsers (like PGlite) cannot execute.\n const sanitized = raw.replace(/^\\s*\\\\(?:un)?restrict.*$/gim, '');\n await client.exec(sanitized);\n }\n}\n\nasync function migrateKysely(root: string, kysely: Kysely<DB>) {\n console.log('init migrating kysely ....');\n const migrator = new Migrator({\n db: kysely,\n migrationTableSchema: appMigrationEpoch.tableSchema,\n migrationTableName: appMigrationEpoch.tableName,\n migrationLockTableName: appMigrationEpoch.lockTableName,\n provider: new FileMigrationProvider({\n fs,\n path,\n // This needs to be an absolute path.\n migrationFolder: path.join(root, appMigrationEpoch.folder),\n }),\n });\n\n console.log('migrating kysely ....');\n const { error, results } = await migrator.migrateToLatest().catch(() => {\n console.error('failed to migrate');\n process.exit(1);\n });\n\n for (const it of results ?? []) {\n if (it.status === 'Success') {\n console.log(`migration \"${it.migrationName}\" was executed successfully`);\n } else if (it.status === 'Error') {\n console.error(`failed to execute migration \"${it.migrationName}\"`);\n }\n }\n\n if (error) {\n console.error('failed to migrate');\n console.error(error);\n process.exit(1);\n }\n}\n\nasync function generateDump(root: string, dumpPath: string) {\n const dataExists = await exists(dumpPath).catch(() => false);\n if (!dataExists) {\n console.log('Creating initial dump');\n const main = new PGlite();\n await migrate(root, main).catch(async (err) => {\n console.error('failed to migrate initial dump');\n await Bun.write(\n '../logs/migrate-initial-dump.json',\n JSON.stringify(err as any, null, 2)\n );\n process.exit(1);\n });\n const dialect = new PGliteDialect(main);\n const kysely = getKysely(dialect, {\n repositoryName: 'localTesting',\n log: undefined,\n });\n await migrateKysely(root, kysely).catch(() => {\n console.error('failed to migrate kysely');\n process.exit(1);\n });\n await codegen\n .generate({\n db: kysely,\n outFile: path.join(root, 'src/v1.generated.ts'),\n defaultSchemas: ['xxx'],\n camelCase: true,\n dialect: codegen.getDialect('postgres'),\n })\n .catch(() => {\n console.error('failed to generate kysely');\n process.exit(1);\n });\n await normalizeGeneratedTypes(root);\n const content = await main.dumpDataDir('none').catch(() => {\n console.error('failed to dump data');\n process.exit(1);\n });\n await Bun.write(dumpPath, content).catch(() => {\n console.error('failed to write dump');\n process.exit(1);\n });\n return content;\n }\n return Bun.file(dumpPath);\n}\n\nexport async function createLocalKysely(root: string, dumpPath: string) {\n let isDestroyed = false;\n //const now = performance.now();\n\n const dump = await generateDump(root, dumpPath);\n\n const pglite = new PGlite({ loadDataDir: dump });\n // warm up\n await pglite.sql`SELECT 1`;\n const dialect = new PGliteDialect(pglite);\n const kysely = getKysely(dialect, {\n repositoryName: 'localTesting',\n log: undefined,\n });\n\n const originalDestroy = kysely.destroy.bind(kysely);\n kysely.destroy = async () => {\n if (isDestroyed) return;\n isDestroyed = true;\n // const destroyTime = performance.now();\n await originalDestroy();\n //console.log(\n // `Database destroyed: ${Math.round(performance.now() - destroyTime)}ms`\n //);\n };\n\n // console.log(`Database initialized: ${Math.round(performance.now() - now)}ms`);\n\n return kysely;\n}\n"]}
1
+ {"version":3,"file":"generate.js","sourceRoot":"src/","sources":["local/generate.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport { exists } from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { join } from 'node:path';\nimport { PGlite } from '@electric-sql/pglite';\nimport { FileMigrationProvider, type Kysely, Migrator } from 'kysely';\nimport codegen from 'kysely-codegen';\nimport { PGliteDialect } from 'kysely-pglite-dialect';\nimport { appMigrationEpoch } from '../migration';\nimport { getKysely } from '../plugins';\nimport type { DB } from '../v1.generated';\n\nasync function normalizeGeneratedTypes(root: string) {\n const outFile = path.join(root, 'src/v1.generated.ts');\n const current = await Bun.file(outFile).text();\n const next = current\n .replace(\n 'export type Timestamp = ColumnType<Date, Date | string, Date | string>;',\n 'export type Timestamp = ColumnType<Date | string, Date | string, Date | string>;'\n )\n .replace(\n ' managedTeamAccess: Generated<string>;\\n',\n ' managedTeamAccess: Generated<\"all\" | \"assigned\">;\\n'\n )\n .replace(' slug: string;\\n', ' slug: Generated<string>;\\n');\n\n if (next !== current) {\n await Bun.write(outFile, next);\n }\n}\n\nasync function migrate(root: string, client: PGlite) {\n console.log('migrating pglite ....');\n const glob = new Bun.Glob('initial/*.sql');\n for await (const migration of glob.scan(root)) {\n const raw = await Bun.file(join(root, migration)).text();\n // `pg_dump` (v17+) prepends `\\restrict`/`\\unrestrict` psql meta commands\n // which PostgreSQL-compatible parsers (like PGlite) cannot execute.\n const sanitized = raw.replace(/^\\s*\\\\(?:un)?restrict.*$/gim, '');\n await client.exec(sanitized);\n }\n}\n\nasync function migrateKysely(root: string, kysely: Kysely<DB>) {\n console.log('init migrating kysely ....');\n const migrator = new Migrator({\n allowUnorderedMigrations: true,\n db: kysely,\n migrationTableSchema: appMigrationEpoch.tableSchema,\n migrationTableName: appMigrationEpoch.tableName,\n migrationLockTableName: appMigrationEpoch.lockTableName,\n provider: new FileMigrationProvider({\n fs,\n path,\n // This needs to be an absolute path.\n migrationFolder: path.join(root, appMigrationEpoch.folder),\n }),\n });\n\n console.log('migrating kysely ....');\n const { error, results } = await migrator.migrateToLatest().catch(() => {\n console.error('failed to migrate');\n process.exit(1);\n });\n\n for (const it of results ?? []) {\n if (it.status === 'Success') {\n console.log(`migration \"${it.migrationName}\" was executed successfully`);\n } else if (it.status === 'Error') {\n console.error(`failed to execute migration \"${it.migrationName}\"`);\n }\n }\n\n if (error) {\n console.error('failed to migrate');\n console.error(error);\n process.exit(1);\n }\n}\n\nasync function generateDump(root: string, dumpPath: string) {\n const dataExists = await exists(dumpPath).catch(() => false);\n if (!dataExists) {\n console.log('Creating initial dump');\n const main = new PGlite();\n await migrate(root, main).catch(async (err) => {\n console.error('failed to migrate initial dump');\n await Bun.write(\n '../logs/migrate-initial-dump.json',\n JSON.stringify(err as any, null, 2)\n );\n process.exit(1);\n });\n const dialect = new PGliteDialect(main);\n const kysely = getKysely(dialect, {\n repositoryName: 'localTesting',\n log: undefined,\n });\n await migrateKysely(root, kysely).catch(() => {\n console.error('failed to migrate kysely');\n process.exit(1);\n });\n await codegen\n .generate({\n db: kysely,\n outFile: path.join(root, 'src/v1.generated.ts'),\n defaultSchemas: ['xxx'],\n camelCase: true,\n dialect: codegen.getDialect('postgres'),\n })\n .catch(() => {\n console.error('failed to generate kysely');\n process.exit(1);\n });\n await normalizeGeneratedTypes(root);\n const content = await main.dumpDataDir('none').catch(() => {\n console.error('failed to dump data');\n process.exit(1);\n });\n await Bun.write(dumpPath, content).catch(() => {\n console.error('failed to write dump');\n process.exit(1);\n });\n return content;\n }\n return Bun.file(dumpPath);\n}\n\nexport async function createLocalKysely(root: string, dumpPath: string) {\n let isDestroyed = false;\n //const now = performance.now();\n\n const dump = await generateDump(root, dumpPath);\n\n const pglite = new PGlite({ loadDataDir: dump });\n // warm up\n await pglite.sql`SELECT 1`;\n const dialect = new PGliteDialect(pglite);\n const kysely = getKysely(dialect, {\n repositoryName: 'localTesting',\n log: undefined,\n });\n\n const originalDestroy = kysely.destroy.bind(kysely);\n kysely.destroy = async () => {\n if (isDestroyed) return;\n isDestroyed = true;\n // const destroyTime = performance.now();\n await originalDestroy();\n //console.log(\n // `Database destroyed: ${Math.round(performance.now() - destroyTime)}ms`\n //);\n };\n\n // console.log(`Database initialized: ${Math.round(performance.now() - now)}ms`);\n\n return kysely;\n}\n"]}
@@ -11,6 +11,7 @@ export const root = path.join(__dirname, '../..');
11
11
  export const dumpPath = path.join(root, '.data.tar');
12
12
  async function migrateToLatest(kysely) {
13
13
  const migrator = new Migrator({
14
+ allowUnorderedMigrations: true,
14
15
  db: kysely,
15
16
  migrationTableSchema: appMigrationEpoch.tableSchema,
16
17
  migrationTableName: appMigrationEpoch.tableName,
@@ -26,17 +27,68 @@ async function migrateToLatest(kysely) {
26
27
  throw error;
27
28
  }
28
29
  }
29
- export async function useLocalKysely(args = {}) {
30
- let isDestroyed = false;
31
- // const now = performance.now();
32
- const dump = Bun.file(dumpPath);
30
+ // Every spec file used to replay this identical bootstrap - migrations, the
31
+ // schema surgery, and the control-plane seed copies - against a freshly loaded
32
+ // snapshot, which measured ~345ms per file against ~185ms for the snapshot load
33
+ // alone. None of it depends on the caller, so it runs once and is cached as a
34
+ // prepared snapshot keyed by everything that determines its content.
35
+ const preparedDumpPrefix = '.data.prepared.';
36
+ let preparedDumpPromise;
37
+ async function preparedDumpKey() {
38
+ const [snapshot, appMigrations, controlPlaneMigrations] = await Promise.all([
39
+ fs.stat(dumpPath),
40
+ fs.readdir(path.join(root, appMigrationEpoch.folder)),
41
+ fs.readdir(path.join(root, 'control-plane-migrations')),
42
+ ]);
43
+ const hasher = new Bun.CryptoHasher('sha256');
44
+ hasher.update(`${snapshot.size}:${snapshot.mtimeMs}`);
45
+ hasher.update(appMigrations.sort().join(','));
46
+ hasher.update(controlPlaneMigrations.sort().join(','));
47
+ // Source of the bootstrap itself, so editing the surgery rebuilds the
48
+ // snapshot. Read through toString() rather than the file: this module is also
49
+ // published as CommonJS, where import.meta is a compile error.
50
+ for (const step of [
51
+ buildPreparedDump,
52
+ migrateToLatest,
53
+ ensureDigestFunction,
54
+ ensureGenRandomBytesFunction,
55
+ ]) {
56
+ hasher.update(step.toString());
57
+ }
58
+ return hasher.digest('hex').slice(0, 16);
59
+ }
60
+ async function resolvePreparedDump() {
61
+ const key = await preparedDumpKey();
62
+ const target = path.join(root, `${preparedDumpPrefix}${key}.tar`);
63
+ const cached = Bun.file(target);
64
+ if (await cached.exists()) {
65
+ return cached;
66
+ }
67
+ const content = await buildPreparedDump();
68
+ // Sibling workers build this concurrently on a cold cache; rename is atomic,
69
+ // so a reader never observes a half-written snapshot.
70
+ const staging = `${target}.${process.pid}.tmp`;
71
+ await Bun.write(staging, content);
72
+ await fs.rename(staging, target);
73
+ // Only finished snapshots: a sibling worker's staging file also carries the
74
+ // prefix, and removing it would break its rename.
75
+ const current = path.basename(target);
76
+ for (const entry of await fs.readdir(root)) {
77
+ if (entry.startsWith(preparedDumpPrefix) &&
78
+ entry.endsWith('.tar') &&
79
+ entry !== current) {
80
+ await fs.rm(path.join(root, entry), { force: true });
81
+ }
82
+ }
83
+ return content;
84
+ }
85
+ async function buildPreparedDump() {
33
86
  const pglite = new PGlite({
34
- loadDataDir: dump,
87
+ loadDataDir: Bun.file(dumpPath),
35
88
  });
36
89
  const dialect = new PGliteDialect(pglite);
37
90
  const kysely = getKysely(dialect, {
38
- repositoryName: args?.repositoryName ?? 'localTesting',
39
- log: args?.log,
91
+ repositoryName: 'localTestingPrepare',
40
92
  });
41
93
  await ensureDigestFunction(kysely);
42
94
  await ensureGenRandomBytesFunction(kysely);
@@ -46,7 +98,7 @@ export async function useLocalKysely(args = {}) {
46
98
  await sql `create schema public`.execute(kysely);
47
99
  await sql `create schema core`.execute(kysely);
48
100
  const controlPlaneBootstrapKysely = getKysely(dialect, {
49
- repositoryName: `${args.repositoryName ?? 'localTesting'}/control-plane-bootstrap`,
101
+ repositoryName: 'localTestingPrepare/control-plane-bootstrap',
50
102
  });
51
103
  const controlPlaneMigrator = new Migrator({
52
104
  db: controlPlaneBootstrapKysely,
@@ -83,9 +135,39 @@ export async function useLocalKysely(args = {}) {
83
135
  'control_plane.build_unique_tenant_slug(text,uuid)'::regprocedure
84
136
  ) into definition;
85
137
  execute replace(definition, 'public.', 'control_plane.');
138
+
139
+ select pg_get_functiondef(
140
+ 'control_plane.refresh_operation_state(uuid)'::regprocedure
141
+ ) into definition;
142
+ execute replace(definition, 'public.', 'control_plane.');
143
+
144
+ select pg_get_functiondef(
145
+ 'control_plane.refresh_destructive_job_operation()'::regprocedure
146
+ ) into definition;
147
+ execute replace(definition, 'public.', 'control_plane.');
86
148
  end;
87
149
  $$;
88
150
  `.execute(controlPlaneBootstrapKysely);
151
+ // The canonical VRPlatform tenant. Production code falls back to this exact id
152
+ // in getCalculatedBillingPartnerId, so specs need it to exist without each of
153
+ // them creating - and mutating - their own copy of a shared singleton.
154
+ await kysely
155
+ .insertInto('public.tenant')
156
+ .values({
157
+ id: '7bfec41f-aa3f-4602-95d2-f7996e4f0e59',
158
+ name: 'VRPlatform',
159
+ type: 'partner',
160
+ status: 'active',
161
+ isActive: true,
162
+ isGeneralLedger: false,
163
+ isOnboarding: false,
164
+ partnerId: null,
165
+ billingPartnerId: null,
166
+ defaultCurrency: 'usd',
167
+ statementStartAt: new Date('2020-01-01T00:00:00Z'),
168
+ })
169
+ .onConflict((oc) => oc.column('id').doNothing())
170
+ .execute();
89
171
  await sql `
90
172
  insert into control_plane.app (
91
173
  authentication,
@@ -241,6 +323,25 @@ export async function useLocalKysely(args = {}) {
241
323
  from public.token
242
324
  on conflict (id) do nothing;
243
325
  `.execute(controlPlaneBootstrapKysely);
326
+ const content = await pglite.dumpDataDir('none');
327
+ await kysely.destroy();
328
+ return content;
329
+ }
330
+ export async function useLocalKysely(args = {}) {
331
+ let isDestroyed = false;
332
+ preparedDumpPromise ??= resolvePreparedDump();
333
+ const pglite = new PGlite({
334
+ loadDataDir: await preparedDumpPromise,
335
+ });
336
+ // The constructor is lazy. The bootstrap this replaced forced the snapshot to
337
+ // load before returning, so keep that contract: a returned handle is usable,
338
+ // and a bad snapshot fails here instead of inside an unrelated test.
339
+ await pglite.waitReady;
340
+ const dialect = new PGliteDialect(pglite);
341
+ const kysely = getKysely(dialect, {
342
+ repositoryName: args?.repositoryName ?? 'localTesting',
343
+ log: args?.log,
344
+ });
244
345
  const controlPlaneKysely = withControlPlaneSchema(getKysely(dialect, {
245
346
  repositoryName: `${args.repositoryName ?? 'localTesting'}/control-plane`,
246
347
  log: args.log,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"src/","sources":["local/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport { PGlite } from '@electric-sql/pglite';\nimport { FileMigrationProvider, type LogConfig, Migrator, sql } from 'kysely';\nimport { PGliteDialect } from 'kysely-pglite-dialect';\nimport { type ControlPlaneDB, withControlPlaneSchema } from '../control-plane';\nimport type { ControlPlaneKysely } from '../index';\nimport { appMigrationEpoch } from '../migration';\nimport { getKysely } from '../plugins';\nimport type { DB } from '../v1.generated';\nimport { ensureDigestFunction, ensureGenRandomBytesFunction } from './digest';\n\nexport const root = path.join(__dirname, '../..');\nexport const dumpPath = path.join(root, '.data.tar');\n\nexport type UseLocalKyselyOptions = {\n repositoryName?: string;\n log?: true | LogConfig;\n};\n\nexport type LocalKysely = import('kysely').Kysely<DB> & {\n controlPlaneKysely: ControlPlaneKysely;\n};\n\nasync function migrateToLatest<Database>(\n kysely: import('kysely').Kysely<Database>\n) {\n const migrator = new Migrator({\n db: kysely,\n migrationTableSchema: appMigrationEpoch.tableSchema,\n migrationTableName: appMigrationEpoch.tableName,\n migrationLockTableName: appMigrationEpoch.lockTableName,\n provider: new FileMigrationProvider({\n fs,\n path,\n migrationFolder: path.join(root, appMigrationEpoch.folder),\n }),\n });\n\n const { error } = await migrator.migrateToLatest();\n if (error) {\n throw error;\n }\n}\n\nexport async function useLocalKysely(\n args: UseLocalKyselyOptions = {}\n): Promise<LocalKysely> {\n let isDestroyed = false;\n // const now = performance.now();\n\n const dump = Bun.file(dumpPath);\n\n const pglite = new PGlite({\n loadDataDir: dump,\n });\n const dialect = new PGliteDialect(pglite);\n const kysely = getKysely<DB>(dialect, {\n repositoryName: args?.repositoryName ?? 'localTesting',\n log: args?.log,\n });\n await ensureDigestFunction(kysely);\n await ensureGenRandomBytesFunction(kysely);\n await migrateToLatest(kysely);\n\n await sql`alter schema public rename to regional_public`.execute(kysely);\n await sql`alter schema core rename to regional_core`.execute(kysely);\n await sql`create schema public`.execute(kysely);\n await sql`create schema core`.execute(kysely);\n\n const controlPlaneBootstrapKysely = getKysely<ControlPlaneDB>(dialect, {\n repositoryName: `${args.repositoryName ?? 'localTesting'}/control-plane-bootstrap`,\n });\n const controlPlaneMigrator = new Migrator({\n db: controlPlaneBootstrapKysely,\n migrationTableSchema: 'public',\n migrationTableName: 'control_plane_kysely_migration',\n migrationLockTableName: 'control_plane_kysely_migration_lock',\n provider: new FileMigrationProvider({\n fs,\n path,\n migrationFolder: path.join(root, 'control-plane-migrations'),\n }),\n });\n const { error: controlPlaneMigrationError } =\n await controlPlaneMigrator.migrateToLatest();\n if (controlPlaneMigrationError) {\n throw controlPlaneMigrationError;\n }\n\n await sql`alter table core.flow set schema public`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`alter table core.issue_message_overwrite set schema public`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`drop schema core`.execute(controlPlaneBootstrapKysely);\n await sql`alter schema public rename to control_plane`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`alter schema regional_public rename to public`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`alter schema regional_core rename to core`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`\n do $$\n declare\n definition text;\n begin\n select pg_get_functiondef(\n 'control_plane.normalize_tenant_slug(text)'::regprocedure\n ) into definition;\n execute replace(definition, 'public.', 'control_plane.');\n\n select pg_get_functiondef(\n 'control_plane.build_unique_tenant_slug(text,uuid)'::regprocedure\n ) into definition;\n execute replace(definition, 'public.', 'control_plane.');\n end;\n $$;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.app (\n authentication,\n category,\n color,\n created_at,\n icon,\n icon_round,\n id,\n info,\n name,\n type,\n version\n )\n select\n authentication,\n category,\n color,\n created_at,\n icon,\n icon_round,\n id,\n info,\n name,\n type,\n version\n from public.app\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.currency (name)\n select name\n from public.currency\n on conflict (name) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.booking_channel (\n channel_ref,\n color,\n created_at,\n icon,\n icon_provider,\n id,\n last_generate_run,\n logo,\n logo_provider,\n selected_booking_channel_icon_candidate_id,\n selected_booking_channel_logo_candidate_id,\n unique_ref,\n updated_at\n )\n select\n channel_ref,\n color,\n created_at,\n icon,\n icon_provider,\n id,\n last_generate_run,\n logo,\n logo_provider,\n selected_booking_channel_icon_candidate_id,\n selected_booking_channel_logo_candidate_id,\n unique_ref,\n updated_at\n from public.booking_channel\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.booking_channel_icon_candidate (\n booking_channel_id,\n cloudflare_image_id,\n color,\n comment,\n created_at,\n external_icon,\n height,\n id,\n provider,\n source,\n updated_at,\n width\n )\n select\n booking_channel_id,\n cloudflare_image_id,\n color,\n comment,\n created_at,\n external_icon,\n height,\n id,\n provider,\n source,\n updated_at,\n width\n from public.booking_channel_icon_candidate\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.tenant (\n billing_partner_id,\n billing_plan,\n created_at,\n data_region,\n id,\n name,\n partner_id,\n slug,\n status,\n storage_realm,\n type,\n unique_ref,\n updated_at\n )\n select\n billing_partner_id,\n billing_plan,\n created_at,\n data_region,\n id,\n name,\n partner_id,\n slug,\n status,\n storage_realm,\n type,\n unique_ref,\n updated_at\n from public.tenant\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.token (\n created_at,\n expires_at,\n id,\n nano_id,\n payload,\n tenant_id,\n type,\n user_id\n )\n select\n created_at,\n expires_at,\n id,\n nano_id,\n payload,\n tenant_id,\n type,\n user_id\n from public.token\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n\n const controlPlaneKysely = withControlPlaneSchema(\n getKysely<ControlPlaneDB>(dialect, {\n repositoryName: `${args.repositoryName ?? 'localTesting'}/control-plane`,\n log: args.log,\n }),\n 'control_plane',\n 'control_plane'\n );\n controlPlaneKysely.destroy = async () => {};\n Object.defineProperty(kysely, 'controlPlaneKysely', {\n value: controlPlaneKysely,\n });\n\n const originalDestroy = kysely.destroy.bind(kysely);\n kysely.destroy = async () => {\n if (isDestroyed) return;\n isDestroyed = true;\n //const destroyTime = performance.now();\n await originalDestroy();\n //console.log(\n // `Database destroyed: ${Math.round(performance.now() - destroyTime)}ms`\n //);\n };\n\n // console.log(`Database initialized: ${Math.round(performance.now() - now)}ms`);\n\n return kysely as LocalKysely;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"src/","sources":["local/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { promises as fs } from 'node:fs';\nimport * as path from 'node:path';\nimport { PGlite } from '@electric-sql/pglite';\nimport { FileMigrationProvider, type LogConfig, Migrator, sql } from 'kysely';\nimport { PGliteDialect } from 'kysely-pglite-dialect';\nimport { type ControlPlaneDB, withControlPlaneSchema } from '../control-plane';\nimport type { ControlPlaneKysely } from '../index';\nimport { appMigrationEpoch } from '../migration';\nimport { getKysely } from '../plugins';\nimport type { DB } from '../v1.generated';\nimport { ensureDigestFunction, ensureGenRandomBytesFunction } from './digest';\n\nexport const root = path.join(__dirname, '../..');\nexport const dumpPath = path.join(root, '.data.tar');\n\nexport type UseLocalKyselyOptions = {\n repositoryName?: string;\n log?: true | LogConfig;\n};\n\nexport type LocalKysely = import('kysely').Kysely<DB> & {\n controlPlaneKysely: ControlPlaneKysely;\n};\n\nasync function migrateToLatest<Database>(\n kysely: import('kysely').Kysely<Database>\n) {\n const migrator = new Migrator({\n allowUnorderedMigrations: true,\n db: kysely,\n migrationTableSchema: appMigrationEpoch.tableSchema,\n migrationTableName: appMigrationEpoch.tableName,\n migrationLockTableName: appMigrationEpoch.lockTableName,\n provider: new FileMigrationProvider({\n fs,\n path,\n migrationFolder: path.join(root, appMigrationEpoch.folder),\n }),\n });\n\n const { error } = await migrator.migrateToLatest();\n if (error) {\n throw error;\n }\n}\n\n// Every spec file used to replay this identical bootstrap - migrations, the\n// schema surgery, and the control-plane seed copies - against a freshly loaded\n// snapshot, which measured ~345ms per file against ~185ms for the snapshot load\n// alone. None of it depends on the caller, so it runs once and is cached as a\n// prepared snapshot keyed by everything that determines its content.\nconst preparedDumpPrefix = '.data.prepared.';\n\nlet preparedDumpPromise: Promise<Blob> | undefined;\n\nasync function preparedDumpKey(): Promise<string> {\n const [snapshot, appMigrations, controlPlaneMigrations] = await Promise.all([\n fs.stat(dumpPath),\n fs.readdir(path.join(root, appMigrationEpoch.folder)),\n fs.readdir(path.join(root, 'control-plane-migrations')),\n ]);\n\n const hasher = new Bun.CryptoHasher('sha256');\n hasher.update(`${snapshot.size}:${snapshot.mtimeMs}`);\n hasher.update(appMigrations.sort().join(','));\n hasher.update(controlPlaneMigrations.sort().join(','));\n // Source of the bootstrap itself, so editing the surgery rebuilds the\n // snapshot. Read through toString() rather than the file: this module is also\n // published as CommonJS, where import.meta is a compile error.\n for (const step of [\n buildPreparedDump,\n migrateToLatest,\n ensureDigestFunction,\n ensureGenRandomBytesFunction,\n ]) {\n hasher.update(step.toString());\n }\n return hasher.digest('hex').slice(0, 16);\n}\n\nasync function resolvePreparedDump(): Promise<Blob> {\n const key = await preparedDumpKey();\n const target = path.join(root, `${preparedDumpPrefix}${key}.tar`);\n const cached = Bun.file(target);\n if (await cached.exists()) {\n return cached;\n }\n\n const content = await buildPreparedDump();\n // Sibling workers build this concurrently on a cold cache; rename is atomic,\n // so a reader never observes a half-written snapshot.\n const staging = `${target}.${process.pid}.tmp`;\n await Bun.write(staging, content);\n await fs.rename(staging, target);\n\n // Only finished snapshots: a sibling worker's staging file also carries the\n // prefix, and removing it would break its rename.\n const current = path.basename(target);\n for (const entry of await fs.readdir(root)) {\n if (\n entry.startsWith(preparedDumpPrefix) &&\n entry.endsWith('.tar') &&\n entry !== current\n ) {\n await fs.rm(path.join(root, entry), { force: true });\n }\n }\n\n return content;\n}\n\nasync function buildPreparedDump(): Promise<Blob> {\n const pglite = new PGlite({\n loadDataDir: Bun.file(dumpPath),\n });\n const dialect = new PGliteDialect(pglite);\n const kysely = getKysely<DB>(dialect, {\n repositoryName: 'localTestingPrepare',\n });\n await ensureDigestFunction(kysely);\n await ensureGenRandomBytesFunction(kysely);\n await migrateToLatest(kysely);\n\n await sql`alter schema public rename to regional_public`.execute(kysely);\n await sql`alter schema core rename to regional_core`.execute(kysely);\n await sql`create schema public`.execute(kysely);\n await sql`create schema core`.execute(kysely);\n\n const controlPlaneBootstrapKysely = getKysely<ControlPlaneDB>(dialect, {\n repositoryName: 'localTestingPrepare/control-plane-bootstrap',\n });\n const controlPlaneMigrator = new Migrator({\n db: controlPlaneBootstrapKysely,\n migrationTableSchema: 'public',\n migrationTableName: 'control_plane_kysely_migration',\n migrationLockTableName: 'control_plane_kysely_migration_lock',\n provider: new FileMigrationProvider({\n fs,\n path,\n migrationFolder: path.join(root, 'control-plane-migrations'),\n }),\n });\n const { error: controlPlaneMigrationError } =\n await controlPlaneMigrator.migrateToLatest();\n if (controlPlaneMigrationError) {\n throw controlPlaneMigrationError;\n }\n\n await sql`alter table core.flow set schema public`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`alter table core.issue_message_overwrite set schema public`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`drop schema core`.execute(controlPlaneBootstrapKysely);\n await sql`alter schema public rename to control_plane`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`alter schema regional_public rename to public`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`alter schema regional_core rename to core`.execute(\n controlPlaneBootstrapKysely\n );\n await sql`\n do $$\n declare\n definition text;\n begin\n select pg_get_functiondef(\n 'control_plane.normalize_tenant_slug(text)'::regprocedure\n ) into definition;\n execute replace(definition, 'public.', 'control_plane.');\n\n select pg_get_functiondef(\n 'control_plane.build_unique_tenant_slug(text,uuid)'::regprocedure\n ) into definition;\n execute replace(definition, 'public.', 'control_plane.');\n\n select pg_get_functiondef(\n 'control_plane.refresh_operation_state(uuid)'::regprocedure\n ) into definition;\n execute replace(definition, 'public.', 'control_plane.');\n\n select pg_get_functiondef(\n 'control_plane.refresh_destructive_job_operation()'::regprocedure\n ) into definition;\n execute replace(definition, 'public.', 'control_plane.');\n end;\n $$;\n `.execute(controlPlaneBootstrapKysely);\n // The canonical VRPlatform tenant. Production code falls back to this exact id\n // in getCalculatedBillingPartnerId, so specs need it to exist without each of\n // them creating - and mutating - their own copy of a shared singleton.\n await kysely\n .insertInto('public.tenant')\n .values({\n id: '7bfec41f-aa3f-4602-95d2-f7996e4f0e59',\n name: 'VRPlatform',\n type: 'partner',\n status: 'active',\n isActive: true,\n isGeneralLedger: false,\n isOnboarding: false,\n partnerId: null,\n billingPartnerId: null,\n defaultCurrency: 'usd',\n statementStartAt: new Date('2020-01-01T00:00:00Z'),\n })\n .onConflict((oc) => oc.column('id').doNothing())\n .execute();\n\n await sql`\n insert into control_plane.app (\n authentication,\n category,\n color,\n created_at,\n icon,\n icon_round,\n id,\n info,\n name,\n type,\n version\n )\n select\n authentication,\n category,\n color,\n created_at,\n icon,\n icon_round,\n id,\n info,\n name,\n type,\n version\n from public.app\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.currency (name)\n select name\n from public.currency\n on conflict (name) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.booking_channel (\n channel_ref,\n color,\n created_at,\n icon,\n icon_provider,\n id,\n last_generate_run,\n logo,\n logo_provider,\n selected_booking_channel_icon_candidate_id,\n selected_booking_channel_logo_candidate_id,\n unique_ref,\n updated_at\n )\n select\n channel_ref,\n color,\n created_at,\n icon,\n icon_provider,\n id,\n last_generate_run,\n logo,\n logo_provider,\n selected_booking_channel_icon_candidate_id,\n selected_booking_channel_logo_candidate_id,\n unique_ref,\n updated_at\n from public.booking_channel\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.booking_channel_icon_candidate (\n booking_channel_id,\n cloudflare_image_id,\n color,\n comment,\n created_at,\n external_icon,\n height,\n id,\n provider,\n source,\n updated_at,\n width\n )\n select\n booking_channel_id,\n cloudflare_image_id,\n color,\n comment,\n created_at,\n external_icon,\n height,\n id,\n provider,\n source,\n updated_at,\n width\n from public.booking_channel_icon_candidate\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.tenant (\n billing_partner_id,\n billing_plan,\n created_at,\n data_region,\n id,\n name,\n partner_id,\n slug,\n status,\n storage_realm,\n type,\n unique_ref,\n updated_at\n )\n select\n billing_partner_id,\n billing_plan,\n created_at,\n data_region,\n id,\n name,\n partner_id,\n slug,\n status,\n storage_realm,\n type,\n unique_ref,\n updated_at\n from public.tenant\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n await sql`\n insert into control_plane.token (\n created_at,\n expires_at,\n id,\n nano_id,\n payload,\n tenant_id,\n type,\n user_id\n )\n select\n created_at,\n expires_at,\n id,\n nano_id,\n payload,\n tenant_id,\n type,\n user_id\n from public.token\n on conflict (id) do nothing;\n `.execute(controlPlaneBootstrapKysely);\n\n const content = await pglite.dumpDataDir('none');\n await kysely.destroy();\n return content;\n}\n\nexport async function useLocalKysely(\n args: UseLocalKyselyOptions = {}\n): Promise<LocalKysely> {\n let isDestroyed = false;\n\n preparedDumpPromise ??= resolvePreparedDump();\n const pglite = new PGlite({\n loadDataDir: await preparedDumpPromise,\n });\n // The constructor is lazy. The bootstrap this replaced forced the snapshot to\n // load before returning, so keep that contract: a returned handle is usable,\n // and a bad snapshot fails here instead of inside an unrelated test.\n await pglite.waitReady;\n const dialect = new PGliteDialect(pglite);\n const kysely = getKysely<DB>(dialect, {\n repositoryName: args?.repositoryName ?? 'localTesting',\n log: args?.log,\n });\n\n const controlPlaneKysely = withControlPlaneSchema(\n getKysely<ControlPlaneDB>(dialect, {\n repositoryName: `${args.repositoryName ?? 'localTesting'}/control-plane`,\n log: args.log,\n }),\n 'control_plane',\n 'control_plane'\n );\n controlPlaneKysely.destroy = async () => {};\n Object.defineProperty(kysely, 'controlPlaneKysely', {\n value: controlPlaneKysely,\n });\n\n const originalDestroy = kysely.destroy.bind(kysely);\n kysely.destroy = async () => {\n if (isDestroyed) return;\n isDestroyed = true;\n //const destroyTime = performance.now();\n await originalDestroy();\n //console.log(\n // `Database destroyed: ${Math.round(performance.now() - destroyTime)}ms`\n //);\n };\n\n // console.log(`Database initialized: ${Math.round(performance.now() - now)}ms`);\n\n return kysely as LocalKysely;\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Matches PostgreSQL errors from both production's postgres.js driver and
3
- * PGlite, which expose constraint names under different property names.
2
+ * Matches PostgreSQL errors by code and, when requested, constraint. Production
3
+ * postgres.js and PGlite expose constraint names under different properties.
4
4
  */
5
5
  export declare function isPostgresError(error: unknown, expected: {
6
6
  code: string;
7
- constraint: string;
7
+ constraint?: string;
8
8
  }): boolean;
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Matches PostgreSQL errors from both production's postgres.js driver and
3
- * PGlite, which expose constraint names under different property names.
2
+ * Matches PostgreSQL errors by code and, when requested, constraint. Production
3
+ * postgres.js and PGlite expose constraint names under different properties.
4
4
  */
5
5
  export function isPostgresError(error, expected) {
6
6
  if (!(error instanceof Error) ||
@@ -13,6 +13,7 @@ export function isPostgresError(error, expected) {
13
13
  : 'constraint_name' in error && typeof error.constraint_name === 'string'
14
14
  ? error.constraint_name
15
15
  : undefined;
16
- return error.code === expected.code && constraint === expected.constraint;
16
+ return (error.code === expected.code &&
17
+ (expected.constraint === undefined || constraint === expected.constraint));
17
18
  }
18
19
  //# sourceMappingURL=postgres-error.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-error.js","sourceRoot":"src/","sources":["postgres-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAc,EACd,QAA8C;IAE9C,IACE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QACzB,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAClB,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC9B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GACd,YAAY,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;QAC3D,CAAC,CAAC,KAAK,CAAC,UAAU;QAClB,CAAC,CAAC,iBAAiB,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,eAAe,KAAK,QAAQ;YACvE,CAAC,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,SAAS,CAAC;IAElB,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC;AAC5E,CAAC","sourcesContent":["/**\n * Matches PostgreSQL errors from both production's postgres.js driver and\n * PGlite, which expose constraint names under different property names.\n */\nexport function isPostgresError(\n error: unknown,\n expected: { code: string; constraint: string }\n): boolean {\n if (\n !(error instanceof Error) ||\n !('code' in error) ||\n typeof error.code !== 'string'\n ) {\n return false;\n }\n\n const constraint =\n 'constraint' in error && typeof error.constraint === 'string'\n ? error.constraint\n : 'constraint_name' in error && typeof error.constraint_name === 'string'\n ? error.constraint_name\n : undefined;\n\n return error.code === expected.code && constraint === expected.constraint;\n}\n"]}
1
+ {"version":3,"file":"postgres-error.js","sourceRoot":"src/","sources":["postgres-error.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAc,EACd,QAA+C;IAE/C,IACE,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;QACzB,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC;QAClB,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,EAC9B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,UAAU,GACd,YAAY,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;QAC3D,CAAC,CAAC,KAAK,CAAC,UAAU;QAClB,CAAC,CAAC,iBAAiB,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,eAAe,KAAK,QAAQ;YACvE,CAAC,CAAC,KAAK,CAAC,eAAe;YACvB,CAAC,CAAC,SAAS,CAAC;IAElB,OAAO,CACL,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI;QAC5B,CAAC,QAAQ,CAAC,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC,CAC1E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Matches PostgreSQL errors by code and, when requested, constraint. Production\n * postgres.js and PGlite expose constraint names under different properties.\n */\nexport function isPostgresError(\n error: unknown,\n expected: { code: string; constraint?: string }\n): boolean {\n if (\n !(error instanceof Error) ||\n !('code' in error) ||\n typeof error.code !== 'string'\n ) {\n return false;\n }\n\n const constraint =\n 'constraint' in error && typeof error.constraint === 'string'\n ? error.constraint\n : 'constraint_name' in error && typeof error.constraint_name === 'string'\n ? error.constraint_name\n : undefined;\n\n return (\n error.code === expected.code &&\n (expected.constraint === undefined || constraint === expected.constraint)\n );\n}\n"]}
@@ -5,7 +5,7 @@ type TenantExpressionBuilder = ExpressionBuilder<DB & {
5
5
  export declare function whereTenantActive(eb: TenantExpressionBuilder): import("kysely").ExpressionWrapper<DB & {
6
6
  tenant: PublicTenant;
7
7
  }, "tenant", SqlBool>;
8
- export declare function whereTenantNotTestTeam(testTeamId: string): import("kysely").RawBuilder<SqlBool>;
8
+ export declare function whereTenantNotTestTeam(testTeamId?: string): import("kysely").RawBuilder<SqlBool>;
9
9
  export declare function whereTenantShouldBePaid(eb: TenantExpressionBuilder, { includePartnerTeams, }?: {
10
10
  includePartnerTeams?: boolean;
11
11
  }): import("kysely").ExpressionWrapper<DB & {
@@ -1,3 +1,4 @@
1
+ import { TEST_TEAM_IDS } from '@vrplatform/api';
1
2
  import { sql, } from '../../isomorphic';
2
3
  import { getCalculatedBillingPartnerId } from './billing-partner';
3
4
  export function whereTenantActive(eb) {
@@ -10,9 +11,10 @@ export function whereTenantActive(eb) {
10
11
  .where('billingPartner.calculatedStatus', 'is distinct from', 'active'))),
11
12
  ]);
12
13
  }
13
- export function whereTenantNotTestTeam(testTeamId) {
14
+ export function whereTenantNotTestTeam(testTeamId = TEST_TEAM_IDS[0]) {
14
15
  return sql `(
15
- ${sql.ref('tenant.id')} != ${testTeamId}
16
+ ${sql.ref('tenant.id')} not in (${sql.join(TEST_TEAM_IDS)})
17
+ and ${sql.ref('tenant.id')} != ${testTeamId}
16
18
  and (
17
19
  ${sql.ref('tenant.partnerId')} is null
18
20
  or ${sql.ref('tenant.partnerId')} != ${testTeamId}
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"src/","sources":["query/tenant/status.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,GAKJ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAOlE,MAAM,UAAU,iBAAiB,CAAC,EAA2B;IAC3D,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE,QAAQ,CAAC;QAC5C,EAAE,CAAC,GAAG,CACJ,EAAE,CAAC,MAAM,CACP,EAAE;aACC,UAAU,CAAC,iCAAiC,CAAC;aAC7C,MAAM,CAAC,mBAAmB,CAAC;aAC3B,KAAK,CACJ,mBAAmB,EACnB,GAAG,EACH,EAAE,CAAC,EAAE,CAAC,QAAQ,CACZ,mCAAmC,EACnC,EAAE,CAAC,GAAG,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAC,CAC1C,CACF;aACA,KAAK,CACJ,iCAAiC,EACjC,kBAAkB,EAClB,QAAQ,CACT,CACJ,CACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACvD,OAAO,GAAG,CAAS;MACf,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,UAAU;;QAEnC,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC;WACxB,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,OAAO,UAAU;;;QAG/C,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC;WAC/B,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC,OAAO,UAAU;;IAE1D,CAAC;AACL,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,EAA2B,EAC3B,EACE,mBAAmB,GAAG,KAAK,MAGzB,EAAE;IAEN,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,iBAAiB,CAAC,EAAE,CAAC;QACrB,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE,KAAK,CAAC;QACrC,GAAG,CAAC,mBAAmB;YACrB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;KACpD,CAAC,CAAC;AACL,CAAC","sourcesContent":["import {\n sql,\n type DB,\n type ExpressionBuilder,\n type PublicTenant,\n type SqlBool,\n} from '../../isomorphic';\nimport { getCalculatedBillingPartnerId } from './billing-partner';\n\ntype TenantExpressionBuilder = ExpressionBuilder<\n DB & { tenant: PublicTenant },\n 'tenant'\n>;\n\nexport function whereTenantActive(eb: TenantExpressionBuilder) {\n return eb.and([\n eb('tenant.calculatedStatus', '=', 'active'),\n eb.not(\n eb.exists(\n eb\n .selectFrom('public.tenant as billingPartner')\n .select('billingPartner.id')\n .where(\n 'billingPartner.id',\n '=',\n eb.fn.coalesce(\n 'tenant.calculatedBillingPartnerId',\n eb.val(getCalculatedBillingPartnerId({}))\n )\n )\n .where(\n 'billingPartner.calculatedStatus',\n 'is distinct from',\n 'active'\n )\n )\n ),\n ]);\n}\n\nexport function whereTenantNotTestTeam(testTeamId: string) {\n return sql<SqlBool>`(\n ${sql.ref('tenant.id')} != ${testTeamId}\n and (\n ${sql.ref('tenant.partnerId')} is null\n or ${sql.ref('tenant.partnerId')} != ${testTeamId}\n )\n and (\n ${sql.ref('tenant.billingPartnerId')} is null\n or ${sql.ref('tenant.billingPartnerId')} != ${testTeamId}\n )\n )`;\n}\n\nexport function whereTenantShouldBePaid(\n eb: TenantExpressionBuilder,\n {\n includePartnerTeams = false,\n }: {\n includePartnerTeams?: boolean;\n } = {}\n) {\n return eb.and([\n whereTenantActive(eb),\n eb('tenant.isOnboarding', '=', false),\n ...(includePartnerTeams\n ? []\n : [eb('tenant.calculatedIsBillable', '=', true)]),\n ]);\n}\n"]}
1
+ {"version":3,"file":"status.js","sourceRoot":"src/","sources":["query/tenant/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EACL,GAAG,GAKJ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAOlE,MAAM,UAAU,iBAAiB,CAAC,EAA2B;IAC3D,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE,QAAQ,CAAC;QAC5C,EAAE,CAAC,GAAG,CACJ,EAAE,CAAC,MAAM,CACP,EAAE;aACC,UAAU,CAAC,iCAAiC,CAAC;aAC7C,MAAM,CAAC,mBAAmB,CAAC;aAC3B,KAAK,CACJ,mBAAmB,EACnB,GAAG,EACH,EAAE,CAAC,EAAE,CAAC,QAAQ,CACZ,mCAAmC,EACnC,EAAE,CAAC,GAAG,CAAC,6BAA6B,CAAC,EAAE,CAAC,CAAC,CAC1C,CACF;aACA,KAAK,CACJ,iCAAiC,EACjC,kBAAkB,EAClB,QAAQ,CACT,CACJ,CACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,aAAqB,aAAa,CAAC,CAAC,CAAC;IAC1E,OAAO,GAAG,CAAS;MACf,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC;UACnD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,UAAU;;QAEvC,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC;WACxB,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,OAAO,UAAU;;;QAG/C,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC;WAC/B,GAAG,CAAC,GAAG,CAAC,yBAAyB,CAAC,OAAO,UAAU;;IAE1D,CAAC;AACL,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,EAA2B,EAC3B,EACE,mBAAmB,GAAG,KAAK,MAGzB,EAAE;IAEN,OAAO,EAAE,CAAC,GAAG,CAAC;QACZ,iBAAiB,CAAC,EAAE,CAAC;QACrB,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE,KAAK,CAAC;QACrC,GAAG,CAAC,mBAAmB;YACrB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,CAAC,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;KACpD,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { TEST_TEAM_IDS } from '@vrplatform/api';\nimport {\n sql,\n type DB,\n type ExpressionBuilder,\n type PublicTenant,\n type SqlBool,\n} from '../../isomorphic';\nimport { getCalculatedBillingPartnerId } from './billing-partner';\n\ntype TenantExpressionBuilder = ExpressionBuilder<\n DB & { tenant: PublicTenant },\n 'tenant'\n>;\n\nexport function whereTenantActive(eb: TenantExpressionBuilder) {\n return eb.and([\n eb('tenant.calculatedStatus', '=', 'active'),\n eb.not(\n eb.exists(\n eb\n .selectFrom('public.tenant as billingPartner')\n .select('billingPartner.id')\n .where(\n 'billingPartner.id',\n '=',\n eb.fn.coalesce(\n 'tenant.calculatedBillingPartnerId',\n eb.val(getCalculatedBillingPartnerId({}))\n )\n )\n .where(\n 'billingPartner.calculatedStatus',\n 'is distinct from',\n 'active'\n )\n )\n ),\n ]);\n}\n\nexport function whereTenantNotTestTeam(testTeamId: string = TEST_TEAM_IDS[0]) {\n return sql<SqlBool>`(\n ${sql.ref('tenant.id')} not in (${sql.join(TEST_TEAM_IDS)})\n and ${sql.ref('tenant.id')} != ${testTeamId}\n and (\n ${sql.ref('tenant.partnerId')} is null\n or ${sql.ref('tenant.partnerId')} != ${testTeamId}\n )\n and (\n ${sql.ref('tenant.billingPartnerId')} is null\n or ${sql.ref('tenant.billingPartnerId')} != ${testTeamId}\n )\n )`;\n}\n\nexport function whereTenantShouldBePaid(\n eb: TenantExpressionBuilder,\n {\n includePartnerTeams = false,\n }: {\n includePartnerTeams?: boolean;\n } = {}\n) {\n return eb.and([\n whereTenantActive(eb),\n eb('tenant.isOnboarding', '=', false),\n ...(includePartnerTeams\n ? []\n : [eb('tenant.calculatedIsBillable', '=', true)]),\n ]);\n}\n"]}
@@ -7,6 +7,7 @@ export declare function resolveTenantDatabaseUrl(dataRegion: TenantDataRegion, e
7
7
  export declare function resolveTenantApiBaseUrl(dataRegion: TenantDataRegion, env: RegionEnvironment): string;
8
8
  export declare function resolveConfiguredTenantDataRegions(env: RegionEnvironment): ("ap" | "crunchy" | "eu" | "hostaway" | "us")[];
9
9
  type UseAsyncRegionalKysely = (connectionString: string, options?: {
10
+ dataRegion?: TenantDataRegion;
10
11
  repositoryName?: string;
11
12
  probe?: true;
12
13
  }) => Promise<Kysely>;
@@ -81,6 +81,7 @@ async function defaultUseAsyncControlPlaneKysely(connectionString, options) {
81
81
  export async function useTenantDatabaseKysely(dataRegion, repositoryName, env, options = {}) {
82
82
  const open = options.useAsyncKysely ?? defaultUseAsyncKysely;
83
83
  return await open(resolveTenantDatabaseUrl(dataRegion, env), {
84
+ dataRegion,
84
85
  repositoryName,
85
86
  probe: options.probe,
86
87
  });
@@ -1 +1 @@
1
- {"version":3,"file":"regional.js","sourceRoot":"src/","sources":["regional.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ControlPlaneKysely, Kysely } from './index';\nimport { KyselyError } from './error';\n\n// The tenant-data-region contract lives here — the region set plus how a\n// region resolves to its database url, API base url, and how to fan a job out\n// across regions. Every runtime resolves regions through these functions;\n// @vrplatform/common re-exports them for app-facing imports, and the trigger\n// runtime wraps them only to default `env` to process.env.\n//\n// Region → resource keys are derived from the region name\n// (`DATABASE_URL_VRTRUST_<REGION>`, `API_BASE_URL_<REGION>`), so adding a\n// region means extending this array plus its env values and migrations — not\n// editing per-region branches.\nexport const tenantDataRegions = [\n 'ap',\n 'crunchy',\n 'eu',\n 'hostaway',\n 'us',\n] as const;\nexport type TenantDataRegion = (typeof tenantDataRegions)[number];\n\nexport type RegionEnvironment = Record<string, string | undefined>;\n\nexport function assertTenantDataRegion(\n value: string | undefined\n): asserts value is TenantDataRegion {\n if (!tenantDataRegions.includes(value as TenantDataRegion)) {\n throw new KyselyError('Missing or invalid tenant data region', { value });\n }\n}\n\nfunction databaseUrlEnvKey(dataRegion: TenantDataRegion) {\n return `DATABASE_URL_VRTRUST_${dataRegion.toUpperCase()}`;\n}\n\nfunction apiBaseUrlEnvKey(dataRegion: TenantDataRegion) {\n return `API_BASE_URL_${dataRegion.toUpperCase()}`;\n}\n\nexport function resolveTenantDatabaseUrl(\n dataRegion: TenantDataRegion,\n env: RegionEnvironment\n) {\n const databaseUrl = env[databaseUrlEnvKey(dataRegion)];\n if (!databaseUrl) {\n throw new KyselyError('Tenant database URL is missing', {\n dataRegion,\n key: databaseUrlEnvKey(dataRegion),\n });\n }\n return databaseUrl;\n}\n\nexport function resolveTenantApiBaseUrl(\n dataRegion: TenantDataRegion,\n env: RegionEnvironment\n) {\n const key = apiBaseUrlEnvKey(dataRegion);\n const baseUrl = env[key];\n if (!baseUrl) {\n throw new KyselyError('Tenant API base URL is missing', {\n dataRegion,\n key,\n });\n }\n return baseUrl;\n}\n\nexport function resolveConfiguredTenantDataRegions(env: RegionEnvironment) {\n const configured = tenantDataRegions.filter((region) =>\n Boolean(env[databaseUrlEnvKey(region)])\n );\n\n if (!configured.length) {\n throw new Error('No tenant data region database urls configured');\n }\n\n // Two regions sharing one URL means every region-fan-out job scans the\n // same physical DB twice (e.g. a stale dashboard env var re-merged into a\n // deploy). Regions must stay unset until they point at distinct databases.\n const regionsByUrl = new Map<string, TenantDataRegion[]>();\n for (const region of configured) {\n const url = env[databaseUrlEnvKey(region)]!;\n regionsByUrl.set(url, [...(regionsByUrl.get(url) ?? []), region]);\n }\n for (const aliased of regionsByUrl.values()) {\n if (aliased.length > 1) {\n throw new KyselyError('Tenant data regions share the same database URL', {\n dataRegions: aliased,\n });\n }\n }\n\n return configured;\n}\n\ntype UseAsyncRegionalKysely = (\n connectionString: string,\n options?: {\n repositoryName?: string;\n probe?: true;\n }\n) => Promise<Kysely>;\n\ntype UseAsyncControlPlaneKysely = (\n connectionString: string,\n options?: {\n repositoryName?: string;\n probe?: true;\n }\n) => Promise<ControlPlaneKysely>;\n\nasync function defaultUseAsyncKysely(\n connectionString: string,\n options?: Parameters<UseAsyncRegionalKysely>[1]\n) {\n const { useAsyncKysely } = await import('./index');\n return await useAsyncKysely(connectionString, options);\n}\n\nasync function defaultUseAsyncControlPlaneKysely(\n connectionString: string,\n options?: Parameters<UseAsyncControlPlaneKysely>[1]\n) {\n const { useAsyncControlPlaneKysely } = await import('./index');\n return await useAsyncControlPlaneKysely(connectionString, options);\n}\n\nexport async function useTenantDatabaseKysely(\n dataRegion: TenantDataRegion,\n repositoryName: string,\n env: RegionEnvironment,\n options: { useAsyncKysely?: UseAsyncRegionalKysely; probe?: true } = {}\n) {\n const open = options.useAsyncKysely ?? defaultUseAsyncKysely;\n return await open(resolveTenantDatabaseUrl(dataRegion, env), {\n repositoryName,\n probe: options.probe,\n });\n}\n\nexport type TenantKysely = {\n id: string;\n slug: string;\n dataRegion: TenantDataRegion;\n kysely: Kysely;\n};\n\n/**\n * Resolve a tenant's regional Kysely connection from a connection id, slug,\n * or tenant id. The control-plane connection is opened and destroyed\n * internally; the returned regional connection must be destroyed by the\n * caller.\n */\nexport async function resolveTenantKysely({\n connectionId,\n slug,\n tenantId,\n repositoryName,\n env,\n ...options\n}: {\n connectionId?: string;\n slug?: string;\n tenantId?: string;\n repositoryName: string;\n env: RegionEnvironment;\n useAsyncControlPlaneKysely?: UseAsyncControlPlaneKysely;\n useAsyncKysely?: UseAsyncRegionalKysely;\n probe?: true;\n}): Promise<TenantKysely> {\n if (!connectionId && !slug && !tenantId) {\n throw new Error('connectionId, slug, or tenantId is required');\n }\n\n const controlPlaneDatabaseUrl = env.CONTROL_PLANE_DATABASE_URL;\n if (!controlPlaneDatabaseUrl)\n throw new Error('CONTROL_PLANE_DATABASE_URL is required');\n\n const controlPlane = await (\n options.useAsyncControlPlaneKysely ?? defaultUseAsyncControlPlaneKysely\n )(controlPlaneDatabaseUrl, {\n repositoryName: `${repositoryName}/control-plane`,\n probe: options.probe,\n });\n let tenant: { id: string; slug: string; dataRegion: string } | undefined;\n try {\n const query = controlPlane\n .selectFrom('controlPlane.tenant as tenant')\n .select([\n 'tenant.id as id',\n 'tenant.slug as slug',\n 'tenant.dataRegion as dataRegion',\n ]);\n tenant = await (\n slug\n ? query.where('tenant.slug', '=', slug)\n : connectionId\n ? query\n .innerJoin(\n 'controlPlane.connectionRoute as connectionRoute',\n 'connectionRoute.tenantId',\n 'tenant.id'\n )\n .where('connectionRoute.connectionId', '=', connectionId)\n : query.where('tenant.id', '=', tenantId!)\n ).executeTakeFirst();\n } finally {\n await controlPlane.destroy();\n }\n if (!tenant) throw new Error('Tenant not found in control plane');\n\n assertTenantDataRegion(tenant.dataRegion);\n return {\n id: tenant.id,\n slug: tenant.slug,\n dataRegion: tenant.dataRegion,\n kysely: await useTenantDatabaseKysely(\n tenant.dataRegion,\n repositoryName,\n env,\n options\n ),\n };\n}\n\nexport type RegionRunError = {\n dataRegion: TenantDataRegion;\n error: string;\n};\n\n/**\n * Run a job once per configured tenant data region with per-region error\n * isolation: one region's outage must not stall the others (regions iterate\n * in a fixed order, so a failing first region would otherwise starve the\n * rest on every attempt). Throws only when every configured region failed.\n */\nexport async function forEachConfiguredRegion<T>(\n run: (dataRegion: TenantDataRegion) => Promise<T>,\n env: RegionEnvironment\n): Promise<{\n results: { dataRegion: TenantDataRegion; result: T }[];\n errors: RegionRunError[];\n}> {\n const results: { dataRegion: TenantDataRegion; result: T }[] = [];\n const errors: RegionRunError[] = [];\n\n for (const dataRegion of resolveConfiguredTenantDataRegions(env)) {\n try {\n results.push({ dataRegion, result: await run(dataRegion) });\n } catch (error) {\n errors.push({\n dataRegion,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n if (!results.length && errors.length) {\n throw new KyselyError('All configured tenant data regions failed', {\n errors,\n });\n }\n\n return { results, errors };\n}\n"]}
1
+ {"version":3,"file":"regional.js","sourceRoot":"src/","sources":["regional.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ControlPlaneKysely, Kysely } from './index';\nimport { KyselyError } from './error';\n\n// The tenant-data-region contract lives here — the region set plus how a\n// region resolves to its database url, API base url, and how to fan a job out\n// across regions. Every runtime resolves regions through these functions;\n// @vrplatform/common re-exports them for app-facing imports, and the trigger\n// runtime wraps them only to default `env` to process.env.\n//\n// Region → resource keys are derived from the region name\n// (`DATABASE_URL_VRTRUST_<REGION>`, `API_BASE_URL_<REGION>`), so adding a\n// region means extending this array plus its env values and migrations — not\n// editing per-region branches.\nexport const tenantDataRegions = [\n 'ap',\n 'crunchy',\n 'eu',\n 'hostaway',\n 'us',\n] as const;\nexport type TenantDataRegion = (typeof tenantDataRegions)[number];\n\nexport type RegionEnvironment = Record<string, string | undefined>;\n\nexport function assertTenantDataRegion(\n value: string | undefined\n): asserts value is TenantDataRegion {\n if (!tenantDataRegions.includes(value as TenantDataRegion)) {\n throw new KyselyError('Missing or invalid tenant data region', { value });\n }\n}\n\nfunction databaseUrlEnvKey(dataRegion: TenantDataRegion) {\n return `DATABASE_URL_VRTRUST_${dataRegion.toUpperCase()}`;\n}\n\nfunction apiBaseUrlEnvKey(dataRegion: TenantDataRegion) {\n return `API_BASE_URL_${dataRegion.toUpperCase()}`;\n}\n\nexport function resolveTenantDatabaseUrl(\n dataRegion: TenantDataRegion,\n env: RegionEnvironment\n) {\n const databaseUrl = env[databaseUrlEnvKey(dataRegion)];\n if (!databaseUrl) {\n throw new KyselyError('Tenant database URL is missing', {\n dataRegion,\n key: databaseUrlEnvKey(dataRegion),\n });\n }\n return databaseUrl;\n}\n\nexport function resolveTenantApiBaseUrl(\n dataRegion: TenantDataRegion,\n env: RegionEnvironment\n) {\n const key = apiBaseUrlEnvKey(dataRegion);\n const baseUrl = env[key];\n if (!baseUrl) {\n throw new KyselyError('Tenant API base URL is missing', {\n dataRegion,\n key,\n });\n }\n return baseUrl;\n}\n\nexport function resolveConfiguredTenantDataRegions(env: RegionEnvironment) {\n const configured = tenantDataRegions.filter((region) =>\n Boolean(env[databaseUrlEnvKey(region)])\n );\n\n if (!configured.length) {\n throw new Error('No tenant data region database urls configured');\n }\n\n // Two regions sharing one URL means every region-fan-out job scans the\n // same physical DB twice (e.g. a stale dashboard env var re-merged into a\n // deploy). Regions must stay unset until they point at distinct databases.\n const regionsByUrl = new Map<string, TenantDataRegion[]>();\n for (const region of configured) {\n const url = env[databaseUrlEnvKey(region)]!;\n regionsByUrl.set(url, [...(regionsByUrl.get(url) ?? []), region]);\n }\n for (const aliased of regionsByUrl.values()) {\n if (aliased.length > 1) {\n throw new KyselyError('Tenant data regions share the same database URL', {\n dataRegions: aliased,\n });\n }\n }\n\n return configured;\n}\n\ntype UseAsyncRegionalKysely = (\n connectionString: string,\n options?: {\n dataRegion?: TenantDataRegion;\n repositoryName?: string;\n probe?: true;\n }\n) => Promise<Kysely>;\n\ntype UseAsyncControlPlaneKysely = (\n connectionString: string,\n options?: {\n repositoryName?: string;\n probe?: true;\n }\n) => Promise<ControlPlaneKysely>;\n\nasync function defaultUseAsyncKysely(\n connectionString: string,\n options?: Parameters<UseAsyncRegionalKysely>[1]\n) {\n const { useAsyncKysely } = await import('./index');\n return await useAsyncKysely(connectionString, options);\n}\n\nasync function defaultUseAsyncControlPlaneKysely(\n connectionString: string,\n options?: Parameters<UseAsyncControlPlaneKysely>[1]\n) {\n const { useAsyncControlPlaneKysely } = await import('./index');\n return await useAsyncControlPlaneKysely(connectionString, options);\n}\n\nexport async function useTenantDatabaseKysely(\n dataRegion: TenantDataRegion,\n repositoryName: string,\n env: RegionEnvironment,\n options: { useAsyncKysely?: UseAsyncRegionalKysely; probe?: true } = {}\n) {\n const open = options.useAsyncKysely ?? defaultUseAsyncKysely;\n return await open(resolveTenantDatabaseUrl(dataRegion, env), {\n dataRegion,\n repositoryName,\n probe: options.probe,\n });\n}\n\nexport type TenantKysely = {\n id: string;\n slug: string;\n dataRegion: TenantDataRegion;\n kysely: Kysely;\n};\n\n/**\n * Resolve a tenant's regional Kysely connection from a connection id, slug,\n * or tenant id. The control-plane connection is opened and destroyed\n * internally; the returned regional connection must be destroyed by the\n * caller.\n */\nexport async function resolveTenantKysely({\n connectionId,\n slug,\n tenantId,\n repositoryName,\n env,\n ...options\n}: {\n connectionId?: string;\n slug?: string;\n tenantId?: string;\n repositoryName: string;\n env: RegionEnvironment;\n useAsyncControlPlaneKysely?: UseAsyncControlPlaneKysely;\n useAsyncKysely?: UseAsyncRegionalKysely;\n probe?: true;\n}): Promise<TenantKysely> {\n if (!connectionId && !slug && !tenantId) {\n throw new Error('connectionId, slug, or tenantId is required');\n }\n\n const controlPlaneDatabaseUrl = env.CONTROL_PLANE_DATABASE_URL;\n if (!controlPlaneDatabaseUrl)\n throw new Error('CONTROL_PLANE_DATABASE_URL is required');\n\n const controlPlane = await (\n options.useAsyncControlPlaneKysely ?? defaultUseAsyncControlPlaneKysely\n )(controlPlaneDatabaseUrl, {\n repositoryName: `${repositoryName}/control-plane`,\n probe: options.probe,\n });\n let tenant: { id: string; slug: string; dataRegion: string } | undefined;\n try {\n const query = controlPlane\n .selectFrom('controlPlane.tenant as tenant')\n .select([\n 'tenant.id as id',\n 'tenant.slug as slug',\n 'tenant.dataRegion as dataRegion',\n ]);\n tenant = await (\n slug\n ? query.where('tenant.slug', '=', slug)\n : connectionId\n ? query\n .innerJoin(\n 'controlPlane.connectionRoute as connectionRoute',\n 'connectionRoute.tenantId',\n 'tenant.id'\n )\n .where('connectionRoute.connectionId', '=', connectionId)\n : query.where('tenant.id', '=', tenantId!)\n ).executeTakeFirst();\n } finally {\n await controlPlane.destroy();\n }\n if (!tenant) throw new Error('Tenant not found in control plane');\n\n assertTenantDataRegion(tenant.dataRegion);\n return {\n id: tenant.id,\n slug: tenant.slug,\n dataRegion: tenant.dataRegion,\n kysely: await useTenantDatabaseKysely(\n tenant.dataRegion,\n repositoryName,\n env,\n options\n ),\n };\n}\n\nexport type RegionRunError = {\n dataRegion: TenantDataRegion;\n error: string;\n};\n\n/**\n * Run a job once per configured tenant data region with per-region error\n * isolation: one region's outage must not stall the others (regions iterate\n * in a fixed order, so a failing first region would otherwise starve the\n * rest on every attempt). Throws only when every configured region failed.\n */\nexport async function forEachConfiguredRegion<T>(\n run: (dataRegion: TenantDataRegion) => Promise<T>,\n env: RegionEnvironment\n): Promise<{\n results: { dataRegion: TenantDataRegion; result: T }[];\n errors: RegionRunError[];\n}> {\n const results: { dataRegion: TenantDataRegion; result: T }[] = [];\n const errors: RegionRunError[] = [];\n\n for (const dataRegion of resolveConfiguredTenantDataRegions(env)) {\n try {\n results.push({ dataRegion, result: await run(dataRegion) });\n } catch (error) {\n errors.push({\n dataRegion,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n if (!results.length && errors.length) {\n throw new KyselyError('All configured tenant data regions failed', {\n errors,\n });\n }\n\n return { results, errors };\n}\n"]}
@@ -1,10 +1,30 @@
1
1
  import { type LocalKysely, type UseLocalKyselyOptions } from '../local';
2
+ type UseTestKyselyOptions = UseLocalKyselyOptions & {
3
+ /**
4
+ * Force a private database even when the worker shares one. Required by specs
5
+ * that model several regions at once, where two handles must be two distinct
6
+ * physical databases.
7
+ */
8
+ isolated?: boolean;
9
+ };
2
10
  declare global {
3
- var __vrplatform_testSharedKysely: Promise<LocalKysely> | LocalKysely | undefined;
11
+ var __vrplatformSharedKysely: Promise<LocalKysely> | undefined;
4
12
  }
5
- export declare function useTestAutoCleanupKysely(args?: UseLocalKyselyOptions): Promise<LocalKysely>;
13
+ /**
14
+ * Locally each worker process shares one database, because specs isolate by
15
+ * tenant and the faster suite is what makes running it before a push realistic.
16
+ *
17
+ * CI keeps a database per spec file. It retries nothing, so a badly scoped spec
18
+ * would surface there as a red build in an unrelated file rather than as a
19
+ * local failure the author can reproduce.
20
+ *
21
+ * VR_TEST_SHARED_KYSELY overrides either way; `isolated: true` opts a single
22
+ * call out when a spec genuinely needs two physical databases at once.
23
+ */
24
+ export declare function resolveSharedKysely(env: Record<string, string | undefined>): boolean;
25
+ export declare function useTestAutoCleanupKysely(args?: UseTestKyselyOptions): Promise<LocalKysely>;
6
26
  type ReturnType = LocalKysely & {
7
27
  [Symbol.dispose]: () => Promise<void>;
8
28
  };
9
- export declare function useLocalKyselyResource(args?: UseLocalKyselyOptions): Promise<ReturnType>;
29
+ export declare function useLocalKyselyResource(args?: UseTestKyselyOptions): Promise<ReturnType>;
10
30
  export {};
@@ -1,28 +1,49 @@
1
1
  import { afterAll } from 'bun:test';
2
2
  import { useLocalKysely, } from '../local';
3
+ /**
4
+ * Locally each worker process shares one database, because specs isolate by
5
+ * tenant and the faster suite is what makes running it before a push realistic.
6
+ *
7
+ * CI keeps a database per spec file. It retries nothing, so a badly scoped spec
8
+ * would surface there as a red build in an unrelated file rather than as a
9
+ * local failure the author can reproduce.
10
+ *
11
+ * VR_TEST_SHARED_KYSELY overrides either way; `isolated: true` opts a single
12
+ * call out when a spec genuinely needs two physical databases at once.
13
+ */
14
+ export function resolveSharedKysely(env) {
15
+ if (env.VR_TEST_SHARED_KYSELY)
16
+ return env.VR_TEST_SHARED_KYSELY !== '0';
17
+ return !(env.CI || env.GITHUB_ACTIONS);
18
+ }
19
+ const sharedKysely = resolveSharedKysely(process.env);
20
+ function acquireKysely(args) {
21
+ if (!sharedKysely || args?.isolated)
22
+ return useLocalKysely(args);
23
+ globalThis.__vrplatformSharedKysely ??= useLocalKysely(args);
24
+ return globalThis.__vrplatformSharedKysely;
25
+ }
26
+ function ownsKysely(args) {
27
+ return !sharedKysely || Boolean(args?.isolated);
28
+ }
3
29
  export async function useTestAutoCleanupKysely(args) {
4
- if (process.env.VR_TEST_SHARED_KYSELY === '1') {
5
- const existing = globalThis.__vrplatform_testSharedKysely;
6
- if (!existing) {
7
- const created = useLocalKysely(args);
8
- globalThis.__vrplatform_testSharedKysely = created;
9
- return created;
10
- }
11
- return await existing;
30
+ const kysely = await acquireKysely(args);
31
+ if (ownsKysely(args)) {
32
+ afterAll(async () => {
33
+ await kysely.destroy();
34
+ });
12
35
  }
13
- const kysely = await useLocalKysely(args);
14
- afterAll(async () => {
15
- await kysely.destroy();
16
- });
17
36
  return kysely;
18
37
  }
19
38
  export async function useLocalKyselyResource(args) {
20
- const kysely = await useLocalKysely(args);
39
+ const kysely = await acquireKysely(args);
21
40
  return new Proxy({}, {
22
41
  get(_target, prop) {
23
42
  // somehow not working if using Symbol.dispose
24
43
  if (prop === Symbol.asyncDispose) {
25
44
  return async () => {
45
+ if (!ownsKysely(args))
46
+ return;
26
47
  await kysely.destroy();
27
48
  };
28
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"src/","sources":["test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAGL,cAAc,GACf,MAAM,UAAU,CAAC;AAWlB,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAA4B;IACzE,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,GAAG,EAAE,CAAC;QAC9C,MAAM,QAAQ,GAAG,UAAU,CAAC,6BAA6B,CAAC;QAC1D,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;YACrC,UAAU,CAAC,6BAA6B,GAAG,OAAO,CAAC;YACnD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,MAAM,QAAQ,CAAC;IACxB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAE1C,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAA4B;IAE5B,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAE1C,OAAO,IAAI,KAAK,CACd,EAAE,EACF;QACE,GAAG,CAAC,OAAO,EAAE,IAAI;YACf,8CAA8C;YAC9C,IAAI,IAAI,KAAK,MAAM,CAAC,YAAY,EAAE,CAAC;gBACjC,OAAO,KAAK,IAAI,EAAE;oBAChB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;gBACzB,CAAC,CAAC;YACJ,CAAC;YACD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtB,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;gBACvC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;KACF,CACY,CAAC;AAClB,CAAC","sourcesContent":["import { afterAll } from 'bun:test';\nimport {\n type LocalKysely,\n type UseLocalKyselyOptions,\n useLocalKysely,\n} from '../local';\n\ndeclare global {\n // Shared Kysely instance for test processes that run multiple files in one Bun process.\n // This must be process-global to allow cross-file reuse.\n var __vrplatform_testSharedKysely:\n | Promise<LocalKysely>\n | LocalKysely\n | undefined;\n}\n\nexport async function useTestAutoCleanupKysely(args?: UseLocalKyselyOptions) {\n if (process.env.VR_TEST_SHARED_KYSELY === '1') {\n const existing = globalThis.__vrplatform_testSharedKysely;\n if (!existing) {\n const created = useLocalKysely(args);\n globalThis.__vrplatform_testSharedKysely = created;\n return created;\n }\n return await existing;\n }\n\n const kysely = await useLocalKysely(args);\n\n afterAll(async () => {\n await kysely.destroy();\n });\n\n return kysely;\n}\n\ntype ReturnType = LocalKysely & {\n [Symbol.dispose]: () => Promise<void>;\n};\n\nexport async function useLocalKyselyResource(\n args?: UseLocalKyselyOptions\n): Promise<ReturnType> {\n const kysely = await useLocalKysely(args);\n\n return new Proxy(\n {},\n {\n get(_target, prop) {\n // somehow not working if using Symbol.dispose\n if (prop === Symbol.asyncDispose) {\n return async () => {\n await kysely.destroy();\n };\n }\n if (prop === 'kysely') {\n return kysely;\n }\n if (typeof kysely[prop] === 'function') {\n return kysely[prop].bind(kysely);\n }\n return kysely[prop];\n },\n }\n ) as ReturnType;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"src/","sources":["test/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAGL,cAAc,GACf,MAAM,UAAU,CAAC;AAelB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAAuC;IAEvC,IAAI,GAAG,CAAC,qBAAqB;QAAE,OAAO,GAAG,CAAC,qBAAqB,KAAK,GAAG,CAAC;IACxE,OAAO,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AAEtD,SAAS,aAAa,CAAC,IAA2B;IAChD,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE,QAAQ;QAAE,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC;IACjE,UAAU,CAAC,wBAAwB,KAAK,cAAc,CAAC,IAAI,CAAC,CAAC;IAC7D,OAAO,UAAU,CAAC,wBAAwB,CAAC;AAC7C,CAAC;AAED,SAAS,UAAU,CAAC,IAA2B;IAC7C,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,IAA2B;IACxE,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IAEzC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,QAAQ,CAAC,KAAK,IAAI,EAAE;YAClB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAMD,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAA2B;IAE3B,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IAEzC,OAAO,IAAI,KAAK,CACd,EAAE,EACF;QACE,GAAG,CAAC,OAAO,EAAE,IAAI;YACf,8CAA8C;YAC9C,IAAI,IAAI,KAAK,MAAM,CAAC,YAAY,EAAE,CAAC;gBACjC,OAAO,KAAK,IAAI,EAAE;oBAChB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;wBAAE,OAAO;oBAC9B,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;gBACzB,CAAC,CAAC;YACJ,CAAC;YACD,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACtB,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,IAAI,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,UAAU,EAAE,CAAC;gBACvC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;KACF,CACY,CAAC;AAClB,CAAC","sourcesContent":["import { afterAll } from 'bun:test';\nimport {\n type LocalKysely,\n type UseLocalKyselyOptions,\n useLocalKysely,\n} from '../local';\n\ntype UseTestKyselyOptions = UseLocalKyselyOptions & {\n /**\n * Force a private database even when the worker shares one. Required by specs\n * that model several regions at once, where two handles must be two distinct\n * physical databases.\n */\n isolated?: boolean;\n};\n\ndeclare global {\n var __vrplatformSharedKysely: Promise<LocalKysely> | undefined;\n}\n\n/**\n * Locally each worker process shares one database, because specs isolate by\n * tenant and the faster suite is what makes running it before a push realistic.\n *\n * CI keeps a database per spec file. It retries nothing, so a badly scoped spec\n * would surface there as a red build in an unrelated file rather than as a\n * local failure the author can reproduce.\n *\n * VR_TEST_SHARED_KYSELY overrides either way; `isolated: true` opts a single\n * call out when a spec genuinely needs two physical databases at once.\n */\nexport function resolveSharedKysely(\n env: Record<string, string | undefined>\n): boolean {\n if (env.VR_TEST_SHARED_KYSELY) return env.VR_TEST_SHARED_KYSELY !== '0';\n return !(env.CI || env.GITHUB_ACTIONS);\n}\n\nconst sharedKysely = resolveSharedKysely(process.env);\n\nfunction acquireKysely(args?: UseTestKyselyOptions): Promise<LocalKysely> {\n if (!sharedKysely || args?.isolated) return useLocalKysely(args);\n globalThis.__vrplatformSharedKysely ??= useLocalKysely(args);\n return globalThis.__vrplatformSharedKysely;\n}\n\nfunction ownsKysely(args?: UseTestKyselyOptions): boolean {\n return !sharedKysely || Boolean(args?.isolated);\n}\n\nexport async function useTestAutoCleanupKysely(args?: UseTestKyselyOptions) {\n const kysely = await acquireKysely(args);\n\n if (ownsKysely(args)) {\n afterAll(async () => {\n await kysely.destroy();\n });\n }\n\n return kysely;\n}\n\ntype ReturnType = LocalKysely & {\n [Symbol.dispose]: () => Promise<void>;\n};\n\nexport async function useLocalKyselyResource(\n args?: UseTestKyselyOptions\n): Promise<ReturnType> {\n const kysely = await acquireKysely(args);\n\n return new Proxy(\n {},\n {\n get(_target, prop) {\n // somehow not working if using Symbol.dispose\n if (prop === Symbol.asyncDispose) {\n return async () => {\n if (!ownsKysely(args)) return;\n await kysely.destroy();\n };\n }\n if (prop === 'kysely') {\n return kysely;\n }\n if (typeof kysely[prop] === 'function') {\n return kysely[prop].bind(kysely);\n }\n return kysely[prop];\n },\n }\n ) as ReturnType;\n}\n"]}