@byline/cli 4.8.0 → 4.10.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 (186) hide show
  1. package/README.md +56 -15
  2. package/dist/cli.js +16 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/doctor.d.ts +8 -0
  5. package/dist/commands/doctor.d.ts.map +1 -1
  6. package/dist/commands/doctor.js +8 -5
  7. package/dist/commands/doctor.js.map +1 -1
  8. package/dist/commands/init.d.ts +2 -1
  9. package/dist/commands/init.d.ts.map +1 -1
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/setup-checks.d.ts +2 -0
  12. package/dist/commands/setup-checks.d.ts.map +1 -1
  13. package/dist/commands/setup-checks.js +14 -6
  14. package/dist/commands/setup-checks.js.map +1 -1
  15. package/dist/commands/setup-flow.test.d.ts +2 -0
  16. package/dist/commands/setup-flow.test.d.ts.map +1 -0
  17. package/dist/commands/setup-flow.test.js +230 -0
  18. package/dist/commands/setup-flow.test.js.map +1 -0
  19. package/dist/commands/setup.d.ts +30 -1
  20. package/dist/commands/setup.d.ts.map +1 -1
  21. package/dist/commands/setup.js +71 -35
  22. package/dist/commands/setup.js.map +1 -1
  23. package/dist/context.d.ts +4 -1
  24. package/dist/context.d.ts.map +1 -1
  25. package/dist/context.js +3 -0
  26. package/dist/context.js.map +1 -1
  27. package/dist/lib/baseline-drift.test.d.ts +2 -0
  28. package/dist/lib/baseline-drift.test.d.ts.map +1 -0
  29. package/dist/lib/baseline-drift.test.js +97 -0
  30. package/dist/lib/baseline-drift.test.js.map +1 -0
  31. package/dist/lib/baseline.d.ts +10 -0
  32. package/dist/lib/baseline.d.ts.map +1 -0
  33. package/dist/lib/baseline.js +12 -0
  34. package/dist/lib/baseline.js.map +1 -0
  35. package/dist/lib/database/adapters.d.ts +54 -0
  36. package/dist/lib/database/adapters.d.ts.map +1 -0
  37. package/dist/lib/database/adapters.js +83 -0
  38. package/dist/lib/database/adapters.js.map +1 -0
  39. package/dist/lib/database/adapters.test.d.ts +2 -0
  40. package/dist/lib/database/adapters.test.d.ts.map +1 -0
  41. package/dist/lib/database/adapters.test.js +36 -0
  42. package/dist/lib/database/adapters.test.js.map +1 -0
  43. package/dist/lib/database/dialect.d.ts +12 -0
  44. package/dist/lib/database/dialect.d.ts.map +1 -0
  45. package/dist/lib/database/dialect.js +53 -0
  46. package/dist/lib/database/dialect.js.map +1 -0
  47. package/dist/lib/database/dialect.test.d.ts +2 -0
  48. package/dist/lib/database/dialect.test.d.ts.map +1 -0
  49. package/dist/lib/database/dialect.test.js +58 -0
  50. package/dist/lib/database/dialect.test.js.map +1 -0
  51. package/dist/lib/database/mysql.d.ts +21 -0
  52. package/dist/lib/database/mysql.d.ts.map +1 -0
  53. package/dist/lib/database/mysql.js +152 -0
  54. package/dist/lib/database/mysql.js.map +1 -0
  55. package/dist/lib/database/mysql.test.d.ts +2 -0
  56. package/dist/lib/database/mysql.test.d.ts.map +1 -0
  57. package/dist/lib/database/mysql.test.js +45 -0
  58. package/dist/lib/database/mysql.test.js.map +1 -0
  59. package/dist/lib/database/postgres.d.ts +8 -0
  60. package/dist/lib/database/postgres.d.ts.map +1 -0
  61. package/dist/lib/database/postgres.js +142 -0
  62. package/dist/lib/database/postgres.js.map +1 -0
  63. package/dist/lib/database/postgres.test.d.ts +2 -0
  64. package/dist/lib/database/postgres.test.d.ts.map +1 -0
  65. package/dist/lib/database/postgres.test.js +14 -0
  66. package/dist/lib/database/postgres.test.js.map +1 -0
  67. package/dist/lib/database/provisioner.d.ts +35 -0
  68. package/dist/lib/database/provisioner.d.ts.map +1 -0
  69. package/dist/lib/database/provisioner.js +20 -0
  70. package/dist/lib/database/provisioner.js.map +1 -0
  71. package/dist/lib/database/provisioner.test.d.ts +2 -0
  72. package/dist/lib/database/provisioner.test.d.ts.map +1 -0
  73. package/dist/lib/database/provisioner.test.js +32 -0
  74. package/dist/lib/database/provisioner.test.js.map +1 -0
  75. package/dist/lib/database/selection.d.ts +11 -0
  76. package/dist/lib/database/selection.d.ts.map +1 -0
  77. package/dist/lib/database/selection.js +52 -0
  78. package/dist/lib/database/selection.js.map +1 -0
  79. package/dist/lib/database/selection.test.d.ts +2 -0
  80. package/dist/lib/database/selection.test.d.ts.map +1 -0
  81. package/dist/lib/database/selection.test.js +58 -0
  82. package/dist/lib/database/selection.test.js.map +1 -0
  83. package/dist/lib/database/state.d.ts +4 -0
  84. package/dist/lib/database/state.d.ts.map +1 -0
  85. package/dist/lib/database/state.js +10 -0
  86. package/dist/lib/database/state.js.map +1 -0
  87. package/dist/lib/database/state.test.d.ts +2 -0
  88. package/dist/lib/database/state.test.d.ts.map +1 -0
  89. package/dist/lib/database/state.test.js +17 -0
  90. package/dist/lib/database/state.test.js.map +1 -0
  91. package/dist/lib/database/urls.d.ts +19 -0
  92. package/dist/lib/database/urls.d.ts.map +1 -0
  93. package/dist/lib/database/urls.js +14 -0
  94. package/dist/lib/database/urls.js.map +1 -0
  95. package/dist/lib/database/urls.test.d.ts +2 -0
  96. package/dist/lib/database/urls.test.d.ts.map +1 -0
  97. package/dist/lib/database/urls.test.js +31 -0
  98. package/dist/lib/database/urls.test.js.map +1 -0
  99. package/dist/lib/dependency-version.d.ts.map +1 -1
  100. package/dist/lib/dependency-version.js +5 -4
  101. package/dist/lib/dependency-version.js.map +1 -1
  102. package/dist/lib/template-hook-boundary.test.node.js +9 -4
  103. package/dist/lib/template-hook-boundary.test.node.js.map +1 -1
  104. package/dist/manifest/deps.d.ts +8 -1
  105. package/dist/manifest/deps.d.ts.map +1 -1
  106. package/dist/manifest/deps.js +40 -13
  107. package/dist/manifest/deps.js.map +1 -1
  108. package/dist/manifest/env.d.ts +4 -1
  109. package/dist/manifest/env.d.ts.map +1 -1
  110. package/dist/manifest/env.js +12 -6
  111. package/dist/manifest/env.js.map +1 -1
  112. package/dist/phases/db-init.d.ts +2 -1
  113. package/dist/phases/db-init.d.ts.map +1 -1
  114. package/dist/phases/db-init.js +141 -124
  115. package/dist/phases/db-init.js.map +1 -1
  116. package/dist/phases/db-init.test.d.ts +2 -0
  117. package/dist/phases/db-init.test.d.ts.map +1 -0
  118. package/dist/phases/db-init.test.js +144 -0
  119. package/dist/phases/db-init.test.js.map +1 -0
  120. package/dist/phases/db.d.ts +0 -8
  121. package/dist/phases/db.d.ts.map +1 -1
  122. package/dist/phases/db.js +69 -49
  123. package/dist/phases/db.js.map +1 -1
  124. package/dist/phases/db.test.d.ts +2 -0
  125. package/dist/phases/db.test.d.ts.map +1 -0
  126. package/dist/phases/db.test.js +69 -0
  127. package/dist/phases/db.test.js.map +1 -0
  128. package/dist/phases/deps.d.ts.map +1 -1
  129. package/dist/phases/deps.js +7 -4
  130. package/dist/phases/deps.js.map +1 -1
  131. package/dist/phases/deps.test.js +67 -5
  132. package/dist/phases/deps.test.js.map +1 -1
  133. package/dist/phases/env.d.ts.map +1 -1
  134. package/dist/phases/env.js +30 -21
  135. package/dist/phases/env.js.map +1 -1
  136. package/dist/phases/env.test.d.ts +2 -0
  137. package/dist/phases/env.test.d.ts.map +1 -0
  138. package/dist/phases/env.test.js +79 -0
  139. package/dist/phases/env.test.js.map +1 -0
  140. package/dist/phases/scaffold-smoke.test.js +44 -10
  141. package/dist/phases/scaffold-smoke.test.js.map +1 -1
  142. package/dist/phases/scaffold.d.ts.map +1 -1
  143. package/dist/phases/scaffold.js +32 -23
  144. package/dist/phases/scaffold.js.map +1 -1
  145. package/dist/phases/scaffold.test.js +22 -0
  146. package/dist/phases/scaffold.test.js.map +1 -1
  147. package/dist/phases/wire/vite-config.d.ts.map +1 -1
  148. package/dist/phases/wire/vite-config.js +2 -0
  149. package/dist/phases/wire/vite-config.js.map +1 -1
  150. package/dist/phases/wire/vite-config.test.js +25 -2
  151. package/dist/phases/wire/vite-config.test.js.map +1 -1
  152. package/dist/state.d.ts +6 -0
  153. package/dist/state.d.ts.map +1 -1
  154. package/dist/state.js +40 -4
  155. package/dist/state.js.map +1 -1
  156. package/dist/state.test.d.ts +2 -0
  157. package/dist/state.test.d.ts.map +1 -0
  158. package/dist/state.test.js +77 -0
  159. package/dist/state.test.js.map +1 -0
  160. package/dist/templates/byline-examples/collections/docs/admin.tsx +1 -1
  161. package/dist/templates/byline-examples/collections/docs/hooks.ts +1 -1
  162. package/dist/templates/byline-examples/collections/docs/schema.ts +8 -8
  163. package/dist/templates/byline-examples/collections/media/schema.ts +7 -7
  164. package/dist/templates/byline-examples/collections/news/schema.ts +7 -7
  165. package/dist/templates/byline-examples/collections/news-categories/schema.ts +7 -7
  166. package/dist/templates/byline-examples/collections/pages/schema.ts +7 -7
  167. package/dist/templates/byline-examples/scripts/import-docs.ts +1 -1
  168. package/dist/templates/dialects/mysql/byline/server.config.ts +105 -0
  169. package/dist/templates/dialects/mysql/byline-examples/server.config.ts +115 -0
  170. package/dist/templates/{byline-examples → dialects/postgres/byline-examples}/scripts/backfill-version-locales.ts +1 -1
  171. package/dist/templates/{byline-examples → dialects/postgres/byline-examples}/scripts/re-anchor.ts +1 -1
  172. package/dist/templates/{byline-examples → dialects/postgres/byline-examples}/server.config.ts +2 -2
  173. package/dist/templates/host/vite.config.ts +4 -3
  174. package/dist/templates/migrations/mysql/0000_unknown_kulan_gath.sql +412 -0
  175. package/dist/templates/migrations/mysql/meta/_journal.json +13 -0
  176. package/dist/test-helpers.d.ts +14 -2
  177. package/dist/test-helpers.d.ts.map +1 -1
  178. package/dist/test-helpers.js +12 -9
  179. package/dist/test-helpers.js.map +1 -1
  180. package/dist/types.d.ts +3 -0
  181. package/dist/types.d.ts.map +1 -1
  182. package/package.json +7 -2
  183. package/dist/templates/migrations/meta/0000_snapshot.json +0 -3471
  184. /package/dist/templates/{byline → dialects/postgres/byline}/server.config.ts +0 -0
  185. /package/dist/templates/migrations/{0000_mushy_luckman.sql → postgres/0000_mushy_luckman.sql} +0 -0
  186. /package/dist/templates/migrations/{meta → postgres/meta}/_journal.json +0 -0
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { dependencySpecsFor } from '../../manifest/deps.js';
3
+ import { envSpecsForAdapter } from '../../manifest/env.js';
4
+ import { DATABASE_ADAPTER_IDS, DATABASE_ADAPTERS, DEFAULT_DATABASE_ADAPTER, } from './adapters.js';
5
+ describe.each(DATABASE_ADAPTER_IDS)('%s database adapter registration', (id) => {
6
+ const adapter = DATABASE_ADAPTERS[id];
7
+ it('connects the adapter to its dependency and environment manifests', () => {
8
+ const dependencies = dependencySpecsFor({ dbAdapter: id, examples: true });
9
+ const environment = envSpecsForAdapter(id);
10
+ expect(dependencies.map((spec) => spec.name)).toContain(adapter.packageName);
11
+ if (adapter.searchPackageName) {
12
+ expect(dependencies.map((spec) => spec.name)).toContain(adapter.searchPackageName);
13
+ }
14
+ expect(environment.map((spec) => spec.key)).toContain(adapter.connectionEnvKey);
15
+ });
16
+ it('declares a coherent URL contract', () => {
17
+ expect(adapter.url.acceptedProtocols.length).toBeGreaterThan(0);
18
+ expect(adapter.url.defaultPort).toBeGreaterThan(0);
19
+ });
20
+ it('declares provisioner prerequisites without phase-level branching', () => {
21
+ expect(adapter.prerequisites).toEqual(id === 'postgres' ? ['pgcrypto'] : []);
22
+ });
23
+ });
24
+ it('keeps the default explicit and first without relying on registry key order', () => {
25
+ expect(DEFAULT_DATABASE_ADAPTER).toBe('postgres');
26
+ expect(DATABASE_ADAPTER_IDS[0]).toBe(DEFAULT_DATABASE_ADAPTER);
27
+ expect(DATABASE_ADAPTERS.mysql.selectionLabel).toContain('8.0.14');
28
+ });
29
+ it('keeps cross-adapter package and environment registrations unique', () => {
30
+ const adapters = DATABASE_ADAPTER_IDS.map((id) => DATABASE_ADAPTERS[id]);
31
+ expect(new Set(adapters.map((adapter) => adapter.packageName)).size).toBe(adapters.length);
32
+ const searchPackages = adapters.flatMap((adapter) => adapter.searchPackageName ? [adapter.searchPackageName] : []);
33
+ expect(new Set(searchPackages).size).toBe(searchPackages.length);
34
+ expect(new Set(adapters.map((adapter) => adapter.connectionEnvKey)).size).toBe(adapters.length);
35
+ });
36
+ //# sourceMappingURL=adapters.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapters.test.js","sourceRoot":"","sources":["../../../src/lib/database/adapters.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Context } from '../../context.js';
2
+ import type { DbDialect } from '../../types.js';
3
+ export declare const DB_DIALECTS: readonly import("../../types.js").DatabaseAdapterId[];
4
+ export interface DbDialectInspection {
5
+ state: 'resolved' | 'pending' | 'blocked';
6
+ dialect?: DbDialect;
7
+ reason?: string;
8
+ }
9
+ export declare function isDbDialect(value: unknown): value is DbDialect;
10
+ export declare function inspectDbDialect(ctx: Context): DbDialectInspection;
11
+ export declare function resolveDbDialect(ctx: Context): Promise<DbDialect | null>;
12
+ //# sourceMappingURL=dialect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialect.d.ts","sourceRoot":"","sources":["../../../src/lib/database/dialect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,eAAO,MAAM,WAAW,uDAAuB,CAAA;AAE/C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAA;IACzC,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,CA0BlE;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAsB9E"}
@@ -0,0 +1,53 @@
1
+ import { DATABASE_ADAPTER_IDS, DATABASE_ADAPTERS } from './adapters.js';
2
+ export const DB_DIALECTS = DATABASE_ADAPTER_IDS;
3
+ export function isDbDialect(value) {
4
+ return typeof value === 'string' && DB_DIALECTS.includes(value);
5
+ }
6
+ export function inspectDbDialect(ctx) {
7
+ const persisted = ctx.state.get().answers.dbDialect;
8
+ const requested = ctx.cliFlags.database;
9
+ if (requested !== undefined && !isDbDialect(requested)) {
10
+ return {
11
+ state: 'blocked',
12
+ reason: `invalid database dialect "${String(requested)}"; expected ${DB_DIALECTS.join(' or ')}`,
13
+ };
14
+ }
15
+ if (persisted !== undefined && !isDbDialect(persisted)) {
16
+ return {
17
+ state: 'blocked',
18
+ reason: `persisted database dialect "${String(persisted)}" is invalid`,
19
+ };
20
+ }
21
+ if (persisted && requested && persisted !== requested) {
22
+ return {
23
+ state: 'blocked',
24
+ reason: `this installation is already configured for ${persisted}; ` +
25
+ `changing it to ${requested} is a data migration, not an installer re-run`,
26
+ };
27
+ }
28
+ const dialect = persisted ?? requested;
29
+ return dialect ? { state: 'resolved', dialect } : { state: 'pending' };
30
+ }
31
+ export async function resolveDbDialect(ctx) {
32
+ const inspection = inspectDbDialect(ctx);
33
+ if (inspection.state === 'blocked') {
34
+ ctx.logger.error(inspection.reason ?? 'database dialect selection is invalid');
35
+ return null;
36
+ }
37
+ if (inspection.dialect) {
38
+ if (!ctx.state.get().answers.dbDialect) {
39
+ ctx.state.patchAnswers({ dbDialect: inspection.dialect });
40
+ }
41
+ return inspection.dialect;
42
+ }
43
+ const dialect = await ctx.prompter.select({
44
+ message: 'Which database should Byline use?',
45
+ options: DB_DIALECTS.map((id, index) => ({
46
+ value: id,
47
+ label: `${DATABASE_ADAPTERS[id].label}${index === 0 ? ' (default)' : ''}`,
48
+ })),
49
+ });
50
+ ctx.state.patchAnswers({ dbDialect: dialect });
51
+ return dialect;
52
+ }
53
+ //# sourceMappingURL=dialect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialect.js","sourceRoot":"","sources":["../../../src/lib/database/dialect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAIvE,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAA;AAQ/C,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAK,WAAiC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;AACxF,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,CAAA;IACnD,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAA;IAEvC,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;QACvD,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,6BAA6B,MAAM,CAAC,SAAS,CAAC,eAAe,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE;SAChG,CAAA;IACH,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,EAAE,CAAC;QACvD,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,+BAA+B,MAAM,CAAC,SAAS,CAAC,cAAc;SACvE,CAAA;IACH,CAAC;IACD,IAAI,SAAS,IAAI,SAAS,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACtD,OAAO;YACL,KAAK,EAAE,SAAS;YAChB,MAAM,EACJ,+CAA+C,SAAS,IAAI;gBAC5D,kBAAkB,SAAS,+CAA+C;SAC7E,CAAA;IACH,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,IAAI,SAAS,CAAA;IACtC,OAAO,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAA;AACxE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAY;IACjD,MAAM,UAAU,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAA;IACxC,IAAI,UAAU,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACnC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,IAAI,uCAAuC,CAAC,CAAA;QAC9E,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;QACvB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,OAAO,UAAU,CAAC,OAAO,CAAA;IAC3B,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAY;QACnD,OAAO,EAAE,mCAAmC;QAC5C,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YACvC,KAAK,EAAE,EAAE;YACT,KAAK,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE;SAC1E,CAAC,CAAC;KACJ,CAAC,CAAA;IACF,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;IAC9C,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dialect.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialect.test.d.ts","sourceRoot":"","sources":["../../../src/lib/database/dialect.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { rmSync } from 'node:fs';
2
+ import { afterEach, describe, expect, it } from 'vitest';
3
+ import { createTestContext } from '../../test-helpers.js';
4
+ import { inspectDbDialect, isDbDialect, resolveDbDialect } from './dialect.js';
5
+ const contexts = [];
6
+ afterEach(() => {
7
+ for (const ctx of contexts.splice(0))
8
+ rmSync(ctx.cwd, { recursive: true, force: true });
9
+ });
10
+ describe('database dialect selection', () => {
11
+ it.each([
12
+ ['postgres', true],
13
+ ['mysql', true],
14
+ ['sqlite', false],
15
+ ['', false],
16
+ [true, false],
17
+ ])('classifies %j as a supported dialect=%s', (value, supported) => {
18
+ expect(isDbDialect(value)).toBe(supported);
19
+ });
20
+ it('persists a command-line selection into fresh state', async () => {
21
+ const ctx = createTestContext({ dbDialect: undefined });
22
+ contexts.push(ctx);
23
+ ctx.cliFlags.database = 'mysql';
24
+ expect(await resolveDbDialect(ctx)).toBe('mysql');
25
+ expect(ctx.state.get().answers.dbDialect).toBe('mysql');
26
+ });
27
+ it('prompts for a fresh state and uses PostgreSQL as the first/default choice', async () => {
28
+ const ctx = createTestContext({ dbDialect: undefined });
29
+ contexts.push(ctx);
30
+ expect(await resolveDbDialect(ctx)).toBe('postgres');
31
+ expect(ctx.state.get().answers.dbDialect).toBe('postgres');
32
+ });
33
+ it('reuses the persisted dialect when the matching flag is supplied', async () => {
34
+ const ctx = createTestContext({ dbDialect: 'mysql' });
35
+ contexts.push(ctx);
36
+ ctx.cliFlags.database = 'mysql';
37
+ expect(inspectDbDialect(ctx)).toEqual({ state: 'resolved', dialect: 'mysql' });
38
+ expect(await resolveDbDialect(ctx)).toBe('mysql');
39
+ });
40
+ it('blocks a conflicting flag without changing persisted state', async () => {
41
+ const ctx = createTestContext({ dbDialect: 'postgres' });
42
+ contexts.push(ctx);
43
+ ctx.cliFlags.database = 'mysql';
44
+ expect(inspectDbDialect(ctx)).toMatchObject({ state: 'blocked' });
45
+ expect(await resolveDbDialect(ctx)).toBeNull();
46
+ expect(ctx.state.get().answers.dbDialect).toBe('postgres');
47
+ });
48
+ it('blocks an invalid value even when a caller bypasses CLI validation', () => {
49
+ const ctx = createTestContext({ dbDialect: undefined });
50
+ contexts.push(ctx);
51
+ ctx.cliFlags.database = 'sqlite';
52
+ expect(inspectDbDialect(ctx)).toMatchObject({
53
+ state: 'blocked',
54
+ reason: expect.stringContaining('sqlite'),
55
+ });
56
+ });
57
+ });
58
+ //# sourceMappingURL=dialect.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialect.test.js","sourceRoot":"","sources":["../../../src/lib/database/dialect.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { DbProvisioner } from './provisioner.js';
2
+ /**
3
+ * Keep this CLI-owned floor synchronized with
4
+ * `packages/db-mysql/src/lib/boot-check.ts`. The CLI cannot import the adapter
5
+ * because it verifies the server before installing host dependencies.
6
+ */
7
+ export declare const MYSQL_MINIMUM_VERSION: {
8
+ readonly major: 8;
9
+ readonly minor: 0;
10
+ readonly patch: 14;
11
+ };
12
+ export declare const mysqlProvisioner: DbProvisioner;
13
+ export declare function assertSupportedMySqlVersion(reported: unknown): asserts reported is string;
14
+ export declare function mysqlAccountSql(user: string): string;
15
+ export declare function mysqlProvisionStatements(args: {
16
+ database: string;
17
+ user: string;
18
+ password: string;
19
+ createDatabase: boolean;
20
+ }): string[];
21
+ //# sourceMappingURL=mysql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../../src/lib/database/mysql.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAEV,aAAa,EAGd,MAAM,kBAAkB,CAAA;AAEzB;;;;GAIG;AACH,eAAO,MAAM,qBAAqB;aAAK,KAAK,EAAE,CAAC;aAAE,KAAK,EAAE,CAAC;aAAE,KAAK,EAAE,EAAE;CAAW,CAAA;AAE/E,eAAO,MAAM,gBAAgB,EAAE,aA0C9B,CAAA;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,IAAI,MAAM,CAgBzF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,OAAO,CAAA;CACxB,GAAG,MAAM,EAAE,CAYX"}
@@ -0,0 +1,152 @@
1
+ import { drizzle } from 'drizzle-orm/mysql2';
2
+ import { migrate } from 'drizzle-orm/mysql2/migrator';
3
+ import { escapeId, escape as escapeValue } from 'mysql2';
4
+ import { createConnection, createPool } from 'mysql2/promise';
5
+ import { databaseAdapterDefinition } from './adapters.js';
6
+ import { parseDbUrl, withDbDatabase } from './urls.js';
7
+ /**
8
+ * Keep this CLI-owned floor synchronized with
9
+ * `packages/db-mysql/src/lib/boot-check.ts`. The CLI cannot import the adapter
10
+ * because it verifies the server before installing host dependencies.
11
+ */
12
+ export const MYSQL_MINIMUM_VERSION = { major: 8, minor: 0, patch: 14 };
13
+ export const mysqlProvisioner = {
14
+ adapter: 'mysql',
15
+ async verifyAdminConnection(adminUrl) {
16
+ const connection = await connect(adminUrl);
17
+ try {
18
+ return `MySQL ${await verifiedServerVersion(connection)}`;
19
+ }
20
+ finally {
21
+ await connection.end();
22
+ }
23
+ },
24
+ async inspectTarget(adminUrl, database) {
25
+ const connection = await connect(adminUrl);
26
+ try {
27
+ await verifiedServerVersion(connection);
28
+ const [schemas] = await connection.query('SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?', [database]);
29
+ if (schemas.length === 0)
30
+ return { exists: false, objects: [] };
31
+ const [objects] = await connection.query(`SELECT TABLE_NAME AS name
32
+ FROM information_schema.TABLES
33
+ WHERE TABLE_SCHEMA = ?
34
+ ORDER BY TABLE_NAME`, [database]);
35
+ return { exists: true, objects: objects.map((row) => row.name) };
36
+ }
37
+ finally {
38
+ await connection.end();
39
+ }
40
+ },
41
+ async provisionTarget(args) {
42
+ await provisionMysqlTarget(args);
43
+ },
44
+ async applyBaseline(args) {
45
+ await applyMysqlBaseline(args);
46
+ },
47
+ };
48
+ export function assertSupportedMySqlVersion(reported) {
49
+ if (typeof reported !== 'string' || reported.length === 0) {
50
+ throw new Error("@byline/cli: could not determine the MySQL server version — 'SELECT VERSION()' returned no usable result.");
51
+ }
52
+ if (/mariadb/i.test(reported))
53
+ throw unsupportedEngineError(reported);
54
+ const match = reported.match(/^(\d+)\.(\d+)\.(\d+)/);
55
+ const [major = 0, minor = 0, patch = 0] = match ? match.slice(1).map(Number) : [];
56
+ const supported = major > MYSQL_MINIMUM_VERSION.major ||
57
+ (major === MYSQL_MINIMUM_VERSION.major &&
58
+ (minor > MYSQL_MINIMUM_VERSION.minor ||
59
+ (minor === MYSQL_MINIMUM_VERSION.minor && patch >= MYSQL_MINIMUM_VERSION.patch)));
60
+ if (!supported)
61
+ throw unsupportedEngineError(reported);
62
+ }
63
+ export function mysqlAccountSql(user) {
64
+ return `${escapeValue(user)}@${escapeValue('%')}`;
65
+ }
66
+ export function mysqlProvisionStatements(args) {
67
+ const account = mysqlAccountSql(args.user);
68
+ const database = escapeId(args.database);
69
+ const statements = [
70
+ `CREATE USER IF NOT EXISTS ${account}`,
71
+ `ALTER USER ${account} IDENTIFIED BY ${escapeValue(args.password)}`,
72
+ ];
73
+ if (args.createDatabase) {
74
+ statements.push(`CREATE DATABASE ${database} CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci`);
75
+ }
76
+ statements.push(`GRANT ALL PRIVILEGES ON ${database}.* TO ${account}`);
77
+ return statements;
78
+ }
79
+ async function provisionMysqlTarget(args) {
80
+ const connection = await connect(args.adminUrl);
81
+ try {
82
+ await verifiedServerVersion(connection);
83
+ const database = escapeId(args.database);
84
+ if (args.reset) {
85
+ args.logger.step(`dropping database ${args.database}`);
86
+ await connection.query(`DROP DATABASE IF EXISTS ${database}`);
87
+ }
88
+ const [schemas] = await connection.query('SELECT SCHEMA_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ?', [args.database]);
89
+ const createDatabase = schemas.length === 0;
90
+ for (const statement of mysqlProvisionStatements({
91
+ database: args.database,
92
+ user: args.user,
93
+ password: args.password,
94
+ createDatabase,
95
+ })) {
96
+ args.logger.step(statement.split(' ').slice(0, 3).join(' '));
97
+ await connection.query(statement);
98
+ }
99
+ }
100
+ finally {
101
+ await connection.end();
102
+ }
103
+ }
104
+ async function applyMysqlBaseline(args) {
105
+ args.logger.step(`running MySQL baseline from ${args.migrationsFolder}`);
106
+ const pool = migrationPool(args.applicationUrl);
107
+ try {
108
+ await verifiedServerVersion(pool);
109
+ const db = drizzle(pool);
110
+ await migrate(db, { migrationsFolder: args.migrationsFolder });
111
+ const [rows] = await pool.query(`SELECT count(*) AS count
112
+ FROM information_schema.TABLES
113
+ WHERE TABLE_SCHEMA = DATABASE()`);
114
+ args.logger.success(`baseline applied — ${rows[0]?.count ?? 'unknown'} tables and views`);
115
+ }
116
+ finally {
117
+ await pool.end();
118
+ }
119
+ }
120
+ async function connect(raw) {
121
+ return createConnection(connectionOptions(raw));
122
+ }
123
+ function migrationPool(raw) {
124
+ return createPool({
125
+ ...connectionOptions(raw),
126
+ connectionLimit: 1,
127
+ });
128
+ }
129
+ async function verifiedServerVersion(connection) {
130
+ const [rows] = await connection.query('SELECT VERSION() AS v');
131
+ const version = rows[0]?.v;
132
+ assertSupportedMySqlVersion(version);
133
+ return version;
134
+ }
135
+ function connectionOptions(raw) {
136
+ const parsed = parseDbUrl(raw, 'mysql');
137
+ const database = parsed.database || databaseAdapterDefinition('mysql').defaultAdminDatabase;
138
+ const connection = withDbDatabase(parsed, database);
139
+ return {
140
+ host: connection.host,
141
+ port: connection.port,
142
+ user: connection.user,
143
+ password: connection.password,
144
+ database: connection.database,
145
+ timezone: 'Z',
146
+ };
147
+ }
148
+ function unsupportedEngineError(reported) {
149
+ const minimum = MYSQL_MINIMUM_VERSION;
150
+ return new Error(`@byline/cli requires MySQL ${minimum.major}.${minimum.minor}.${minimum.patch}+ for @byline/db-mysql compatibility (LATERAL joins); server reports ${reported}. MariaDB is not supported.`);
151
+ }
152
+ //# sourceMappingURL=mysql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.js","sourceRoot":"","sources":["../../../src/lib/database/mysql.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mysql.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.test.d.ts","sourceRoot":"","sources":["../../../src/lib/database/mysql.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { assertSupportedMySqlVersion, MYSQL_MINIMUM_VERSION, mysqlAccountSql, mysqlProvisionStatements, } from './mysql.js';
5
+ describe('MySQL version support', () => {
6
+ it.each(['8.0.14', '8.4.0', '9.7.1', '8.0.35-0ubuntu0.22.04.1'])('accepts %s', (version) => {
7
+ expect(() => assertSupportedMySqlVersion(version)).not.toThrow();
8
+ });
9
+ it.each(['8.0.13', '8.0.0', '5.7.44'])('rejects old MySQL %s against the numeric floor', (version) => {
10
+ expect(() => assertSupportedMySqlVersion(version)).toThrow(/@byline\/cli requires MySQL 8\.0\.14/);
11
+ });
12
+ it.each(['11.4.2-MariaDB', '5.5.5-10.11.2-MariaDB'])('rejects MariaDB %s explicitly', (version) => {
13
+ expect(() => assertSupportedMySqlVersion(version)).toThrow(/MariaDB is not supported/);
14
+ });
15
+ it.each([undefined, '', 'not-a-version'])('rejects malformed result %j clearly', (version) => {
16
+ expect(() => assertSupportedMySqlVersion(version)).toThrow(/@byline\/cli/);
17
+ });
18
+ it('pins the CLI floor to the adapter boot check', () => {
19
+ const adapterCheck = readFileSync(fileURLToPath(new URL('../../../../db-mysql/src/lib/boot-check.ts', import.meta.url)), 'utf8');
20
+ const match = adapterCheck.match(/const MIN = \{ major: (\d+), minor: (\d+), patch: (\d+) \}/);
21
+ expect(match, 'db-mysql minimum-version declaration changed shape').not.toBeNull();
22
+ expect(match?.slice(1).map(Number)).toEqual([
23
+ MYSQL_MINIMUM_VERSION.major,
24
+ MYSQL_MINIMUM_VERSION.minor,
25
+ MYSQL_MINIMUM_VERSION.patch,
26
+ ]);
27
+ });
28
+ });
29
+ describe('MySQL SQL escaping', () => {
30
+ it('escapes account names as values rather than identifiers', () => {
31
+ expect(mysqlAccountSql("editor'ops")).toBe("'editor\\'ops'@'%'");
32
+ });
33
+ it('escapes identifiers and hostile passwords in provisioning statements', () => {
34
+ const statements = mysqlProvisionStatements({
35
+ database: 'byline`content',
36
+ user: "editor'ops",
37
+ password: "p@ss'word\\end",
38
+ createDatabase: true,
39
+ });
40
+ expect(statements).toContain("ALTER USER 'editor\\'ops'@'%' IDENTIFIED BY 'p@ss\\'word\\\\end'");
41
+ expect(statements).toContain('CREATE DATABASE `byline``content` CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci');
42
+ expect(statements).toContain("GRANT ALL PRIVILEGES ON `byline``content`.* TO 'editor\\'ops'@'%'");
43
+ });
44
+ });
45
+ //# sourceMappingURL=mysql.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.test.js","sourceRoot":"","sources":["../../../src/lib/database/mysql.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Client } from 'pg';
2
+ import type { DbProvisioner } from './provisioner.js';
3
+ export declare const postgresProvisioner: DbProvisioner;
4
+ export declare function postgresRoleStatements(escaper: Pick<Client, 'escapeIdentifier' | 'escapeLiteral'>, user: string, password: string): {
5
+ create: string;
6
+ alter: string;
7
+ };
8
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../src/lib/database/postgres.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,IAAI,CAAA;AAI3B,OAAO,KAAK,EAEV,aAAa,EAGd,MAAM,kBAAkB,CAAA;AAEzB,eAAO,MAAM,mBAAmB,EAAE,aAmDjC,CAAA;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,kBAAkB,GAAG,eAAe,CAAC,EAC3D,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAMnC"}
@@ -0,0 +1,142 @@
1
+ import { drizzle } from 'drizzle-orm/node-postgres';
2
+ import { migrate } from 'drizzle-orm/node-postgres/migrator';
3
+ import { Client } from 'pg';
4
+ import { databaseAdapterDefinition } from './adapters.js';
5
+ import { buildDbUrl, parseDbUrl, withDbDatabase } from './urls.js';
6
+ export const postgresProvisioner = {
7
+ adapter: 'postgres',
8
+ async verifyAdminConnection(adminUrl) {
9
+ const connectionString = normalizedAdminUrl(adminUrl);
10
+ const client = new Client({ connectionString });
11
+ await client.connect();
12
+ try {
13
+ const result = await client.query('SELECT version() AS version');
14
+ return result.rows[0]?.version ?? 'PostgreSQL (version unavailable)';
15
+ }
16
+ finally {
17
+ await client.end().catch(() => { });
18
+ }
19
+ },
20
+ async inspectTarget(adminUrl, database) {
21
+ const adminConnection = parseAdminUrl(adminUrl);
22
+ const admin = new Client({ connectionString: buildDbUrl('postgres', adminConnection) });
23
+ await admin.connect();
24
+ try {
25
+ const exists = await admin.query('SELECT 1 FROM pg_database WHERE datname = $1', [database]);
26
+ if ((exists.rowCount ?? 0) === 0)
27
+ return { exists: false, objects: [] };
28
+ }
29
+ finally {
30
+ await admin.end().catch(() => { });
31
+ }
32
+ const target = new Client({
33
+ connectionString: buildDbUrl('postgres', withDbDatabase(adminConnection, database)),
34
+ });
35
+ await target.connect();
36
+ try {
37
+ const result = await target.query(`SELECT table_name AS name
38
+ FROM information_schema.tables
39
+ WHERE table_schema = 'public'
40
+ AND table_type IN ('BASE TABLE', 'VIEW')
41
+ ORDER BY table_name`);
42
+ return { exists: true, objects: result.rows.map((row) => row.name) };
43
+ }
44
+ finally {
45
+ await target.end().catch(() => { });
46
+ }
47
+ },
48
+ async provisionTarget(args) {
49
+ await provisionPostgresTarget(args);
50
+ },
51
+ async applyBaseline(args) {
52
+ await applyPostgresBaseline(args);
53
+ },
54
+ };
55
+ export function postgresRoleStatements(escaper, user, password) {
56
+ const identifier = escaper.escapeIdentifier(user);
57
+ return {
58
+ create: `CREATE ROLE ${identifier} WITH LOGIN`,
59
+ alter: `ALTER ROLE ${identifier} WITH PASSWORD ${escaper.escapeLiteral(password)}`,
60
+ };
61
+ }
62
+ async function provisionPostgresTarget(args) {
63
+ const adminConnection = parseAdminUrl(args.adminUrl);
64
+ const client = new Client({ connectionString: buildDbUrl('postgres', adminConnection) });
65
+ await client.connect();
66
+ try {
67
+ const userIdentifier = client.escapeIdentifier(args.user);
68
+ const databaseIdentifier = client.escapeIdentifier(args.database);
69
+ const roleStatements = postgresRoleStatements(client, args.user, args.password);
70
+ const roleExists = await client.query('SELECT 1 FROM pg_roles WHERE rolname = $1', [args.user]);
71
+ if ((roleExists.rowCount ?? 0) === 0) {
72
+ args.logger.step(`creating role ${args.user}`);
73
+ await client.query(roleStatements.create);
74
+ }
75
+ else {
76
+ args.logger.step(`role ${args.user} already exists`);
77
+ }
78
+ await client.query(roleStatements.alter);
79
+ if (args.reset) {
80
+ args.logger.step(`terminating connections to ${args.database}`);
81
+ await client.query(`SELECT pg_terminate_backend(pid)
82
+ FROM pg_stat_activity
83
+ WHERE datname = $1 AND pid <> pg_backend_pid()`, [args.database]);
84
+ args.logger.step(`dropping database ${args.database}`);
85
+ await client.query(`DROP DATABASE IF EXISTS ${databaseIdentifier}`);
86
+ }
87
+ const databaseExists = await client.query('SELECT 1 FROM pg_database WHERE datname = $1', [
88
+ args.database,
89
+ ]);
90
+ if ((databaseExists.rowCount ?? 0) === 0) {
91
+ args.logger.step(`creating database ${args.database}`);
92
+ await client.query(`CREATE DATABASE ${databaseIdentifier} WITH OWNER ${userIdentifier}`);
93
+ }
94
+ else {
95
+ args.logger.step(`database ${args.database} already exists — assigning owner`);
96
+ await client.query(`ALTER DATABASE ${databaseIdentifier} OWNER TO ${userIdentifier}`);
97
+ }
98
+ }
99
+ finally {
100
+ await client.end().catch(() => { });
101
+ }
102
+ const target = new Client({
103
+ connectionString: buildDbUrl('postgres', withDbDatabase(adminConnection, args.database)),
104
+ });
105
+ await target.connect();
106
+ try {
107
+ const userIdentifier = target.escapeIdentifier(args.user);
108
+ await target.query(`GRANT ALL ON SCHEMA public TO ${userIdentifier}`);
109
+ for (const extension of databaseAdapterDefinition('postgres').prerequisites) {
110
+ args.logger.step(`CREATE EXTENSION IF NOT EXISTS ${extension}`);
111
+ await target.query(`CREATE EXTENSION IF NOT EXISTS ${target.escapeIdentifier(extension)}`);
112
+ }
113
+ }
114
+ finally {
115
+ await target.end().catch(() => { });
116
+ }
117
+ }
118
+ async function applyPostgresBaseline(args) {
119
+ args.logger.step(`running PostgreSQL baseline from ${args.migrationsFolder}`);
120
+ const client = new Client({ connectionString: args.applicationUrl });
121
+ await client.connect();
122
+ try {
123
+ const db = drizzle(client);
124
+ await migrate(db, { migrationsFolder: args.migrationsFolder });
125
+ const result = await client.query(`SELECT count(*)::text AS count
126
+ FROM information_schema.tables
127
+ WHERE table_schema = 'public'`);
128
+ args.logger.success(`baseline applied — ${result.rows[0]?.count ?? 'unknown'} tables in public schema`);
129
+ }
130
+ finally {
131
+ await client.end().catch(() => { });
132
+ }
133
+ }
134
+ function normalizedAdminUrl(raw) {
135
+ return buildDbUrl('postgres', parseAdminUrl(raw));
136
+ }
137
+ function parseAdminUrl(raw) {
138
+ const parsed = parseDbUrl(raw, 'postgres');
139
+ const database = parsed.database || databaseAdapterDefinition('postgres').defaultAdminDatabase;
140
+ return withDbDatabase(parsed, database);
141
+ }
142
+ //# sourceMappingURL=postgres.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.js","sourceRoot":"","sources":["../../../src/lib/database/postgres.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=postgres.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.test.d.ts","sourceRoot":"","sources":["../../../src/lib/database/postgres.test.ts"],"names":[],"mappings":""}