@highstate/backend 0.17.0 → 0.18.0

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 (84) hide show
  1. package/dist/highstate.manifest.json +2 -3
  2. package/dist/index.js +190 -280
  3. package/dist/index.js.map +1 -1
  4. package/package.json +9 -10
  5. package/prisma/backend/postgresql/main.prisma +0 -2
  6. package/prisma/backend/sqlite/main.prisma +0 -2
  7. package/prisma/project/main.prisma +0 -1
  8. package/src/business/project.ts +1 -2
  9. package/src/database/_generated/backend/postgresql/browser.ts +54 -0
  10. package/src/database/_generated/backend/postgresql/client.ts +7 -8
  11. package/src/database/_generated/backend/postgresql/commonInputTypes.ts +3 -2
  12. package/src/database/_generated/backend/postgresql/enums.ts +3 -1
  13. package/src/database/_generated/backend/postgresql/internal/class.ts +24 -71
  14. package/src/database/_generated/backend/postgresql/internal/prismaNamespace.ts +41 -43
  15. package/src/database/_generated/backend/postgresql/internal/prismaNamespaceBrowser.ts +191 -0
  16. package/src/database/_generated/backend/postgresql/models/BackendUnlockMethod.ts +12 -11
  17. package/src/database/_generated/backend/postgresql/models/Library.ts +29 -28
  18. package/src/database/_generated/backend/postgresql/models/Project.ts +69 -68
  19. package/src/database/_generated/backend/postgresql/models/ProjectModelStorage.ts +29 -28
  20. package/src/database/_generated/backend/postgresql/models/ProjectSpace.ts +26 -25
  21. package/src/database/_generated/backend/postgresql/models/PulumiBackend.ts +29 -28
  22. package/src/database/_generated/backend/postgresql/models/UserWorkspaceLayout.ts +12 -11
  23. package/src/database/_generated/backend/postgresql/models.ts +2 -1
  24. package/src/database/_generated/backend/postgresql/pjtg.ts +1 -0
  25. package/src/database/_generated/backend/sqlite/browser.ts +54 -0
  26. package/src/database/_generated/backend/sqlite/client.ts +7 -8
  27. package/src/database/_generated/backend/sqlite/commonInputTypes.ts +3 -2
  28. package/src/database/_generated/backend/sqlite/enums.ts +3 -1
  29. package/src/database/_generated/backend/sqlite/internal/class.ts +24 -71
  30. package/src/database/_generated/backend/sqlite/internal/prismaNamespace.ts +41 -43
  31. package/src/database/_generated/backend/sqlite/internal/prismaNamespaceBrowser.ts +188 -0
  32. package/src/database/_generated/backend/sqlite/models/BackendUnlockMethod.ts +12 -11
  33. package/src/database/_generated/backend/sqlite/models/Library.ts +29 -28
  34. package/src/database/_generated/backend/sqlite/models/Project.ts +69 -68
  35. package/src/database/_generated/backend/sqlite/models/ProjectModelStorage.ts +29 -28
  36. package/src/database/_generated/backend/sqlite/models/ProjectSpace.ts +26 -25
  37. package/src/database/_generated/backend/sqlite/models/PulumiBackend.ts +29 -28
  38. package/src/database/_generated/backend/sqlite/models/UserWorkspaceLayout.ts +12 -11
  39. package/src/database/_generated/backend/sqlite/models.ts +2 -1
  40. package/src/database/_generated/backend/sqlite/pjtg.ts +1 -0
  41. package/src/database/_generated/project/browser.ts +1 -0
  42. package/src/database/_generated/project/client.ts +4 -5
  43. package/src/database/_generated/project/commonInputTypes.ts +1 -0
  44. package/src/database/_generated/project/enums.ts +1 -0
  45. package/src/database/_generated/project/internal/class.ts +20 -62
  46. package/src/database/_generated/project/internal/prismaNamespace.ts +40 -36
  47. package/src/database/_generated/project/internal/prismaNamespaceBrowser.ts +9 -6
  48. package/src/database/_generated/project/models/ApiKey.ts +1 -0
  49. package/src/database/_generated/project/models/Artifact.ts +1 -0
  50. package/src/database/_generated/project/models/HubModel.ts +1 -0
  51. package/src/database/_generated/project/models/InstanceCustomStatus.ts +1 -0
  52. package/src/database/_generated/project/models/InstanceEvaluationState.ts +1 -0
  53. package/src/database/_generated/project/models/InstanceLock.ts +1 -0
  54. package/src/database/_generated/project/models/InstanceModel.ts +1 -0
  55. package/src/database/_generated/project/models/InstanceOperationState.ts +1 -0
  56. package/src/database/_generated/project/models/InstanceState.ts +1 -0
  57. package/src/database/_generated/project/models/Operation.ts +1 -0
  58. package/src/database/_generated/project/models/OperationLog.ts +1 -0
  59. package/src/database/_generated/project/models/Page.ts +1 -0
  60. package/src/database/_generated/project/models/Secret.ts +1 -0
  61. package/src/database/_generated/project/models/ServiceAccount.ts +1 -0
  62. package/src/database/_generated/project/models/Terminal.ts +1 -0
  63. package/src/database/_generated/project/models/TerminalSession.ts +1 -0
  64. package/src/database/_generated/project/models/TerminalSessionLog.ts +1 -0
  65. package/src/database/_generated/project/models/Trigger.ts +1 -0
  66. package/src/database/_generated/project/models/UnlockMethod.ts +1 -0
  67. package/src/database/_generated/project/models/UserCompositeViewport.ts +1 -0
  68. package/src/database/_generated/project/models/UserProjectViewport.ts +1 -0
  69. package/src/database/_generated/project/models/Worker.ts +1 -0
  70. package/src/database/_generated/project/models/WorkerUnitRegistration.ts +1 -0
  71. package/src/database/_generated/project/models/WorkerVersion.ts +1 -0
  72. package/src/database/_generated/project/models/WorkerVersionLog.ts +1 -0
  73. package/src/database/_generated/project/models.ts +1 -0
  74. package/src/database/abstractions.ts +1 -7
  75. package/src/database/index.ts +1 -0
  76. package/src/database/local/backend.ts +19 -30
  77. package/src/database/local/project.ts +4 -9
  78. package/src/database/manager.ts +28 -34
  79. package/src/database/migration.ts +126 -0
  80. package/src/test-utils/database.ts +28 -14
  81. package/dist/database/local/prisma.config.js +0 -26
  82. package/dist/database/local/prisma.config.js.map +0 -1
  83. package/src/database/local/prisma.config.ts +0 -25
  84. package/src/database/migrate.ts +0 -35
@@ -0,0 +1,126 @@
1
+ import type { Logger } from "pino"
2
+ import { readFile } from "node:fs/promises"
3
+ import { dirname, join, resolve } from "node:path"
4
+ import { fileURLToPath } from "node:url"
5
+ import { resolve as importMetaResolve } from "import-meta-resolve"
6
+ import { BackendError } from "../shared"
7
+
8
+ export type MigrationClient = {
9
+ $transaction<T>(callback: (tx: unknown) => Promise<T>): Promise<T>
10
+ $executeRawUnsafe(query: string): Promise<number>
11
+ }
12
+
13
+ export type MigrationPack = {
14
+ type: "backend" | "project"
15
+ schemaPath: string
16
+ migrationNames: string[]
17
+ }
18
+
19
+ export const migrationPacks = {
20
+ "backend/sqlite": {
21
+ type: "backend" as const,
22
+ schemaPath: "backend/sqlite",
23
+ migrationNames: [
24
+ //
25
+ "20250928124105_initial_migration",
26
+ ],
27
+ },
28
+ project: {
29
+ type: "project" as const,
30
+ schemaPath: "project",
31
+ migrationNames: [
32
+ //
33
+ "20250928130725_initial_migration",
34
+ "20260123000000_add_instance_state_self_hash",
35
+ ],
36
+ },
37
+ }
38
+
39
+ export const backendDatabaseVersion = migrationPacks["backend/sqlite"].migrationNames.length
40
+ export const projectDatabaseVersion = migrationPacks.project.migrationNames.length
41
+
42
+ export async function migrateDatabase(
43
+ client: MigrationClient,
44
+ migrationPack: MigrationPack,
45
+ currentVersion: number,
46
+ writeVersion: (version: number) => Promise<void>,
47
+ logger: Logger,
48
+ ): Promise<void> {
49
+ const { type, schemaPath, migrationNames } = migrationPack
50
+ const targetVersion = migrationNames.length
51
+
52
+ const backendIndexPath = importMetaResolve("@highstate/backend", import.meta.url)
53
+ const backendRootPath = join(dirname(fileURLToPath(backendIndexPath)), "..")
54
+ const migrationsDir = resolve(backendRootPath, `prisma`, schemaPath, "migrations")
55
+
56
+ logger.debug(`migrations dir: "%s"`, migrationsDir)
57
+
58
+ if (currentVersion > targetVersion) {
59
+ throw new BackendError(
60
+ `The version of the ${type} database (${currentVersion}) is newer than expected (${targetVersion}). Do you need to upgrade Highstate?`,
61
+ )
62
+ }
63
+
64
+ for (let version = currentVersion; version < migrationNames.length; version++) {
65
+ const nextVersion = version + 1
66
+ const nextMigrationName = migrationNames[nextVersion - 1]
67
+
68
+ logger.info(
69
+ `applying migration for %s database, %d -> %d: %s`,
70
+ type,
71
+ version,
72
+ nextVersion,
73
+ nextMigrationName,
74
+ )
75
+
76
+ const migrationPath = join(migrationsDir, nextMigrationName, "migration.sql")
77
+ const content = await readFile(migrationPath, "utf-8")
78
+
79
+ const statements = splitToStatements(content)
80
+
81
+ // execute each statement and update the version in a single transaction
82
+ await client.$transaction(async tx => {
83
+ for (const statement of statements) {
84
+ await (tx as MigrationClient).$executeRawUnsafe(statement)
85
+ }
86
+
87
+ await writeVersion(nextVersion)
88
+ })
89
+ }
90
+
91
+ logger.info(`database is up to date at version %d`, migrationNames.length)
92
+ }
93
+
94
+ function splitToStatements(sql: string): string[] {
95
+ const statements: string[] = []
96
+ let currentStatement = ""
97
+ let insideString = false
98
+ let stringChar = ""
99
+
100
+ for (let i = 0; i < sql.length; i++) {
101
+ const char = sql[i]
102
+
103
+ if ((char === "'" || char === '"') && !insideString) {
104
+ insideString = true
105
+ stringChar = char
106
+ } else if (char === stringChar && insideString) {
107
+ insideString = false
108
+ stringChar = ""
109
+ }
110
+
111
+ if (char === ";" && !insideString) {
112
+ if (currentStatement.trim().length > 0) {
113
+ statements.push(currentStatement.trim())
114
+ }
115
+ currentStatement = ""
116
+ } else {
117
+ currentStatement += char
118
+ }
119
+ }
120
+
121
+ if (currentStatement.trim().length > 0) {
122
+ statements.push(currentStatement.trim())
123
+ }
124
+
125
+ return statements
126
+ }
@@ -4,12 +4,17 @@ import { constants } from "node:fs"
4
4
  import { access, mkdtemp, rm } from "node:fs/promises"
5
5
  import { hostname, tmpdir } from "node:os"
6
6
  import { join } from "node:path"
7
- import { PrismaLibSQL } from "@prisma/adapter-libsql"
7
+ import { PrismaLibSql } from "@prisma/adapter-libsql"
8
8
  import { generateIdentity, identityToRecipient } from "age-encryption"
9
- import { type DatabaseManager, ensureWellKnownEntitiesCreated } from "../database"
9
+ import { createProjectLogger } from "../common"
10
+ import {
11
+ type DatabaseManager,
12
+ ensureWellKnownEntitiesCreated,
13
+ migrateDatabase,
14
+ migrationPacks,
15
+ } from "../database"
10
16
  import { PrismaClient as BackendPrismaClient } from "../database/_generated/backend/sqlite/client"
11
17
  import { getInitialBackendUnlockMethodMeta } from "../database/local/backend"
12
- import { migrateDatabase } from "../database/migrate"
13
18
  import {
14
19
  type BackendDatabase as BackendDatabaseClient,
15
20
  ProjectDatabase as ProjectDatabaseClient,
@@ -58,12 +63,15 @@ export class TestDatabaseManager implements DatabaseManager {
58
63
  async _forProject(projectId: string): Promise<ProjectDatabase> {
59
64
  const tempPath = await this.createTempPath()
60
65
  const projectUrl = `file:${join(tempPath, `${projectId}.db`)}`
66
+ const logger = createProjectLogger(this.logger, projectId)
61
67
 
62
- await migrateDatabase(projectUrl, "project", undefined, this.logger)
63
-
64
- return new ProjectDatabaseClient({
65
- adapter: new PrismaLibSQL({ url: projectUrl }),
68
+ const client = new ProjectDatabaseClient({
69
+ adapter: new PrismaLibSql({ url: projectUrl }),
66
70
  })
71
+
72
+ await migrateDatabase(client, migrationPacks.project, 0, () => Promise.resolve(), logger)
73
+
74
+ return client
67
75
  }
68
76
 
69
77
  static async create(
@@ -75,20 +83,26 @@ export class TestDatabaseManager implements DatabaseManager {
75
83
  const tempPath = await mkdtemp(join(tempRoot, "highstate"))
76
84
  const backendUrl = `file:${join(tempPath, "backend.db")}`
77
85
 
78
- await migrateDatabase(backendUrl, "backend/sqlite", undefined, logger)
79
-
80
- const backend = new BackendPrismaClient({
81
- adapter: new PrismaLibSQL({ url: backendUrl }),
86
+ const backendClient = new BackendPrismaClient({
87
+ adapter: new PrismaLibSql({ url: backendUrl }),
82
88
  }) as BackendDatabaseClient
83
89
 
84
- await ensureWellKnownEntitiesCreated(backend)
90
+ await migrateDatabase(
91
+ backendClient,
92
+ migrationPacks["backend/sqlite"],
93
+ 0,
94
+ () => Promise.resolve(),
95
+ logger,
96
+ )
97
+
98
+ await ensureWellKnownEntitiesCreated(backendClient)
85
99
 
86
100
  if (isEncryptionEnabled) {
87
101
  const identity = await generateIdentity()
88
102
  const recipient = await identityToRecipient(identity)
89
103
  const meta = getInitialBackendUnlockMethodMeta(hostname())
90
104
 
91
- await backend.backendUnlockMethod.create({
105
+ await backendClient.backendUnlockMethod.create({
92
106
  data: {
93
107
  recipient,
94
108
  meta,
@@ -96,7 +110,7 @@ export class TestDatabaseManager implements DatabaseManager {
96
110
  })
97
111
  }
98
112
 
99
- return new TestDatabaseManager(backend, logger, isEncryptionEnabled)
113
+ return new TestDatabaseManager(backendClient, logger, isEncryptionEnabled)
100
114
  }
101
115
 
102
116
  private async createTempPath(): Promise<string> {
@@ -1,26 +0,0 @@
1
- import '../../chunk-I7BWSAN6.js';
2
- import { PrismaLibSQL } from '@prisma/adapter-libsql';
3
- import { defineConfig } from 'prisma/config';
4
-
5
- if (!process.env.HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH) {
6
- throw new Error("HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH is not set");
7
- }
8
- var prisma_config_default = defineConfig({
9
- experimental: {
10
- adapter: true
11
- },
12
- schema: `../../../prisma/${process.env.HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH}`,
13
- async adapter() {
14
- if (!process.env.HIGHSTATE_MIGRATION_DATABASE_URL) {
15
- throw new Error("HIGHSTATE_MIGRATION_DATABASE_URL is not set");
16
- }
17
- return new PrismaLibSQL({
18
- url: process.env.HIGHSTATE_MIGRATION_DATABASE_URL,
19
- encryptionKey: process.env.HIGHSTATE_MIGRATION_DATABASE_ENCRYPTION_KEY || void 0
20
- });
21
- }
22
- });
23
-
24
- export { prisma_config_default as default };
25
- //# sourceMappingURL=prisma.config.js.map
26
- //# sourceMappingURL=prisma.config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/database/local/prisma.config.ts"],"names":[],"mappings":";;;;AAGA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,wCAAA,EAA0C;AACzD,EAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AACvE;AAEA,IAAO,wBAAQ,YAAA,CAAa;AAAA,EAC1B,YAAA,EAAc;AAAA,IACZ,OAAA,EAAS;AAAA,GACX;AAAA,EAEA,MAAA,EAAQ,CAAA,gBAAA,EAAmB,OAAA,CAAQ,GAAA,CAAI,wCAAwC,CAAA,CAAA;AAAA,EAE/E,MAAM,OAAA,GAAU;AACd,IAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,gCAAA,EAAkC;AACjD,MAAA,MAAM,IAAI,MAAM,6CAA6C,CAAA;AAAA,IAC/D;AAEA,IAAA,OAAO,IAAI,YAAA,CAAa;AAAA,MACtB,GAAA,EAAK,QAAQ,GAAA,CAAI,gCAAA;AAAA,MACjB,aAAA,EAAe,OAAA,CAAQ,GAAA,CAAI,2CAAA,IAA+C;AAAA,KAC3E,CAAA;AAAA,EACH;AACF,CAAC","file":"prisma.config.js","sourcesContent":["import { PrismaLibSQL } from \"@prisma/adapter-libsql\"\nimport { defineConfig } from \"prisma/config\"\n\nif (!process.env.HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH) {\n throw new Error(\"HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH is not set\")\n}\n\nexport default defineConfig({\n experimental: {\n adapter: true,\n },\n\n schema: `../../../prisma/${process.env.HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH}`,\n\n async adapter() {\n if (!process.env.HIGHSTATE_MIGRATION_DATABASE_URL) {\n throw new Error(\"HIGHSTATE_MIGRATION_DATABASE_URL is not set\")\n }\n\n return new PrismaLibSQL({\n url: process.env.HIGHSTATE_MIGRATION_DATABASE_URL,\n encryptionKey: process.env.HIGHSTATE_MIGRATION_DATABASE_ENCRYPTION_KEY || undefined,\n })\n },\n})\n"]}
@@ -1,25 +0,0 @@
1
- import { PrismaLibSQL } from "@prisma/adapter-libsql"
2
- import { defineConfig } from "prisma/config"
3
-
4
- if (!process.env.HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH) {
5
- throw new Error("HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH is not set")
6
- }
7
-
8
- export default defineConfig({
9
- experimental: {
10
- adapter: true,
11
- },
12
-
13
- schema: `../../../prisma/${process.env.HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH}`,
14
-
15
- async adapter() {
16
- if (!process.env.HIGHSTATE_MIGRATION_DATABASE_URL) {
17
- throw new Error("HIGHSTATE_MIGRATION_DATABASE_URL is not set")
18
- }
19
-
20
- return new PrismaLibSQL({
21
- url: process.env.HIGHSTATE_MIGRATION_DATABASE_URL,
22
- encryptionKey: process.env.HIGHSTATE_MIGRATION_DATABASE_ENCRYPTION_KEY || undefined,
23
- })
24
- },
25
- })
@@ -1,35 +0,0 @@
1
- import type { Logger } from "pino"
2
- import { join } from "node:path"
3
- import { fileURLToPath } from "node:url"
4
- import { execa } from "execa"
5
- import { resolve } from "import-meta-resolve"
6
- import { detectPackageManager } from "nypm"
7
-
8
- export async function migrateDatabase(
9
- databaseUrl: string,
10
- schemaPath: "backend/sqlite" | "project",
11
- masterKey: string | undefined,
12
- logger: Logger,
13
- ): Promise<void> {
14
- logger.info("applying database migrations")
15
-
16
- const backendIndexPath = resolve("@highstate/backend", import.meta.url)
17
- const backendRootPath = join(fileURLToPath(backendIndexPath), "..")
18
-
19
- const packageManager = await detectPackageManager(backendRootPath)
20
- if (!packageManager) {
21
- throw new Error("Could not detect package manager to run migrations")
22
- }
23
-
24
- const hasCorepack = await execa`"corepack" --version`.then(() => true).catch(() => false)
25
- const command = hasCorepack ? `corepack ${packageManager.command}` : packageManager.command
26
-
27
- await execa({
28
- cwd: backendRootPath,
29
- env: {
30
- HIGHSTATE_MIGRATION_DATABASE_SCHEMA_PATH: schemaPath,
31
- HIGHSTATE_MIGRATION_DATABASE_URL: databaseUrl,
32
- HIGHSTATE_MIGRATION_DATABASE_ENCRYPTION_KEY: masterKey ?? "",
33
- },
34
- })`${command} migrate`
35
- }