airfoil 0.1.1 → 0.3.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.
- package/dist/index.js +72253 -73199
- package/package.json +6 -7
- package/src/commands/cluster/data-lake/create-iceberg.ts +7 -7
- package/src/commands/cluster/data-lake/create-parquet.ts +7 -7
- package/src/commands/cluster/data-lake/create.ts +1 -1
- package/src/commands/cluster/data-lake/delete.ts +5 -5
- package/src/commands/cluster/data-lake/get.ts +5 -5
- package/src/commands/cluster/data-lake/list.ts +5 -5
- package/src/commands/cluster/index.ts +1 -1
- package/src/commands/cluster/namespace/create.ts +17 -17
- package/src/commands/cluster/namespace/delete.ts +5 -5
- package/src/commands/cluster/namespace/get.ts +5 -5
- package/src/commands/cluster/namespace/list.ts +5 -5
- package/src/commands/cluster/object-store/create-aws.ts +19 -21
- package/src/commands/cluster/object-store/create-azure.ts +16 -16
- package/src/commands/cluster/object-store/create-google.ts +16 -16
- package/src/commands/cluster/object-store/create-s3.ts +22 -21
- package/src/commands/cluster/object-store/create.ts +1 -1
- package/src/commands/cluster/object-store/delete.ts +5 -5
- package/src/commands/cluster/object-store/get.ts +5 -5
- package/src/commands/cluster/object-store/list.ts +5 -5
- package/src/commands/cluster/tenant/create.ts +5 -7
- package/src/commands/cluster/tenant/delete.ts +5 -5
- package/src/commands/cluster/tenant/get.ts +5 -5
- package/src/commands/cluster/tenant/list.ts +3 -3
- package/src/commands/cluster/topic/create.ts +69 -58
- package/src/commands/cluster/topic/delete.ts +8 -8
- package/src/commands/cluster/topic/get.ts +5 -5
- package/src/commands/cluster/topic/list.ts +10 -16
- package/src/commands/dev.ts +23 -23
- package/src/commands/sql.ts +17 -17
- package/src/index.ts +7 -5
- package/src/utils/client.ts +1 -1
- package/src/utils/options.ts +18 -17
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { Effect } from "effect";
|
|
4
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
6
6
|
import { handleCliError } from "../../../utils/effect.js";
|
|
7
7
|
import { forceOption, hostOption, portOption } from "../../../utils/options.js";
|
|
8
8
|
|
|
9
|
-
const nameOption =
|
|
10
|
-
|
|
9
|
+
const nameOption = Flag.string("name").pipe(
|
|
10
|
+
Flag.withDescription(
|
|
11
11
|
"Object store name in format: tenants/{tenant}/object-stores/{object-store}",
|
|
12
12
|
),
|
|
13
13
|
);
|
|
@@ -54,5 +54,5 @@ export const deleteObjectStoreCommand = Command.make(
|
|
|
54
54
|
|
|
55
55
|
s.stop("Object store deleted successfully");
|
|
56
56
|
p.outro("✓ Done");
|
|
57
|
-
}).pipe(Effect.
|
|
57
|
+
}).pipe(Effect.catch(handleCliError("Failed to delete object store"))),
|
|
58
58
|
).pipe(Command.withDescription("Delete an object store from the cluster"));
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { printTable } from "console-table-printer";
|
|
5
4
|
import { Effect } from "effect";
|
|
5
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
6
6
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
7
7
|
import { handleCliError } from "../../../utils/effect.js";
|
|
8
8
|
import { hostOption, portOption } from "../../../utils/options.js";
|
|
9
9
|
|
|
10
|
-
const nameOption =
|
|
11
|
-
|
|
10
|
+
const nameOption = Flag.string("name").pipe(
|
|
11
|
+
Flag.withDescription(
|
|
12
12
|
"Object store name in format: tenants/{tenant}/object-stores/{object-store}",
|
|
13
13
|
),
|
|
14
14
|
);
|
|
@@ -47,5 +47,5 @@ export const getObjectStoreCommand = Command.make(
|
|
|
47
47
|
]);
|
|
48
48
|
p.outro("✓ Done");
|
|
49
49
|
});
|
|
50
|
-
}).pipe(Effect.
|
|
50
|
+
}).pipe(Effect.catch(handleCliError("Failed to get object store"))),
|
|
51
51
|
).pipe(Command.withDescription("Get details of a specific object store"));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { printTable } from "console-table-printer";
|
|
5
4
|
import { Effect, Option } from "effect";
|
|
5
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
6
6
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
7
7
|
import { handleCliError } from "../../../utils/effect.js";
|
|
8
8
|
import {
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
portOption,
|
|
13
13
|
} from "../../../utils/options.js";
|
|
14
14
|
|
|
15
|
-
const parentOption =
|
|
16
|
-
|
|
15
|
+
const parentOption = Flag.string("parent").pipe(
|
|
16
|
+
Flag.withDescription("Parent tenant in format: tenants/{tenant}"),
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
export const listObjectStoresCommand = Command.make(
|
|
@@ -68,5 +68,5 @@ export const listObjectStoresCommand = Command.make(
|
|
|
68
68
|
|
|
69
69
|
p.outro("✓ Done");
|
|
70
70
|
});
|
|
71
|
-
}).pipe(Effect.
|
|
71
|
+
}).pipe(Effect.catch(handleCliError("Failed to list object stores"))),
|
|
72
72
|
).pipe(Command.withDescription("List all object stores belonging to a tenant"));
|
|
@@ -1,16 +1,14 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { printTable } from "console-table-printer";
|
|
5
4
|
import { Effect } from "effect";
|
|
5
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
6
6
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
7
7
|
import { handleCliError } from "../../../utils/effect.js";
|
|
8
8
|
import { hostOption, portOption } from "../../../utils/options.js";
|
|
9
9
|
|
|
10
|
-
const tenantIdOption =
|
|
11
|
-
|
|
12
|
-
"Unique identifier for the tenant (e.g., 'acme-corp')",
|
|
13
|
-
),
|
|
10
|
+
const tenantIdOption = Flag.string("tenant-id").pipe(
|
|
11
|
+
Flag.withDescription("Unique identifier for the tenant (e.g., 'acme-corp')"),
|
|
14
12
|
);
|
|
15
13
|
|
|
16
14
|
export const createTenantCommand = Command.make(
|
|
@@ -44,5 +42,5 @@ export const createTenantCommand = Command.make(
|
|
|
44
42
|
printTable([{ name: tenant.name }]);
|
|
45
43
|
p.outro("✓ Done");
|
|
46
44
|
});
|
|
47
|
-
}).pipe(Effect.
|
|
45
|
+
}).pipe(Effect.catch(handleCliError("Failed to create tenant"))),
|
|
48
46
|
).pipe(Command.withDescription("Create a new tenant in the cluster"));
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { Effect } from "effect";
|
|
4
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
6
6
|
import { handleCliError } from "../../../utils/effect.js";
|
|
7
7
|
import { forceOption, hostOption, portOption } from "../../../utils/options.js";
|
|
8
8
|
|
|
9
|
-
const nameOption =
|
|
10
|
-
|
|
9
|
+
const nameOption = Flag.string("name").pipe(
|
|
10
|
+
Flag.withDescription(
|
|
11
11
|
"Tenant name in format: tenants/{tenant} (e.g., 'tenants/acme-corp')",
|
|
12
12
|
),
|
|
13
13
|
);
|
|
@@ -54,7 +54,7 @@ export const deleteTenantCommand = Command.make(
|
|
|
54
54
|
|
|
55
55
|
s.stop("Tenant deleted successfully");
|
|
56
56
|
p.outro("✓ Done");
|
|
57
|
-
}).pipe(Effect.
|
|
57
|
+
}).pipe(Effect.catch(handleCliError("Failed to delete tenant"))),
|
|
58
58
|
).pipe(
|
|
59
59
|
Command.withDescription(
|
|
60
60
|
"Delete a tenant from the cluster (fails if tenant has any namespaces)",
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { printTable } from "console-table-printer";
|
|
5
4
|
import { Effect } from "effect";
|
|
5
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
6
6
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
7
7
|
import { handleCliError } from "../../../utils/effect.js";
|
|
8
8
|
import { hostOption, portOption } from "../../../utils/options.js";
|
|
9
9
|
|
|
10
|
-
const nameOption =
|
|
11
|
-
|
|
10
|
+
const nameOption = Flag.string("name").pipe(
|
|
11
|
+
Flag.withDescription(
|
|
12
12
|
"Tenant name in format: tenants/{tenant} (e.g., 'tenants/acme-corp')",
|
|
13
13
|
),
|
|
14
14
|
);
|
|
@@ -40,5 +40,5 @@ export const getTenantCommand = Command.make(
|
|
|
40
40
|
printTable([{ name: tenant.name }]);
|
|
41
41
|
p.outro("✓ Done");
|
|
42
42
|
});
|
|
43
|
-
}).pipe(Effect.
|
|
43
|
+
}).pipe(Effect.catch(handleCliError("Failed to get tenant"))),
|
|
44
44
|
).pipe(Command.withDescription("Get details of a specific tenant"));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { printTable } from "console-table-printer";
|
|
5
4
|
import { Effect, Option } from "effect";
|
|
5
|
+
import { Command } from "effect/unstable/cli";
|
|
6
6
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
7
7
|
import { handleCliError } from "../../../utils/effect.js";
|
|
8
8
|
import {
|
|
@@ -56,5 +56,5 @@ export const listTenantsCommand = Command.make(
|
|
|
56
56
|
|
|
57
57
|
p.outro("✓ Done");
|
|
58
58
|
});
|
|
59
|
-
}).pipe(Effect.
|
|
59
|
+
}).pipe(Effect.catch(handleCliError("Failed to list tenants"))),
|
|
60
60
|
).pipe(Command.withDescription("List all tenants in the cluster"));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
-
import type { FieldConfig } from "@airfoil/wings";
|
|
3
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
4
2
|
import * as p from "@clack/prompts";
|
|
5
|
-
import {
|
|
3
|
+
import type { FieldConfig } from "@useairfoil/wings";
|
|
4
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
6
5
|
import { printTable } from "console-table-printer";
|
|
7
6
|
import { Effect, Option } from "effect";
|
|
7
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
8
8
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
9
9
|
import { handleCliError } from "../../../utils/effect.js";
|
|
10
10
|
import { hostOption, portOption } from "../../../utils/options.js";
|
|
@@ -26,14 +26,8 @@ const SUPPORTED_INLINE_TYPES = [
|
|
|
26
26
|
"Float16",
|
|
27
27
|
"Float32",
|
|
28
28
|
"Float64",
|
|
29
|
-
"LargeUtf8",
|
|
30
|
-
"LargeBinary",
|
|
31
29
|
"DateDay",
|
|
32
30
|
"DateMillisecond",
|
|
33
|
-
"TimeSecond",
|
|
34
|
-
"TimeMillisecond",
|
|
35
|
-
"TimeMicrosecond",
|
|
36
|
-
"TimeNanosecond",
|
|
37
31
|
"TimestampSecond",
|
|
38
32
|
"TimestampMillisecond",
|
|
39
33
|
"TimestampMicrosecond",
|
|
@@ -42,61 +36,63 @@ const SUPPORTED_INLINE_TYPES = [
|
|
|
42
36
|
"DurationMillisecond",
|
|
43
37
|
"DurationMicrosecond",
|
|
44
38
|
"DurationNanosecond",
|
|
45
|
-
"IntervalDayTime",
|
|
46
|
-
"IntervalYearMonth",
|
|
47
|
-
"IntervalMonthDayNano",
|
|
48
39
|
] as const;
|
|
49
40
|
|
|
50
41
|
type SupportedInlineType = (typeof SUPPORTED_INLINE_TYPES)[number];
|
|
51
42
|
|
|
52
|
-
const parentOption =
|
|
53
|
-
|
|
43
|
+
const parentOption = Flag.string("parent").pipe(
|
|
44
|
+
Flag.withDescription(
|
|
54
45
|
"Parent namespace in format: tenants/{tenant}/namespaces/{namespace}",
|
|
55
46
|
),
|
|
56
47
|
);
|
|
57
48
|
|
|
58
|
-
const topicIdOption =
|
|
59
|
-
|
|
49
|
+
const topicIdOption = Flag.string("topic-id").pipe(
|
|
50
|
+
Flag.withDescription("Unique identifier for the topic"),
|
|
60
51
|
);
|
|
61
52
|
|
|
62
|
-
const descriptionOption =
|
|
63
|
-
|
|
64
|
-
|
|
53
|
+
const descriptionOption = Flag.string("description").pipe(
|
|
54
|
+
Flag.withDescription("Topic description"),
|
|
55
|
+
Flag.optional,
|
|
65
56
|
);
|
|
66
57
|
|
|
67
|
-
const fieldsOption =
|
|
68
|
-
|
|
58
|
+
const fieldsOption = Flag.string("fields").pipe(
|
|
59
|
+
Flag.withDescription(
|
|
69
60
|
'Field definitions in format "name:Type" or "name:Type?" for nullable',
|
|
70
61
|
),
|
|
71
|
-
|
|
62
|
+
Flag.atLeast(0),
|
|
72
63
|
);
|
|
73
64
|
|
|
74
|
-
const schemaFileOption =
|
|
75
|
-
|
|
65
|
+
const schemaFileOption = Flag.string("schema-file").pipe(
|
|
66
|
+
Flag.withDescription(
|
|
76
67
|
"Path to JSON file containing FieldConfig[] (for complex types)",
|
|
77
68
|
),
|
|
78
|
-
|
|
69
|
+
Flag.optional,
|
|
79
70
|
);
|
|
80
71
|
|
|
81
|
-
const partitionKeyOption =
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
const partitionKeyOption = Flag.string("partition-key").pipe(
|
|
73
|
+
Flag.withDescription("Name of the field used for partitioning"),
|
|
74
|
+
Flag.optional,
|
|
84
75
|
);
|
|
85
76
|
|
|
86
|
-
const freshnessSecondsOption =
|
|
87
|
-
|
|
88
|
-
|
|
77
|
+
const freshnessSecondsOption = Flag.integer("freshness-seconds").pipe(
|
|
78
|
+
Flag.withDescription("How often to compact the topic (seconds)"),
|
|
79
|
+
Flag.withDefault(60),
|
|
89
80
|
);
|
|
90
81
|
|
|
91
|
-
const ttlSecondsOption =
|
|
92
|
-
|
|
93
|
-
|
|
82
|
+
const ttlSecondsOption = Flag.integer("ttl-seconds").pipe(
|
|
83
|
+
Flag.withDescription("How long to keep topic data (seconds)"),
|
|
84
|
+
Flag.optional,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const targetFileSizeBytesOption = Flag.integer("target-file-size-bytes").pipe(
|
|
88
|
+
Flag.withDescription("Target file size for compaction (bytes)"),
|
|
89
|
+
Flag.withDefault(1024 * 1024),
|
|
94
90
|
);
|
|
95
91
|
|
|
96
92
|
/**
|
|
97
93
|
* Parse a field string in format "name:Type" or "name:Type?" (nullable)
|
|
98
94
|
*/
|
|
99
|
-
function parseFieldString(fieldStr: string): FieldConfig {
|
|
95
|
+
function parseFieldString(fieldStr: string, index: number): FieldConfig {
|
|
100
96
|
const match = fieldStr.match(/^([^:]+):([^?]+)(\?)?$/);
|
|
101
97
|
if (!match) {
|
|
102
98
|
throw new Error(
|
|
@@ -126,14 +122,15 @@ function parseFieldString(fieldStr: string): FieldConfig {
|
|
|
126
122
|
name: name.trim(),
|
|
127
123
|
dataType: dataType as SupportedInlineType,
|
|
128
124
|
nullable,
|
|
129
|
-
|
|
125
|
+
id: BigInt(index + 1),
|
|
126
|
+
};
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
/**
|
|
133
130
|
* Parse multiple field strings into FieldConfig array
|
|
134
131
|
*/
|
|
135
|
-
function parseFieldsFromArgs(fields: string
|
|
136
|
-
return fields.map(parseFieldString);
|
|
132
|
+
function parseFieldsFromArgs(fields: ReadonlyArray<string>): FieldConfig[] {
|
|
133
|
+
return fields.map((field, index) => parseFieldString(field, index));
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
/**
|
|
@@ -165,7 +162,13 @@ function loadFieldsFromFile(filePath: string): FieldConfig[] {
|
|
|
165
162
|
);
|
|
166
163
|
}
|
|
167
164
|
|
|
168
|
-
return parsed as FieldConfig[]
|
|
165
|
+
return (parsed as FieldConfig[]).map((field, index) => {
|
|
166
|
+
const fieldId = (field as { id?: bigint | number }).id;
|
|
167
|
+
return {
|
|
168
|
+
...field,
|
|
169
|
+
id: typeof fieldId === "bigint" ? fieldId : BigInt(fieldId ?? index + 1),
|
|
170
|
+
};
|
|
171
|
+
});
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
export const createTopicCommand = Command.make(
|
|
@@ -179,6 +182,7 @@ export const createTopicCommand = Command.make(
|
|
|
179
182
|
partitionKey: partitionKeyOption,
|
|
180
183
|
freshnessSeconds: freshnessSecondsOption,
|
|
181
184
|
ttlSeconds: ttlSecondsOption,
|
|
185
|
+
targetFileSizeBytes: targetFileSizeBytesOption,
|
|
182
186
|
host: hostOption,
|
|
183
187
|
port: portOption,
|
|
184
188
|
},
|
|
@@ -191,6 +195,7 @@ export const createTopicCommand = Command.make(
|
|
|
191
195
|
partitionKey,
|
|
192
196
|
freshnessSeconds,
|
|
193
197
|
ttlSeconds,
|
|
198
|
+
targetFileSizeBytes,
|
|
194
199
|
host,
|
|
195
200
|
port,
|
|
196
201
|
}) =>
|
|
@@ -226,7 +231,7 @@ export const createTopicCommand = Command.make(
|
|
|
226
231
|
}
|
|
227
232
|
|
|
228
233
|
const partitionKeyName = Option.getOrUndefined(partitionKey);
|
|
229
|
-
let
|
|
234
|
+
let partitionKeyId: bigint | undefined;
|
|
230
235
|
if (partitionKeyName) {
|
|
231
236
|
const index = topicFields.findIndex(
|
|
232
237
|
(field) => field.name === partitionKeyName,
|
|
@@ -238,7 +243,15 @@ export const createTopicCommand = Command.make(
|
|
|
238
243
|
),
|
|
239
244
|
);
|
|
240
245
|
}
|
|
241
|
-
|
|
246
|
+
const partitionField = topicFields[index];
|
|
247
|
+
if (!partitionField) {
|
|
248
|
+
return yield* Effect.fail(
|
|
249
|
+
new Error(
|
|
250
|
+
`Partition key field "${partitionKeyName}" not found in fields. Available fields: ${topicFields.map((field) => field.name).join(", ")}`,
|
|
251
|
+
),
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
partitionKeyId = partitionField.id;
|
|
242
255
|
}
|
|
243
256
|
|
|
244
257
|
const layer = makeClusterMetadataLayer(host, port);
|
|
@@ -251,12 +264,13 @@ export const createTopicCommand = Command.make(
|
|
|
251
264
|
topicId,
|
|
252
265
|
description: Option.getOrUndefined(description),
|
|
253
266
|
fields: topicFields,
|
|
254
|
-
partitionKey:
|
|
267
|
+
partitionKey: partitionKeyId,
|
|
255
268
|
compaction: {
|
|
256
269
|
freshnessSeconds: BigInt(freshnessSeconds),
|
|
257
270
|
ttlSeconds: Option.getOrUndefined(
|
|
258
271
|
Option.map(ttlSeconds, (ttl) => BigInt(ttl)),
|
|
259
272
|
),
|
|
273
|
+
targetFileSizeBytes: BigInt(targetFileSizeBytes),
|
|
260
274
|
},
|
|
261
275
|
}).pipe(
|
|
262
276
|
Effect.provide(layer),
|
|
@@ -274,33 +288,30 @@ export const createTopicCommand = Command.make(
|
|
|
274
288
|
description: topic.description || "-",
|
|
275
289
|
partition_key:
|
|
276
290
|
topic.partitionKey !== undefined
|
|
277
|
-
? topicFields
|
|
278
|
-
|
|
291
|
+
? topicFields.find((field) => field.id === topic.partitionKey)
|
|
292
|
+
?.name || topic.partitionKey.toString()
|
|
279
293
|
: "-",
|
|
280
294
|
freshness_seconds: topic.compaction.freshnessSeconds.toString(),
|
|
281
295
|
ttl_seconds: topic.compaction.ttlSeconds?.toString() || "-",
|
|
282
|
-
|
|
296
|
+
target_file_size_bytes:
|
|
297
|
+
topic.compaction.targetFileSizeBytes.toString(),
|
|
298
|
+
fields_count: topic.schema.fields.length.toString(),
|
|
283
299
|
},
|
|
284
300
|
]);
|
|
285
301
|
|
|
286
|
-
if (topic.fields.length > 0) {
|
|
302
|
+
if (topic.schema.fields.length > 0) {
|
|
287
303
|
console.log("\nFields:");
|
|
288
304
|
printTable(
|
|
289
|
-
topic.fields.map(
|
|
290
|
-
(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
name: field.name,
|
|
296
|
-
type: field.dataType,
|
|
297
|
-
nullable: field.nullable ? "yes" : "no",
|
|
298
|
-
}),
|
|
299
|
-
),
|
|
305
|
+
topic.schema.fields.map((field, index) => ({
|
|
306
|
+
index: index.toString(),
|
|
307
|
+
name: field.name,
|
|
308
|
+
type: field.arrowType?._tag ?? "unknown",
|
|
309
|
+
nullable: field.nullable ? "yes" : "no",
|
|
310
|
+
})),
|
|
300
311
|
);
|
|
301
312
|
}
|
|
302
313
|
|
|
303
314
|
p.outro("✓ Done");
|
|
304
315
|
});
|
|
305
|
-
}).pipe(Effect.
|
|
316
|
+
}).pipe(Effect.catch(handleCliError("Failed to create topic"))),
|
|
306
317
|
).pipe(Command.withDescription("Create a new topic belonging to a namespace"));
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { Effect } from "effect";
|
|
4
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
5
5
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
6
6
|
import { handleCliError } from "../../../utils/effect.js";
|
|
7
7
|
import { forceOption, hostOption, portOption } from "../../../utils/options.js";
|
|
8
8
|
|
|
9
|
-
const nameOption =
|
|
10
|
-
|
|
9
|
+
const nameOption = Flag.string("name").pipe(
|
|
10
|
+
Flag.withDescription(
|
|
11
11
|
"Topic name in format: tenants/{tenant}/namespaces/{namespace}/topics/{topic}",
|
|
12
12
|
),
|
|
13
13
|
);
|
|
14
14
|
|
|
15
|
-
const forceDeleteOption =
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const forceDeleteOption = Flag.boolean("force-delete").pipe(
|
|
16
|
+
Flag.withDescription("Also delete data associated with the topic"),
|
|
17
|
+
Flag.withDefault(false),
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
export const deleteTopicCommand = Command.make(
|
|
@@ -63,5 +63,5 @@ export const deleteTopicCommand = Command.make(
|
|
|
63
63
|
|
|
64
64
|
s.stop("Topic deleted successfully");
|
|
65
65
|
p.outro("✓ Done");
|
|
66
|
-
}).pipe(Effect.
|
|
66
|
+
}).pipe(Effect.catch(handleCliError("Failed to delete topic"))),
|
|
67
67
|
).pipe(Command.withDescription("Delete a topic from the cluster"));
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { printTable } from "console-table-printer";
|
|
5
4
|
import { Effect } from "effect";
|
|
5
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
6
6
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
7
7
|
import { handleCliError } from "../../../utils/effect.js";
|
|
8
8
|
import { hostOption, portOption } from "../../../utils/options.js";
|
|
9
9
|
|
|
10
|
-
const nameOption =
|
|
11
|
-
|
|
10
|
+
const nameOption = Flag.string("name").pipe(
|
|
11
|
+
Flag.withDescription(
|
|
12
12
|
"Topic name in format: tenants/{tenant}/namespaces/{namespace}/topics/{topic}",
|
|
13
13
|
),
|
|
14
14
|
);
|
|
@@ -47,5 +47,5 @@ export const getTopicCommand = Command.make(
|
|
|
47
47
|
|
|
48
48
|
p.outro("✓ Done");
|
|
49
49
|
});
|
|
50
|
-
}).pipe(Effect.
|
|
50
|
+
}).pipe(Effect.catch(handleCliError("Failed to get topic"))),
|
|
51
51
|
).pipe(Command.withDescription("Get details of a specific topic"));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { WingsClusterMetadata } from "@airfoil/wings";
|
|
2
1
|
import * as p from "@clack/prompts";
|
|
3
|
-
import {
|
|
2
|
+
import { WingsClusterMetadata } from "@useairfoil/wings";
|
|
4
3
|
import { printTable } from "console-table-printer";
|
|
5
4
|
import { Effect, Option } from "effect";
|
|
5
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
6
6
|
import { makeClusterMetadataLayer } from "../../../utils/client.js";
|
|
7
7
|
import { handleCliError } from "../../../utils/effect.js";
|
|
8
8
|
import {
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
portOption,
|
|
13
13
|
} from "../../../utils/options.js";
|
|
14
14
|
|
|
15
|
-
const parentOption =
|
|
16
|
-
|
|
15
|
+
const parentOption = Flag.string("parent").pipe(
|
|
16
|
+
Flag.withDescription(
|
|
17
17
|
"Parent namespace in format: tenants/{tenant}/namespaces/{namespace}",
|
|
18
18
|
),
|
|
19
19
|
);
|
|
@@ -52,17 +52,11 @@ export const listTopicsCommand = Command.make(
|
|
|
52
52
|
p.log.warn("No topics found");
|
|
53
53
|
} else {
|
|
54
54
|
printTable(
|
|
55
|
-
response.topics.map(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}) => ({
|
|
61
|
-
name: topic.name,
|
|
62
|
-
description: topic.description || "-",
|
|
63
|
-
partition_key: topic.partitionKey?.toString() || "-",
|
|
64
|
-
}),
|
|
65
|
-
),
|
|
55
|
+
response.topics.map((topic) => ({
|
|
56
|
+
name: topic.name,
|
|
57
|
+
description: topic.description || "-",
|
|
58
|
+
partition_key: topic.partitionKey?.toString() || "-",
|
|
59
|
+
})),
|
|
66
60
|
);
|
|
67
61
|
}
|
|
68
62
|
|
|
@@ -72,5 +66,5 @@ export const listTopicsCommand = Command.make(
|
|
|
72
66
|
|
|
73
67
|
p.outro("✓ Done");
|
|
74
68
|
});
|
|
75
|
-
}).pipe(Effect.
|
|
69
|
+
}).pipe(Effect.catch(handleCliError("Failed to list topics"))),
|
|
76
70
|
).pipe(Command.withDescription("List all topics belonging to a namespace"));
|
package/src/commands/dev.ts
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
|
-
import { Command, Options } from "@effect/cli";
|
|
3
2
|
import { Effect } from "effect";
|
|
3
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
4
4
|
import { handleCliError } from "../utils/effect.js";
|
|
5
5
|
import { downloadWings, getWingsPath, verifyChecksum } from "../utils/wings.js";
|
|
6
6
|
|
|
7
|
-
const dockerOption =
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const dockerOption = Flag.boolean("docker").pipe(
|
|
8
|
+
Flag.withDescription("Run Wings using Docker (recommended)"),
|
|
9
|
+
Flag.withDefault(false),
|
|
10
10
|
);
|
|
11
11
|
|
|
12
|
-
const versionOption =
|
|
13
|
-
|
|
12
|
+
const versionOption = Flag.string("version").pipe(
|
|
13
|
+
Flag.withDescription(
|
|
14
14
|
"Specify Wings version (e.g., v0.1.0-alpha.11 or 'latest')",
|
|
15
15
|
),
|
|
16
|
-
|
|
16
|
+
Flag.withDefault("latest"),
|
|
17
17
|
);
|
|
18
18
|
|
|
19
|
-
const tagOption =
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
const tagOption = Flag.string("tag").pipe(
|
|
20
|
+
Flag.withDescription("Docker image tag (only with --docker)"),
|
|
21
|
+
Flag.withDefault("latest"),
|
|
22
22
|
);
|
|
23
23
|
|
|
24
|
-
const forcePullOption =
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const forcePullOption = Flag.boolean("force-pull").pipe(
|
|
25
|
+
Flag.withDescription("Force pull Docker image even if it exists locally"),
|
|
26
|
+
Flag.withDefault(false),
|
|
27
27
|
);
|
|
28
28
|
|
|
29
|
-
const stressOption =
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const stressOption = Flag.boolean("stress").pipe(
|
|
30
|
+
Flag.withDescription("Use Wings stress binary variant"),
|
|
31
|
+
Flag.withDefault(false),
|
|
32
32
|
);
|
|
33
33
|
|
|
34
|
-
const yesOption =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
const yesOption = Flag.boolean("yes").pipe(
|
|
35
|
+
Flag.withAlias("y"),
|
|
36
|
+
Flag.withDescription("Skip confirmation prompts"),
|
|
37
|
+
Flag.withDefault(false),
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
export const devCommand = Command.make(
|
|
@@ -53,7 +53,7 @@ export const devCommand = Command.make(
|
|
|
53
53
|
options.docker ? runWithDocker(options) : runWithBinary(options),
|
|
54
54
|
catch: (error) =>
|
|
55
55
|
error instanceof Error ? error : new Error("Operation failed"),
|
|
56
|
-
}).pipe(Effect.
|
|
56
|
+
}).pipe(Effect.catch(handleCliError("Operation failed"))),
|
|
57
57
|
).pipe(
|
|
58
58
|
Command.withDescription(
|
|
59
59
|
"Download and run Wings dev server locally (Docker recommended for portability)",
|
|
@@ -221,8 +221,8 @@ async function runWithDocker(options: { tag: string; forcePull: boolean }) {
|
|
|
221
221
|
"7780:7780",
|
|
222
222
|
image,
|
|
223
223
|
"dev",
|
|
224
|
-
"--http
|
|
225
|
-
"--metadata
|
|
224
|
+
"--http.address=0.0.0.0:7780",
|
|
225
|
+
"--metadata.address=0.0.0.0:7777",
|
|
226
226
|
],
|
|
227
227
|
{
|
|
228
228
|
stdout: "inherit",
|