@tinybirdco/sdk 0.0.41 → 0.0.43
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/LICENSE +7 -0
- package/README.md +29 -3
- package/dist/api/resources.d.ts +72 -1
- package/dist/api/resources.d.ts.map +1 -1
- package/dist/api/resources.js +197 -1
- package/dist/api/resources.js.map +1 -1
- package/dist/api/resources.test.js +82 -1
- package/dist/api/resources.test.js.map +1 -1
- package/dist/cli/commands/migrate.d.ts +11 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +196 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/migrate.test.d.ts +2 -0
- package/dist/cli/commands/migrate.test.d.ts.map +1 -0
- package/dist/cli/commands/migrate.test.js +473 -0
- package/dist/cli/commands/migrate.test.js.map +1 -0
- package/dist/cli/commands/pull.d.ts +59 -0
- package/dist/cli/commands/pull.d.ts.map +1 -0
- package/dist/cli/commands/pull.js +104 -0
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/pull.test.d.ts +2 -0
- package/dist/cli/commands/pull.test.d.ts.map +1 -0
- package/dist/cli/commands/pull.test.js +140 -0
- package/dist/cli/commands/pull.test.js.map +1 -0
- package/dist/cli/config.d.ts +10 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +22 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/index.js +77 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/generator/client.js +2 -2
- package/dist/generator/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/migrate/discovery.d.ts +7 -0
- package/dist/migrate/discovery.d.ts.map +1 -0
- package/dist/migrate/discovery.js +125 -0
- package/dist/migrate/discovery.js.map +1 -0
- package/dist/migrate/emit-ts.d.ts +4 -0
- package/dist/migrate/emit-ts.d.ts.map +1 -0
- package/dist/migrate/emit-ts.js +387 -0
- package/dist/migrate/emit-ts.js.map +1 -0
- package/dist/migrate/parse-connection.d.ts +3 -0
- package/dist/migrate/parse-connection.d.ts.map +1 -0
- package/dist/migrate/parse-connection.js +74 -0
- package/dist/migrate/parse-connection.js.map +1 -0
- package/dist/migrate/parse-datasource.d.ts +3 -0
- package/dist/migrate/parse-datasource.d.ts.map +1 -0
- package/dist/migrate/parse-datasource.js +324 -0
- package/dist/migrate/parse-datasource.js.map +1 -0
- package/dist/migrate/parse-pipe.d.ts +3 -0
- package/dist/migrate/parse-pipe.d.ts.map +1 -0
- package/dist/migrate/parse-pipe.js +332 -0
- package/dist/migrate/parse-pipe.js.map +1 -0
- package/dist/migrate/parse.d.ts +3 -0
- package/dist/migrate/parse.d.ts.map +1 -0
- package/dist/migrate/parse.js +18 -0
- package/dist/migrate/parse.js.map +1 -0
- package/dist/migrate/parser-utils.d.ts +20 -0
- package/dist/migrate/parser-utils.d.ts.map +1 -0
- package/dist/migrate/parser-utils.js +130 -0
- package/dist/migrate/parser-utils.js.map +1 -0
- package/dist/migrate/types.d.ts +110 -0
- package/dist/migrate/types.d.ts.map +1 -0
- package/dist/migrate/types.js +2 -0
- package/dist/migrate/types.js.map +1 -0
- package/dist/schema/project.d.ts +20 -9
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/schema/project.js +127 -136
- package/dist/schema/project.js.map +1 -1
- package/dist/schema/project.test.js +22 -0
- package/dist/schema/project.test.js.map +1 -1
- package/package.json +2 -1
- package/src/api/resources.test.ts +121 -0
- package/src/api/resources.ts +292 -1
- package/src/cli/commands/migrate.test.ts +564 -0
- package/src/cli/commands/migrate.ts +240 -0
- package/src/cli/commands/pull.test.ts +173 -0
- package/src/cli/commands/pull.ts +177 -0
- package/src/cli/config.ts +26 -0
- package/src/cli/index.ts +112 -0
- package/src/generator/client.ts +2 -2
- package/src/index.ts +1 -1
- package/src/migrate/discovery.ts +151 -0
- package/src/migrate/emit-ts.ts +469 -0
- package/src/migrate/parse-connection.ts +128 -0
- package/src/migrate/parse-datasource.ts +453 -0
- package/src/migrate/parse-pipe.ts +518 -0
- package/src/migrate/parse.ts +20 -0
- package/src/migrate/parser-utils.ts +160 -0
- package/src/migrate/types.ts +125 -0
- package/src/schema/project.test.ts +28 -0
- package/src/schema/project.ts +173 -181
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { MigrationParseError, isBlank, parseDirectiveLine, parseQuotedValue, splitCommaSeparated, splitLines, splitTopLevelComma, stripIndent, } from "./parser-utils.js";
|
|
2
|
+
function readIndentedBlock(lines, startIndex) {
|
|
3
|
+
const collected = [];
|
|
4
|
+
let i = startIndex;
|
|
5
|
+
while (i < lines.length) {
|
|
6
|
+
const line = lines[i] ?? "";
|
|
7
|
+
if (line.startsWith(" ")) {
|
|
8
|
+
collected.push(stripIndent(line));
|
|
9
|
+
i += 1;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (isBlank(line)) {
|
|
13
|
+
let j = i + 1;
|
|
14
|
+
while (j < lines.length && isBlank(lines[j] ?? "")) {
|
|
15
|
+
j += 1;
|
|
16
|
+
}
|
|
17
|
+
if (j < lines.length && (lines[j] ?? "").startsWith(" ")) {
|
|
18
|
+
collected.push("");
|
|
19
|
+
i += 1;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
return { lines: collected, nextIndex: i };
|
|
26
|
+
}
|
|
27
|
+
function findTokenOutsideContexts(input, token) {
|
|
28
|
+
let depth = 0;
|
|
29
|
+
let inSingle = false;
|
|
30
|
+
let inDouble = false;
|
|
31
|
+
let inBacktick = false;
|
|
32
|
+
for (let i = 0; i <= input.length - token.length; i += 1) {
|
|
33
|
+
const char = input[i];
|
|
34
|
+
const prev = i > 0 ? input[i - 1] : "";
|
|
35
|
+
if (char === "'" && !inDouble && !inBacktick && prev !== "\\") {
|
|
36
|
+
inSingle = !inSingle;
|
|
37
|
+
}
|
|
38
|
+
else if (char === '"' && !inSingle && !inBacktick && prev !== "\\") {
|
|
39
|
+
inDouble = !inDouble;
|
|
40
|
+
}
|
|
41
|
+
else if (char === "`" && !inSingle && !inDouble) {
|
|
42
|
+
inBacktick = !inBacktick;
|
|
43
|
+
}
|
|
44
|
+
else if (!inSingle && !inDouble && !inBacktick) {
|
|
45
|
+
if (char === "(") {
|
|
46
|
+
depth += 1;
|
|
47
|
+
}
|
|
48
|
+
else if (char === ")") {
|
|
49
|
+
depth -= 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!inSingle && !inDouble && !inBacktick && depth === 0) {
|
|
53
|
+
if (input.slice(i, i + token.length) === token) {
|
|
54
|
+
return i;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return -1;
|
|
59
|
+
}
|
|
60
|
+
function parseColumnLine(filePath, resourceName, rawLine) {
|
|
61
|
+
const line = rawLine.trim().replace(/,$/, "");
|
|
62
|
+
if (!line) {
|
|
63
|
+
throw new MigrationParseError(filePath, "datasource", resourceName, "Empty schema line.");
|
|
64
|
+
}
|
|
65
|
+
const firstSpace = line.search(/\s/);
|
|
66
|
+
if (firstSpace === -1) {
|
|
67
|
+
throw new MigrationParseError(filePath, "datasource", resourceName, `Invalid schema column definition: "${rawLine}"`);
|
|
68
|
+
}
|
|
69
|
+
const columnName = line.slice(0, firstSpace).trim();
|
|
70
|
+
let rest = line.slice(firstSpace + 1).trim();
|
|
71
|
+
const codecMatch = rest.match(/\s+CODEC\((.+)\)\s*$/);
|
|
72
|
+
const codec = codecMatch ? codecMatch[1].trim() : undefined;
|
|
73
|
+
if (codecMatch?.index !== undefined) {
|
|
74
|
+
rest = rest.slice(0, codecMatch.index).trim();
|
|
75
|
+
}
|
|
76
|
+
let defaultExpression;
|
|
77
|
+
const defaultMarkerIndex = findTokenOutsideContexts(rest, " DEFAULT ");
|
|
78
|
+
if (defaultMarkerIndex >= 0) {
|
|
79
|
+
defaultExpression = rest.slice(defaultMarkerIndex + " DEFAULT ".length).trim();
|
|
80
|
+
rest = rest.slice(0, defaultMarkerIndex).trim();
|
|
81
|
+
}
|
|
82
|
+
let jsonPath;
|
|
83
|
+
const jsonMatch = rest.match(/`json:([^`]+)`/);
|
|
84
|
+
if (jsonMatch) {
|
|
85
|
+
jsonPath = jsonMatch[1].trim();
|
|
86
|
+
rest = rest.replace(/`json:[^`]+`/, "").trim();
|
|
87
|
+
}
|
|
88
|
+
if (!rest) {
|
|
89
|
+
throw new MigrationParseError(filePath, "datasource", resourceName, `Missing type in schema column: "${rawLine}"`);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
name: columnName,
|
|
93
|
+
type: rest,
|
|
94
|
+
jsonPath,
|
|
95
|
+
defaultExpression,
|
|
96
|
+
codec,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function parseEngineSettings(value) {
|
|
100
|
+
const raw = parseQuotedValue(value);
|
|
101
|
+
const parts = splitTopLevelComma(raw);
|
|
102
|
+
const settings = {};
|
|
103
|
+
for (const part of parts) {
|
|
104
|
+
const equalIndex = part.indexOf("=");
|
|
105
|
+
if (equalIndex === -1) {
|
|
106
|
+
throw new Error(`Invalid ENGINE_SETTINGS part: "${part}"`);
|
|
107
|
+
}
|
|
108
|
+
const key = part.slice(0, equalIndex).trim();
|
|
109
|
+
const rawValue = part.slice(equalIndex + 1).trim();
|
|
110
|
+
if (!key) {
|
|
111
|
+
throw new Error(`Invalid ENGINE_SETTINGS key in "${part}"`);
|
|
112
|
+
}
|
|
113
|
+
if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
114
|
+
settings[key] = rawValue.slice(1, -1).replace(/\\'/g, "'");
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (/^-?\d+(\.\d+)?$/.test(rawValue)) {
|
|
118
|
+
settings[key] = Number(rawValue);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (rawValue === "true") {
|
|
122
|
+
settings[key] = true;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (rawValue === "false") {
|
|
126
|
+
settings[key] = false;
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
throw new Error(`Unsupported ENGINE_SETTINGS value: "${rawValue}"`);
|
|
130
|
+
}
|
|
131
|
+
return settings;
|
|
132
|
+
}
|
|
133
|
+
function parseToken(filePath, resourceName, value) {
|
|
134
|
+
const parts = value.split(/\s+/).filter(Boolean);
|
|
135
|
+
if (parts.length < 2) {
|
|
136
|
+
throw new MigrationParseError(filePath, "datasource", resourceName, `Invalid TOKEN line: "${value}"`);
|
|
137
|
+
}
|
|
138
|
+
if (parts.length > 2) {
|
|
139
|
+
throw new MigrationParseError(filePath, "datasource", resourceName, `Unsupported TOKEN syntax in strict mode: "${value}"`);
|
|
140
|
+
}
|
|
141
|
+
const name = parts[0];
|
|
142
|
+
const scope = parts[1];
|
|
143
|
+
if (scope !== "READ" && scope !== "APPEND") {
|
|
144
|
+
throw new MigrationParseError(filePath, "datasource", resourceName, `Unsupported datasource token scope: "${scope}"`);
|
|
145
|
+
}
|
|
146
|
+
return { name, scope };
|
|
147
|
+
}
|
|
148
|
+
export function parseDatasourceFile(resource) {
|
|
149
|
+
const lines = splitLines(resource.content);
|
|
150
|
+
const columns = [];
|
|
151
|
+
const tokens = [];
|
|
152
|
+
const sharedWith = [];
|
|
153
|
+
let description;
|
|
154
|
+
let forwardQuery;
|
|
155
|
+
let engineType;
|
|
156
|
+
let sortingKey = [];
|
|
157
|
+
let partitionKey;
|
|
158
|
+
let primaryKey;
|
|
159
|
+
let ttl;
|
|
160
|
+
let ver;
|
|
161
|
+
let sign;
|
|
162
|
+
let version;
|
|
163
|
+
let summingColumns;
|
|
164
|
+
let settings;
|
|
165
|
+
let kafkaConnectionName;
|
|
166
|
+
let kafkaTopic;
|
|
167
|
+
let kafkaGroupId;
|
|
168
|
+
let kafkaAutoOffsetReset;
|
|
169
|
+
let i = 0;
|
|
170
|
+
while (i < lines.length) {
|
|
171
|
+
const rawLine = lines[i] ?? "";
|
|
172
|
+
const line = rawLine.trim();
|
|
173
|
+
if (!line) {
|
|
174
|
+
i += 1;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (line === "DESCRIPTION >") {
|
|
178
|
+
const block = readIndentedBlock(lines, i + 1);
|
|
179
|
+
if (block.lines.length === 0) {
|
|
180
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, "DESCRIPTION block is empty.");
|
|
181
|
+
}
|
|
182
|
+
description = block.lines.join("\n");
|
|
183
|
+
i = block.nextIndex;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (line === "SCHEMA >") {
|
|
187
|
+
const block = readIndentedBlock(lines, i + 1);
|
|
188
|
+
if (block.lines.length === 0) {
|
|
189
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, "SCHEMA block is empty.");
|
|
190
|
+
}
|
|
191
|
+
for (const schemaLine of block.lines) {
|
|
192
|
+
if (isBlank(schemaLine)) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
columns.push(parseColumnLine(resource.filePath, resource.name, schemaLine));
|
|
196
|
+
}
|
|
197
|
+
i = block.nextIndex;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (line === "FORWARD_QUERY >") {
|
|
201
|
+
const block = readIndentedBlock(lines, i + 1);
|
|
202
|
+
if (block.lines.length === 0) {
|
|
203
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, "FORWARD_QUERY block is empty.");
|
|
204
|
+
}
|
|
205
|
+
forwardQuery = block.lines.join("\n");
|
|
206
|
+
i = block.nextIndex;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (line === "SHARED_WITH >") {
|
|
210
|
+
const block = readIndentedBlock(lines, i + 1);
|
|
211
|
+
for (const sharedLine of block.lines) {
|
|
212
|
+
const normalized = sharedLine.trim().replace(/,$/, "");
|
|
213
|
+
if (normalized) {
|
|
214
|
+
sharedWith.push(normalized);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
i = block.nextIndex;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
const { key, value } = parseDirectiveLine(line);
|
|
221
|
+
switch (key) {
|
|
222
|
+
case "ENGINE":
|
|
223
|
+
engineType = parseQuotedValue(value);
|
|
224
|
+
break;
|
|
225
|
+
case "ENGINE_SORTING_KEY":
|
|
226
|
+
sortingKey = splitCommaSeparated(parseQuotedValue(value));
|
|
227
|
+
break;
|
|
228
|
+
case "ENGINE_PARTITION_KEY":
|
|
229
|
+
partitionKey = parseQuotedValue(value);
|
|
230
|
+
break;
|
|
231
|
+
case "ENGINE_PRIMARY_KEY":
|
|
232
|
+
primaryKey = splitCommaSeparated(parseQuotedValue(value));
|
|
233
|
+
break;
|
|
234
|
+
case "ENGINE_TTL":
|
|
235
|
+
ttl = parseQuotedValue(value);
|
|
236
|
+
break;
|
|
237
|
+
case "ENGINE_VER":
|
|
238
|
+
ver = parseQuotedValue(value);
|
|
239
|
+
break;
|
|
240
|
+
case "ENGINE_SIGN":
|
|
241
|
+
sign = parseQuotedValue(value);
|
|
242
|
+
break;
|
|
243
|
+
case "ENGINE_VERSION":
|
|
244
|
+
version = parseQuotedValue(value);
|
|
245
|
+
break;
|
|
246
|
+
case "ENGINE_SUMMING_COLUMNS":
|
|
247
|
+
summingColumns = splitCommaSeparated(parseQuotedValue(value));
|
|
248
|
+
break;
|
|
249
|
+
case "ENGINE_SETTINGS":
|
|
250
|
+
try {
|
|
251
|
+
settings = parseEngineSettings(value);
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, error.message);
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
case "KAFKA_CONNECTION_NAME":
|
|
258
|
+
kafkaConnectionName = value.trim();
|
|
259
|
+
break;
|
|
260
|
+
case "KAFKA_TOPIC":
|
|
261
|
+
kafkaTopic = value.trim();
|
|
262
|
+
break;
|
|
263
|
+
case "KAFKA_GROUP_ID":
|
|
264
|
+
kafkaGroupId = value.trim();
|
|
265
|
+
break;
|
|
266
|
+
case "KAFKA_AUTO_OFFSET_RESET":
|
|
267
|
+
if (value !== "earliest" && value !== "latest") {
|
|
268
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, `Invalid KAFKA_AUTO_OFFSET_RESET value: "${value}"`);
|
|
269
|
+
}
|
|
270
|
+
kafkaAutoOffsetReset = value;
|
|
271
|
+
break;
|
|
272
|
+
case "TOKEN":
|
|
273
|
+
tokens.push(parseToken(resource.filePath, resource.name, value));
|
|
274
|
+
break;
|
|
275
|
+
default:
|
|
276
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, `Unsupported datasource directive in strict mode: "${line}"`);
|
|
277
|
+
}
|
|
278
|
+
i += 1;
|
|
279
|
+
}
|
|
280
|
+
if (columns.length === 0) {
|
|
281
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, "SCHEMA block is required.");
|
|
282
|
+
}
|
|
283
|
+
if (!engineType) {
|
|
284
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, "ENGINE directive is required.");
|
|
285
|
+
}
|
|
286
|
+
if (sortingKey.length === 0) {
|
|
287
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, "ENGINE_SORTING_KEY directive is required.");
|
|
288
|
+
}
|
|
289
|
+
const kafka = kafkaConnectionName || kafkaTopic || kafkaGroupId || kafkaAutoOffsetReset
|
|
290
|
+
? {
|
|
291
|
+
connectionName: kafkaConnectionName ?? "",
|
|
292
|
+
topic: kafkaTopic ?? "",
|
|
293
|
+
groupId: kafkaGroupId,
|
|
294
|
+
autoOffsetReset: kafkaAutoOffsetReset,
|
|
295
|
+
}
|
|
296
|
+
: undefined;
|
|
297
|
+
if (kafka && (!kafka.connectionName || !kafka.topic)) {
|
|
298
|
+
throw new MigrationParseError(resource.filePath, "datasource", resource.name, "KAFKA_CONNECTION_NAME and KAFKA_TOPIC are required when Kafka directives are used.");
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
kind: "datasource",
|
|
302
|
+
name: resource.name,
|
|
303
|
+
filePath: resource.filePath,
|
|
304
|
+
description,
|
|
305
|
+
columns,
|
|
306
|
+
engine: {
|
|
307
|
+
type: engineType,
|
|
308
|
+
sortingKey,
|
|
309
|
+
partitionKey,
|
|
310
|
+
primaryKey,
|
|
311
|
+
ttl,
|
|
312
|
+
ver,
|
|
313
|
+
sign,
|
|
314
|
+
version,
|
|
315
|
+
summingColumns,
|
|
316
|
+
settings,
|
|
317
|
+
},
|
|
318
|
+
kafka,
|
|
319
|
+
forwardQuery,
|
|
320
|
+
tokens,
|
|
321
|
+
sharedWith,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
//# sourceMappingURL=parse-datasource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-datasource.js","sourceRoot":"","sources":["../../src/migrate/parse-datasource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-pipe.d.ts","sourceRoot":"","sources":["../../src/migrate/parse-pipe.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkC,YAAY,EAAE,MAAM,YAAY,CAAC;AA6P1F,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAuQ/D"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { MigrationParseError, isBlank, parseDirectiveLine, splitLines, splitTopLevelComma, stripIndent, } from "./parser-utils.js";
|
|
2
|
+
function readIndentedBlock(lines, startIndex) {
|
|
3
|
+
const collected = [];
|
|
4
|
+
let i = startIndex;
|
|
5
|
+
while (i < lines.length) {
|
|
6
|
+
const line = lines[i] ?? "";
|
|
7
|
+
if (line.startsWith(" ")) {
|
|
8
|
+
collected.push(stripIndent(line));
|
|
9
|
+
i += 1;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (isBlank(line)) {
|
|
13
|
+
let j = i + 1;
|
|
14
|
+
while (j < lines.length && isBlank(lines[j] ?? "")) {
|
|
15
|
+
j += 1;
|
|
16
|
+
}
|
|
17
|
+
if (j < lines.length && (lines[j] ?? "").startsWith(" ")) {
|
|
18
|
+
collected.push("");
|
|
19
|
+
i += 1;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
return { lines: collected, nextIndex: i };
|
|
26
|
+
}
|
|
27
|
+
function nextNonBlank(lines, startIndex) {
|
|
28
|
+
let i = startIndex;
|
|
29
|
+
while (i < lines.length && isBlank(lines[i] ?? "")) {
|
|
30
|
+
i += 1;
|
|
31
|
+
}
|
|
32
|
+
return i;
|
|
33
|
+
}
|
|
34
|
+
function inferOutputColumnsFromSql(sql) {
|
|
35
|
+
const match = sql.match(/select\s+([\s\S]+?)\s+from\s/iu);
|
|
36
|
+
if (!match) {
|
|
37
|
+
return ["result"];
|
|
38
|
+
}
|
|
39
|
+
const selectClause = match[1] ?? "";
|
|
40
|
+
const expressions = splitTopLevelComma(selectClause);
|
|
41
|
+
const columns = [];
|
|
42
|
+
for (const expression of expressions) {
|
|
43
|
+
const aliasMatch = expression.match(/\s+AS\s+`?([a-zA-Z_][a-zA-Z0-9_]*)`?\s*$/iu);
|
|
44
|
+
if (aliasMatch?.[1]) {
|
|
45
|
+
columns.push(aliasMatch[1]);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const simpleMatch = expression.match(/(?:^|\.)`?([a-zA-Z_][a-zA-Z0-9_]*)`?\s*$/u);
|
|
49
|
+
if (simpleMatch?.[1]) {
|
|
50
|
+
columns.push(simpleMatch[1]);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return Array.from(new Set(columns.length > 0 ? columns : ["result"]));
|
|
55
|
+
}
|
|
56
|
+
function mapTemplateFunctionToParamType(func) {
|
|
57
|
+
const known = new Set([
|
|
58
|
+
"String",
|
|
59
|
+
"UUID",
|
|
60
|
+
"Int8",
|
|
61
|
+
"Int16",
|
|
62
|
+
"Int32",
|
|
63
|
+
"Int64",
|
|
64
|
+
"UInt8",
|
|
65
|
+
"UInt16",
|
|
66
|
+
"UInt32",
|
|
67
|
+
"UInt64",
|
|
68
|
+
"Float32",
|
|
69
|
+
"Float64",
|
|
70
|
+
"Boolean",
|
|
71
|
+
"Bool",
|
|
72
|
+
"Date",
|
|
73
|
+
"DateTime",
|
|
74
|
+
"DateTime64",
|
|
75
|
+
"Array",
|
|
76
|
+
]);
|
|
77
|
+
if (known.has(func)) {
|
|
78
|
+
return func;
|
|
79
|
+
}
|
|
80
|
+
if (func.startsWith("DateTime64")) {
|
|
81
|
+
return "DateTime64";
|
|
82
|
+
}
|
|
83
|
+
if (func.startsWith("DateTime")) {
|
|
84
|
+
return "DateTime";
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
function parseParamDefault(rawValue) {
|
|
89
|
+
const trimmed = rawValue.trim();
|
|
90
|
+
if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
|
|
91
|
+
return Number(trimmed);
|
|
92
|
+
}
|
|
93
|
+
if ((trimmed.startsWith("'") && trimmed.endsWith("'")) ||
|
|
94
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"'))) {
|
|
95
|
+
return trimmed.slice(1, -1);
|
|
96
|
+
}
|
|
97
|
+
throw new Error(`Unsupported parameter default value: "${rawValue}"`);
|
|
98
|
+
}
|
|
99
|
+
function inferParamsFromSql(sql, filePath, resourceName) {
|
|
100
|
+
const regex = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\(([^{}]*)\)\s*\}\}/g;
|
|
101
|
+
const params = new Map();
|
|
102
|
+
let match = regex.exec(sql);
|
|
103
|
+
while (match) {
|
|
104
|
+
const templateFunction = match[1] ?? "";
|
|
105
|
+
const argsRaw = match[2] ?? "";
|
|
106
|
+
const args = splitTopLevelComma(argsRaw);
|
|
107
|
+
if (args.length === 0) {
|
|
108
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, `Invalid template placeholder: "${match[0]}"`);
|
|
109
|
+
}
|
|
110
|
+
const paramName = args[0]?.trim();
|
|
111
|
+
if (!paramName || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(paramName)) {
|
|
112
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, `Unsupported parameter name in placeholder: "${match[0]}"`);
|
|
113
|
+
}
|
|
114
|
+
const mappedType = mapTemplateFunctionToParamType(templateFunction);
|
|
115
|
+
if (!mappedType) {
|
|
116
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, `Unsupported placeholder function in strict mode: "${templateFunction}"`);
|
|
117
|
+
}
|
|
118
|
+
let defaultValue;
|
|
119
|
+
if (args.length > 1) {
|
|
120
|
+
try {
|
|
121
|
+
defaultValue = parseParamDefault(args[1] ?? "");
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, error.message);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const existing = params.get(paramName);
|
|
128
|
+
if (existing) {
|
|
129
|
+
if (existing.type !== mappedType) {
|
|
130
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, `Parameter "${paramName}" is used with multiple types: "${existing.type}" and "${mappedType}".`);
|
|
131
|
+
}
|
|
132
|
+
if (existing.defaultValue !== undefined && defaultValue !== undefined) {
|
|
133
|
+
if (existing.defaultValue !== defaultValue) {
|
|
134
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, `Parameter "${paramName}" uses multiple defaults: "${existing.defaultValue}" and "${defaultValue}".`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (existing.defaultValue === undefined && defaultValue !== undefined) {
|
|
138
|
+
existing.defaultValue = defaultValue;
|
|
139
|
+
existing.required = false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
params.set(paramName, {
|
|
144
|
+
name: paramName,
|
|
145
|
+
type: mappedType,
|
|
146
|
+
required: defaultValue === undefined,
|
|
147
|
+
defaultValue,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
match = regex.exec(sql);
|
|
151
|
+
}
|
|
152
|
+
return Array.from(params.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
153
|
+
}
|
|
154
|
+
function parseToken(filePath, resourceName, value) {
|
|
155
|
+
const parts = value.split(/\s+/).filter(Boolean);
|
|
156
|
+
if (parts.length === 0) {
|
|
157
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, "Invalid TOKEN line.");
|
|
158
|
+
}
|
|
159
|
+
if (parts.length > 2) {
|
|
160
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, `Unsupported TOKEN syntax in strict mode: "${value}"`);
|
|
161
|
+
}
|
|
162
|
+
const tokenName = parts[0];
|
|
163
|
+
const scope = parts[1] ?? "READ";
|
|
164
|
+
if (scope !== "READ") {
|
|
165
|
+
throw new MigrationParseError(filePath, "pipe", resourceName, `Unsupported pipe token scope: "${scope}"`);
|
|
166
|
+
}
|
|
167
|
+
return { name: tokenName, scope: "READ" };
|
|
168
|
+
}
|
|
169
|
+
export function parsePipeFile(resource) {
|
|
170
|
+
const lines = splitLines(resource.content);
|
|
171
|
+
const nodes = [];
|
|
172
|
+
const tokens = [];
|
|
173
|
+
let description;
|
|
174
|
+
let pipeType = "pipe";
|
|
175
|
+
let cacheTtl;
|
|
176
|
+
let materializedDatasource;
|
|
177
|
+
let deploymentMethod;
|
|
178
|
+
let copyTargetDatasource;
|
|
179
|
+
let copySchedule;
|
|
180
|
+
let copyMode;
|
|
181
|
+
let i = 0;
|
|
182
|
+
while (i < lines.length) {
|
|
183
|
+
const line = (lines[i] ?? "").trim();
|
|
184
|
+
if (!line) {
|
|
185
|
+
i += 1;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (line === "DESCRIPTION >") {
|
|
189
|
+
const block = readIndentedBlock(lines, i + 1);
|
|
190
|
+
if (block.lines.length === 0) {
|
|
191
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, "DESCRIPTION block is empty.");
|
|
192
|
+
}
|
|
193
|
+
if (!description) {
|
|
194
|
+
description = block.lines.join("\n");
|
|
195
|
+
}
|
|
196
|
+
else if (nodes.length > 0) {
|
|
197
|
+
nodes[nodes.length - 1] = {
|
|
198
|
+
...nodes[nodes.length - 1],
|
|
199
|
+
description: block.lines.join("\n"),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, "DESCRIPTION block is not attached to a node or pipe header.");
|
|
204
|
+
}
|
|
205
|
+
i = block.nextIndex;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (line.startsWith("NODE ")) {
|
|
209
|
+
const nodeName = line.slice("NODE ".length).trim();
|
|
210
|
+
if (!nodeName) {
|
|
211
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, "NODE directive requires a name.");
|
|
212
|
+
}
|
|
213
|
+
i += 1;
|
|
214
|
+
i = nextNonBlank(lines, i);
|
|
215
|
+
let nodeDescription;
|
|
216
|
+
if ((lines[i] ?? "").trim() === "DESCRIPTION >") {
|
|
217
|
+
const descriptionBlock = readIndentedBlock(lines, i + 1);
|
|
218
|
+
if (descriptionBlock.lines.length === 0) {
|
|
219
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Node "${nodeName}" has an empty DESCRIPTION block.`);
|
|
220
|
+
}
|
|
221
|
+
nodeDescription = descriptionBlock.lines.join("\n");
|
|
222
|
+
i = descriptionBlock.nextIndex;
|
|
223
|
+
i = nextNonBlank(lines, i);
|
|
224
|
+
}
|
|
225
|
+
if ((lines[i] ?? "").trim() !== "SQL >") {
|
|
226
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Node "${nodeName}" is missing SQL > block.`);
|
|
227
|
+
}
|
|
228
|
+
const sqlBlock = readIndentedBlock(lines, i + 1);
|
|
229
|
+
if (sqlBlock.lines.length === 0) {
|
|
230
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Node "${nodeName}" has an empty SQL block.`);
|
|
231
|
+
}
|
|
232
|
+
const normalizedSqlLines = sqlBlock.lines[0] === "%" ? sqlBlock.lines.slice(1) : sqlBlock.lines;
|
|
233
|
+
const sql = normalizedSqlLines.join("\n").trim();
|
|
234
|
+
if (!sql) {
|
|
235
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Node "${nodeName}" has SQL marker '%' but no SQL body.`);
|
|
236
|
+
}
|
|
237
|
+
nodes.push({
|
|
238
|
+
name: nodeName,
|
|
239
|
+
description: nodeDescription,
|
|
240
|
+
sql,
|
|
241
|
+
});
|
|
242
|
+
i = sqlBlock.nextIndex;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
const { key, value } = parseDirectiveLine(line);
|
|
246
|
+
switch (key) {
|
|
247
|
+
case "TYPE":
|
|
248
|
+
if (value === "endpoint") {
|
|
249
|
+
pipeType = "endpoint";
|
|
250
|
+
}
|
|
251
|
+
else if (value === "MATERIALIZED") {
|
|
252
|
+
pipeType = "materialized";
|
|
253
|
+
}
|
|
254
|
+
else if (value === "COPY") {
|
|
255
|
+
pipeType = "copy";
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Unsupported TYPE value in strict mode: "${value}"`);
|
|
259
|
+
}
|
|
260
|
+
break;
|
|
261
|
+
case "CACHE": {
|
|
262
|
+
const ttl = Number(value);
|
|
263
|
+
if (!Number.isFinite(ttl) || ttl < 0) {
|
|
264
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Invalid CACHE value: "${value}"`);
|
|
265
|
+
}
|
|
266
|
+
cacheTtl = ttl;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case "DATASOURCE":
|
|
270
|
+
materializedDatasource = value.trim();
|
|
271
|
+
break;
|
|
272
|
+
case "DEPLOYMENT_METHOD":
|
|
273
|
+
if (value !== "alter") {
|
|
274
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Unsupported DEPLOYMENT_METHOD: "${value}"`);
|
|
275
|
+
}
|
|
276
|
+
deploymentMethod = "alter";
|
|
277
|
+
break;
|
|
278
|
+
case "TARGET_DATASOURCE":
|
|
279
|
+
copyTargetDatasource = value.trim();
|
|
280
|
+
break;
|
|
281
|
+
case "COPY_SCHEDULE":
|
|
282
|
+
copySchedule = value;
|
|
283
|
+
break;
|
|
284
|
+
case "COPY_MODE":
|
|
285
|
+
if (value !== "append" && value !== "replace") {
|
|
286
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Unsupported COPY_MODE: "${value}"`);
|
|
287
|
+
}
|
|
288
|
+
copyMode = value;
|
|
289
|
+
break;
|
|
290
|
+
case "TOKEN":
|
|
291
|
+
tokens.push(parseToken(resource.filePath, resource.name, value));
|
|
292
|
+
break;
|
|
293
|
+
default:
|
|
294
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, `Unsupported pipe directive in strict mode: "${line}"`);
|
|
295
|
+
}
|
|
296
|
+
i += 1;
|
|
297
|
+
}
|
|
298
|
+
if (nodes.length === 0) {
|
|
299
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, "At least one NODE is required.");
|
|
300
|
+
}
|
|
301
|
+
if (pipeType !== "endpoint" && cacheTtl !== undefined) {
|
|
302
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, "CACHE is only supported for TYPE endpoint.");
|
|
303
|
+
}
|
|
304
|
+
if (pipeType === "materialized" && !materializedDatasource) {
|
|
305
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, "DATASOURCE is required for TYPE MATERIALIZED.");
|
|
306
|
+
}
|
|
307
|
+
if (pipeType === "copy" && !copyTargetDatasource) {
|
|
308
|
+
throw new MigrationParseError(resource.filePath, "pipe", resource.name, "TARGET_DATASOURCE is required for TYPE COPY.");
|
|
309
|
+
}
|
|
310
|
+
const params = pipeType === "materialized" || pipeType === "copy"
|
|
311
|
+
? []
|
|
312
|
+
: inferParamsFromSql(nodes.map((node) => node.sql).join("\n"), resource.filePath, resource.name);
|
|
313
|
+
const inferredOutputColumns = pipeType === "endpoint" ? inferOutputColumnsFromSql(nodes[nodes.length - 1].sql) : [];
|
|
314
|
+
return {
|
|
315
|
+
kind: "pipe",
|
|
316
|
+
name: resource.name,
|
|
317
|
+
filePath: resource.filePath,
|
|
318
|
+
description,
|
|
319
|
+
type: pipeType,
|
|
320
|
+
nodes,
|
|
321
|
+
cacheTtl,
|
|
322
|
+
materializedDatasource,
|
|
323
|
+
deploymentMethod,
|
|
324
|
+
copyTargetDatasource,
|
|
325
|
+
copySchedule,
|
|
326
|
+
copyMode,
|
|
327
|
+
tokens,
|
|
328
|
+
params,
|
|
329
|
+
inferredOutputColumns,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=parse-pipe.js.map
|