relq 1.0.0 → 1.0.2

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 (159) hide show
  1. package/dist/cjs/addon/buffer/index.cjs +1881 -0
  2. package/dist/cjs/addon/pg/index.cjs +4812 -0
  3. package/dist/cjs/addon/pg-cursor/index.cjs +1451 -0
  4. package/dist/cjs/addon/pg-format/index.cjs +2270 -0
  5. package/dist/cjs/cli/commands/add.cjs +30 -1
  6. package/dist/cjs/cli/commands/branch.cjs +141 -0
  7. package/dist/cjs/cli/commands/checkout.cjs +134 -0
  8. package/dist/cjs/cli/commands/cherry-pick.cjs +283 -0
  9. package/dist/cjs/cli/commands/diff.cjs +148 -69
  10. package/dist/cjs/cli/commands/export.cjs +64 -5
  11. package/dist/cjs/cli/commands/fetch.cjs +34 -4
  12. package/dist/cjs/cli/commands/history.cjs +1 -1
  13. package/dist/cjs/cli/commands/import.cjs +283 -12
  14. package/dist/cjs/cli/commands/log.cjs +75 -0
  15. package/dist/cjs/cli/commands/merge.cjs +224 -0
  16. package/dist/cjs/cli/commands/migrate.cjs +1 -1
  17. package/dist/cjs/cli/commands/pull.cjs +123 -7
  18. package/dist/cjs/cli/commands/push.cjs +245 -29
  19. package/dist/cjs/cli/commands/remote.cjs +16 -0
  20. package/dist/cjs/cli/commands/reset.cjs +169 -0
  21. package/dist/cjs/cli/commands/resolve.cjs +193 -0
  22. package/dist/cjs/cli/commands/rollback.cjs +1 -1
  23. package/dist/cjs/cli/commands/stash.cjs +154 -0
  24. package/dist/cjs/cli/commands/status.cjs +48 -0
  25. package/dist/cjs/cli/commands/tag.cjs +147 -0
  26. package/dist/cjs/cli/index.cjs +46 -2
  27. package/dist/cjs/cli/utils/commit-manager.cjs +3 -3
  28. package/dist/cjs/cli/utils/env-loader.cjs +3 -2
  29. package/dist/cjs/cli/utils/fast-introspect.cjs +1 -1
  30. package/dist/cjs/cli/utils/project-root.cjs +56 -0
  31. package/dist/cjs/cli/utils/relqignore.cjs +296 -38
  32. package/dist/cjs/cli/utils/repo-manager.cjs +45 -3
  33. package/dist/cjs/cli/utils/schema-introspect.cjs +2 -2
  34. package/dist/cjs/cli/utils/sql-generator.cjs +1 -1
  35. package/dist/cjs/cli/utils/sql-parser.cjs +102 -13
  36. package/dist/cjs/condition/array-condition-builder.cjs +1 -1
  37. package/dist/cjs/condition/condition-collector.cjs +1 -1
  38. package/dist/cjs/condition/fulltext-condition-builder.cjs +1 -1
  39. package/dist/cjs/condition/geometric-condition-builder.cjs +1 -1
  40. package/dist/cjs/condition/jsonb-condition-builder.cjs +1 -1
  41. package/dist/cjs/condition/network-condition-builder.cjs +1 -1
  42. package/dist/cjs/condition/range-condition-builder.cjs +1 -1
  43. package/dist/cjs/copy/copy-builder.cjs +1 -1
  44. package/dist/cjs/core/query-builder.cjs +1 -1
  45. package/dist/cjs/core/relq-client.cjs +2 -2
  46. package/dist/cjs/count/count-builder.cjs +1 -1
  47. package/dist/cjs/cte/cte-builder.cjs +1 -1
  48. package/dist/cjs/delete/delete-builder.cjs +1 -1
  49. package/dist/cjs/function/create-function-builder.cjs +1 -1
  50. package/dist/cjs/functions/advanced-functions.cjs +1 -1
  51. package/dist/cjs/functions/case-builder.cjs +1 -1
  52. package/dist/cjs/functions/geometric-functions.cjs +1 -1
  53. package/dist/cjs/functions/network-functions.cjs +1 -1
  54. package/dist/cjs/functions/sql-functions.cjs +1 -1
  55. package/dist/cjs/indexing/create-index-builder.cjs +1 -1
  56. package/dist/cjs/indexing/drop-index-builder.cjs +1 -1
  57. package/dist/cjs/insert/conflict-builder.cjs +1 -1
  58. package/dist/cjs/insert/insert-builder.cjs +1 -1
  59. package/dist/cjs/maintenance/vacuum-builder.cjs +1 -1
  60. package/dist/cjs/pubsub/listen-notify-builder.cjs +1 -1
  61. package/dist/cjs/pubsub/listener-connection.cjs +2 -2
  62. package/dist/cjs/raw/raw-query-builder.cjs +1 -1
  63. package/dist/cjs/schema/schema-builder.cjs +1 -1
  64. package/dist/cjs/schema-definition/table-definition.cjs +1 -1
  65. package/dist/cjs/select/aggregate-builder.cjs +1 -1
  66. package/dist/cjs/select/select-builder.cjs +1 -1
  67. package/dist/cjs/sequence/sequence-builder.cjs +1 -1
  68. package/dist/cjs/table/alter-table-builder.cjs +1 -1
  69. package/dist/cjs/table/constraint-builder.cjs +1 -1
  70. package/dist/cjs/table/create-table-builder.cjs +1 -1
  71. package/dist/cjs/table/partition-builder.cjs +1 -1
  72. package/dist/cjs/table/truncate-builder.cjs +1 -1
  73. package/dist/cjs/transaction/transaction-builder.cjs +1 -1
  74. package/dist/cjs/trigger/create-trigger-builder.cjs +1 -1
  75. package/dist/cjs/update/array-update-builder.cjs +1 -1
  76. package/dist/cjs/update/update-builder.cjs +1 -1
  77. package/dist/cjs/utils/index.cjs +1 -1
  78. package/dist/cjs/view/create-view-builder.cjs +1 -1
  79. package/dist/cjs/window/window-builder.cjs +1 -1
  80. package/dist/esm/cli/commands/add.js +30 -1
  81. package/dist/esm/cli/commands/branch.js +105 -0
  82. package/dist/esm/cli/commands/checkout.js +98 -0
  83. package/dist/esm/cli/commands/cherry-pick.js +247 -0
  84. package/dist/esm/cli/commands/diff.js +148 -69
  85. package/dist/esm/cli/commands/export.js +64 -5
  86. package/dist/esm/cli/commands/fetch.js +35 -5
  87. package/dist/esm/cli/commands/history.js +1 -1
  88. package/dist/esm/cli/commands/import.js +283 -12
  89. package/dist/esm/cli/commands/log.js +74 -0
  90. package/dist/esm/cli/commands/merge.js +188 -0
  91. package/dist/esm/cli/commands/migrate.js +1 -1
  92. package/dist/esm/cli/commands/pull.js +124 -8
  93. package/dist/esm/cli/commands/push.js +246 -30
  94. package/dist/esm/cli/commands/remote.js +13 -0
  95. package/dist/esm/cli/commands/reset.js +133 -0
  96. package/dist/esm/cli/commands/resolve.js +157 -0
  97. package/dist/esm/cli/commands/rollback.js +1 -1
  98. package/dist/esm/cli/commands/stash.js +118 -0
  99. package/dist/esm/cli/commands/status.js +15 -0
  100. package/dist/esm/cli/commands/tag.js +111 -0
  101. package/dist/esm/cli/index.js +47 -3
  102. package/dist/esm/cli/utils/commit-manager.js +3 -3
  103. package/dist/esm/cli/utils/env-loader.js +3 -2
  104. package/dist/esm/cli/utils/fast-introspect.js +1 -1
  105. package/dist/esm/cli/utils/project-root.js +19 -0
  106. package/dist/esm/cli/utils/relqignore.js +277 -37
  107. package/dist/esm/cli/utils/repo-manager.js +41 -3
  108. package/dist/esm/cli/utils/schema-introspect.js +2 -2
  109. package/dist/esm/cli/utils/sql-generator.js +1 -1
  110. package/dist/esm/cli/utils/sql-parser.js +102 -13
  111. package/dist/esm/condition/array-condition-builder.js +1 -1
  112. package/dist/esm/condition/condition-collector.js +1 -1
  113. package/dist/esm/condition/fulltext-condition-builder.js +1 -1
  114. package/dist/esm/condition/geometric-condition-builder.js +1 -1
  115. package/dist/esm/condition/jsonb-condition-builder.js +1 -1
  116. package/dist/esm/condition/network-condition-builder.js +1 -1
  117. package/dist/esm/condition/range-condition-builder.js +1 -1
  118. package/dist/esm/copy/copy-builder.js +1 -1
  119. package/dist/esm/core/query-builder.js +1 -1
  120. package/dist/esm/core/relq-client.js +2 -2
  121. package/dist/esm/count/count-builder.js +1 -1
  122. package/dist/esm/cte/cte-builder.js +1 -1
  123. package/dist/esm/delete/delete-builder.js +1 -1
  124. package/dist/esm/function/create-function-builder.js +1 -1
  125. package/dist/esm/functions/advanced-functions.js +1 -1
  126. package/dist/esm/functions/case-builder.js +1 -1
  127. package/dist/esm/functions/geometric-functions.js +1 -1
  128. package/dist/esm/functions/network-functions.js +1 -1
  129. package/dist/esm/functions/sql-functions.js +1 -1
  130. package/dist/esm/indexing/create-index-builder.js +1 -1
  131. package/dist/esm/indexing/drop-index-builder.js +1 -1
  132. package/dist/esm/insert/conflict-builder.js +1 -1
  133. package/dist/esm/insert/insert-builder.js +1 -1
  134. package/dist/esm/maintenance/vacuum-builder.js +1 -1
  135. package/dist/esm/pubsub/listen-notify-builder.js +1 -1
  136. package/dist/esm/pubsub/listener-connection.js +2 -2
  137. package/dist/esm/raw/raw-query-builder.js +1 -1
  138. package/dist/esm/schema/schema-builder.js +1 -1
  139. package/dist/esm/schema-definition/table-definition.js +1 -1
  140. package/dist/esm/select/aggregate-builder.js +1 -1
  141. package/dist/esm/select/select-builder.js +1 -1
  142. package/dist/esm/sequence/sequence-builder.js +1 -1
  143. package/dist/esm/table/alter-table-builder.js +1 -1
  144. package/dist/esm/table/constraint-builder.js +1 -1
  145. package/dist/esm/table/create-table-builder.js +1 -1
  146. package/dist/esm/table/partition-builder.js +1 -1
  147. package/dist/esm/table/truncate-builder.js +1 -1
  148. package/dist/esm/transaction/transaction-builder.js +1 -1
  149. package/dist/esm/trigger/create-trigger-builder.js +1 -1
  150. package/dist/esm/update/array-update-builder.js +1 -1
  151. package/dist/esm/update/update-builder.js +1 -1
  152. package/dist/esm/utils/index.js +1 -1
  153. package/dist/esm/view/create-view-builder.js +1 -1
  154. package/dist/esm/window/window-builder.js +1 -1
  155. package/package.json +1 -1
  156. /package/dist/{addons/buffer.js → esm/addon/buffer/index.js} +0 -0
  157. /package/dist/{addons/pg.js → esm/addon/pg/index.js} +0 -0
  158. /package/dist/{addons/pg-cursor.js → esm/addon/pg-cursor/index.js} +0 -0
  159. /package/dist/{addons/pg-format.js → esm/addon/pg-format/index.js} +0 -0
@@ -1,7 +1,7 @@
1
1
  import { requireValidConfig } from "../utils/config-loader.js";
2
2
  import { getConnectionDescription } from "../utils/env-loader.js";
3
3
  import { colors, createSpinner } from "../utils/spinner.js";
4
- import { isInitialized, getHead, loadCommit, saveCommit, setFetchHead, shortHash, fetchRemoteCommits, ensureRemoteTable, } from "../utils/repo-manager.js";
4
+ import { isInitialized, getHead, loadCommit, saveCommit, setFetchHead, fetchRemoteCommits, ensureRemoteTable, } from "../utils/repo-manager.js";
5
5
  export async function fetchCommand(context) {
6
6
  const { config, flags } = context;
7
7
  if (!config) {
@@ -41,10 +41,25 @@ export async function fetchCommand(context) {
41
41
  setFetchHead(latestRemote.hash, projectRoot);
42
42
  spinner.succeed(`Fetched ${remoteCommits.length} commits (${newCommits} new)`);
43
43
  const localHead = getHead(projectRoot);
44
- if (localHead && latestRemote.hash !== localHead) {
45
- console.log('');
46
- console.log(`${colors.muted('From')} ${getConnectionDescription(connection)}`);
47
- console.log(` ${shortHash(localHead)}..${shortHash(latestRemote.hash)} ${colors.muted('main -> origin/main')}`);
44
+ const localCommits = localHead ? getAllLocalCommitHashes(projectRoot) : new Set();
45
+ const remoteHashes = new Set(remoteCommits.map(c => c.hash));
46
+ const behind = remoteCommits.filter(c => !localCommits.has(c.hash)).length;
47
+ const ahead = [...localCommits].filter(h => !remoteHashes.has(h)).length;
48
+ console.log('');
49
+ if (behind > 0 && ahead > 0) {
50
+ console.log(`Your branch and 'origin/main' have diverged.`);
51
+ console.log(` ${colors.yellow(String(ahead))} commit(s) ahead, ${colors.yellow(String(behind))} commit(s) behind`);
52
+ }
53
+ else if (behind > 0) {
54
+ console.log(`Your branch is ${colors.yellow(String(behind))} commit(s) behind 'origin/main'.`);
55
+ console.log(` ${colors.muted('Use')} ${colors.cyan('relq pull')} ${colors.muted('to update.')}`);
56
+ }
57
+ else if (ahead > 0) {
58
+ console.log(`Your branch is ${colors.green(String(ahead))} commit(s) ahead of 'origin/main'.`);
59
+ console.log(` ${colors.muted('Use')} ${colors.cyan('relq push')} ${colors.muted('to publish.')}`);
60
+ }
61
+ else {
62
+ console.log(`${colors.green('✓')} Your branch is up to date with 'origin/main'.`);
48
63
  }
49
64
  }
50
65
  catch (error) {
@@ -54,3 +69,18 @@ export async function fetchCommand(context) {
54
69
  }
55
70
  console.log('');
56
71
  }
72
+ function getAllLocalCommitHashes(projectRoot) {
73
+ const fs = require('fs');
74
+ const path = require('path');
75
+ const commitsDir = path.join(projectRoot, '.relq', 'commits');
76
+ const hashes = new Set();
77
+ if (fs.existsSync(commitsDir)) {
78
+ const files = fs.readdirSync(commitsDir);
79
+ for (const file of files) {
80
+ if (file.endsWith('.json')) {
81
+ hashes.add(file.replace('.json', ''));
82
+ }
83
+ }
84
+ }
85
+ return hashes;
86
+ }
@@ -87,7 +87,7 @@ function getMigrationFiles(migrationsDir) {
87
87
  .sort();
88
88
  }
89
89
  async function getAppliedMigrations(connection, tableName) {
90
- const { Pool } = await import("../../addon/pg.js");
90
+ const { Pool } = await import("../../addon/pg/index.js");
91
91
  const pool = new Pool({
92
92
  host: connection.host,
93
93
  port: connection.port || 5432,
@@ -5,6 +5,7 @@ import { generateTypeScript } from "../utils/type-generator.js";
5
5
  import { saveSnapshot, loadSnapshot, isInitialized, initRepository, stageChanges, addUnstagedChanges } from "../utils/repo-manager.js";
6
6
  import { compareSchemas } from "../utils/schema-comparator.js";
7
7
  import { getChangeDisplayName } from "../utils/change-tracker.js";
8
+ import { loadRelqignore, validateIgnoreDependencies, isTableIgnored, isColumnIgnored, isIndexIgnored, isConstraintIgnored, isEnumIgnored, isDomainIgnored, isSequenceIgnored, isCompositeTypeIgnored, isFunctionIgnored, } from "../utils/relqignore.js";
8
9
  import { colors, error, fatal, warning, hint, getWorkingTreeStatus, printDirtyWorkingTreeError, printMergeStrategyHelp, readSQLFile, createSpinner, formatBytes, } from "../utils/git-utils.js";
9
10
  export async function importCommand(sqlFilePath, options = {}) {
10
11
  const { includeFunctions = false, includeTriggers = false, force = false, dryRun = false } = options;
@@ -108,26 +109,143 @@ export async function importCommand(sqlFilePath, options = {}) {
108
109
  }
109
110
  }
110
111
  console.log('');
112
+ const ignorePatterns = loadRelqignore(projectRoot);
113
+ let ignoredCount = 0;
114
+ const filteredTables = parsedSchema.tables.filter(table => {
115
+ if (isTableIgnored(table.name, ignorePatterns).ignored) {
116
+ ignoredCount++;
117
+ return false;
118
+ }
119
+ return true;
120
+ }).map(table => {
121
+ const filteredColumns = table.columns.filter(col => {
122
+ if (isColumnIgnored(table.name, col.name, ignorePatterns).ignored) {
123
+ ignoredCount++;
124
+ return false;
125
+ }
126
+ return true;
127
+ });
128
+ const filteredIndexes = table.indexes.filter(idx => {
129
+ if (isIndexIgnored(table.name, idx.name, ignorePatterns).ignored) {
130
+ ignoredCount++;
131
+ return false;
132
+ }
133
+ return true;
134
+ });
135
+ const filteredConstraints = table.constraints.filter(con => {
136
+ if (isConstraintIgnored(table.name, con.name, ignorePatterns).ignored) {
137
+ ignoredCount++;
138
+ return false;
139
+ }
140
+ return true;
141
+ });
142
+ return {
143
+ ...table,
144
+ columns: filteredColumns,
145
+ indexes: filteredIndexes,
146
+ constraints: filteredConstraints,
147
+ };
148
+ });
149
+ const filteredEnums = parsedSchema.enums.filter(e => {
150
+ if (isEnumIgnored(e.name, ignorePatterns).ignored) {
151
+ ignoredCount++;
152
+ return false;
153
+ }
154
+ return true;
155
+ });
156
+ const filteredDomains = parsedSchema.domains.filter(d => {
157
+ if (isDomainIgnored(d.name, ignorePatterns).ignored) {
158
+ ignoredCount++;
159
+ return false;
160
+ }
161
+ return true;
162
+ });
163
+ const filteredCompositeTypes = parsedSchema.compositeTypes.filter(c => {
164
+ if (isCompositeTypeIgnored(c.name, ignorePatterns).ignored) {
165
+ ignoredCount++;
166
+ return false;
167
+ }
168
+ return true;
169
+ });
170
+ const filteredSequences = parsedSchema.sequences.filter(s => {
171
+ if (isSequenceIgnored(s.name, ignorePatterns).ignored) {
172
+ ignoredCount++;
173
+ return false;
174
+ }
175
+ return true;
176
+ });
177
+ const filteredFunctions = functions.filter(f => {
178
+ if (isFunctionIgnored(f.name, ignorePatterns).ignored) {
179
+ ignoredCount++;
180
+ return false;
181
+ }
182
+ return true;
183
+ });
184
+ const filteredSchema = {
185
+ tables: filteredTables,
186
+ enums: filteredEnums,
187
+ domains: filteredDomains,
188
+ compositeTypes: filteredCompositeTypes,
189
+ sequences: filteredSequences,
190
+ collations: parsedSchema.collations,
191
+ foreignTables: parsedSchema.foreignTables,
192
+ views: parsedSchema.views,
193
+ materializedViews: parsedSchema.materializedViews,
194
+ foreignServers: parsedSchema.foreignServers,
195
+ extensions: parsedSchema.extensions,
196
+ partitions: parsedSchema.partitions,
197
+ };
198
+ if (ignoredCount > 0) {
199
+ console.log(`${colors.gray(`ℹ ${ignoredCount} object(s) ignored by .relqignore`)}`);
200
+ }
201
+ const dependencyErrors = validateIgnoreDependencies({
202
+ tables: filteredTables.map(t => ({
203
+ name: t.name,
204
+ columns: t.columns.map(c => ({
205
+ name: c.name,
206
+ type: c.dataType,
207
+ default: c.defaultValue,
208
+ })),
209
+ })),
210
+ enums: parsedSchema.enums,
211
+ domains: parsedSchema.domains,
212
+ sequences: parsedSchema.sequences,
213
+ compositeTypes: parsedSchema.compositeTypes,
214
+ }, ignorePatterns);
215
+ if (dependencyErrors.length > 0) {
216
+ spinner.stop();
217
+ error('Dependency validation failed:');
218
+ console.log('');
219
+ for (const depError of dependencyErrors) {
220
+ console.log(` ${colors.red('✗')} ${depError.message}`);
221
+ }
222
+ console.log('');
223
+ hint('Either un-ignore the type or add the column to .relqignore');
224
+ process.exit(1);
225
+ }
111
226
  spinner.start('Generating TypeScript schema');
112
- const dbSchema = convertToDbSchema(parsedSchema, functions, triggers, comments);
227
+ const dbSchema = convertToDbSchema(filteredSchema, filteredFunctions, triggers, comments);
113
228
  const typescriptContent = generateTypeScript(dbSchema, {
114
229
  camelCase: true,
115
230
  includeFunctions,
116
231
  includeTriggers,
117
232
  });
118
233
  spinner.succeed('Generated TypeScript schema');
119
- const normalizedSchema = convertToNormalizedSchema(parsedSchema, functions, triggers);
234
+ const incomingSchema = convertToNormalizedSchema(filteredSchema, filteredFunctions, triggers);
120
235
  const existingSnapshot = loadSnapshot(projectRoot);
236
+ const replaceAll = options.theirs === true;
237
+ let mergedSchema;
121
238
  let changes = [];
122
239
  if (existingSnapshot) {
123
240
  spinner.start('Detecting changes');
241
+ mergedSchema = mergeSchemas(existingSnapshot, incomingSchema, replaceAll);
124
242
  const beforeSchema = snapshotToDbSchema(existingSnapshot);
125
- const afterSchema = snapshotToDbSchema(normalizedSchema);
243
+ const afterSchema = snapshotToDbSchema(mergedSchema);
126
244
  changes = compareSchemas(beforeSchema, afterSchema);
127
245
  spinner.stop();
128
246
  if (changes.length > 0) {
129
247
  console.log('');
130
- console.log(`${colors.bold('Changes detected:')}`);
248
+ console.log(`${colors.bold('Changes detected:')}${replaceAll ? colors.yellow(' (--theirs: full replacement)') : ''}`);
131
249
  console.log('');
132
250
  const creates = changes.filter(c => c.type === 'CREATE');
133
251
  const alters = changes.filter(c => c.type === 'ALTER');
@@ -149,11 +267,17 @@ export async function importCommand(sqlFilePath, options = {}) {
149
267
  }
150
268
  }
151
269
  if (drops.length > 0) {
152
- for (const change of drops.slice(0, 10)) {
153
- console.log(`\t${colors.red(getChangeDisplayName(change))}`);
270
+ if (replaceAll) {
271
+ for (const change of drops.slice(0, 10)) {
272
+ console.log(`\t${colors.red(getChangeDisplayName(change))}`);
273
+ }
274
+ if (drops.length > 10) {
275
+ console.log(`\t${colors.gray(`... and ${drops.length - 10} more deletions`)}`);
276
+ }
154
277
  }
155
- if (drops.length > 10) {
156
- console.log(`\t${colors.gray(`... and ${drops.length - 10} more deletions`)}`);
278
+ else {
279
+ console.log('');
280
+ console.log(`${colors.gray(`ℹ ${drops.length} object(s) only in existing schema (preserved)`)}`);
157
281
  }
158
282
  }
159
283
  console.log('');
@@ -165,15 +289,22 @@ export async function importCommand(sqlFilePath, options = {}) {
165
289
  }
166
290
  }
167
291
  else {
292
+ mergedSchema = incomingSchema;
168
293
  changes = [];
169
294
  console.log(`${colors.cyan('First import')} - creating initial snapshot`);
170
295
  }
296
+ const mergedDbSchema = snapshotToDbSchemaForGeneration(mergedSchema);
297
+ const finalTypescriptContent = generateTypeScript(mergedDbSchema, {
298
+ camelCase: true,
299
+ includeFunctions,
300
+ includeTriggers,
301
+ });
171
302
  if (dryRun) {
172
303
  console.log('');
173
304
  console.log(`${colors.yellow('Dry run mode')} - no files written`);
174
305
  console.log('');
175
306
  console.log('Would write:');
176
- console.log(` ${colors.cyan(options.output || './db/schema.ts')} ${colors.gray(`(${formatBytes(typescriptContent.length)})`)}`);
307
+ console.log(` ${colors.cyan(options.output || './db/schema.ts')} ${colors.gray(`(${formatBytes(finalTypescriptContent.length)})`)}`);
177
308
  console.log(` ${colors.cyan('.relq/snapshot.json')}`);
178
309
  if (changes.length > 0) {
179
310
  console.log(` Stage ${changes.length} change(s)`);
@@ -187,9 +318,9 @@ export async function importCommand(sqlFilePath, options = {}) {
187
318
  if (!fs.existsSync(outputDir)) {
188
319
  fs.mkdirSync(outputDir, { recursive: true });
189
320
  }
190
- fs.writeFileSync(absoluteOutputPath, typescriptContent, 'utf-8');
191
- console.log(`Written ${colors.cyan(outputPath)} ${colors.gray(`(${formatBytes(typescriptContent.length)})`)}`);
192
- saveSnapshot(normalizedSchema, projectRoot);
321
+ fs.writeFileSync(absoluteOutputPath, finalTypescriptContent, 'utf-8');
322
+ console.log(`Written ${colors.cyan(outputPath)} ${colors.gray(`(${formatBytes(finalTypescriptContent.length)})`)}`);
323
+ saveSnapshot(mergedSchema, projectRoot);
193
324
  if (changes.length > 0) {
194
325
  addUnstagedChanges(changes, projectRoot);
195
326
  stageChanges(['.'], projectRoot);
@@ -406,6 +537,17 @@ function convertToNormalizedSchema(parsed, functions = [], triggers = []) {
406
537
  forEach: 'STATEMENT',
407
538
  functionName: t.functionName || '',
408
539
  })),
540
+ views: parsed.views.map(v => ({
541
+ name: v.name,
542
+ schema: v.schema || 'public',
543
+ definition: v.definition,
544
+ })),
545
+ materializedViews: parsed.materializedViews.map(mv => ({
546
+ name: mv.name,
547
+ schema: mv.schema || 'public',
548
+ definition: mv.definition,
549
+ withData: mv.withData ?? true,
550
+ })),
409
551
  };
410
552
  }
411
553
  function snapshotToDbSchema(snapshot) {
@@ -485,4 +627,133 @@ function snapshotToDbSchema(snapshot) {
485
627
  sequences: [],
486
628
  });
487
629
  }
630
+ function snapshotToDbSchemaForGeneration(snapshot) {
631
+ return {
632
+ tables: snapshot.tables.map(t => ({
633
+ name: t.name,
634
+ schema: t.schema || 'public',
635
+ comment: null,
636
+ columns: t.columns.map(c => ({
637
+ name: c.name,
638
+ dataType: c.type,
639
+ udtName: c.type,
640
+ isNullable: c.nullable,
641
+ defaultValue: c.default ?? null,
642
+ isPrimaryKey: c.primaryKey || false,
643
+ isUnique: c.unique || false,
644
+ maxLength: null,
645
+ precision: null,
646
+ scale: null,
647
+ references: null,
648
+ comment: null,
649
+ isGenerated: c.isGenerated || false,
650
+ generationExpression: c.generationExpression || null,
651
+ identityGeneration: c.identity?.type || null,
652
+ })),
653
+ indexes: t.indexes.map(i => ({
654
+ name: i.name,
655
+ columns: Array.isArray(i.columns) ? i.columns : [i.columns],
656
+ isUnique: i.unique || false,
657
+ isPrimary: false,
658
+ type: i.type || 'btree',
659
+ definition: i.definition,
660
+ whereClause: i.whereClause,
661
+ comment: null,
662
+ })),
663
+ constraints: t.constraints?.map(c => ({
664
+ name: c.name,
665
+ type: c.type,
666
+ columns: [],
667
+ definition: c.definition,
668
+ })) || [],
669
+ rowCount: 0,
670
+ isPartitioned: t.isPartitioned || false,
671
+ partitionType: t.partitionType,
672
+ partitionKey: t.partitionKey ? [t.partitionKey].flat() : undefined,
673
+ })),
674
+ enums: snapshot.enums?.map(e => ({
675
+ name: e.name,
676
+ values: e.values,
677
+ })) || [],
678
+ domains: snapshot.domains?.map(d => ({
679
+ name: d.name,
680
+ baseType: d.baseType,
681
+ isNotNull: d.notNull || false,
682
+ defaultValue: d.default || null,
683
+ checkExpression: d.check || null,
684
+ check: d.check,
685
+ default: d.default,
686
+ notNull: d.notNull,
687
+ })) || [],
688
+ compositeTypes: snapshot.compositeTypes?.map(c => ({
689
+ name: c.name,
690
+ attributes: c.attributes || [],
691
+ })) || [],
692
+ sequences: snapshot.sequences?.map(s => ({
693
+ name: s.name,
694
+ dataType: s.dataType,
695
+ start: s.startValue,
696
+ increment: s.increment,
697
+ minValue: s.minValue,
698
+ maxValue: s.maxValue,
699
+ cache: s.cache,
700
+ cycle: s.cycle,
701
+ ownedBy: s.ownedBy ?? undefined,
702
+ })) || [],
703
+ extensions: (snapshot.extensions || []).map(e => typeof e === 'string' ? e : e.name),
704
+ partitions: [],
705
+ functions: snapshot.functions?.map(f => ({
706
+ name: f.name,
707
+ schema: 'public',
708
+ returnType: f.returnType,
709
+ argTypes: f.argTypes || [],
710
+ language: f.language,
711
+ definition: '',
712
+ isAggregate: false,
713
+ volatility: 'VOLATILE',
714
+ })) || [],
715
+ triggers: snapshot.triggers?.map(t => ({
716
+ name: t.name,
717
+ tableName: t.table,
718
+ timing: t.timing,
719
+ event: t.events?.[0] || '',
720
+ functionName: t.functionName || '',
721
+ definition: '',
722
+ isEnabled: t.isEnabled ?? true,
723
+ })) || [],
724
+ policies: [],
725
+ foreignServers: [],
726
+ foreignTables: [],
727
+ };
728
+ }
729
+ function mergeSchemas(existing, incoming, replaceAll = false) {
730
+ if (replaceAll) {
731
+ return incoming;
732
+ }
733
+ function mergeByName(existingArr, incomingArr) {
734
+ const incomingMap = new Map(incomingArr.map(item => [item.name, item]));
735
+ const resultMap = new Map();
736
+ for (const item of existingArr) {
737
+ resultMap.set(item.name, item);
738
+ }
739
+ for (const item of incomingArr) {
740
+ resultMap.set(item.name, item);
741
+ }
742
+ return Array.from(resultMap.values());
743
+ }
744
+ return {
745
+ extensions: mergeByName(existing.extensions || [], incoming.extensions || []),
746
+ enums: mergeByName(existing.enums || [], incoming.enums || []),
747
+ domains: mergeByName(existing.domains || [], incoming.domains || []),
748
+ compositeTypes: mergeByName(existing.compositeTypes || [], incoming.compositeTypes || []),
749
+ sequences: mergeByName(existing.sequences || [], incoming.sequences || []),
750
+ collations: mergeByName(existing.collations || [], incoming.collations || []),
751
+ tables: mergeByName(existing.tables || [], incoming.tables || []),
752
+ functions: mergeByName(existing.functions || [], incoming.functions || []),
753
+ triggers: mergeByName(existing.triggers || [], incoming.triggers || []),
754
+ views: mergeByName(existing.views || [], incoming.views || []),
755
+ materializedViews: mergeByName(existing.materializedViews || [], incoming.materializedViews || []),
756
+ foreignTables: mergeByName(existing.foreignTables || [], incoming.foreignTables || []),
757
+ };
758
+ }
488
759
  export default importCommand;
@@ -56,4 +56,78 @@ export async function logCommand(context) {
56
56
  console.log('');
57
57
  }
58
58
  }
59
+ export async function showCommand(context) {
60
+ const { args } = context;
61
+ const projectRoot = process.cwd();
62
+ const target = args[0];
63
+ console.log('');
64
+ if (!isInitialized(projectRoot)) {
65
+ console.log(`${colors.red('fatal:')} not a relq repository`);
66
+ return;
67
+ }
68
+ if (!target) {
69
+ console.log(`${colors.red('error:')} Please specify a commit or tag`);
70
+ console.log('');
71
+ console.log(`Usage: ${colors.cyan('relq show <commit|tag>')}`);
72
+ console.log('');
73
+ return;
74
+ }
75
+ const fs = require('fs');
76
+ const path = require('path');
77
+ const { resolveRef, loadCommit } = require('../utils/repo-manager');
78
+ const hash = resolveRef(target, projectRoot);
79
+ if (!hash) {
80
+ console.log(`${colors.red('error:')} Commit or tag not found: ${target}`);
81
+ console.log('');
82
+ console.log('Available commits:');
83
+ const commitsDir = path.join(projectRoot, '.relq', 'commits');
84
+ if (fs.existsSync(commitsDir)) {
85
+ const files = fs.readdirSync(commitsDir);
86
+ for (const f of files.slice(0, 5)) {
87
+ console.log(` ${colors.yellow(shortHash(f.replace('.json', '')))}`);
88
+ }
89
+ }
90
+ return;
91
+ }
92
+ const commitPath = path.join(projectRoot, '.relq', 'commits', `${hash}.json`);
93
+ if (!fs.existsSync(commitPath)) {
94
+ console.log(`${colors.red('error:')} Commit not found: ${hash}`);
95
+ return;
96
+ }
97
+ const commitData = JSON.parse(fs.readFileSync(commitPath, 'utf-8'));
98
+ console.log(`${colors.yellow(`commit ${commitData.hash}`)}`);
99
+ console.log(`Author: ${commitData.author}`);
100
+ console.log(`Date: ${formatDate(commitData.timestamp)}`);
101
+ console.log('');
102
+ console.log(` ${commitData.message}`);
103
+ console.log('');
104
+ if (commitData.stats) {
105
+ console.log(`${colors.bold('Stats:')}`);
106
+ console.log(` Tables: ${commitData.stats.tables}`);
107
+ console.log(` Columns: ${commitData.stats.columns}`);
108
+ console.log(` Indexes: ${commitData.stats.indexes || 0}`);
109
+ console.log('');
110
+ }
111
+ if (commitData.changes && commitData.changes.length > 0) {
112
+ console.log(`${colors.bold('Changes:')}`);
113
+ for (const change of commitData.changes.slice(0, 20)) {
114
+ const symbol = change.action === 'CREATE'
115
+ ? colors.green('+')
116
+ : change.action === 'DROP'
117
+ ? colors.red('-')
118
+ : colors.yellow('~');
119
+ console.log(` ${symbol} ${change.objectType.toLowerCase()}: ${change.name}`);
120
+ }
121
+ if (commitData.changes.length > 20) {
122
+ console.log(` ${colors.muted(`... and ${commitData.changes.length - 20} more`)}`);
123
+ }
124
+ console.log('');
125
+ }
126
+ if (commitData.sql && commitData.sql.trim()) {
127
+ console.log(`${colors.bold('SQL:')}`);
128
+ console.log('');
129
+ console.log(`${colors.cyan(commitData.sql)}`);
130
+ console.log('');
131
+ }
132
+ }
59
133
  export { logCommand as historyCommand };