@housekit/kit 0.1.16 → 0.1.18

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 (3) hide show
  1. package/README.md +4 -2
  2. package/dist/index.js +36 -12
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -15,6 +15,7 @@ HouseKit CLI brings a modern, streamlined developer experience to the ClickHouse
15
15
 
16
16
  - **Declarative Workflows**: Define your source of truth in TypeScript.
17
17
  - **Automatic Drift Detection**: Compares your code against the live DB schema instantly.
18
+ - **Engine-Aware Diffing**: Normalizes local engine objects vs. remote SQL to avoid false changes.
18
19
  - **Blue-Green Deployments**: Safe, zero-downtime structural changes for Materialized Views and Tables.
19
20
  - **Cluster Awareness**: First-class support for sharded clusters using `{cluster}` macros and sharding keys.
20
21
  - **Zero Runtime Dependencies**: Powered by `jiti` for native TS loading—no pre-compilation or heavy binaries required.
@@ -105,7 +106,7 @@ HouseKit simplifies managing Replicated and Distributed tables across a cluster.
105
106
 
106
107
  ```typescript
107
108
  // Define a table that lives on a cluster
108
- export const events = defineTable('events', { ... }, {
109
+ export const events = defineTable('events', (t) => ({ ... }), {
109
110
  engine: Engine.ReplicatedMergeTree(),
110
111
 
111
112
  // High Portability: Using '{cluster}' tells ClickHouse to use the
@@ -131,6 +132,7 @@ Have an existing database with 100 tables? Don't write the code by hand.
131
132
  bunx housekit pull --database production
132
133
  ```
133
134
  This will scan your ClickHouse instance and generate a clean, readable `schema.ts` file with all table definitions, engines, and settings preserved.
135
+ Engine strings from ClickHouse are stored as `customEngine` to guarantee a lossless round-trip.
134
136
 
135
137
  ---
136
138
 
@@ -144,4 +146,4 @@ This will scan your ClickHouse instance and generate a clean, readable `schema.t
144
146
 
145
147
  ## License
146
148
 
147
- MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
149
+ MIT © [Pablo Fernandez Ruiz](https://github.com/pablofdezr)
package/dist/index.js CHANGED
@@ -6004,7 +6004,7 @@ function resolveDatabase(config, name) {
6004
6004
  import { detectMaterializedViewDrift, extractMVQuery } from "@housekit/orm";
6005
6005
 
6006
6006
  // src/schema/diff.ts
6007
- import { normalizeHousekitMetadata, upgradeMetadataVersion } from "@housekit/orm";
6007
+ import { normalizeHousekitMetadata, upgradeMetadataVersion, renderEngineSQL } from "@housekit/orm";
6008
6008
  function columnType(col) {
6009
6009
  return col.toSQL();
6010
6010
  }
@@ -6290,9 +6290,20 @@ function diffTable(table, localCols, remote, opts) {
6290
6290
  warnings.push(`${label} differs (remote="${r || "unset"}", local="${l || "unset"}")`);
6291
6291
  }
6292
6292
  };
6293
- compare("engine", localOpts.engine || "MergeTree", remoteOpts.engine, () => {
6294
- destructiveReasons.push("engine change (requires shadow swap)");
6295
- });
6293
+ let localEngineSQL = "MergeTree";
6294
+ if (localOpts.customEngine) {
6295
+ localEngineSQL = localOpts.customEngine;
6296
+ } else if (localOpts.engine) {
6297
+ localEngineSQL = renderEngineSQL(localOpts.engine);
6298
+ }
6299
+ const normalizeEngine = (engine) => engine.replace(/\s+/g, "").replace(/\(\)/g, "").toLowerCase();
6300
+ const localEngineNorm = normalizeEngine(localEngineSQL);
6301
+ const remoteEngineNorm = normalizeEngine(remoteOpts.engine || "MergeTree");
6302
+ if (localEngineNorm !== remoteEngineNorm) {
6303
+ optionChanges.push("engine");
6304
+ destructiveReasons.push(`engine change (local="${localEngineSQL}", remote="${remoteOpts.engine}") (requires shadow swap)`);
6305
+ warnings.push("Engine mismatch requires full table recreation");
6306
+ }
6296
6307
  compare("orderBy", localOpts.orderBy, remoteOpts.orderBy, () => {
6297
6308
  destructiveReasons.push("order by change");
6298
6309
  plan.push(`ALTER TABLE \`${tableName}\` MODIFY ORDER BY (${normalizeClause(localOpts.orderBy)})`);
@@ -7314,7 +7325,11 @@ function extractHousekitMetadata2(comment) {
7314
7325
  }
7315
7326
  return null;
7316
7327
  }
7317
- function buildTableFile(table, columns, format3 = "ts", metadata) {
7328
+ function extractEngineFromCreate(statement) {
7329
+ const match2 = statement.match(/ENGINE\s*=\s*(.+?)(\s+(?:ORDER|PARTITION|PRIMARY|SAMPLE|TTL|SETTINGS|COMMENT)|$)/i);
7330
+ return match2 ? match2[1].trim() : "MergeTree()";
7331
+ }
7332
+ function buildTableFile(table, columns, engineSQL, format3 = "ts", metadata) {
7318
7333
  const mappedColumns = columns.map((col) => ({
7319
7334
  name: col.name,
7320
7335
  comment: col.comment,
@@ -7341,6 +7356,8 @@ function buildTableFile(table, columns, format3 = "ts", metadata) {
7341
7356
  const comment = `// Auto-generated on ${timestamp}
7342
7357
  `;
7343
7358
  const optionLines = [];
7359
+ const escapedEngine = engineSQL.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
7360
+ optionLines.push(` customEngine: "${escapedEngine}"`);
7344
7361
  if (metadata?.version) {
7345
7362
  optionLines.push(` metadataVersion: "${metadata.version}"`);
7346
7363
  } else {
@@ -7359,9 +7376,12 @@ ${optionLines.join(`,
7359
7376
  const exportStatement = `export const ${variableName} = defineTable('${table}', (t) => ({
7360
7377
  ${columnLines}
7361
7378
  })${optionsBlock});`;
7362
- return `${comment}import { t, defineTable, Engine } from '@housekit/orm';
7379
+ const importLine = `import { t, defineTable, Engine } from '@housekit/orm';`;
7380
+ const typeAlias = `
7381
+ `;
7382
+ return `${comment}${importLine}
7363
7383
 
7364
- ${exportStatement}
7384
+ ${exportStatement}${typeAlias}
7365
7385
  `;
7366
7386
  }
7367
7387
  function persistLanguagePreference(configPath, lang) {
@@ -7543,6 +7563,7 @@ async function pullCommand(options) {
7543
7563
  const createParsed = await createResult.json();
7544
7564
  const createRows = Array.isArray(createParsed) ? createParsed : createParsed?.data ?? [];
7545
7565
  const createStatement = Array.isArray(createRows) && createRows.length > 0 ? createRows[0]?.statement || "" : "";
7566
+ const engineSQL = extractEngineFromCreate(createStatement);
7546
7567
  let tableMetadata = null;
7547
7568
  try {
7548
7569
  const commentRes = await client.query({
@@ -7691,7 +7712,7 @@ async function pullCommand(options) {
7691
7712
  comment: commentFromDescribe || commentFromCreate
7692
7713
  };
7693
7714
  });
7694
- const content = buildTableFile(tableName, columns, fileFormat, tableMetadata);
7715
+ const content = buildTableFile(tableName, columns, engineSQL, fileFormat, tableMetadata);
7695
7716
  writeFileSync3(join4(targetDir, `${tableName}.${fileExtension}`), content);
7696
7717
  describeSpinner.stop();
7697
7718
  success(`Wrote schema file for ${quoteName(tableName)}`);
@@ -8764,22 +8785,25 @@ export default {
8764
8785
  mkdirSync4(schemaPath, { recursive: true });
8765
8786
  success(`Created schema directory: ${quoteName(schemaPath)}`);
8766
8787
  const exampleSchemaPath = join7(schemaPath, `logs.${fileType}`);
8788
+ const importLine = `import { t, defineTable, Engine } from '@housekit/orm';`;
8789
+ const typeAliasLine = `
8790
+ `;
8767
8791
  const exampleSchemaContent = `// Example table - This is a sample schema file to demonstrate HouseKit usage
8768
- import { t, defineTable, Engine } from '@housekit/orm';
8792
+ ${importLine}
8769
8793
 
8770
- export const logs = defineTable('logs', {
8794
+ export const logs = defineTable('logs', (t) => ({
8771
8795
  id: t.uuid('id').autoGenerate().primaryKey(),
8772
8796
  message: t.string('message').comment('Log message content'),
8773
8797
  level: t.enum('level', ['info', 'warning', 'error']).default('info').comment('Log severity level'),
8774
8798
  tags: t.array(t.string('tag')).nullable().comment('Array of tags for categorizing logs'),
8775
8799
  metadata: t.json('metadata').nullable().comment('Additional log metadata'),
8776
8800
  createdAt: t.timestamp('created_at').default('now()'),
8777
- }, {
8801
+ }), {
8778
8802
  engine: Engine.MergeTree(),
8779
8803
  orderBy: ['createdAt', 'id'],
8780
8804
  appendOnly: false
8781
8805
  });
8782
- `;
8806
+ ${typeAliasLine}`;
8783
8807
  writeFileSync4(exampleSchemaPath, exampleSchemaContent);
8784
8808
  info(`Created example schema: ${quoteName(exampleSchemaPath)}`);
8785
8809
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@housekit/kit",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "CLI tool for HouseKit - manage ClickHouse schemas, migrations, and database operations with type-safe workflows.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@clickhouse/client": "^1.14.0",
51
- "@housekit/orm": "^0.1.16",
51
+ "@housekit/orm": "workspace:*",
52
52
  "chalk": "^5.6.2",
53
53
  "cli-table3": "^0.6.5",
54
54
  "commander": "^12.0.0",