@risingwave/wavelet-cli 0.1.3 → 0.2.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.
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +96 -51
- package/dist/codegen.js.map +1 -1
- package/dist/index.js +17 -17
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/codegen.ts +110 -63
- package/src/index.ts +17 -17
package/dist/codegen.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,qBAAqB,CAAA;AAI3F,wBAAsB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA+DzE"}
|
package/dist/codegen.js
CHANGED
|
@@ -8,48 +8,93 @@ const node_fs_1 = require("node:fs");
|
|
|
8
8
|
const pg_1 = __importDefault(require("pg"));
|
|
9
9
|
const { Client } = pg_1.default;
|
|
10
10
|
async function generateClient(config) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
for (const
|
|
15
|
-
|
|
16
|
-
// Query column info from RisingWave
|
|
17
|
-
const result = await client.query(`SELECT column_name, data_type FROM information_schema.columns WHERE table_name = $1 ORDER BY ordinal_position`, [viewName]);
|
|
18
|
-
const columns = result.rows.map((row) => ({
|
|
19
|
-
name: row.column_name,
|
|
20
|
-
tsType: pgTypeToTs(row.data_type),
|
|
21
|
-
}));
|
|
22
|
-
viewTypes.push({ name: viewName, columns });
|
|
23
|
-
}
|
|
24
|
-
catch {
|
|
25
|
-
// If we can't query schema, generate generic types
|
|
26
|
-
viewTypes.push({ name: viewName, columns: [] });
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
const streamTypes = [];
|
|
30
|
-
for (const [streamName, streamDef] of Object.entries(config.streams ?? {})) {
|
|
31
|
-
const columns = Object.entries(streamDef.columns).map(([name, type]) => ({
|
|
11
|
+
// Event types are always known from config
|
|
12
|
+
const eventTypes = [];
|
|
13
|
+
const events = config.events ?? config.streams ?? {};
|
|
14
|
+
for (const [eventName, eventDef] of Object.entries(events)) {
|
|
15
|
+
const columns = Object.entries(eventDef.columns).map(([name, type]) => ({
|
|
32
16
|
name,
|
|
33
17
|
tsType: columnTypeToTs(type),
|
|
34
18
|
}));
|
|
35
|
-
|
|
19
|
+
eventTypes.push({ name: eventName, columns });
|
|
20
|
+
}
|
|
21
|
+
// Query types: try config-declared columns first, then RisingWave introspection, then generic
|
|
22
|
+
const queryTypes = [];
|
|
23
|
+
let dbClient = null;
|
|
24
|
+
const queries = config.queries ?? config.views ?? {};
|
|
25
|
+
for (const [queryName, queryDef] of Object.entries(queries)) {
|
|
26
|
+
// Tier 1: explicit columns in config
|
|
27
|
+
const declaredColumns = getQueryColumns(queryDef);
|
|
28
|
+
if (declaredColumns) {
|
|
29
|
+
queryTypes.push({ name: queryName, columns: declaredColumns });
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
// Tier 2: introspect from RisingWave (if reachable)
|
|
33
|
+
if (!dbClient) {
|
|
34
|
+
dbClient = await tryConnect(config.database);
|
|
35
|
+
}
|
|
36
|
+
if (dbClient) {
|
|
37
|
+
try {
|
|
38
|
+
const result = await dbClient.query(`SELECT column_name, data_type FROM information_schema.columns WHERE table_name = $1 ORDER BY ordinal_position`, [queryName]);
|
|
39
|
+
if (result.rows.length > 0) {
|
|
40
|
+
queryTypes.push({
|
|
41
|
+
name: queryName,
|
|
42
|
+
columns: result.rows.map((row) => ({
|
|
43
|
+
name: row.column_name,
|
|
44
|
+
tsType: pgTypeToTs(row.data_type),
|
|
45
|
+
})),
|
|
46
|
+
});
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// Fall through to tier 3
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Tier 3: generic fallback
|
|
55
|
+
queryTypes.push({ name: queryName, columns: [] });
|
|
36
56
|
}
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
if (dbClient) {
|
|
58
|
+
await dbClient.end().catch(() => { });
|
|
59
|
+
}
|
|
60
|
+
const code = generateCode(queryTypes, eventTypes);
|
|
39
61
|
(0, node_fs_1.mkdirSync)('.wavelet', { recursive: true });
|
|
40
62
|
(0, node_fs_1.writeFileSync)('.wavelet/client.ts', code);
|
|
41
63
|
}
|
|
42
|
-
function
|
|
64
|
+
function getQueryColumns(queryDef) {
|
|
65
|
+
// SqlFragment has no columns
|
|
66
|
+
if ('_tag' in queryDef && queryDef._tag === 'sql')
|
|
67
|
+
return null;
|
|
68
|
+
// QueryDef might have explicit columns
|
|
69
|
+
const qd = queryDef;
|
|
70
|
+
if (!qd.columns)
|
|
71
|
+
return null;
|
|
72
|
+
return Object.entries(qd.columns).map(([name, type]) => ({
|
|
73
|
+
name,
|
|
74
|
+
tsType: columnTypeToTs(type),
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
async function tryConnect(connectionString) {
|
|
78
|
+
const client = new Client({ connectionString, connectionTimeoutMillis: 3000 });
|
|
79
|
+
try {
|
|
80
|
+
await client.connect();
|
|
81
|
+
return client;
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function generateCode(queries, events) {
|
|
43
88
|
let code = `// Auto-generated by wavelet generate - do not edit\n`;
|
|
44
89
|
code += `// Run 'npx wavelet generate' to regenerate\n\n`;
|
|
45
90
|
code += `import { WaveletClient } from '@risingwave/wavelet-sdk'\n`;
|
|
46
|
-
code += `import type {
|
|
47
|
-
//
|
|
48
|
-
for (const
|
|
49
|
-
const typeName = pascalCase(
|
|
50
|
-
if (
|
|
91
|
+
code += `import type { QueryHandle, EventHandle, Diff, WaveletClientOptions } from '@risingwave/wavelet-sdk'\n\n`;
|
|
92
|
+
// Query row types
|
|
93
|
+
for (const q of queries) {
|
|
94
|
+
const typeName = pascalCase(q.name) + 'Row';
|
|
95
|
+
if (q.columns.length > 0) {
|
|
51
96
|
code += `export interface ${typeName} {\n`;
|
|
52
|
-
for (const col of
|
|
97
|
+
for (const col of q.columns) {
|
|
53
98
|
code += ` ${col.name}: ${col.tsType}\n`;
|
|
54
99
|
}
|
|
55
100
|
code += `}\n\n`;
|
|
@@ -58,11 +103,11 @@ function generateCode(views, streams) {
|
|
|
58
103
|
code += `export type ${typeName} = Record<string, unknown>\n\n`;
|
|
59
104
|
}
|
|
60
105
|
}
|
|
61
|
-
//
|
|
62
|
-
for (const
|
|
63
|
-
const typeName = pascalCase(
|
|
106
|
+
// Event types
|
|
107
|
+
for (const ev of events) {
|
|
108
|
+
const typeName = pascalCase(ev.name) + 'Event';
|
|
64
109
|
code += `export interface ${typeName} {\n`;
|
|
65
|
-
for (const col of
|
|
110
|
+
for (const col of ev.columns) {
|
|
66
111
|
code += ` ${col.name}: ${col.tsType}\n`;
|
|
67
112
|
}
|
|
68
113
|
code += `}\n\n`;
|
|
@@ -73,30 +118,30 @@ function generateCode(views, streams) {
|
|
|
73
118
|
code += ` constructor(options: WaveletClientOptions) {\n`;
|
|
74
119
|
code += ` this.client = new WaveletClient(options)\n`;
|
|
75
120
|
code += ` }\n\n`;
|
|
76
|
-
//
|
|
77
|
-
code += `
|
|
78
|
-
for (const
|
|
79
|
-
const typeName = pascalCase(
|
|
80
|
-
code += ` ${
|
|
121
|
+
// Query accessors
|
|
122
|
+
code += ` queries = {\n`;
|
|
123
|
+
for (const q of queries) {
|
|
124
|
+
const typeName = pascalCase(q.name) + 'Row';
|
|
125
|
+
code += ` ${q.name}: this.client.query<${typeName}>('${q.name}'),\n`;
|
|
81
126
|
}
|
|
82
127
|
code += ` }\n\n`;
|
|
83
|
-
//
|
|
84
|
-
code += `
|
|
85
|
-
for (const
|
|
86
|
-
const typeName = pascalCase(
|
|
87
|
-
code += ` ${
|
|
128
|
+
// Event accessors
|
|
129
|
+
code += ` events = {\n`;
|
|
130
|
+
for (const ev of events) {
|
|
131
|
+
const typeName = pascalCase(ev.name) + 'Event';
|
|
132
|
+
code += ` ${ev.name}: this.client.event<${typeName}>('${ev.name}'),\n`;
|
|
88
133
|
}
|
|
89
134
|
code += ` }\n`;
|
|
90
135
|
code += `}\n\n`;
|
|
91
|
-
//
|
|
92
|
-
const
|
|
93
|
-
code += `export type
|
|
136
|
+
// Query name literal type
|
|
137
|
+
const queryNames = queries.map(q => `'${q.name}'`).join(' | ');
|
|
138
|
+
code += `export type QueryName = ${queryNames || 'never'}\n\n`;
|
|
94
139
|
// React hook overloads
|
|
95
140
|
code += `// React hook overloads for type-safe useWavelet\n`;
|
|
96
141
|
code += `import type { UseWaveletResult } from '@risingwave/wavelet-sdk/react'\n\n`;
|
|
97
|
-
for (const
|
|
98
|
-
const typeName = pascalCase(
|
|
99
|
-
code += `export declare function useWavelet(
|
|
142
|
+
for (const q of queries) {
|
|
143
|
+
const typeName = pascalCase(q.name) + 'Row';
|
|
144
|
+
code += `export declare function useWavelet(query: '${q.name}'): UseWaveletResult<${typeName}>\n`;
|
|
100
145
|
}
|
|
101
146
|
return code;
|
|
102
147
|
}
|
package/dist/codegen.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codegen.js","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":";;;;;AAMA,
|
|
1
|
+
{"version":3,"file":"codegen.js","sourceRoot":"","sources":["../src/codegen.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -10,8 +10,8 @@ Usage:
|
|
|
10
10
|
|
|
11
11
|
Commands:
|
|
12
12
|
dev Start local development server
|
|
13
|
-
generate Generate typed client from
|
|
14
|
-
push Sync
|
|
13
|
+
generate Generate typed client from query definitions
|
|
14
|
+
push Sync query definitions to Wavelet server
|
|
15
15
|
status Show current configuration and connection status
|
|
16
16
|
init Initialize a new Wavelet project
|
|
17
17
|
|
|
@@ -65,9 +65,9 @@ async function runInit() {
|
|
|
65
65
|
export default defineConfig({
|
|
66
66
|
database: process.env.WAVELET_DATABASE_URL ?? 'postgres://root@localhost:4566/dev',
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
// Define your
|
|
70
|
-
//
|
|
68
|
+
events: {
|
|
69
|
+
// Define your events here
|
|
70
|
+
// game_events: {
|
|
71
71
|
// columns: {
|
|
72
72
|
// user_id: 'string',
|
|
73
73
|
// action: 'string',
|
|
@@ -76,11 +76,11 @@ export default defineConfig({
|
|
|
76
76
|
// }
|
|
77
77
|
},
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
// Define your materialized views here
|
|
79
|
+
queries: {
|
|
80
|
+
// Define your queries (materialized views) here
|
|
81
81
|
// leaderboard: sql\`
|
|
82
82
|
// SELECT user_id, SUM(value) as total
|
|
83
|
-
// FROM
|
|
83
|
+
// FROM game_events
|
|
84
84
|
// GROUP BY user_id
|
|
85
85
|
// ORDER BY total DESC
|
|
86
86
|
// LIMIT 100
|
|
@@ -91,7 +91,7 @@ export default defineConfig({
|
|
|
91
91
|
console.log('Created wavelet.config.ts');
|
|
92
92
|
console.log('');
|
|
93
93
|
console.log('Next steps:');
|
|
94
|
-
console.log(' 1. Edit wavelet.config.ts to define your
|
|
94
|
+
console.log(' 1. Edit wavelet.config.ts to define your events and queries');
|
|
95
95
|
console.log(' 2. Run: wavelet dev');
|
|
96
96
|
}
|
|
97
97
|
async function runDev() {
|
|
@@ -106,7 +106,7 @@ async function runDev() {
|
|
|
106
106
|
const { DdlManager, WaveletServer } = await import('@risingwave/wavelet-server');
|
|
107
107
|
const ddl = new DdlManager(config.database);
|
|
108
108
|
await ddl.connect();
|
|
109
|
-
console.log('\nSyncing
|
|
109
|
+
console.log('\nSyncing events and queries...');
|
|
110
110
|
const actions = await ddl.sync(config);
|
|
111
111
|
printDdlActions(actions);
|
|
112
112
|
await ddl.close();
|
|
@@ -157,15 +157,15 @@ async function runStatus() {
|
|
|
157
157
|
const configPath = getConfigPath();
|
|
158
158
|
try {
|
|
159
159
|
const config = await loadConfig(configPath);
|
|
160
|
-
const
|
|
161
|
-
const
|
|
160
|
+
const eventCount = Object.keys(config.events ?? config.streams ?? {}).length;
|
|
161
|
+
const queryCount = Object.keys(config.queries ?? config.views ?? {}).length;
|
|
162
162
|
console.log(`Config: ${configPath}`);
|
|
163
163
|
console.log(`Database: ${config.database.replace(/\/\/[^@]+@/, '//***@')}`);
|
|
164
|
-
console.log(`
|
|
165
|
-
console.log(`
|
|
166
|
-
if (
|
|
167
|
-
console.log('\
|
|
168
|
-
for (const name of Object.keys(config.views ?? {})) {
|
|
164
|
+
console.log(`Events: ${eventCount}`);
|
|
165
|
+
console.log(`Queries: ${queryCount}`);
|
|
166
|
+
if (queryCount > 0) {
|
|
167
|
+
console.log('\nQueries:');
|
|
168
|
+
for (const name of Object.keys(config.queries ?? config.views ?? {})) {
|
|
169
169
|
console.log(` - ${name}`);
|
|
170
170
|
}
|
|
171
171
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAE/B,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuBZ,CAAA;AAED,KAAK,UAAU,IAAI;IACjB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,MAAM,OAAO,EAAE,CAAA;YACf,MAAK;QACP,KAAK,KAAK;YACR,MAAM,MAAM,EAAE,CAAA;YACd,MAAK;QACP,KAAK,UAAU;YACb,MAAM,WAAW,EAAE,CAAA;YACnB,MAAK;QACP,KAAK,MAAM;YACT,MAAM,OAAO,EAAE,CAAA;YACf,MAAK;QACP,KAAK,QAAQ;YACX,MAAM,SAAS,EAAE,CAAA;YACjB,MAAK;QACP,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI,CAAC;QACV,KAAK,SAAS;YACZ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACjB,MAAK;QACP;YACE,OAAO,CAAC,KAAK,CAAC,qBAAqB,OAAO,GAAG,CAAC,CAAA;YAC9C,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAA;YAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,OAAO;IACpB,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;IAE7D,IAAI,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAA;QAC1D,OAAM;IACR,CAAC;IAED,aAAa,CAAC,mBAAmB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BpC,CAAC,CAAA;IAEA,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACf,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IAC1B,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAEA,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AAE/B,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;CAuBZ,CAAA;AAED,KAAK,UAAU,IAAI;IACjB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,MAAM,OAAO,EAAE,CAAA;YACf,MAAK;QACP,KAAK,KAAK;YACR,MAAM,MAAM,EAAE,CAAA;YACd,MAAK;QACP,KAAK,UAAU;YACb,MAAM,WAAW,EAAE,CAAA;YACnB,MAAK;QACP,KAAK,MAAM;YACT,MAAM,OAAO,EAAE,CAAA;YACf,MAAK;QACP,KAAK,QAAQ;YACX,MAAM,SAAS,EAAE,CAAA;YACjB,MAAK;QACP,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI,CAAC;QACV,KAAK,SAAS;YACZ,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YACjB,MAAK;QACP;YACE,OAAO,CAAC,KAAK,CAAC,qBAAqB,OAAO,GAAG,CAAC,CAAA;YAC9C,OAAO,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAA;YAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,OAAO;IACpB,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAA;IAE7D,IAAI,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAA;QAC1D,OAAM;IACR,CAAC;IAED,aAAa,CAAC,mBAAmB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BpC,CAAC,CAAA;IAEA,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACf,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;IAC1B,OAAO,CAAC,GAAG,CAAC,+DAA+D,CAAC,CAAA;IAC5E,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;AACtC,CAAC;AAED,KAAK,UAAU,MAAM;IACnB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACzD,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,0BAA0B,CAAC,CAAA;IACrE,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAElC,OAAO,CAAC,GAAG,CAAC,uBAAuB,UAAU,KAAK,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAA;IAE3C,+BAA+B;IAC/B,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAEzD,kCAAkC;IAClC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAA;IAChF,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC3C,MAAM,GAAG,CAAC,OAAO,EAAE,CAAA;IAEnB,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAA;IAC9C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACtC,eAAe,CAAC,OAAO,CAAC,CAAA;IACxB,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;IAEjB,eAAe;IACf,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAA;IACxC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAA;IAEpB,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;QACjC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAA;QACnB,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAA;YACrC,SAAS,CAAC,IAAI,EAAE,CAAA;QAClB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAA;IACD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAA;IAC9B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;AACjC,CAAC;AAED,KAAK,UAAU,WAAW;IACxB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACzD,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAA;IACvD,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAElC,OAAO,CAAC,GAAG,CAAC,uBAAuB,UAAU,KAAK,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAA;IAE3C,MAAM,cAAc,CAAC,MAAM,CAAC,CAAA;IAC5B,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAA;AAC7C,CAAC;AAED,KAAK,UAAU,OAAO;IACpB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACzD,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAElC,OAAO,CAAC,GAAG,CAAC,uBAAuB,UAAU,KAAK,CAAC,CAAA;IACnD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAA;IAE3C,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAA;IACjE,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC3C,MAAM,GAAG,CAAC,OAAO,EAAE,CAAA;IAEnB,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACtC,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;IAEjB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC9C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAA;IAC1C,CAAC;SAAM,CAAC;QACN,eAAe,CAAC,OAAO,CAAC,CAAA;IAC1B,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS;IACtB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAA;IACzD,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;IAElC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAA;QAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAA;QAC5E,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAA;QAE3E,OAAO,CAAC,GAAG,CAAC,aAAa,UAAU,EAAE,CAAC,CAAA;QACtC,OAAO,CAAC,GAAG,CAAC,aAAa,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAA;QAC3E,OAAO,CAAC,GAAG,CAAC,aAAa,UAAU,EAAE,CAAC,CAAA;QACtC,OAAO,CAAC,GAAG,CAAC,aAAa,UAAU,EAAE,CAAC,CAAA;QAEtC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;YACzB,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;gBACrE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,OAA4E;IACnG,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;IAC3D,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAA;IAE7D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAClF,MAAM,KAAK,GAAG,GAAG,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,IAAI,GAAG,CAAA;QACnD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;QAEzD,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,KAAK,EAAE,CAAC,CAAA;QACnC,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,KAAK,aAAa,MAAM,EAAE,CAAC,CAAA;QACtD,CAAC;aAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,KAAK,aAAa,MAAM,EAAE,CAAC,CAAA;QACtD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,MAAM,aAAa,SAAS,CAAC,MAAM,YAAY,CAAC,CAAA;AAC3E,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAC5C,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;QACxC,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAA;IAC9B,CAAC;IACD,OAAO,qBAAqB,CAAA;AAC9B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;IACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@risingwave/wavelet-cli",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Wavelet CLI - manage views, generate types, run dev server",
|
|
5
5
|
"bin": {
|
|
6
6
|
"wavelet": "./dist/index.js"
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"dev": "tsx src/index.ts"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@risingwave/wavelet": "0.1
|
|
14
|
-
"@risingwave/wavelet-server": "0.1
|
|
13
|
+
"@risingwave/wavelet": "0.2.1",
|
|
14
|
+
"@risingwave/wavelet-server": "0.2.1",
|
|
15
15
|
"pg": "^8.13.0",
|
|
16
16
|
"tsx": "^4.0.0"
|
|
17
17
|
},
|
package/src/codegen.ts
CHANGED
|
@@ -1,68 +1,115 @@
|
|
|
1
1
|
import { writeFileSync, mkdirSync } from 'node:fs'
|
|
2
2
|
import pg from 'pg'
|
|
3
|
-
import type { WaveletConfig, SqlFragment,
|
|
3
|
+
import type { WaveletConfig, SqlFragment, QueryDef, ColumnType } from '@risingwave/wavelet'
|
|
4
4
|
|
|
5
5
|
const { Client } = pg
|
|
6
6
|
|
|
7
7
|
export async function generateClient(config: WaveletConfig): Promise<void> {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
for (const viewName of Object.keys(config.views ?? {})) {
|
|
14
|
-
try {
|
|
15
|
-
// Query column info from RisingWave
|
|
16
|
-
const result = await client.query(
|
|
17
|
-
`SELECT column_name, data_type FROM information_schema.columns WHERE table_name = $1 ORDER BY ordinal_position`,
|
|
18
|
-
[viewName]
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
const columns = result.rows.map((row: any) => ({
|
|
22
|
-
name: row.column_name,
|
|
23
|
-
tsType: pgTypeToTs(row.data_type),
|
|
24
|
-
}))
|
|
25
|
-
|
|
26
|
-
viewTypes.push({ name: viewName, columns })
|
|
27
|
-
} catch {
|
|
28
|
-
// If we can't query schema, generate generic types
|
|
29
|
-
viewTypes.push({ name: viewName, columns: [] })
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const streamTypes: { name: string; columns: { name: string; tsType: string }[] }[] = []
|
|
34
|
-
|
|
35
|
-
for (const [streamName, streamDef] of Object.entries(config.streams ?? {})) {
|
|
36
|
-
const columns = Object.entries(streamDef.columns).map(([name, type]) => ({
|
|
8
|
+
// Event types are always known from config
|
|
9
|
+
const eventTypes: TypedEntity[] = []
|
|
10
|
+
const events = config.events ?? config.streams ?? {}
|
|
11
|
+
for (const [eventName, eventDef] of Object.entries(events)) {
|
|
12
|
+
const columns = Object.entries(eventDef.columns).map(([name, type]) => ({
|
|
37
13
|
name,
|
|
38
14
|
tsType: columnTypeToTs(type),
|
|
39
15
|
}))
|
|
40
|
-
|
|
16
|
+
eventTypes.push({ name: eventName, columns })
|
|
41
17
|
}
|
|
42
18
|
|
|
43
|
-
|
|
19
|
+
// Query types: try config-declared columns first, then RisingWave introspection, then generic
|
|
20
|
+
const queryTypes: TypedEntity[] = []
|
|
21
|
+
let dbClient: InstanceType<typeof Client> | null = null
|
|
22
|
+
const queries = config.queries ?? config.views ?? {}
|
|
23
|
+
|
|
24
|
+
for (const [queryName, queryDef] of Object.entries(queries)) {
|
|
25
|
+
// Tier 1: explicit columns in config
|
|
26
|
+
const declaredColumns = getQueryColumns(queryDef)
|
|
27
|
+
if (declaredColumns) {
|
|
28
|
+
queryTypes.push({ name: queryName, columns: declaredColumns })
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Tier 2: introspect from RisingWave (if reachable)
|
|
33
|
+
if (!dbClient) {
|
|
34
|
+
dbClient = await tryConnect(config.database)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (dbClient) {
|
|
38
|
+
try {
|
|
39
|
+
const result = await dbClient.query(
|
|
40
|
+
`SELECT column_name, data_type FROM information_schema.columns WHERE table_name = $1 ORDER BY ordinal_position`,
|
|
41
|
+
[queryName]
|
|
42
|
+
)
|
|
43
|
+
if (result.rows.length > 0) {
|
|
44
|
+
queryTypes.push({
|
|
45
|
+
name: queryName,
|
|
46
|
+
columns: result.rows.map((row: any) => ({
|
|
47
|
+
name: row.column_name,
|
|
48
|
+
tsType: pgTypeToTs(row.data_type),
|
|
49
|
+
})),
|
|
50
|
+
})
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Fall through to tier 3
|
|
55
|
+
}
|
|
56
|
+
}
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
// Tier 3: generic fallback
|
|
59
|
+
queryTypes.push({ name: queryName, columns: [] })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (dbClient) {
|
|
63
|
+
await dbClient.end().catch(() => {})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const code = generateCode(queryTypes, eventTypes)
|
|
46
67
|
|
|
47
68
|
mkdirSync('.wavelet', { recursive: true })
|
|
48
69
|
writeFileSync('.wavelet/client.ts', code)
|
|
49
70
|
}
|
|
50
71
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
interface TypedEntity {
|
|
73
|
+
name: string
|
|
74
|
+
columns: { name: string; tsType: string }[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getQueryColumns(queryDef: QueryDef | SqlFragment): { name: string; tsType: string }[] | null {
|
|
78
|
+
// SqlFragment has no columns
|
|
79
|
+
if ('_tag' in queryDef && queryDef._tag === 'sql') return null
|
|
80
|
+
|
|
81
|
+
// QueryDef might have explicit columns
|
|
82
|
+
const qd = queryDef as QueryDef
|
|
83
|
+
if (!qd.columns) return null
|
|
84
|
+
|
|
85
|
+
return Object.entries(qd.columns).map(([name, type]) => ({
|
|
86
|
+
name,
|
|
87
|
+
tsType: columnTypeToTs(type),
|
|
88
|
+
}))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function tryConnect(connectionString: string): Promise<InstanceType<typeof Client> | null> {
|
|
92
|
+
const client = new Client({ connectionString, connectionTimeoutMillis: 3000 })
|
|
93
|
+
try {
|
|
94
|
+
await client.connect()
|
|
95
|
+
return client
|
|
96
|
+
} catch {
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function generateCode(queries: TypedEntity[], events: TypedEntity[]): string {
|
|
55
102
|
let code = `// Auto-generated by wavelet generate - do not edit\n`
|
|
56
103
|
code += `// Run 'npx wavelet generate' to regenerate\n\n`
|
|
57
104
|
code += `import { WaveletClient } from '@risingwave/wavelet-sdk'\n`
|
|
58
|
-
code += `import type {
|
|
105
|
+
code += `import type { QueryHandle, EventHandle, Diff, WaveletClientOptions } from '@risingwave/wavelet-sdk'\n\n`
|
|
59
106
|
|
|
60
|
-
//
|
|
61
|
-
for (const
|
|
62
|
-
const typeName = pascalCase(
|
|
63
|
-
if (
|
|
107
|
+
// Query row types
|
|
108
|
+
for (const q of queries) {
|
|
109
|
+
const typeName = pascalCase(q.name) + 'Row'
|
|
110
|
+
if (q.columns.length > 0) {
|
|
64
111
|
code += `export interface ${typeName} {\n`
|
|
65
|
-
for (const col of
|
|
112
|
+
for (const col of q.columns) {
|
|
66
113
|
code += ` ${col.name}: ${col.tsType}\n`
|
|
67
114
|
}
|
|
68
115
|
code += `}\n\n`
|
|
@@ -71,11 +118,11 @@ function generateCode(
|
|
|
71
118
|
}
|
|
72
119
|
}
|
|
73
120
|
|
|
74
|
-
//
|
|
75
|
-
for (const
|
|
76
|
-
const typeName = pascalCase(
|
|
121
|
+
// Event types
|
|
122
|
+
for (const ev of events) {
|
|
123
|
+
const typeName = pascalCase(ev.name) + 'Event'
|
|
77
124
|
code += `export interface ${typeName} {\n`
|
|
78
|
-
for (const col of
|
|
125
|
+
for (const col of ev.columns) {
|
|
79
126
|
code += ` ${col.name}: ${col.tsType}\n`
|
|
80
127
|
}
|
|
81
128
|
code += `}\n\n`
|
|
@@ -88,33 +135,33 @@ function generateCode(
|
|
|
88
135
|
code += ` this.client = new WaveletClient(options)\n`
|
|
89
136
|
code += ` }\n\n`
|
|
90
137
|
|
|
91
|
-
//
|
|
92
|
-
code += `
|
|
93
|
-
for (const
|
|
94
|
-
const typeName = pascalCase(
|
|
95
|
-
code += ` ${
|
|
138
|
+
// Query accessors
|
|
139
|
+
code += ` queries = {\n`
|
|
140
|
+
for (const q of queries) {
|
|
141
|
+
const typeName = pascalCase(q.name) + 'Row'
|
|
142
|
+
code += ` ${q.name}: this.client.query<${typeName}>('${q.name}'),\n`
|
|
96
143
|
}
|
|
97
144
|
code += ` }\n\n`
|
|
98
145
|
|
|
99
|
-
//
|
|
100
|
-
code += `
|
|
101
|
-
for (const
|
|
102
|
-
const typeName = pascalCase(
|
|
103
|
-
code += ` ${
|
|
146
|
+
// Event accessors
|
|
147
|
+
code += ` events = {\n`
|
|
148
|
+
for (const ev of events) {
|
|
149
|
+
const typeName = pascalCase(ev.name) + 'Event'
|
|
150
|
+
code += ` ${ev.name}: this.client.event<${typeName}>('${ev.name}'),\n`
|
|
104
151
|
}
|
|
105
152
|
code += ` }\n`
|
|
106
153
|
code += `}\n\n`
|
|
107
154
|
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
code += `export type
|
|
155
|
+
// Query name literal type
|
|
156
|
+
const queryNames = queries.map(q => `'${q.name}'`).join(' | ')
|
|
157
|
+
code += `export type QueryName = ${queryNames || 'never'}\n\n`
|
|
111
158
|
|
|
112
159
|
// React hook overloads
|
|
113
160
|
code += `// React hook overloads for type-safe useWavelet\n`
|
|
114
161
|
code += `import type { UseWaveletResult } from '@risingwave/wavelet-sdk/react'\n\n`
|
|
115
|
-
for (const
|
|
116
|
-
const typeName = pascalCase(
|
|
117
|
-
code += `export declare function useWavelet(
|
|
162
|
+
for (const q of queries) {
|
|
163
|
+
const typeName = pascalCase(q.name) + 'Row'
|
|
164
|
+
code += `export declare function useWavelet(query: '${q.name}'): UseWaveletResult<${typeName}>\n`
|
|
118
165
|
}
|
|
119
166
|
|
|
120
167
|
return code
|
package/src/index.ts
CHANGED
|
@@ -10,8 +10,8 @@ Usage:
|
|
|
10
10
|
|
|
11
11
|
Commands:
|
|
12
12
|
dev Start local development server
|
|
13
|
-
generate Generate typed client from
|
|
14
|
-
push Sync
|
|
13
|
+
generate Generate typed client from query definitions
|
|
14
|
+
push Sync query definitions to Wavelet server
|
|
15
15
|
status Show current configuration and connection status
|
|
16
16
|
init Initialize a new Wavelet project
|
|
17
17
|
|
|
@@ -69,9 +69,9 @@ async function runInit() {
|
|
|
69
69
|
export default defineConfig({
|
|
70
70
|
database: process.env.WAVELET_DATABASE_URL ?? 'postgres://root@localhost:4566/dev',
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
// Define your
|
|
74
|
-
//
|
|
72
|
+
events: {
|
|
73
|
+
// Define your events here
|
|
74
|
+
// game_events: {
|
|
75
75
|
// columns: {
|
|
76
76
|
// user_id: 'string',
|
|
77
77
|
// action: 'string',
|
|
@@ -80,11 +80,11 @@ export default defineConfig({
|
|
|
80
80
|
// }
|
|
81
81
|
},
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
// Define your materialized views here
|
|
83
|
+
queries: {
|
|
84
|
+
// Define your queries (materialized views) here
|
|
85
85
|
// leaderboard: sql\`
|
|
86
86
|
// SELECT user_id, SUM(value) as total
|
|
87
|
-
// FROM
|
|
87
|
+
// FROM game_events
|
|
88
88
|
// GROUP BY user_id
|
|
89
89
|
// ORDER BY total DESC
|
|
90
90
|
// LIMIT 100
|
|
@@ -96,7 +96,7 @@ export default defineConfig({
|
|
|
96
96
|
console.log('Created wavelet.config.ts')
|
|
97
97
|
console.log('')
|
|
98
98
|
console.log('Next steps:')
|
|
99
|
-
console.log(' 1. Edit wavelet.config.ts to define your
|
|
99
|
+
console.log(' 1. Edit wavelet.config.ts to define your events and queries')
|
|
100
100
|
console.log(' 2. Run: wavelet dev')
|
|
101
101
|
}
|
|
102
102
|
|
|
@@ -116,7 +116,7 @@ async function runDev() {
|
|
|
116
116
|
const ddl = new DdlManager(config.database)
|
|
117
117
|
await ddl.connect()
|
|
118
118
|
|
|
119
|
-
console.log('\nSyncing
|
|
119
|
+
console.log('\nSyncing events and queries...')
|
|
120
120
|
const actions = await ddl.sync(config)
|
|
121
121
|
printDdlActions(actions)
|
|
122
122
|
await ddl.close()
|
|
@@ -178,17 +178,17 @@ async function runStatus() {
|
|
|
178
178
|
|
|
179
179
|
try {
|
|
180
180
|
const config = await loadConfig(configPath)
|
|
181
|
-
const
|
|
182
|
-
const
|
|
181
|
+
const eventCount = Object.keys(config.events ?? config.streams ?? {}).length
|
|
182
|
+
const queryCount = Object.keys(config.queries ?? config.views ?? {}).length
|
|
183
183
|
|
|
184
184
|
console.log(`Config: ${configPath}`)
|
|
185
185
|
console.log(`Database: ${config.database.replace(/\/\/[^@]+@/, '//***@')}`)
|
|
186
|
-
console.log(`
|
|
187
|
-
console.log(`
|
|
186
|
+
console.log(`Events: ${eventCount}`)
|
|
187
|
+
console.log(`Queries: ${queryCount}`)
|
|
188
188
|
|
|
189
|
-
if (
|
|
190
|
-
console.log('\
|
|
191
|
-
for (const name of Object.keys(config.views ?? {})) {
|
|
189
|
+
if (queryCount > 0) {
|
|
190
|
+
console.log('\nQueries:')
|
|
191
|
+
for (const name of Object.keys(config.queries ?? config.views ?? {})) {
|
|
192
192
|
console.log(` - ${name}`)
|
|
193
193
|
}
|
|
194
194
|
}
|