@nextlyhq/adapter-drizzle 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +9 -0
  3. package/dist/adapter-BxJVtttb.d.ts +592 -0
  4. package/dist/adapter-nvlxFkF-.d.cts +592 -0
  5. package/dist/core-CVO7WYDj.d.cts +74 -0
  6. package/dist/core-CVO7WYDj.d.ts +74 -0
  7. package/dist/error-um1d_3Uo.d.cts +105 -0
  8. package/dist/error-um1d_3Uo.d.ts +105 -0
  9. package/dist/index.cjs +1137 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.d.cts +57 -0
  12. package/dist/index.d.ts +57 -0
  13. package/dist/index.mjs +1134 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/dist/migration-BbO5meEV.d.cts +622 -0
  16. package/dist/migration-Qe70wDOC.d.ts +622 -0
  17. package/dist/migrations.cjs +195 -0
  18. package/dist/migrations.cjs.map +1 -0
  19. package/dist/migrations.d.cts +351 -0
  20. package/dist/migrations.d.ts +351 -0
  21. package/dist/migrations.mjs +185 -0
  22. package/dist/migrations.mjs.map +1 -0
  23. package/dist/schema/index.cjs +10 -0
  24. package/dist/schema/index.cjs.map +1 -0
  25. package/dist/schema/index.d.cts +133 -0
  26. package/dist/schema/index.d.ts +133 -0
  27. package/dist/schema/index.mjs +7 -0
  28. package/dist/schema/index.mjs.map +1 -0
  29. package/dist/schema-BDn8WfSL.d.cts +200 -0
  30. package/dist/schema-BIQ0YQZ_.d.ts +200 -0
  31. package/dist/types/index.cjs +24 -0
  32. package/dist/types/index.cjs.map +1 -0
  33. package/dist/types/index.d.cts +210 -0
  34. package/dist/types/index.d.ts +210 -0
  35. package/dist/types/index.mjs +21 -0
  36. package/dist/types/index.mjs.map +1 -0
  37. package/dist/version-check.cjs +154 -0
  38. package/dist/version-check.cjs.map +1 -0
  39. package/dist/version-check.d.cts +43 -0
  40. package/dist/version-check.d.ts +43 -0
  41. package/dist/version-check.mjs +150 -0
  42. package/dist/version-check.mjs.map +1 -0
  43. package/package.json +94 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version-check.ts"],"names":[],"mappings":"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","file":"version-check.cjs","sourcesContent":["// What: shared connect-time DB version check.\n// Why: each dialect has version-dependent capabilities (RENAME COLUMN on\n// SQLite 3.25+, transactional DDL on MySQL 8+, etc). This helper hard-fails\n// at connect on real dialects below minimum, lets recognized cloud variants\n// (MariaDB, TiDB, Aurora, PlanetScale, Vitess) proceed with a warning, and\n// hard-fails on completely unparseable strings. F17 in the schema\n// architecture plan.\n\n// Why local imports only: adapter-drizzle is upstream of nextly\n// in the dep graph. Importing from nextly here would create a\n// circular dependency. SupportedDialect and DatabaseError already exist\n// in adapter-drizzle's own type system.\nimport type { SupportedDialect } from \"./types\";\nimport type { DatabaseError } from \"./types/error\";\n\n// Why these minimums: PG 15 ships native MERGE + nbtree dedup needed for\n// our schema rename pipeline. MySQL 8.0 ships native RENAME COLUMN.\n// SQLite 3.38 ships strict tables + native unixepoch(). Older versions\n// would force fallback paths that v1 explicitly does not implement.\nexport const NEXTLY_MIN_DB_VERSIONS = {\n postgresql: { major: 15, minor: 0 },\n mysql: { major: 8, minor: 0 },\n sqlite: { major: 3, minor: 38 },\n} as const;\n\n// What: typed error thrown when a real DB version is below minimum or a\n// version string cannot be parsed.\n// Why: implements adapter-drizzle's DatabaseError interface so existing\n// isDatabaseError() type guards work. Stores dialect + detected/required\n// versions on the instance so callers can build upgrade-guidance UI from\n// the error fields without parsing the message.\nexport class UnsupportedDialectVersionError\n extends Error\n implements DatabaseError\n{\n public readonly kind = \"unsupported_version\" as const;\n public readonly dialect: SupportedDialect;\n public readonly detectedVersion: string;\n public readonly requiredVersion: string;\n public override readonly cause?: Error;\n\n constructor(args: {\n dialect: SupportedDialect;\n detectedVersion: string;\n requiredVersion: string;\n message: string;\n cause?: Error;\n }) {\n super(args.message);\n this.name = \"UnsupportedDialectVersionError\";\n this.dialect = args.dialect;\n this.detectedVersion = args.detectedVersion;\n this.requiredVersion = args.requiredVersion;\n this.cause = args.cause;\n }\n}\n\n// What: minimal duck-typed query interface so this helper does not pull in\n// pg / mysql2 / better-sqlite3 types directly.\n// Why: keeps adapter-drizzle dependency-free of the per-dialect drivers.\n// query() returns unknown because Promise<unknown> is already a subtype of\n// unknown; the helper awaits at the call site, which works for both sync\n// and async return shapes.\nexport interface VersionQueryClient {\n query?: (sql: string) => unknown;\n prepare?: (sql: string) => { get: () => unknown };\n}\n\n// What: optional callback so adapters can route the variant warning through\n// their own logger.\n// Why: the helper itself does not depend on a logger module; the adapter\n// owns logger config and is the right place to surface the warning.\nexport interface CheckDialectVersionOptions {\n onWarning?: (message: string) => void;\n}\n\n// What: doc URL embedded in error messages.\n// Why: gives users a single landing page with upgrade instructions per\n// dialect + cloud provider. Filled in PR-2.\nconst DOCS_URL = \"https://nextlyhq.com/docs/database/support\";\n\n// What: recognized MySQL-protocol-compatible variants. Detection of any of\n// these tokens in the version string causes a warning instead of a hard-\n// fail.\n// Why: these databases speak the MySQL wire protocol but use their own\n// version schemes (MariaDB 10.x, TiDB 6.x advertising MySQL 5.7, Aurora's\n// custom suffix). Hard-failing them would lock out a large fraction of\n// real cloud users on first install. We warn so the user knows we have not\n// regression-tested against their variant; we proceed because the wire\n// protocol is compatible enough that most operations work.\nconst VARIANT_TOKENS = [\n \"mariadb\",\n \"tidb\",\n \"aurora\",\n \"planetscale\",\n \"vitess\",\n] as const;\n\nconst POSTGRES_REGEX = /^PostgreSQL (\\d+)\\.(\\d+)/;\nconst MYSQL_REGEX = /^(\\d+)\\.(\\d+)\\.(\\d+)/;\nconst SQLITE_REGEX = /^(\\d+)\\.(\\d+)\\.(\\d+)/;\n\ninterface ParsedVersion {\n major: number;\n minor: number;\n}\n\nfunction parsePostgres(raw: string): ParsedVersion | null {\n const match = POSTGRES_REGEX.exec(raw);\n if (!match) return null;\n return { major: Number(match[1]), minor: Number(match[2]) };\n}\n\nfunction parseMySQL(raw: string): ParsedVersion | null {\n const match = MYSQL_REGEX.exec(raw);\n if (!match) return null;\n return { major: Number(match[1]), minor: Number(match[2]) };\n}\n\nfunction parseSQLite(raw: string): ParsedVersion | null {\n const match = SQLITE_REGEX.exec(raw);\n if (!match) return null;\n return { major: Number(match[1]), minor: Number(match[2]) };\n}\n\nfunction detectVariant(raw: string): string | null {\n const lower = raw.toLowerCase();\n for (const token of VARIANT_TOKENS) {\n if (lower.includes(token)) return token;\n }\n return null;\n}\n\n// What: compares detected version to required minimum.\n// Why: simple major-minor comparison is sufficient; we do not gate on patch.\nfunction meetsMinimum(\n detected: ParsedVersion,\n required: { major: number; minor: number }\n): boolean {\n if (detected.major > required.major) return true;\n if (detected.major < required.major) return false;\n return detected.minor >= required.minor;\n}\n\n// What: queries the DB for its version string and validates it under the\n// hybrid policy.\n// Why: called as the first DB-content step inside each adapter's connect()\n// method.\n//\n// Decision tree:\n// 1. Variant token present (MariaDB/TiDB/Aurora/PlanetScale/Vitess)?\n// -> emit warning via options.onWarning, return successfully.\n// 2. Strict regex matches and version >= minimum?\n// -> return successfully.\n// 3. Strict regex matches but version < minimum?\n// -> throw UnsupportedDialectVersionError (real-DB-too-old path).\n// 4. Strict regex does not match AND no variant token detected?\n// -> throw UnsupportedDialectVersionError (truly unparseable path).\nexport async function checkDialectVersion(\n client: VersionQueryClient,\n dialect: SupportedDialect,\n options?: CheckDialectVersionOptions\n): Promise<void> {\n const required = NEXTLY_MIN_DB_VERSIONS[dialect];\n const requiredStr = `${required.major}.${required.minor}+`;\n\n let raw: string;\n if (dialect === \"sqlite\") {\n raw = await querySqliteVersion(client);\n } else if (dialect === \"postgresql\") {\n raw = await queryPostgresVersion(client);\n } else {\n raw = await queryMysqlVersion(client);\n }\n\n // Step 1: variant check. Only meaningful for MySQL — Postgres-flavored\n // services (Neon, Supabase, RDS PG, Aurora PG) all return canonical\n // \"PostgreSQL X.Y...\" strings and pass the strict regex naturally.\n const variant = dialect === \"mysql\" ? detectVariant(raw) : null;\n if (variant) {\n const warning =\n `Detected ${variant} ('${raw}'). Nextly is regression-tested ` +\n `against real MySQL 8.0+ only; this MySQL-compatible variant is not ` +\n `officially supported in v1 but most operations should work. ` +\n `See ${DOCS_URL}.`;\n if (options?.onWarning) options.onWarning(warning);\n return;\n }\n\n // Step 2/3: strict regex parse for real dialects.\n let parsed: ParsedVersion | null;\n if (dialect === \"postgresql\") parsed = parsePostgres(raw);\n else if (dialect === \"mysql\") parsed = parseMySQL(raw);\n else parsed = parseSQLite(raw);\n\n if (!parsed) {\n // Step 4: truly unparseable. Hard-fail so we surface the issue at boot\n // rather than silently letting the user proceed and hit cryptic errors\n // later.\n throw new UnsupportedDialectVersionError({\n dialect,\n detectedVersion: raw,\n requiredVersion: requiredStr,\n message:\n `Could not parse ${dialect} version from '${raw}'. ` +\n `Nextly requires ${dialect} ${requiredStr}. ` +\n `If you are on a known MySQL-compatible variant we did not detect, ` +\n `please file an issue. See ${DOCS_URL}.`,\n });\n }\n\n if (!meetsMinimum(parsed, required)) {\n const labels: Record<SupportedDialect, string> = {\n postgresql: \"PostgreSQL\",\n mysql: \"MySQL\",\n sqlite: \"SQLite\",\n };\n throw new UnsupportedDialectVersionError({\n dialect,\n detectedVersion: `${parsed.major}.${parsed.minor}`,\n requiredVersion: requiredStr,\n message:\n `${labels[dialect]} ${requiredStr} required; detected ` +\n `${parsed.major}.${parsed.minor}. See ${DOCS_URL}.`,\n });\n }\n}\n\n// What: PG version query.\n// Why: pg's Client/PoolClient.query() returns { rows: [{ version: \"...\" }] }.\nasync function queryPostgresVersion(\n client: VersionQueryClient\n): Promise<string> {\n if (!client.query) {\n throw new Error(\"PostgreSQL client missing query() method\");\n }\n const result = (await client.query(\"SELECT version()\")) as {\n rows?: Array<{ version?: unknown }>;\n };\n const row = result.rows?.[0];\n const version = row?.version;\n if (typeof version !== \"string\") {\n throw new Error(\n `Unexpected response shape from SELECT version(): ${JSON.stringify(result)}`\n );\n }\n return version;\n}\n\n// What: MySQL version query.\n// Why: mysql2's Connection.query() returns [rows, fields] where rows[0]\n// has the column aliased as 'version'.\nasync function queryMysqlVersion(client: VersionQueryClient): Promise<string> {\n if (!client.query) {\n throw new Error(\"MySQL client missing query() method\");\n }\n const result = (await client.query(\"SELECT VERSION() AS version\")) as Array<\n Array<{ version?: unknown }>\n >;\n const row = result?.[0]?.[0];\n const version = row?.version;\n if (typeof version !== \"string\") {\n throw new Error(\n `Unexpected response shape from SELECT VERSION(): ${JSON.stringify(result)}`\n );\n }\n return version;\n}\n\n// What: SQLite version query via better-sqlite3's prepare/get.\n// Why: better-sqlite3 is synchronous, so this is a plain function returning\n// a resolved Promise. The helper awaits it uniformly with PG/MySQL paths.\nfunction querySqliteVersion(client: VersionQueryClient): Promise<string> {\n if (!client.prepare) {\n return Promise.reject(new Error(\"SQLite client missing prepare() method\"));\n }\n const stmt = client.prepare(\"SELECT sqlite_version() AS version\");\n const row = stmt.get() as { version?: unknown } | undefined;\n const version = row?.version;\n if (typeof version !== \"string\") {\n return Promise.reject(\n new Error(\n `Unexpected response shape from SELECT sqlite_version(): ${JSON.stringify(row)}`\n )\n );\n }\n return Promise.resolve(version);\n}\n"]}
@@ -0,0 +1,43 @@
1
+ import { S as SupportedDialect } from './core-CVO7WYDj.cjs';
2
+ import { a as DatabaseError } from './error-um1d_3Uo.cjs';
3
+
4
+ declare const NEXTLY_MIN_DB_VERSIONS: {
5
+ readonly postgresql: {
6
+ readonly major: 15;
7
+ readonly minor: 0;
8
+ };
9
+ readonly mysql: {
10
+ readonly major: 8;
11
+ readonly minor: 0;
12
+ };
13
+ readonly sqlite: {
14
+ readonly major: 3;
15
+ readonly minor: 38;
16
+ };
17
+ };
18
+ declare class UnsupportedDialectVersionError extends Error implements DatabaseError {
19
+ readonly kind: "unsupported_version";
20
+ readonly dialect: SupportedDialect;
21
+ readonly detectedVersion: string;
22
+ readonly requiredVersion: string;
23
+ readonly cause?: Error;
24
+ constructor(args: {
25
+ dialect: SupportedDialect;
26
+ detectedVersion: string;
27
+ requiredVersion: string;
28
+ message: string;
29
+ cause?: Error;
30
+ });
31
+ }
32
+ interface VersionQueryClient {
33
+ query?: (sql: string) => unknown;
34
+ prepare?: (sql: string) => {
35
+ get: () => unknown;
36
+ };
37
+ }
38
+ interface CheckDialectVersionOptions {
39
+ onWarning?: (message: string) => void;
40
+ }
41
+ declare function checkDialectVersion(client: VersionQueryClient, dialect: SupportedDialect, options?: CheckDialectVersionOptions): Promise<void>;
42
+
43
+ export { type CheckDialectVersionOptions, NEXTLY_MIN_DB_VERSIONS, UnsupportedDialectVersionError, type VersionQueryClient, checkDialectVersion };
@@ -0,0 +1,43 @@
1
+ import { S as SupportedDialect } from './core-CVO7WYDj.js';
2
+ import { a as DatabaseError } from './error-um1d_3Uo.js';
3
+
4
+ declare const NEXTLY_MIN_DB_VERSIONS: {
5
+ readonly postgresql: {
6
+ readonly major: 15;
7
+ readonly minor: 0;
8
+ };
9
+ readonly mysql: {
10
+ readonly major: 8;
11
+ readonly minor: 0;
12
+ };
13
+ readonly sqlite: {
14
+ readonly major: 3;
15
+ readonly minor: 38;
16
+ };
17
+ };
18
+ declare class UnsupportedDialectVersionError extends Error implements DatabaseError {
19
+ readonly kind: "unsupported_version";
20
+ readonly dialect: SupportedDialect;
21
+ readonly detectedVersion: string;
22
+ readonly requiredVersion: string;
23
+ readonly cause?: Error;
24
+ constructor(args: {
25
+ dialect: SupportedDialect;
26
+ detectedVersion: string;
27
+ requiredVersion: string;
28
+ message: string;
29
+ cause?: Error;
30
+ });
31
+ }
32
+ interface VersionQueryClient {
33
+ query?: (sql: string) => unknown;
34
+ prepare?: (sql: string) => {
35
+ get: () => unknown;
36
+ };
37
+ }
38
+ interface CheckDialectVersionOptions {
39
+ onWarning?: (message: string) => void;
40
+ }
41
+ declare function checkDialectVersion(client: VersionQueryClient, dialect: SupportedDialect, options?: CheckDialectVersionOptions): Promise<void>;
42
+
43
+ export { type CheckDialectVersionOptions, NEXTLY_MIN_DB_VERSIONS, UnsupportedDialectVersionError, type VersionQueryClient, checkDialectVersion };
@@ -0,0 +1,150 @@
1
+ // src/version-check.ts
2
+ var NEXTLY_MIN_DB_VERSIONS = {
3
+ postgresql: { major: 15, minor: 0 },
4
+ mysql: { major: 8, minor: 0 },
5
+ sqlite: { major: 3, minor: 38 }
6
+ };
7
+ var UnsupportedDialectVersionError = class extends Error {
8
+ kind = "unsupported_version";
9
+ dialect;
10
+ detectedVersion;
11
+ requiredVersion;
12
+ cause;
13
+ constructor(args) {
14
+ super(args.message);
15
+ this.name = "UnsupportedDialectVersionError";
16
+ this.dialect = args.dialect;
17
+ this.detectedVersion = args.detectedVersion;
18
+ this.requiredVersion = args.requiredVersion;
19
+ this.cause = args.cause;
20
+ }
21
+ };
22
+ var DOCS_URL = "https://nextlyhq.com/docs/database/support";
23
+ var VARIANT_TOKENS = [
24
+ "mariadb",
25
+ "tidb",
26
+ "aurora",
27
+ "planetscale",
28
+ "vitess"
29
+ ];
30
+ var POSTGRES_REGEX = /^PostgreSQL (\d+)\.(\d+)/;
31
+ var MYSQL_REGEX = /^(\d+)\.(\d+)\.(\d+)/;
32
+ var SQLITE_REGEX = /^(\d+)\.(\d+)\.(\d+)/;
33
+ function parsePostgres(raw) {
34
+ const match = POSTGRES_REGEX.exec(raw);
35
+ if (!match) return null;
36
+ return { major: Number(match[1]), minor: Number(match[2]) };
37
+ }
38
+ function parseMySQL(raw) {
39
+ const match = MYSQL_REGEX.exec(raw);
40
+ if (!match) return null;
41
+ return { major: Number(match[1]), minor: Number(match[2]) };
42
+ }
43
+ function parseSQLite(raw) {
44
+ const match = SQLITE_REGEX.exec(raw);
45
+ if (!match) return null;
46
+ return { major: Number(match[1]), minor: Number(match[2]) };
47
+ }
48
+ function detectVariant(raw) {
49
+ const lower = raw.toLowerCase();
50
+ for (const token of VARIANT_TOKENS) {
51
+ if (lower.includes(token)) return token;
52
+ }
53
+ return null;
54
+ }
55
+ function meetsMinimum(detected, required) {
56
+ if (detected.major > required.major) return true;
57
+ if (detected.major < required.major) return false;
58
+ return detected.minor >= required.minor;
59
+ }
60
+ async function checkDialectVersion(client, dialect, options) {
61
+ const required = NEXTLY_MIN_DB_VERSIONS[dialect];
62
+ const requiredStr = `${required.major}.${required.minor}+`;
63
+ let raw;
64
+ if (dialect === "sqlite") {
65
+ raw = await querySqliteVersion(client);
66
+ } else if (dialect === "postgresql") {
67
+ raw = await queryPostgresVersion(client);
68
+ } else {
69
+ raw = await queryMysqlVersion(client);
70
+ }
71
+ const variant = dialect === "mysql" ? detectVariant(raw) : null;
72
+ if (variant) {
73
+ const warning = `Detected ${variant} ('${raw}'). Nextly is regression-tested against real MySQL 8.0+ only; this MySQL-compatible variant is not officially supported in v1 but most operations should work. See ${DOCS_URL}.`;
74
+ if (options?.onWarning) options.onWarning(warning);
75
+ return;
76
+ }
77
+ let parsed;
78
+ if (dialect === "postgresql") parsed = parsePostgres(raw);
79
+ else if (dialect === "mysql") parsed = parseMySQL(raw);
80
+ else parsed = parseSQLite(raw);
81
+ if (!parsed) {
82
+ throw new UnsupportedDialectVersionError({
83
+ dialect,
84
+ detectedVersion: raw,
85
+ requiredVersion: requiredStr,
86
+ message: `Could not parse ${dialect} version from '${raw}'. Nextly requires ${dialect} ${requiredStr}. If you are on a known MySQL-compatible variant we did not detect, please file an issue. See ${DOCS_URL}.`
87
+ });
88
+ }
89
+ if (!meetsMinimum(parsed, required)) {
90
+ const labels = {
91
+ postgresql: "PostgreSQL",
92
+ mysql: "MySQL",
93
+ sqlite: "SQLite"
94
+ };
95
+ throw new UnsupportedDialectVersionError({
96
+ dialect,
97
+ detectedVersion: `${parsed.major}.${parsed.minor}`,
98
+ requiredVersion: requiredStr,
99
+ message: `${labels[dialect]} ${requiredStr} required; detected ${parsed.major}.${parsed.minor}. See ${DOCS_URL}.`
100
+ });
101
+ }
102
+ }
103
+ async function queryPostgresVersion(client) {
104
+ if (!client.query) {
105
+ throw new Error("PostgreSQL client missing query() method");
106
+ }
107
+ const result = await client.query("SELECT version()");
108
+ const row = result.rows?.[0];
109
+ const version = row?.version;
110
+ if (typeof version !== "string") {
111
+ throw new Error(
112
+ `Unexpected response shape from SELECT version(): ${JSON.stringify(result)}`
113
+ );
114
+ }
115
+ return version;
116
+ }
117
+ async function queryMysqlVersion(client) {
118
+ if (!client.query) {
119
+ throw new Error("MySQL client missing query() method");
120
+ }
121
+ const result = await client.query("SELECT VERSION() AS version");
122
+ const row = result?.[0]?.[0];
123
+ const version = row?.version;
124
+ if (typeof version !== "string") {
125
+ throw new Error(
126
+ `Unexpected response shape from SELECT VERSION(): ${JSON.stringify(result)}`
127
+ );
128
+ }
129
+ return version;
130
+ }
131
+ function querySqliteVersion(client) {
132
+ if (!client.prepare) {
133
+ return Promise.reject(new Error("SQLite client missing prepare() method"));
134
+ }
135
+ const stmt = client.prepare("SELECT sqlite_version() AS version");
136
+ const row = stmt.get();
137
+ const version = row?.version;
138
+ if (typeof version !== "string") {
139
+ return Promise.reject(
140
+ new Error(
141
+ `Unexpected response shape from SELECT sqlite_version(): ${JSON.stringify(row)}`
142
+ )
143
+ );
144
+ }
145
+ return Promise.resolve(version);
146
+ }
147
+
148
+ export { NEXTLY_MIN_DB_VERSIONS, UnsupportedDialectVersionError, checkDialectVersion };
149
+ //# sourceMappingURL=version-check.mjs.map
150
+ //# sourceMappingURL=version-check.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/version-check.ts"],"names":[],"mappings":"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","file":"version-check.mjs","sourcesContent":["// What: shared connect-time DB version check.\n// Why: each dialect has version-dependent capabilities (RENAME COLUMN on\n// SQLite 3.25+, transactional DDL on MySQL 8+, etc). This helper hard-fails\n// at connect on real dialects below minimum, lets recognized cloud variants\n// (MariaDB, TiDB, Aurora, PlanetScale, Vitess) proceed with a warning, and\n// hard-fails on completely unparseable strings. F17 in the schema\n// architecture plan.\n\n// Why local imports only: adapter-drizzle is upstream of nextly\n// in the dep graph. Importing from nextly here would create a\n// circular dependency. SupportedDialect and DatabaseError already exist\n// in adapter-drizzle's own type system.\nimport type { SupportedDialect } from \"./types\";\nimport type { DatabaseError } from \"./types/error\";\n\n// Why these minimums: PG 15 ships native MERGE + nbtree dedup needed for\n// our schema rename pipeline. MySQL 8.0 ships native RENAME COLUMN.\n// SQLite 3.38 ships strict tables + native unixepoch(). Older versions\n// would force fallback paths that v1 explicitly does not implement.\nexport const NEXTLY_MIN_DB_VERSIONS = {\n postgresql: { major: 15, minor: 0 },\n mysql: { major: 8, minor: 0 },\n sqlite: { major: 3, minor: 38 },\n} as const;\n\n// What: typed error thrown when a real DB version is below minimum or a\n// version string cannot be parsed.\n// Why: implements adapter-drizzle's DatabaseError interface so existing\n// isDatabaseError() type guards work. Stores dialect + detected/required\n// versions on the instance so callers can build upgrade-guidance UI from\n// the error fields without parsing the message.\nexport class UnsupportedDialectVersionError\n extends Error\n implements DatabaseError\n{\n public readonly kind = \"unsupported_version\" as const;\n public readonly dialect: SupportedDialect;\n public readonly detectedVersion: string;\n public readonly requiredVersion: string;\n public override readonly cause?: Error;\n\n constructor(args: {\n dialect: SupportedDialect;\n detectedVersion: string;\n requiredVersion: string;\n message: string;\n cause?: Error;\n }) {\n super(args.message);\n this.name = \"UnsupportedDialectVersionError\";\n this.dialect = args.dialect;\n this.detectedVersion = args.detectedVersion;\n this.requiredVersion = args.requiredVersion;\n this.cause = args.cause;\n }\n}\n\n// What: minimal duck-typed query interface so this helper does not pull in\n// pg / mysql2 / better-sqlite3 types directly.\n// Why: keeps adapter-drizzle dependency-free of the per-dialect drivers.\n// query() returns unknown because Promise<unknown> is already a subtype of\n// unknown; the helper awaits at the call site, which works for both sync\n// and async return shapes.\nexport interface VersionQueryClient {\n query?: (sql: string) => unknown;\n prepare?: (sql: string) => { get: () => unknown };\n}\n\n// What: optional callback so adapters can route the variant warning through\n// their own logger.\n// Why: the helper itself does not depend on a logger module; the adapter\n// owns logger config and is the right place to surface the warning.\nexport interface CheckDialectVersionOptions {\n onWarning?: (message: string) => void;\n}\n\n// What: doc URL embedded in error messages.\n// Why: gives users a single landing page with upgrade instructions per\n// dialect + cloud provider. Filled in PR-2.\nconst DOCS_URL = \"https://nextlyhq.com/docs/database/support\";\n\n// What: recognized MySQL-protocol-compatible variants. Detection of any of\n// these tokens in the version string causes a warning instead of a hard-\n// fail.\n// Why: these databases speak the MySQL wire protocol but use their own\n// version schemes (MariaDB 10.x, TiDB 6.x advertising MySQL 5.7, Aurora's\n// custom suffix). Hard-failing them would lock out a large fraction of\n// real cloud users on first install. We warn so the user knows we have not\n// regression-tested against their variant; we proceed because the wire\n// protocol is compatible enough that most operations work.\nconst VARIANT_TOKENS = [\n \"mariadb\",\n \"tidb\",\n \"aurora\",\n \"planetscale\",\n \"vitess\",\n] as const;\n\nconst POSTGRES_REGEX = /^PostgreSQL (\\d+)\\.(\\d+)/;\nconst MYSQL_REGEX = /^(\\d+)\\.(\\d+)\\.(\\d+)/;\nconst SQLITE_REGEX = /^(\\d+)\\.(\\d+)\\.(\\d+)/;\n\ninterface ParsedVersion {\n major: number;\n minor: number;\n}\n\nfunction parsePostgres(raw: string): ParsedVersion | null {\n const match = POSTGRES_REGEX.exec(raw);\n if (!match) return null;\n return { major: Number(match[1]), minor: Number(match[2]) };\n}\n\nfunction parseMySQL(raw: string): ParsedVersion | null {\n const match = MYSQL_REGEX.exec(raw);\n if (!match) return null;\n return { major: Number(match[1]), minor: Number(match[2]) };\n}\n\nfunction parseSQLite(raw: string): ParsedVersion | null {\n const match = SQLITE_REGEX.exec(raw);\n if (!match) return null;\n return { major: Number(match[1]), minor: Number(match[2]) };\n}\n\nfunction detectVariant(raw: string): string | null {\n const lower = raw.toLowerCase();\n for (const token of VARIANT_TOKENS) {\n if (lower.includes(token)) return token;\n }\n return null;\n}\n\n// What: compares detected version to required minimum.\n// Why: simple major-minor comparison is sufficient; we do not gate on patch.\nfunction meetsMinimum(\n detected: ParsedVersion,\n required: { major: number; minor: number }\n): boolean {\n if (detected.major > required.major) return true;\n if (detected.major < required.major) return false;\n return detected.minor >= required.minor;\n}\n\n// What: queries the DB for its version string and validates it under the\n// hybrid policy.\n// Why: called as the first DB-content step inside each adapter's connect()\n// method.\n//\n// Decision tree:\n// 1. Variant token present (MariaDB/TiDB/Aurora/PlanetScale/Vitess)?\n// -> emit warning via options.onWarning, return successfully.\n// 2. Strict regex matches and version >= minimum?\n// -> return successfully.\n// 3. Strict regex matches but version < minimum?\n// -> throw UnsupportedDialectVersionError (real-DB-too-old path).\n// 4. Strict regex does not match AND no variant token detected?\n// -> throw UnsupportedDialectVersionError (truly unparseable path).\nexport async function checkDialectVersion(\n client: VersionQueryClient,\n dialect: SupportedDialect,\n options?: CheckDialectVersionOptions\n): Promise<void> {\n const required = NEXTLY_MIN_DB_VERSIONS[dialect];\n const requiredStr = `${required.major}.${required.minor}+`;\n\n let raw: string;\n if (dialect === \"sqlite\") {\n raw = await querySqliteVersion(client);\n } else if (dialect === \"postgresql\") {\n raw = await queryPostgresVersion(client);\n } else {\n raw = await queryMysqlVersion(client);\n }\n\n // Step 1: variant check. Only meaningful for MySQL — Postgres-flavored\n // services (Neon, Supabase, RDS PG, Aurora PG) all return canonical\n // \"PostgreSQL X.Y...\" strings and pass the strict regex naturally.\n const variant = dialect === \"mysql\" ? detectVariant(raw) : null;\n if (variant) {\n const warning =\n `Detected ${variant} ('${raw}'). Nextly is regression-tested ` +\n `against real MySQL 8.0+ only; this MySQL-compatible variant is not ` +\n `officially supported in v1 but most operations should work. ` +\n `See ${DOCS_URL}.`;\n if (options?.onWarning) options.onWarning(warning);\n return;\n }\n\n // Step 2/3: strict regex parse for real dialects.\n let parsed: ParsedVersion | null;\n if (dialect === \"postgresql\") parsed = parsePostgres(raw);\n else if (dialect === \"mysql\") parsed = parseMySQL(raw);\n else parsed = parseSQLite(raw);\n\n if (!parsed) {\n // Step 4: truly unparseable. Hard-fail so we surface the issue at boot\n // rather than silently letting the user proceed and hit cryptic errors\n // later.\n throw new UnsupportedDialectVersionError({\n dialect,\n detectedVersion: raw,\n requiredVersion: requiredStr,\n message:\n `Could not parse ${dialect} version from '${raw}'. ` +\n `Nextly requires ${dialect} ${requiredStr}. ` +\n `If you are on a known MySQL-compatible variant we did not detect, ` +\n `please file an issue. See ${DOCS_URL}.`,\n });\n }\n\n if (!meetsMinimum(parsed, required)) {\n const labels: Record<SupportedDialect, string> = {\n postgresql: \"PostgreSQL\",\n mysql: \"MySQL\",\n sqlite: \"SQLite\",\n };\n throw new UnsupportedDialectVersionError({\n dialect,\n detectedVersion: `${parsed.major}.${parsed.minor}`,\n requiredVersion: requiredStr,\n message:\n `${labels[dialect]} ${requiredStr} required; detected ` +\n `${parsed.major}.${parsed.minor}. See ${DOCS_URL}.`,\n });\n }\n}\n\n// What: PG version query.\n// Why: pg's Client/PoolClient.query() returns { rows: [{ version: \"...\" }] }.\nasync function queryPostgresVersion(\n client: VersionQueryClient\n): Promise<string> {\n if (!client.query) {\n throw new Error(\"PostgreSQL client missing query() method\");\n }\n const result = (await client.query(\"SELECT version()\")) as {\n rows?: Array<{ version?: unknown }>;\n };\n const row = result.rows?.[0];\n const version = row?.version;\n if (typeof version !== \"string\") {\n throw new Error(\n `Unexpected response shape from SELECT version(): ${JSON.stringify(result)}`\n );\n }\n return version;\n}\n\n// What: MySQL version query.\n// Why: mysql2's Connection.query() returns [rows, fields] where rows[0]\n// has the column aliased as 'version'.\nasync function queryMysqlVersion(client: VersionQueryClient): Promise<string> {\n if (!client.query) {\n throw new Error(\"MySQL client missing query() method\");\n }\n const result = (await client.query(\"SELECT VERSION() AS version\")) as Array<\n Array<{ version?: unknown }>\n >;\n const row = result?.[0]?.[0];\n const version = row?.version;\n if (typeof version !== \"string\") {\n throw new Error(\n `Unexpected response shape from SELECT VERSION(): ${JSON.stringify(result)}`\n );\n }\n return version;\n}\n\n// What: SQLite version query via better-sqlite3's prepare/get.\n// Why: better-sqlite3 is synchronous, so this is a plain function returning\n// a resolved Promise. The helper awaits it uniformly with PG/MySQL paths.\nfunction querySqliteVersion(client: VersionQueryClient): Promise<string> {\n if (!client.prepare) {\n return Promise.reject(new Error(\"SQLite client missing prepare() method\"));\n }\n const stmt = client.prepare(\"SELECT sqlite_version() AS version\");\n const row = stmt.get() as { version?: unknown } | undefined;\n const version = row?.version;\n if (typeof version !== \"string\") {\n return Promise.reject(\n new Error(\n `Unexpected response shape from SELECT sqlite_version(): ${JSON.stringify(row)}`\n )\n );\n }\n return Promise.resolve(version);\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@nextlyhq/adapter-drizzle",
3
+ "version": "0.0.1",
4
+ "description": "Shared Drizzle ORM adapter logic for Nextly database adapters",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.cjs",
8
+ "module": "dist/index.mjs",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.cjs"
15
+ },
16
+ "./types": {
17
+ "types": "./dist/types/index.d.ts",
18
+ "import": "./dist/types/index.mjs",
19
+ "require": "./dist/types/index.cjs"
20
+ },
21
+ "./schema": {
22
+ "types": "./dist/schema/index.d.ts",
23
+ "import": "./dist/schema/index.mjs",
24
+ "require": "./dist/schema/index.cjs"
25
+ },
26
+ "./migrations": {
27
+ "types": "./dist/migrations.d.ts",
28
+ "import": "./dist/migrations.mjs",
29
+ "require": "./dist/migrations.cjs"
30
+ },
31
+ "./version-check": {
32
+ "types": "./dist/version-check.d.ts",
33
+ "import": "./dist/version-check.mjs",
34
+ "require": "./dist/version-check.cjs"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "engines": {
41
+ "node": ">=20.0.0"
42
+ },
43
+ "dependencies": {
44
+ "drizzle-orm": "^0.45.2"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^20.0.0",
48
+ "@vitest/coverage-v8": "^4.0.8",
49
+ "@vitest/ui": "^4.0.8",
50
+ "eslint": "^9.34.0",
51
+ "tsup": "^8.5.0",
52
+ "typescript": "^5.9.3",
53
+ "vite-tsconfig-paths": "^5.1.4",
54
+ "vitest": "^4.0.8",
55
+ "@nextlyhq/eslint-config": "0.0.1",
56
+ "@nextlyhq/tsconfig": "0.0.1"
57
+ },
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "git+https://github.com/nextlyhq/nextly.git",
61
+ "directory": "packages/adapter-drizzle"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public",
65
+ "registry": "https://registry.npmjs.org/",
66
+ "provenance": true
67
+ },
68
+ "homepage": "https://nextlyhq.com",
69
+ "bugs": {
70
+ "url": "https://github.com/nextlyhq/nextly/issues"
71
+ },
72
+ "author": "Nextly <contact@nextlyhq.com> (https://nextlyhq.com)",
73
+ "keywords": [
74
+ "nextly",
75
+ "drizzle",
76
+ "drizzle-orm",
77
+ "database",
78
+ "adapter",
79
+ "orm",
80
+ "cms"
81
+ ],
82
+ "scripts": {
83
+ "build": "tsup",
84
+ "dev": "tsup --watch",
85
+ "check-types": "tsc --noEmit",
86
+ "lint": "eslint . --max-warnings 0",
87
+ "lint:fix": "eslint . --fix",
88
+ "test": "vitest run --passWithNoTests",
89
+ "test:watch": "vitest",
90
+ "test:ui": "vitest --ui",
91
+ "test:coverage": "vitest run --coverage",
92
+ "clean": "rimraf dist"
93
+ }
94
+ }