@storyshelf/db-mysql 0.6.2 → 0.8.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 (98) hide show
  1. package/README.md +36 -0
  2. package/dist/ddl.d.mts +1 -3
  3. package/dist/ddl.mjs +391 -141
  4. package/dist/ddl.mjs.map +1 -1
  5. package/dist/drizzle-factory-mysql.mjs +15 -8
  6. package/dist/drizzle-factory-mysql.mjs.map +1 -1
  7. package/dist/drizzle-factory.mjs +1 -2
  8. package/dist/foreign-keys.mjs +338 -0
  9. package/dist/foreign-keys.mjs.map +1 -0
  10. package/dist/index.d.mts +0 -2
  11. package/dist/index.mjs +11 -7
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/migrate.mjs +349 -73
  14. package/dist/migrate.mjs.map +1 -1
  15. package/dist/planetscale.d.mts +0 -2
  16. package/dist/planetscale.mjs +1 -2
  17. package/dist/schema/baseline.d.mts +89 -19
  18. package/dist/schema/baseline.mjs +8 -4
  19. package/dist/schema/baseline.mjs.map +1 -1
  20. package/dist/schema/{status-config.d.mts → build-component.d.mts} +47 -42
  21. package/dist/schema/build-component.mjs +25 -0
  22. package/dist/schema/build-component.mjs.map +1 -0
  23. package/dist/schema/build-review.d.mts +116 -0
  24. package/dist/schema/build-review.mjs +23 -0
  25. package/dist/schema/build-review.mjs.map +1 -0
  26. package/dist/schema/build.d.mts +65 -31
  27. package/dist/schema/build.mjs +6 -3
  28. package/dist/schema/build.mjs.map +1 -1
  29. package/dist/schema/capture-attempt.d.mts +43 -45
  30. package/dist/schema/capture-attempt.mjs +3 -3
  31. package/dist/schema/capture-attempt.mjs.map +1 -1
  32. package/dist/schema/capture-job.d.mts +328 -0
  33. package/dist/schema/capture-job.mjs +56 -0
  34. package/dist/schema/capture-job.mjs.map +1 -0
  35. package/dist/schema/capture-shard.d.mts +255 -0
  36. package/dist/schema/capture-shard.mjs +40 -0
  37. package/dist/schema/capture-shard.mjs.map +1 -0
  38. package/dist/schema/comment.d.mts +35 -19
  39. package/dist/schema/comment.mjs +6 -5
  40. package/dist/schema/comment.mjs.map +1 -1
  41. package/dist/schema/connection.d.mts +171 -0
  42. package/dist/schema/connection.mjs +35 -0
  43. package/dist/schema/connection.mjs.map +1 -0
  44. package/dist/schema/content-refs.d.mts +11 -13
  45. package/dist/schema/content-refs.mjs +3 -3
  46. package/dist/schema/content-refs.mjs.map +1 -1
  47. package/dist/schema/datetime.mjs +46 -0
  48. package/dist/schema/datetime.mjs.map +1 -0
  49. package/dist/schema/design.d.mts +587 -0
  50. package/dist/schema/design.mjs +73 -0
  51. package/dist/schema/design.mjs.map +1 -0
  52. package/dist/schema/insight.d.mts +631 -0
  53. package/dist/schema/insight.mjs +68 -0
  54. package/dist/schema/insight.mjs.map +1 -0
  55. package/dist/schema/label.d.mts +21 -23
  56. package/dist/schema/label.mjs +3 -3
  57. package/dist/schema/label.mjs.map +1 -1
  58. package/dist/schema/member.d.mts +10 -12
  59. package/dist/schema/member.mjs +3 -3
  60. package/dist/schema/member.mjs.map +1 -1
  61. package/dist/schema/notification.d.mts +63 -29
  62. package/dist/schema/notification.mjs +8 -3
  63. package/dist/schema/notification.mjs.map +1 -1
  64. package/dist/schema/project-group-mapping.d.mts +9 -11
  65. package/dist/schema/project-group-mapping.mjs +3 -3
  66. package/dist/schema/project-group-mapping.mjs.map +1 -1
  67. package/dist/schema/project.d.mts +149 -25
  68. package/dist/schema/project.mjs +11 -4
  69. package/dist/schema/project.mjs.map +1 -1
  70. package/dist/schema/site-setting.d.mts +97 -0
  71. package/dist/schema/site-setting.mjs +23 -0
  72. package/dist/schema/site-setting.mjs.map +1 -0
  73. package/dist/schema/snapshot.d.mts +195 -35
  74. package/dist/schema/snapshot.mjs +17 -4
  75. package/dist/schema/snapshot.mjs.map +1 -1
  76. package/dist/schema/token.d.mts +14 -16
  77. package/dist/schema/token.mjs +3 -3
  78. package/dist/schema/token.mjs.map +1 -1
  79. package/dist/schema/usage.d.mts +265 -0
  80. package/dist/schema/usage.mjs +36 -0
  81. package/dist/schema/usage.mjs.map +1 -0
  82. package/dist/schema/user-invite.d.mts +16 -18
  83. package/dist/schema/user-invite.mjs +3 -3
  84. package/dist/schema/user-invite.mjs.map +1 -1
  85. package/dist/schema/user.d.mts +88 -16
  86. package/dist/schema/user.mjs +8 -4
  87. package/dist/schema/user.mjs.map +1 -1
  88. package/dist/schema/webhook.d.mts +14 -16
  89. package/dist/schema/webhook.mjs +3 -3
  90. package/dist/schema/webhook.mjs.map +1 -1
  91. package/dist/schema.d.mts +24 -5
  92. package/dist/schema.mjs +26 -6
  93. package/dist/schema.mjs.map +1 -1
  94. package/dist/tidb.d.mts +0 -2
  95. package/dist/tidb.mjs +1 -2
  96. package/package.json +7 -7
  97. package/dist/schema/status-config.mjs +0 -26
  98. package/dist/schema/status-config.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { drizzle } from \"drizzle-orm/mysql2\";\nimport mysql from \"mysql2/promise\";\nimport { createDrizzleMySqlAdapter } from \"./drizzle-factory-mysql.ts\";\nimport { runMigrations } from \"./migrate.ts\";\nimport { schema } from \"./schema/index.ts\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\nexport interface MysqlDatabaseOptions {\n url?: string;\n connectionString?: string;\n host?: string;\n port?: number;\n user?: string;\n password?: string;\n database?: string;\n ssl?: unknown;\n connectionLimit?: number;\n client?: mysql.Pool | mysql.Connection;\n}\n\ntype PoolLike = {\n query(sql: string): Promise<unknown>;\n execute?(sql: string): Promise<unknown>;\n end?(): Promise<void>;\n};\n\nfunction resolvePool(options: MysqlDatabaseOptions): {\n pool: PoolLike;\n owned: boolean;\n} {\n if (options.client) {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- Pool vs PoolLike width\n return { pool: options.client as unknown as PoolLike, owned: false };\n }\n const connectionString = options.connectionString ?? options.url;\n if (connectionString) {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- Pool vs PoolLike width\n return { pool: mysql.createPool(connectionString) as unknown as PoolLike, owned: true };\n }\n if (options.host) {\n return {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- Pool vs PoolLike width\n pool: mysql.createPool({\n host: options.host,\n port: options.port,\n user: options.user,\n password: options.password,\n database: options.database,\n ssl: options.ssl as never,\n waitForConnections: true,\n connectionLimit: options.connectionLimit ?? 10,\n }) as unknown as PoolLike,\n owned: true,\n };\n }\n throw new Error(\n \"createMysqlDatabase: provide `url`/`connectionString`/`host` or a preconfigured `client`\",\n );\n}\n\n/**\n * Create a MySQL/MariaDB-backed DatabaseAdapter using mysql2 + Drizzle.\n * Works with MySQL, MariaDB, PlanetScale (via mysql2), TiDB.\n */\nexport function createMysqlDatabase(options: MysqlDatabaseOptions): DatabaseAdapter {\n const { pool, owned } = resolvePool(options);\n const db = drizzle(pool as never, { schema, mode: \"default\" });\n\n return createDrizzleMySqlAdapter(db, {\n metadata: {\n name: \"MySQL\",\n version: (globalThis as unknown as { __PKG_VERSION__?: string }).__PKG_VERSION__ ?? \"0.0.0\",\n description: \"MySQL/MariaDB database adapter (mysql2 + Drizzle)\",\n kind: \"mysql\",\n category: \"database\",\n },\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- schema is AnyMySqlTable, adapter expects Table\n tables: schema as unknown as DatabaseAdapter[\"tables\"],\n migrate: async (): Promise<void> => {\n // oxlint-disable-next-line unicorn/consistent-function-scoping -- closes over pool from createMysqlDatabase\n const run = async (sql: string): Promise<unknown[]> => {\n if (typeof pool.query === \"function\") {\n await pool.query(sql);\n } else if (pool.execute) {\n await pool.execute(sql);\n }\n return [];\n };\n await runMigrations(run);\n },\n close: async (): Promise<void> => {\n if (owned && pool.end) {\n await pool.end();\n }\n },\n ping: async (): Promise<void> => {\n await pool.query(\"SELECT 1\");\n },\n });\n}\n"],"mappings":";;;;;;;;;AA4BA,SAAS,YAAY,SAGnB;CACA,IAAI,QAAQ,QAEV,OAAO;EAAE,MAAM,QAAQ;EAA+B,OAAO;CAAM;CAErE,MAAM,mBAAmB,QAAQ,oBAAoB,QAAQ;CAC7D,IAAI,kBAEF,OAAO;EAAE,MAAM,MAAM,WAAW,gBAAgB;EAA0B,OAAO;CAAK;CAExF,IAAI,QAAQ,MACV,OAAO;EAEL,MAAM,MAAM,WAAW;GACrB,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,UAAU,QAAQ;GAClB,KAAK,QAAQ;GACb,oBAAoB;GACpB,iBAAiB,QAAQ,mBAAmB;EAC9C,CAAC;EACD,OAAO;CACT;CAEF,MAAM,IAAI,MACR,0FACF;AACF;;;;;AAMA,SAAgB,oBAAoB,SAAgD;CAClF,MAAM,EAAE,MAAM,UAAU,YAAY,OAAO;CAC3C,MAAM,KAAK,QAAQ,MAAe;EAAE;EAAQ,MAAM;CAAU,CAAC;CAE7D,OAAO,0BAA0B,IAAI;EACnC,UAAU;GACR,MAAM;GACN,SAAU,WAAuD,mBAAmB;GACpF,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EAEA,QAAQ;EACR,SAAS,YAA2B;GAElC,MAAM,MAAM,OAAO,QAAoC;IACrD,IAAI,OAAO,KAAK,UAAU,YACxB,MAAM,KAAK,MAAM,GAAG;SACf,IAAI,KAAK,SACd,MAAM,KAAK,QAAQ,GAAG;IAExB,OAAO,CAAC;GACV;GACA,MAAM,cAAc,GAAG;EACzB;EACA,OAAO,YAA2B;GAChC,IAAI,SAAS,KAAK,KAChB,MAAM,KAAK,IAAI;EAEnB;EACA,MAAM,YAA2B;GAC/B,MAAM,KAAK,MAAM,UAAU;EAC7B;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { DatabaseAdapter } from \"@storyshelf/core/adapter/database\";\nimport { drizzle } from \"drizzle-orm/mysql2\";\nimport mysql from \"mysql2/promise\";\nimport { createDrizzleMySqlAdapter } from \"./drizzle-factory-mysql.ts\";\nimport { runMigrations } from \"./migrate.ts\";\nimport { schema } from \"./schema/index.ts\";\n\ndeclare const __PKG_VERSION__: string | undefined;\n\nexport interface MysqlDatabaseOptions {\n url?: string;\n connectionString?: string;\n host?: string;\n port?: number;\n user?: string;\n password?: string;\n database?: string;\n ssl?: unknown;\n connectionLimit?: number;\n client?: mysql.Pool | mysql.Connection;\n}\n\ntype PoolLike = {\n query(sql: string): Promise<unknown>;\n execute?(sql: string): Promise<unknown>;\n end?(): Promise<void>;\n};\n\nfunction resolvePool(options: MysqlDatabaseOptions): {\n pool: PoolLike;\n owned: boolean;\n} {\n if (options.client) {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- Pool vs PoolLike width\n return { pool: options.client as unknown as PoolLike, owned: false };\n }\n const connectionString = options.connectionString ?? options.url;\n if (connectionString) {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- Pool vs PoolLike width\n return {\n pool: mysql.createPool({ uri: connectionString, dateStrings: true }),\n owned: true,\n };\n }\n if (options.host) {\n return {\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- Pool vs PoolLike width\n pool: mysql.createPool({\n host: options.host,\n port: options.port,\n user: options.user,\n password: options.password,\n database: options.database,\n ssl: options.ssl as never,\n dateStrings: true,\n waitForConnections: true,\n connectionLimit: options.connectionLimit ?? 10,\n }) as unknown as PoolLike,\n owned: true,\n };\n }\n throw new Error(\n \"createMysqlDatabase: provide `url`/`connectionString`/`host` or a preconfigured `client`\",\n );\n}\n\n/**\n * Create a MySQL/MariaDB-backed DatabaseAdapter using mysql2 + Drizzle.\n * Works with MySQL, MariaDB, PlanetScale (via mysql2), TiDB.\n */\nexport function createMysqlDatabase(options: MysqlDatabaseOptions): DatabaseAdapter {\n const { pool, owned } = resolvePool(options);\n const db = drizzle(pool as never, { schema, mode: \"default\" });\n\n return createDrizzleMySqlAdapter(db, {\n metadata: {\n name: \"MySQL\",\n version: typeof __PKG_VERSION__ === \"string\" ? __PKG_VERSION__ : \"0.0.0\",\n description: \"MySQL/MariaDB database adapter (mysql2 + Drizzle)\",\n kind: \"mysql\",\n category: \"database\",\n },\n // oxlint-disable-next-line typescript/no-unnecessary-type-assertion -- schema is AnyMySqlTable, adapter expects Table\n tables: schema as unknown as DatabaseAdapter[\"tables\"],\n migrate: async (): Promise<void> => {\n // oxlint-disable-next-line unicorn/consistent-function-scoping -- closes over pool from createMysqlDatabase\n const run = async (sql: string): Promise<unknown[]> => {\n const result: unknown =\n typeof pool.query === \"function\" ? await pool.query(sql) : await pool.execute?.(sql);\n if (!Array.isArray(result)) {\n return [];\n }\n const rows: unknown = result[0];\n return Array.isArray(rows) ? (rows as unknown[]) : [];\n };\n await runMigrations(run);\n },\n close: async (): Promise<void> => {\n if (owned && pool.end) {\n await pool.end();\n }\n },\n ping: async (): Promise<void> => {\n await pool.query(\"SELECT 1\");\n },\n });\n}\n"],"mappings":";;;;;;;;;AA4BA,SAAS,YAAY,SAGnB;CACA,IAAI,QAAQ,QAEV,OAAO;EAAE,MAAM,QAAQ;EAA+B,OAAO;CAAM;CAErE,MAAM,mBAAmB,QAAQ,oBAAoB,QAAQ;CAC7D,IAAI,kBAEF,OAAO;EACL,MAAM,MAAM,WAAW;GAAE,KAAK;GAAkB,aAAa;EAAK,CAAC;EACnE,OAAO;CACT;CAEF,IAAI,QAAQ,MACV,OAAO;EAEL,MAAM,MAAM,WAAW;GACrB,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,UAAU,QAAQ;GAClB,KAAK,QAAQ;GACb,aAAa;GACb,oBAAoB;GACpB,iBAAiB,QAAQ,mBAAmB;EAC9C,CAAC;EACD,OAAO;CACT;CAEF,MAAM,IAAI,MACR,0FACF;AACF;;;;;AAMA,SAAgB,oBAAoB,SAAgD;CAClF,MAAM,EAAE,MAAM,UAAU,YAAY,OAAO;CAC3C,MAAM,KAAK,QAAQ,MAAe;EAAE;EAAQ,MAAM;CAAU,CAAC;CAE7D,OAAO,0BAA0B,IAAI;EACnC,UAAU;GACR,MAAM;GACN,SAAA;GACA,aAAa;GACb,MAAM;GACN,UAAU;EACZ;EAEA,QAAQ;EACR,SAAS,YAA2B;GAElC,MAAM,MAAM,OAAO,QAAoC;IACrD,MAAM,SACJ,OAAO,KAAK,UAAU,aAAa,MAAM,KAAK,MAAM,GAAG,IAAI,MAAM,KAAK,UAAU,GAAG;IACrF,IAAI,CAAC,MAAM,QAAQ,MAAM,GACvB,OAAO,CAAC;IAEV,MAAM,OAAgB,OAAO;IAC7B,OAAO,MAAM,QAAQ,IAAI,IAAK,OAAqB,CAAC;GACtD;GACA,MAAM,cAAc,GAAG;EACzB;EACA,OAAO,YAA2B;GAChC,IAAI,SAAS,KAAK,KAChB,MAAM,KAAK,IAAI;EAEnB;EACA,MAAM,YAA2B;GAC/B,MAAM,KAAK,MAAM,UAAU;EAC7B;CACF,CAAC;AACH"}
package/dist/migrate.mjs CHANGED
@@ -2,90 +2,366 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { DDL } from "./ddl.mjs";
5
- //#region src/migrate.ts
5
+ import { FOREIGN_KEYS } from "./foreign-keys.mjs";
6
+ // src/migrate.ts
6
7
  /**
7
- * Shared Postgres migrations for every `db-postgres` preset.
8
+ * Shared MySQL migrations for every `db-mysql` preset.
8
9
  *
9
- * The DDL plus idempotent follow-up ALTERs run through a minimal row-query
10
- * runner so all drivers (`postgres.js`, `node-postgres`, Neon, …) share one
11
- * migration path instead of drifting per preset.
10
+ * MySQL 8 has no `ADD COLUMN IF NOT EXISTS`, no `CREATE INDEX IF NOT EXISTS`
11
+ * and no `ALTER COLUMN ... DROP NOT NULL`. The DDL is split into statements,
12
+ * missing columns/indexes are found in `information_schema`, and only a
13
+ * duplicate-index error from a repeated DDL statement is ignored.
12
14
  */
13
- const STORYBOOK_META_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS storybook_meta TEXT";
14
- const EXECUTE_PLAY_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS execute_play BOOLEAN NOT NULL DEFAULT false";
15
- const PLAY_TIMEOUT_MS_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS play_timeout_ms INTEGER NOT NULL DEFAULT 10000";
16
- const RUN_A11Y_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS run_a11y BOOLEAN NOT NULL DEFAULT false";
17
- const PROJECT_BROWSER_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS browser TEXT NOT NULL DEFAULT 'chromium'";
18
- const PROJECT_VIEWPORTS_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS viewports TEXT";
19
- const PROJECT_AUTOMIGRATE_ALTER = "ALTER TABLE projects ADD COLUMN IF NOT EXISTS automigrate BOOLEAN NOT NULL DEFAULT false";
20
- const SNAPSHOT_INFRA_HASH_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS infra_hash TEXT";
21
- const SNAPSHOT_INHERITED_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS inherited BOOLEAN NOT NULL DEFAULT false";
22
- const SNAPSHOT_BASELINE_ID_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_id TEXT";
23
- const SNAPSHOT_BASELINE_VERSION_ALTER = "ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_version TEXT";
24
- const BASELINE_INFRA_HASH_ALTER = "ALTER TABLE baselines ADD COLUMN IF NOT EXISTS infra_hash TEXT";
25
- const BUILD_AFFECTED_ONLY_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_only BOOLEAN NOT NULL DEFAULT true";
26
- const BUILD_BASELINE_SHA_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS baseline_sha TEXT";
27
- const BUILD_CHANGED_FILES_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS changed_files TEXT";
28
- const BUILD_AFFECTED_PATHS_ALTER = "ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_import_paths TEXT";
29
- const WEBHOOK_SECRET_ALTER = "ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS secret_encrypted TEXT NOT NULL DEFAULT ''";
30
- const WEBHOOK_SECRET_DROP = "ALTER TABLE webhooks DROP COLUMN IF EXISTS secret";
31
- const TOKEN_USER_ALTER = "ALTER TABLE tokens ADD COLUMN IF NOT EXISTS user_id TEXT REFERENCES users(id) ON DELETE CASCADE";
32
- const MEMBER_SOURCE_ALTER = "ALTER TABLE project_members ADD COLUMN IF NOT EXISTS source TEXT NOT NULL DEFAULT 'manual'";
33
- /**
34
- * Re-runs share `(project_id, git_sha)` by design (ULID build ids), so the
35
- * lookup index must not be unique. Existing databases created it UNIQUE —
36
- * drop (MySQL syntax, no IF EXISTS) and recreate it plain.
37
- */
38
- const BUILDS_GITSHA_DEDUP = "DROP INDEX builds_project_gitsha_idx ON builds";
39
- const BUILDS_GITSHA_INDEX = "CREATE INDEX builds_project_gitsha_idx ON builds (project_id, git_sha)";
40
- async function migrateSnapshotColumns(run) {
41
- await execIgnore(run, SNAPSHOT_INFRA_HASH_ALTER);
42
- await execIgnore(run, SNAPSHOT_INHERITED_ALTER);
43
- await execIgnore(run, SNAPSHOT_BASELINE_ID_ALTER);
44
- await execIgnore(run, SNAPSHOT_BASELINE_VERSION_ALTER);
45
- }
46
- async function migrateProjectExtras(run) {
47
- await execIgnore(run, EXECUTE_PLAY_ALTER);
48
- await execIgnore(run, PLAY_TIMEOUT_MS_ALTER);
49
- await execIgnore(run, PROJECT_AUTOMIGRATE_ALTER);
50
- await migrateSnapshotColumns(run);
51
- await execIgnore(run, BASELINE_INFRA_HASH_ALTER);
52
- await execIgnore(run, BUILD_AFFECTED_ONLY_ALTER);
53
- await execIgnore(run, BUILD_BASELINE_SHA_ALTER);
54
- await execIgnore(run, BUILD_CHANGED_FILES_ALTER);
55
- await execIgnore(run, BUILD_AFFECTED_PATHS_ALTER);
56
- }
15
+ const COLUMN_MIGRATIONS = [
16
+ {
17
+ table: "projects",
18
+ column: "storybook_meta",
19
+ definition: "TEXT"
20
+ },
21
+ {
22
+ table: "projects",
23
+ column: "execute_play",
24
+ definition: "TINYINT(1) NOT NULL DEFAULT 0"
25
+ },
26
+ {
27
+ table: "projects",
28
+ column: "play_timeout_ms",
29
+ definition: "INTEGER NOT NULL DEFAULT 10000"
30
+ },
31
+ {
32
+ table: "projects",
33
+ column: "run_a11y",
34
+ definition: "TINYINT(1) NOT NULL DEFAULT 1"
35
+ },
36
+ {
37
+ table: "projects",
38
+ column: "browser",
39
+ definition: "VARCHAR(64) NOT NULL DEFAULT 'chromium'"
40
+ },
41
+ {
42
+ table: "projects",
43
+ column: "viewports",
44
+ definition: "TEXT"
45
+ },
46
+ {
47
+ table: "projects",
48
+ column: "browsers",
49
+ definition: "TEXT"
50
+ },
51
+ {
52
+ table: "projects",
53
+ column: "modes_allowlist",
54
+ definition: "TEXT"
55
+ },
56
+ {
57
+ table: "projects",
58
+ column: "automigrate",
59
+ definition: "TINYINT(1) NOT NULL DEFAULT 0"
60
+ },
61
+ {
62
+ table: "projects",
63
+ column: "ai_profile",
64
+ definition: "TEXT"
65
+ },
66
+ {
67
+ table: "projects",
68
+ column: "review_enabled",
69
+ definition: "TINYINT(1) NOT NULL DEFAULT 1"
70
+ },
71
+ {
72
+ table: "projects",
73
+ column: "review_approvals",
74
+ definition: "INT NOT NULL DEFAULT 1"
75
+ },
76
+ {
77
+ table: "projects",
78
+ column: "storybook_url_ttl_seconds",
79
+ definition: "INT"
80
+ },
81
+ {
82
+ table: "projects",
83
+ column: "run_tests",
84
+ definition: "TINYINT(1) NOT NULL DEFAULT 0"
85
+ },
86
+ {
87
+ table: "snapshots",
88
+ column: "infra_hash",
89
+ definition: "TEXT"
90
+ },
91
+ {
92
+ table: "snapshots",
93
+ column: "inherited",
94
+ definition: "TINYINT(1) NOT NULL DEFAULT 0"
95
+ },
96
+ {
97
+ table: "snapshots",
98
+ column: "baseline_id",
99
+ definition: "VARCHAR(64)"
100
+ },
101
+ {
102
+ table: "snapshots",
103
+ column: "baseline_version",
104
+ definition: "TEXT"
105
+ },
106
+ {
107
+ table: "snapshots",
108
+ column: "source_build_id",
109
+ definition: "TEXT"
110
+ },
111
+ {
112
+ table: "snapshots",
113
+ column: "comparison_image_path",
114
+ definition: "TEXT"
115
+ },
116
+ {
117
+ table: "snapshots",
118
+ column: "comparison_metadata",
119
+ definition: "TEXT"
120
+ },
121
+ {
122
+ table: "snapshots",
123
+ column: "design_url",
124
+ definition: "TEXT"
125
+ },
126
+ {
127
+ table: "snapshots",
128
+ column: "env_change",
129
+ definition: "TEXT"
130
+ },
131
+ {
132
+ table: "snapshots",
133
+ column: "mode",
134
+ definition: "VARCHAR(64)"
135
+ },
136
+ {
137
+ table: "snapshots",
138
+ column: "browser",
139
+ definition: "VARCHAR(64) NOT NULL DEFAULT 'chromium'"
140
+ },
141
+ {
142
+ table: "snapshots",
143
+ column: "duration_ms",
144
+ definition: "INT"
145
+ },
146
+ {
147
+ table: "snapshots",
148
+ column: "a11y_violations",
149
+ definition: "TEXT"
150
+ },
151
+ {
152
+ table: "baselines",
153
+ column: "infra_hash",
154
+ definition: "TEXT"
155
+ },
156
+ {
157
+ table: "baselines",
158
+ column: "fingerprint",
159
+ definition: "TEXT"
160
+ },
161
+ {
162
+ table: "baselines",
163
+ column: "source_build_id",
164
+ definition: "TEXT"
165
+ },
166
+ {
167
+ table: "baselines",
168
+ column: "mode",
169
+ definition: "VARCHAR(64)"
170
+ },
171
+ {
172
+ table: "baselines",
173
+ column: "browser",
174
+ definition: "VARCHAR(64) NOT NULL DEFAULT 'chromium'"
175
+ },
176
+ {
177
+ table: "builds",
178
+ column: "affected_only",
179
+ definition: "TINYINT(1) NOT NULL DEFAULT 1"
180
+ },
181
+ {
182
+ table: "builds",
183
+ column: "baseline_sha",
184
+ definition: "TEXT"
185
+ },
186
+ {
187
+ table: "builds",
188
+ column: "changed_files",
189
+ definition: "TEXT"
190
+ },
191
+ {
192
+ table: "builds",
193
+ column: "affected_import_paths",
194
+ definition: "TEXT"
195
+ },
196
+ {
197
+ table: "builds",
198
+ column: "inherited_count",
199
+ definition: "INTEGER NOT NULL DEFAULT 0"
200
+ },
201
+ {
202
+ table: "builds",
203
+ column: "test_results",
204
+ definition: "TEXT"
205
+ },
206
+ {
207
+ table: "webhooks",
208
+ column: "secret_encrypted",
209
+ definition: "TEXT NOT NULL"
210
+ },
211
+ {
212
+ table: "tokens",
213
+ column: "user_id",
214
+ definition: "VARCHAR(64) NULL"
215
+ },
216
+ {
217
+ table: "project_members",
218
+ column: "source",
219
+ definition: "VARCHAR(32) NOT NULL DEFAULT 'manual'"
220
+ },
221
+ {
222
+ table: "comments",
223
+ column: "story_id",
224
+ definition: "TEXT"
225
+ },
226
+ {
227
+ table: "users",
228
+ column: "password_hash",
229
+ definition: "TEXT"
230
+ },
231
+ {
232
+ table: "users",
233
+ column: "display_name_override",
234
+ definition: "TEXT"
235
+ },
236
+ {
237
+ table: "users",
238
+ column: "auth_provider",
239
+ definition: "VARCHAR(32) NOT NULL DEFAULT 'oidc'"
240
+ },
241
+ {
242
+ table: "users",
243
+ column: "disabled",
244
+ definition: "TINYINT(1) NOT NULL DEFAULT 0"
245
+ },
246
+ {
247
+ table: "notification_channels",
248
+ column: "last_error",
249
+ definition: "TEXT"
250
+ },
251
+ {
252
+ table: "notification_channels",
253
+ column: "last_error_at",
254
+ definition: "DATETIME(3)"
255
+ }
256
+ ];
257
+ const IDENTITY_INDEXES = [{
258
+ table: "snapshots",
259
+ name: "snapshots_build_story_mode_browser_idx",
260
+ sql: "CREATE UNIQUE INDEX snapshots_build_story_mode_browser_idx ON snapshots (build_id, story_id, viewport_name, browser)"
261
+ }, {
262
+ table: "baselines",
263
+ name: "baselines_project_story_mode_browser_branch_idx",
264
+ sql: "CREATE UNIQUE INDEX baselines_project_story_mode_browser_branch_idx ON baselines (project_id, story_id, viewport_name, browser, branch)"
265
+ }];
57
266
  /** Run the full migration set through any row-query runner. */
58
267
  async function runMigrations(run) {
59
- await run(DDL);
60
- await migrateCoreColumns(run);
61
- await migrateProjectExtras(run);
268
+ await runDdl(run);
269
+ await ensureColumns(run);
270
+ await migrateIndexes(run);
271
+ await migrateLegacyColumns(run);
272
+ await ensureForeignKeys(run);
273
+ }
274
+ async function ensureColumns(run) {
275
+ for (const migration of COLUMN_MIGRATIONS) await ensureColumn(run, migration);
276
+ }
277
+ async function migrateIndexes(run) {
278
+ await dropIndexIfExists(run, "snapshots", "snapshots_build_story_viewport_idx");
279
+ await dropIndexIfExists(run, "baselines", "baselines_project_story_viewport_branch_idx");
280
+ for (const index of IDENTITY_INDEXES) await ensureIndex(run, index.table, index.name, index.sql);
281
+ if (await indexIsUnique(run, "builds", "builds_project_gitsha_idx")) await dropIndexIfExists(run, "builds", "builds_project_gitsha_idx");
282
+ await ensureIndex(run, "builds", "builds_project_gitsha_idx", "CREATE INDEX builds_project_gitsha_idx ON builds (project_id, git_sha)");
283
+ await ensureIndex(run, "design_links", "design_links_project_import_path_idx", "CREATE UNIQUE INDEX design_links_project_import_path_idx ON design_links (project_id, import_path)");
284
+ }
285
+ async function migrateLegacyColumns(run) {
286
+ await dropColumnIfExists(run, "webhooks", "secret");
62
287
  await migrateCommentsTable(run);
63
288
  }
64
- async function migrateCoreColumns(run) {
65
- await execIgnore(run, STORYBOOK_META_ALTER);
66
- await execIgnore(run, RUN_A11Y_ALTER);
67
- await execIgnore(run, PROJECT_BROWSER_ALTER);
68
- await execIgnore(run, PROJECT_VIEWPORTS_ALTER);
69
- await execIgnore(run, WEBHOOK_SECRET_ALTER);
70
- await execIgnore(run, WEBHOOK_SECRET_DROP);
71
- await execIgnore(run, TOKEN_USER_ALTER);
72
- await execIgnore(run, MEMBER_SOURCE_ALTER);
73
- await execIgnore(run, BUILDS_GITSHA_DEDUP);
74
- await execIgnore(run, BUILDS_GITSHA_INDEX);
75
- }
76
- async function execIgnore(run, sql) {
289
+ async function ensureForeignKeys(run) {
290
+ for (const foreignKey of FOREIGN_KEYS) await ensureForeignKey(run, foreignKey);
291
+ }
292
+ async function ensureForeignKey(run, foreignKey) {
293
+ if (await hasForeignKey(run, foreignKey)) return;
294
+ await deleteOrphans(run, foreignKey);
295
+ const table = quote(foreignKey.table);
296
+ const column = quote(foreignKey.column);
297
+ const parent = quote(foreignKey.parent);
298
+ const parentColumn = quote(foreignKey.parentColumn);
299
+ await run(`ALTER TABLE ${table} ADD CONSTRAINT ${foreignKey.name} FOREIGN KEY (${column}) REFERENCES ${parent}(${parentColumn}) ON DELETE CASCADE`);
300
+ }
301
+ async function hasForeignKey(run, foreignKey) {
302
+ return (await run(`SELECT 1 FROM information_schema.key_column_usage WHERE constraint_schema = DATABASE() AND table_name = '${foreignKey.table}' AND column_name = '${foreignKey.column}' AND referenced_table_name = '${foreignKey.parent}' AND referenced_column_name = '${foreignKey.parentColumn}' LIMIT 1`)).length > 0;
303
+ }
304
+ async function deleteOrphans(run, foreignKey) {
305
+ const table = quote(foreignKey.table);
306
+ const column = quote(foreignKey.column);
307
+ const parent = quote(foreignKey.parent);
308
+ await run(`DELETE FROM ${table} WHERE ${column} IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${parent} WHERE ${parent}.${quote(foreignKey.parentColumn)} = ${table}.${column})`);
309
+ }
310
+ function quote(identifier) {
311
+ return `\`${identifier}\``;
312
+ }
313
+ async function runDdl(run) {
314
+ for (const statement of splitStatements(DDL)) try {
315
+ await run(statement);
316
+ } catch (error) {
317
+ if (!isDuplicateIndexError(error)) throw error;
318
+ }
319
+ }
320
+ function splitStatements(ddl) {
321
+ return ddl.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
322
+ }
323
+ function isDuplicateIndexError(error) {
324
+ const code = error.code;
325
+ const errno = error.errno;
326
+ return code === "ER_DUP_KEYNAME" || errno === 1061;
327
+ }
328
+ async function ensureColumn(run, migration) {
329
+ if (await hasColumn(run, migration.table, migration.column)) return;
77
330
  try {
78
- await run(sql);
79
- } catch {}
331
+ await run(`ALTER TABLE ${migration.table} ADD COLUMN ${migration.column} ${migration.definition}`);
332
+ } catch (error) {
333
+ if (!isDuplicateColumnError(error)) throw error;
334
+ }
335
+ }
336
+ function isDuplicateColumnError(error) {
337
+ const code = error.code;
338
+ const errno = error.errno;
339
+ return code === "ER_DUP_FIELDNAME" || errno === 1060;
340
+ }
341
+ async function hasColumn(run, table, column) {
342
+ return (await run(`SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = '${table}' AND column_name = '${column}' LIMIT 1`)).length > 0;
343
+ }
344
+ async function ensureIndex(run, table, name, sql) {
345
+ if (await hasIndex(run, table, name)) return;
346
+ await run(sql);
347
+ }
348
+ async function hasIndex(run, table, name) {
349
+ return (await run(`SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = '${table}' AND index_name = '${name}' LIMIT 1`)).length > 0;
350
+ }
351
+ async function indexIsUnique(run, table, name) {
352
+ const nonUnique = (await run(`SELECT NON_UNIQUE AS non_unique FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = '${table}' AND index_name = '${name}' LIMIT 1`))[0]?.non_unique;
353
+ return nonUnique !== void 0 && Number(nonUnique) === 0;
354
+ }
355
+ async function dropIndexIfExists(run, table, name) {
356
+ if (await hasIndex(run, table, name)) await run(`ALTER TABLE ${table} DROP INDEX ${name}`);
357
+ }
358
+ async function dropColumnIfExists(run, table, column) {
359
+ if (await hasColumn(run, table, column)) await run(`ALTER TABLE ${table} DROP COLUMN ${column}`);
80
360
  }
81
361
  async function migrateCommentsTable(run) {
82
- try {
83
- const isNullable = (await run("SELECT is_nullable FROM information_schema.columns WHERE table_name='comments' AND column_name='user_id'"))[0]?.is_nullable;
84
- if (isNullable === "YES" || isNullable === void 0) return;
85
- await run("ALTER TABLE comments ALTER COLUMN user_id DROP NOT NULL");
86
- } catch {}
362
+ if ((await run("SELECT IS_NULLABLE AS is_nullable FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'comments' AND column_name = 'user_id' LIMIT 1"))[0]?.is_nullable !== "NO") return;
363
+ await run("ALTER TABLE comments MODIFY COLUMN user_id VARCHAR(64) NULL");
87
364
  }
88
- //#endregion
89
365
  export { runMigrations };
90
366
 
91
367
  //# sourceMappingURL=migrate.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.mjs","names":[],"sources":["../src/migrate.ts"],"sourcesContent":["/**\n * Shared Postgres migrations for every `db-postgres` preset.\n *\n * The DDL plus idempotent follow-up ALTERs run through a minimal row-query\n * runner so all drivers (`postgres.js`, `node-postgres`, Neon, …) share one\n * migration path instead of drifting per preset.\n */\nimport { DDL } from \"./ddl.ts\";\n\n/** Minimal row-query surface a migration runner needs. */\nexport type MigrationRunner = (sql: string) => Promise<unknown[]>;\n\nconst STORYBOOK_META_ALTER = \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS storybook_meta TEXT\";\nconst EXECUTE_PLAY_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS execute_play BOOLEAN NOT NULL DEFAULT false\";\nconst PLAY_TIMEOUT_MS_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS play_timeout_ms INTEGER NOT NULL DEFAULT 10000\";\nconst RUN_A11Y_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS run_a11y BOOLEAN NOT NULL DEFAULT false\";\nconst PROJECT_BROWSER_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS browser TEXT NOT NULL DEFAULT 'chromium'\";\nconst PROJECT_VIEWPORTS_ALTER = \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS viewports TEXT\";\nconst PROJECT_AUTOMIGRATE_ALTER =\n \"ALTER TABLE projects ADD COLUMN IF NOT EXISTS automigrate BOOLEAN NOT NULL DEFAULT false\";\nconst SNAPSHOT_INFRA_HASH_ALTER = \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS infra_hash TEXT\";\nconst SNAPSHOT_INHERITED_ALTER =\n \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS inherited BOOLEAN NOT NULL DEFAULT false\";\nconst SNAPSHOT_BASELINE_ID_ALTER =\n \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_id TEXT\";\nconst SNAPSHOT_BASELINE_VERSION_ALTER =\n \"ALTER TABLE snapshots ADD COLUMN IF NOT EXISTS baseline_version TEXT\";\nconst BASELINE_INFRA_HASH_ALTER = \"ALTER TABLE baselines ADD COLUMN IF NOT EXISTS infra_hash TEXT\";\nconst BUILD_AFFECTED_ONLY_ALTER =\n \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_only BOOLEAN NOT NULL DEFAULT true\";\nconst BUILD_BASELINE_SHA_ALTER = \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS baseline_sha TEXT\";\nconst BUILD_CHANGED_FILES_ALTER = \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS changed_files TEXT\";\nconst BUILD_AFFECTED_PATHS_ALTER =\n \"ALTER TABLE builds ADD COLUMN IF NOT EXISTS affected_import_paths TEXT\";\nconst WEBHOOK_SECRET_ALTER =\n \"ALTER TABLE webhooks ADD COLUMN IF NOT EXISTS secret_encrypted TEXT NOT NULL DEFAULT ''\";\nconst WEBHOOK_SECRET_DROP = \"ALTER TABLE webhooks DROP COLUMN IF EXISTS secret\";\nconst TOKEN_USER_ALTER =\n \"ALTER TABLE tokens ADD COLUMN IF NOT EXISTS user_id TEXT REFERENCES users(id) ON DELETE CASCADE\";\nconst MEMBER_SOURCE_ALTER =\n \"ALTER TABLE project_members ADD COLUMN IF NOT EXISTS source TEXT NOT NULL DEFAULT 'manual'\";\n/**\n * Re-runs share `(project_id, git_sha)` by design (ULID build ids), so the\n * lookup index must not be unique. Existing databases created it UNIQUE —\n * drop (MySQL syntax, no IF EXISTS) and recreate it plain.\n */\nconst BUILDS_GITSHA_DEDUP = \"DROP INDEX builds_project_gitsha_idx ON builds\";\nconst BUILDS_GITSHA_INDEX =\n \"CREATE INDEX builds_project_gitsha_idx ON builds (project_id, git_sha)\";\n\nasync function migrateSnapshotColumns(run: MigrationRunner): Promise<void> {\n await execIgnore(run, SNAPSHOT_INFRA_HASH_ALTER);\n await execIgnore(run, SNAPSHOT_INHERITED_ALTER);\n await execIgnore(run, SNAPSHOT_BASELINE_ID_ALTER);\n await execIgnore(run, SNAPSHOT_BASELINE_VERSION_ALTER);\n}\n\nasync function migrateProjectExtras(run: MigrationRunner): Promise<void> {\n await execIgnore(run, EXECUTE_PLAY_ALTER);\n await execIgnore(run, PLAY_TIMEOUT_MS_ALTER);\n await execIgnore(run, PROJECT_AUTOMIGRATE_ALTER);\n await migrateSnapshotColumns(run);\n await execIgnore(run, BASELINE_INFRA_HASH_ALTER);\n await execIgnore(run, BUILD_AFFECTED_ONLY_ALTER);\n await execIgnore(run, BUILD_BASELINE_SHA_ALTER);\n await execIgnore(run, BUILD_CHANGED_FILES_ALTER);\n await execIgnore(run, BUILD_AFFECTED_PATHS_ALTER);\n}\n\n/** Run the full migration set through any row-query runner. */\nexport async function runMigrations(run: MigrationRunner): Promise<void> {\n await run(DDL);\n await migrateCoreColumns(run);\n await migrateProjectExtras(run);\n await migrateCommentsTable(run);\n}\n\nasync function migrateCoreColumns(run: MigrationRunner): Promise<void> {\n await execIgnore(run, STORYBOOK_META_ALTER);\n await execIgnore(run, RUN_A11Y_ALTER);\n await execIgnore(run, PROJECT_BROWSER_ALTER);\n await execIgnore(run, PROJECT_VIEWPORTS_ALTER);\n await execIgnore(run, WEBHOOK_SECRET_ALTER);\n await execIgnore(run, WEBHOOK_SECRET_DROP);\n await execIgnore(run, TOKEN_USER_ALTER);\n await execIgnore(run, MEMBER_SOURCE_ALTER);\n await execIgnore(run, BUILDS_GITSHA_DEDUP);\n await execIgnore(run, BUILDS_GITSHA_INDEX);\n}\n\nasync function execIgnore(run: MigrationRunner, sql: string): Promise<void> {\n try {\n await run(sql);\n } catch {\n // idempotent — already migrated\n }\n}\n\nasync function migrateCommentsTable(run: MigrationRunner): Promise<void> {\n try {\n const rows = (await run(\n \"SELECT is_nullable FROM information_schema.columns WHERE table_name='comments' AND column_name='user_id'\",\n )) as { is_nullable: string }[];\n const isNullable = rows[0]?.is_nullable;\n if (isNullable === \"YES\" || isNullable === undefined) {\n return;\n }\n await run(\"ALTER TABLE comments ALTER COLUMN user_id DROP NOT NULL\");\n } catch {\n // already nullable or table missing — ignore\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,uBAAuB;AAC7B,MAAM,qBACJ;AACF,MAAM,wBACJ;AACF,MAAM,iBACJ;AACF,MAAM,wBACJ;AACF,MAAM,0BAA0B;AAChC,MAAM,4BACJ;AACF,MAAM,4BAA4B;AAClC,MAAM,2BACJ;AACF,MAAM,6BACJ;AACF,MAAM,kCACJ;AACF,MAAM,4BAA4B;AAClC,MAAM,4BACJ;AACF,MAAM,2BAA2B;AACjC,MAAM,4BAA4B;AAClC,MAAM,6BACJ;AACF,MAAM,uBACJ;AACF,MAAM,sBAAsB;AAC5B,MAAM,mBACJ;AACF,MAAM,sBACJ;;;;;;AAMF,MAAM,sBAAsB;AAC5B,MAAM,sBACJ;AAEF,eAAe,uBAAuB,KAAqC;CACzE,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,wBAAwB;CAC9C,MAAM,WAAW,KAAK,0BAA0B;CAChD,MAAM,WAAW,KAAK,+BAA+B;AACvD;AAEA,eAAe,qBAAqB,KAAqC;CACvE,MAAM,WAAW,KAAK,kBAAkB;CACxC,MAAM,WAAW,KAAK,qBAAqB;CAC3C,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,uBAAuB,GAAG;CAChC,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,wBAAwB;CAC9C,MAAM,WAAW,KAAK,yBAAyB;CAC/C,MAAM,WAAW,KAAK,0BAA0B;AAClD;;AAGA,eAAsB,cAAc,KAAqC;CACvE,MAAM,IAAI,GAAG;CACb,MAAM,mBAAmB,GAAG;CAC5B,MAAM,qBAAqB,GAAG;CAC9B,MAAM,qBAAqB,GAAG;AAChC;AAEA,eAAe,mBAAmB,KAAqC;CACrE,MAAM,WAAW,KAAK,oBAAoB;CAC1C,MAAM,WAAW,KAAK,cAAc;CACpC,MAAM,WAAW,KAAK,qBAAqB;CAC3C,MAAM,WAAW,KAAK,uBAAuB;CAC7C,MAAM,WAAW,KAAK,oBAAoB;CAC1C,MAAM,WAAW,KAAK,mBAAmB;CACzC,MAAM,WAAW,KAAK,gBAAgB;CACtC,MAAM,WAAW,KAAK,mBAAmB;CACzC,MAAM,WAAW,KAAK,mBAAmB;CACzC,MAAM,WAAW,KAAK,mBAAmB;AAC3C;AAEA,eAAe,WAAW,KAAsB,KAA4B;CAC1E,IAAI;EACF,MAAM,IAAI,GAAG;CACf,QAAQ,CAER;AACF;AAEA,eAAe,qBAAqB,KAAqC;CACvE,IAAI;EAIF,MAAM,cAAa,MAHC,IAClB,0GACF,EAAA,CACwB,EAAE,EAAE;EAC5B,IAAI,eAAe,SAAS,eAAe,KAAA,GACzC;EAEF,MAAM,IAAI,yDAAyD;CACrE,QAAQ,CAER;AACF"}
1
+ {"version":3,"file":"migrate.mjs","names":[],"sources":["../src/migrate.ts"],"sourcesContent":["/**\n * Shared MySQL migrations for every `db-mysql` preset.\n *\n * MySQL 8 has no `ADD COLUMN IF NOT EXISTS`, no `CREATE INDEX IF NOT EXISTS`\n * and no `ALTER COLUMN ... DROP NOT NULL`. The DDL is split into statements,\n * missing columns/indexes are found in `information_schema`, and only a\n * duplicate-index error from a repeated DDL statement is ignored.\n */\nimport { DDL } from \"./ddl.ts\";\nimport { FOREIGN_KEYS, type ForeignKeyMigration } from \"./foreign-keys.ts\";\n\n/** Minimal row-query surface a migration runner needs. */\nexport type MigrationRunner = (sql: string) => Promise<unknown[]>;\n\ninterface ColumnMigration {\n table: string;\n column: string;\n definition: string;\n}\n\nconst COLUMN_MIGRATIONS: ColumnMigration[] = [\n { table: \"projects\", column: \"storybook_meta\", definition: \"TEXT\" },\n {\n table: \"projects\",\n column: \"execute_play\",\n definition: \"TINYINT(1) NOT NULL DEFAULT 0\",\n },\n {\n table: \"projects\",\n column: \"play_timeout_ms\",\n definition: \"INTEGER NOT NULL DEFAULT 10000\",\n },\n { table: \"projects\", column: \"run_a11y\", definition: \"TINYINT(1) NOT NULL DEFAULT 1\" },\n {\n table: \"projects\",\n column: \"browser\",\n definition: \"VARCHAR(64) NOT NULL DEFAULT 'chromium'\",\n },\n { table: \"projects\", column: \"viewports\", definition: \"TEXT\" },\n { table: \"projects\", column: \"browsers\", definition: \"TEXT\" },\n { table: \"projects\", column: \"modes_allowlist\", definition: \"TEXT\" },\n { table: \"projects\", column: \"automigrate\", definition: \"TINYINT(1) NOT NULL DEFAULT 0\" },\n { table: \"projects\", column: \"ai_profile\", definition: \"TEXT\" },\n {\n table: \"projects\",\n column: \"review_enabled\",\n definition: \"TINYINT(1) NOT NULL DEFAULT 1\",\n },\n { table: \"projects\", column: \"review_approvals\", definition: \"INT NOT NULL DEFAULT 1\" },\n { table: \"projects\", column: \"storybook_url_ttl_seconds\", definition: \"INT\" },\n { table: \"projects\", column: \"run_tests\", definition: \"TINYINT(1) NOT NULL DEFAULT 0\" },\n { table: \"snapshots\", column: \"infra_hash\", definition: \"TEXT\" },\n {\n table: \"snapshots\",\n column: \"inherited\",\n definition: \"TINYINT(1) NOT NULL DEFAULT 0\",\n },\n { table: \"snapshots\", column: \"baseline_id\", definition: \"VARCHAR(64)\" },\n { table: \"snapshots\", column: \"baseline_version\", definition: \"TEXT\" },\n { table: \"snapshots\", column: \"source_build_id\", definition: \"TEXT\" },\n { table: \"snapshots\", column: \"comparison_image_path\", definition: \"TEXT\" },\n { table: \"snapshots\", column: \"comparison_metadata\", definition: \"TEXT\" },\n { table: \"snapshots\", column: \"design_url\", definition: \"TEXT\" },\n { table: \"snapshots\", column: \"env_change\", definition: \"TEXT\" },\n { table: \"snapshots\", column: \"mode\", definition: \"VARCHAR(64)\" },\n {\n table: \"snapshots\",\n column: \"browser\",\n definition: \"VARCHAR(64) NOT NULL DEFAULT 'chromium'\",\n },\n { table: \"snapshots\", column: \"duration_ms\", definition: \"INT\" },\n { table: \"snapshots\", column: \"a11y_violations\", definition: \"TEXT\" },\n { table: \"baselines\", column: \"infra_hash\", definition: \"TEXT\" },\n { table: \"baselines\", column: \"fingerprint\", definition: \"TEXT\" },\n { table: \"baselines\", column: \"source_build_id\", definition: \"TEXT\" },\n { table: \"baselines\", column: \"mode\", definition: \"VARCHAR(64)\" },\n {\n table: \"baselines\",\n column: \"browser\",\n definition: \"VARCHAR(64) NOT NULL DEFAULT 'chromium'\",\n },\n {\n table: \"builds\",\n column: \"affected_only\",\n definition: \"TINYINT(1) NOT NULL DEFAULT 1\",\n },\n { table: \"builds\", column: \"baseline_sha\", definition: \"TEXT\" },\n { table: \"builds\", column: \"changed_files\", definition: \"TEXT\" },\n { table: \"builds\", column: \"affected_import_paths\", definition: \"TEXT\" },\n { table: \"builds\", column: \"inherited_count\", definition: \"INTEGER NOT NULL DEFAULT 0\" },\n { table: \"builds\", column: \"test_results\", definition: \"TEXT\" },\n { table: \"webhooks\", column: \"secret_encrypted\", definition: \"TEXT NOT NULL\" },\n { table: \"tokens\", column: \"user_id\", definition: \"VARCHAR(64) NULL\" },\n {\n table: \"project_members\",\n column: \"source\",\n definition: \"VARCHAR(32) NOT NULL DEFAULT 'manual'\",\n },\n { table: \"comments\", column: \"story_id\", definition: \"TEXT\" },\n // Auth columns the SQLite `users` table always had (access control: `disabled`).\n { table: \"users\", column: \"password_hash\", definition: \"TEXT\" },\n { table: \"users\", column: \"display_name_override\", definition: \"TEXT\" },\n {\n table: \"users\",\n column: \"auth_provider\",\n definition: \"VARCHAR(32) NOT NULL DEFAULT 'oidc'\",\n },\n { table: \"users\", column: \"disabled\", definition: \"TINYINT(1) NOT NULL DEFAULT 0\" },\n { table: \"notification_channels\", column: \"last_error\", definition: \"TEXT\" },\n { table: \"notification_channels\", column: \"last_error_at\", definition: \"DATETIME(3)\" },\n];\n\nconst IDENTITY_INDEXES = [\n {\n table: \"snapshots\",\n name: \"snapshots_build_story_mode_browser_idx\",\n sql: \"CREATE UNIQUE INDEX snapshots_build_story_mode_browser_idx ON snapshots (build_id, story_id, viewport_name, browser)\",\n },\n {\n table: \"baselines\",\n name: \"baselines_project_story_mode_browser_branch_idx\",\n sql: \"CREATE UNIQUE INDEX baselines_project_story_mode_browser_branch_idx ON baselines (project_id, story_id, viewport_name, browser, branch)\",\n },\n];\n\n/** Run the full migration set through any row-query runner. */\nexport async function runMigrations(run: MigrationRunner): Promise<void> {\n await runDdl(run);\n await ensureColumns(run);\n await migrateIndexes(run);\n await migrateLegacyColumns(run);\n await ensureForeignKeys(run);\n}\n\nasync function ensureColumns(run: MigrationRunner): Promise<void> {\n for (const migration of COLUMN_MIGRATIONS) {\n // oxlint-disable-next-line no-await-in-loop -- information_schema query then ALTER\n await ensureColumn(run, migration);\n }\n}\n\nasync function migrateIndexes(run: MigrationRunner): Promise<void> {\n await dropIndexIfExists(run, \"snapshots\", \"snapshots_build_story_viewport_idx\");\n await dropIndexIfExists(run, \"baselines\", \"baselines_project_story_viewport_branch_idx\");\n for (const index of IDENTITY_INDEXES) {\n // oxlint-disable-next-line no-await-in-loop -- index creation follows the legacy drops\n await ensureIndex(run, index.table, index.name, index.sql);\n }\n if (await indexIsUnique(run, \"builds\", \"builds_project_gitsha_idx\")) {\n await dropIndexIfExists(run, \"builds\", \"builds_project_gitsha_idx\");\n }\n await ensureIndex(\n run,\n \"builds\",\n \"builds_project_gitsha_idx\",\n \"CREATE INDEX builds_project_gitsha_idx ON builds (project_id, git_sha)\",\n );\n await ensureIndex(\n run,\n \"design_links\",\n \"design_links_project_import_path_idx\",\n \"CREATE UNIQUE INDEX design_links_project_import_path_idx ON design_links (project_id, import_path)\",\n );\n}\n\nasync function migrateLegacyColumns(run: MigrationRunner): Promise<void> {\n await dropColumnIfExists(run, \"webhooks\", \"secret\");\n await migrateCommentsTable(run);\n}\n\nasync function ensureForeignKeys(run: MigrationRunner): Promise<void> {\n for (const foreignKey of FOREIGN_KEYS) {\n // oxlint-disable-next-line no-await-in-loop -- each constraint needs its own check\n await ensureForeignKey(run, foreignKey);\n }\n}\n\nasync function ensureForeignKey(\n run: MigrationRunner,\n foreignKey: ForeignKeyMigration,\n): Promise<void> {\n if (await hasForeignKey(run, foreignKey)) {\n return;\n }\n await deleteOrphans(run, foreignKey);\n const table = quote(foreignKey.table);\n const column = quote(foreignKey.column);\n const parent = quote(foreignKey.parent);\n const parentColumn = quote(foreignKey.parentColumn);\n await run(\n `ALTER TABLE ${table} ADD CONSTRAINT ${foreignKey.name} FOREIGN KEY (${column}) REFERENCES ${parent}(${parentColumn}) ON DELETE CASCADE`,\n );\n}\n\nasync function hasForeignKey(\n run: MigrationRunner,\n foreignKey: ForeignKeyMigration,\n): Promise<boolean> {\n const rows = await run(\n `SELECT 1 FROM information_schema.key_column_usage WHERE constraint_schema = DATABASE() AND table_name = '${foreignKey.table}' AND column_name = '${foreignKey.column}' AND referenced_table_name = '${foreignKey.parent}' AND referenced_column_name = '${foreignKey.parentColumn}' LIMIT 1`,\n );\n return rows.length > 0;\n}\n\nasync function deleteOrphans(run: MigrationRunner, foreignKey: ForeignKeyMigration): Promise<void> {\n const table = quote(foreignKey.table);\n const column = quote(foreignKey.column);\n const parent = quote(foreignKey.parent);\n const parentColumn = quote(foreignKey.parentColumn);\n await run(\n `DELETE FROM ${table} WHERE ${column} IS NOT NULL AND NOT EXISTS (SELECT 1 FROM ${parent} WHERE ${parent}.${parentColumn} = ${table}.${column})`,\n );\n}\n\nfunction quote(identifier: string): string {\n return `\\`${identifier}\\``;\n}\n\nasync function runDdl(run: MigrationRunner): Promise<void> {\n for (const statement of splitStatements(DDL)) {\n try {\n // oxlint-disable-next-line no-await-in-loop -- DDL order matters\n await run(statement);\n } catch (error) {\n if (!isDuplicateIndexError(error)) {\n throw error;\n }\n }\n }\n}\n\nfunction splitStatements(ddl: string): string[] {\n return ddl\n .split(\";\")\n .map((statement) => statement.trim())\n .filter((statement) => statement.length > 0);\n}\n\nfunction isDuplicateIndexError(error: unknown): boolean {\n const code = (error as { code?: string; errno?: number }).code;\n const errno = (error as { errno?: number }).errno;\n return code === \"ER_DUP_KEYNAME\" || errno === 1061;\n}\n\nasync function ensureColumn(run: MigrationRunner, migration: ColumnMigration): Promise<void> {\n if (await hasColumn(run, migration.table, migration.column)) {\n return;\n }\n try {\n await run(\n `ALTER TABLE ${migration.table} ADD COLUMN ${migration.column} ${migration.definition}`,\n );\n } catch (error) {\n // Another instance booting at the same time may have added it between the check and the ALTER.\n if (!isDuplicateColumnError(error)) {\n throw error;\n }\n }\n}\n\nfunction isDuplicateColumnError(error: unknown): boolean {\n const code = (error as { code?: string }).code;\n const errno = (error as { errno?: number }).errno;\n return code === \"ER_DUP_FIELDNAME\" || errno === 1060;\n}\n\nasync function hasColumn(run: MigrationRunner, table: string, column: string): Promise<boolean> {\n const rows = await run(\n `SELECT 1 FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = '${table}' AND column_name = '${column}' LIMIT 1`,\n );\n return rows.length > 0;\n}\n\nasync function ensureIndex(\n run: MigrationRunner,\n table: string,\n name: string,\n sql: string,\n): Promise<void> {\n if (await hasIndex(run, table, name)) {\n return;\n }\n await run(sql);\n}\n\nasync function hasIndex(run: MigrationRunner, table: string, name: string): Promise<boolean> {\n const rows = await run(\n `SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = '${table}' AND index_name = '${name}' LIMIT 1`,\n );\n return rows.length > 0;\n}\n\nasync function indexIsUnique(run: MigrationRunner, table: string, name: string): Promise<boolean> {\n const rows = await run(\n `SELECT NON_UNIQUE AS non_unique FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = '${table}' AND index_name = '${name}' LIMIT 1`,\n );\n const nonUnique = (rows[0] as { non_unique?: number | string } | undefined)?.non_unique;\n return nonUnique !== undefined && Number(nonUnique) === 0;\n}\n\nasync function dropIndexIfExists(run: MigrationRunner, table: string, name: string): Promise<void> {\n if (await hasIndex(run, table, name)) {\n await run(`ALTER TABLE ${table} DROP INDEX ${name}`);\n }\n}\n\nasync function dropColumnIfExists(\n run: MigrationRunner,\n table: string,\n column: string,\n): Promise<void> {\n if (await hasColumn(run, table, column)) {\n await run(`ALTER TABLE ${table} DROP COLUMN ${column}`);\n }\n}\n\nasync function migrateCommentsTable(run: MigrationRunner): Promise<void> {\n const rows = await run(\n \"SELECT IS_NULLABLE AS is_nullable FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'comments' AND column_name = 'user_id' LIMIT 1\",\n );\n const isNullable = (rows[0] as { is_nullable?: string } | undefined)?.is_nullable;\n if (isNullable !== \"NO\") {\n return;\n }\n await run(\"ALTER TABLE comments MODIFY COLUMN user_id VARCHAR(64) NULL\");\n}\n"],"mappings":"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"}
@@ -2,7 +2,6 @@ import __tsdown_shims_path from 'node:path';
2
2
  import __tsdown_shims_url from 'node:url';
3
3
  import { MysqlDatabaseOptions } from "./index.mjs";
4
4
  import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
5
- //#region src/planetscale.d.ts
6
5
  /**
7
6
  * PlanetScale (serverless MySQL HTTP) preset.
8
7
  * For now an alias over the mysql2 pool — PlanetScale's HTTP driver
@@ -13,6 +12,5 @@ import { DatabaseAdapter } from "@storyshelf/core/adapter/database";
13
12
  */
14
13
  type PlanetscaleDatabaseOptions = MysqlDatabaseOptions;
15
14
  declare function createPlanetscaleDatabase(options: PlanetscaleDatabaseOptions): DatabaseAdapter;
16
- //#endregion
17
15
  export { PlanetscaleDatabaseOptions, createPlanetscaleDatabase };
18
16
  //# sourceMappingURL=planetscale.d.mts.map
@@ -2,11 +2,10 @@ import "node:path";
2
2
  import "node:url";
3
3
  import.meta.url;
4
4
  import { createMysqlDatabase } from "./index.mjs";
5
- //#region src/planetscale.ts
5
+ // src/planetscale.ts
6
6
  function createPlanetscaleDatabase(options) {
7
7
  return createMysqlDatabase(options);
8
8
  }
9
- //#endregion
10
9
  export { createPlanetscaleDatabase };
11
10
 
12
11
  //# sourceMappingURL=planetscale.mjs.map