@tinybirdco/sdk 0.0.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/README.md +518 -0
- package/bin/tinybird.js +7 -0
- package/dist/api/branches.d.ts +98 -0
- package/dist/api/branches.d.ts.map +1 -0
- package/dist/api/branches.js +203 -0
- package/dist/api/branches.js.map +1 -0
- package/dist/api/branches.test.d.ts +2 -0
- package/dist/api/branches.test.d.ts.map +1 -0
- package/dist/api/branches.test.js +286 -0
- package/dist/api/branches.test.js.map +1 -0
- package/dist/api/build.d.ts +130 -0
- package/dist/api/build.d.ts.map +1 -0
- package/dist/api/build.js +143 -0
- package/dist/api/build.js.map +1 -0
- package/dist/api/build.test.d.ts +2 -0
- package/dist/api/build.test.d.ts.map +1 -0
- package/dist/api/build.test.js +138 -0
- package/dist/api/build.test.js.map +1 -0
- package/dist/api/deploy.d.ts +39 -0
- package/dist/api/deploy.d.ts.map +1 -0
- package/dist/api/deploy.js +135 -0
- package/dist/api/deploy.js.map +1 -0
- package/dist/api/deploy.test.d.ts +2 -0
- package/dist/api/deploy.test.d.ts.map +1 -0
- package/dist/api/deploy.test.js +118 -0
- package/dist/api/deploy.test.js.map +1 -0
- package/dist/api/workspaces.d.ts +46 -0
- package/dist/api/workspaces.d.ts.map +1 -0
- package/dist/api/workspaces.js +39 -0
- package/dist/api/workspaces.js.map +1 -0
- package/dist/api/workspaces.test.d.ts +2 -0
- package/dist/api/workspaces.test.d.ts.map +1 -0
- package/dist/api/workspaces.test.js +65 -0
- package/dist/api/workspaces.test.js.map +1 -0
- package/dist/cli/auth.d.ts +86 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +284 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/branch-store.d.ts +53 -0
- package/dist/cli/branch-store.d.ts.map +1 -0
- package/dist/cli/branch-store.js +91 -0
- package/dist/cli/branch-store.js.map +1 -0
- package/dist/cli/branch-store.test.d.ts +2 -0
- package/dist/cli/branch-store.test.d.ts.map +1 -0
- package/dist/cli/branch-store.test.js +115 -0
- package/dist/cli/branch-store.test.js.map +1 -0
- package/dist/cli/commands/branch.d.ts +82 -0
- package/dist/cli/commands/branch.d.ts.map +1 -0
- package/dist/cli/commands/branch.js +215 -0
- package/dist/cli/commands/branch.js.map +1 -0
- package/dist/cli/commands/build.d.ts +43 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +138 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +78 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +226 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/init.d.ts +45 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +277 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/init.test.d.ts +2 -0
- package/dist/cli/commands/init.test.d.ts.map +1 -0
- package/dist/cli/commands/init.test.js +158 -0
- package/dist/cli/commands/init.test.js.map +1 -0
- package/dist/cli/commands/login.d.ts +37 -0
- package/dist/cli/commands/login.d.ts.map +1 -0
- package/dist/cli/commands/login.js +64 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/config.d.ts +114 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +258 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/config.test.d.ts +2 -0
- package/dist/cli/config.test.d.ts.map +1 -0
- package/dist/cli/config.test.js +243 -0
- package/dist/cli/config.test.js.map +1 -0
- package/dist/cli/env.d.ts +29 -0
- package/dist/cli/env.d.ts.map +1 -0
- package/dist/cli/env.js +66 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/git.d.ts +29 -0
- package/dist/cli/git.d.ts.map +1 -0
- package/dist/cli/git.js +114 -0
- package/dist/cli/git.js.map +1 -0
- package/dist/cli/git.test.d.ts +2 -0
- package/dist/cli/git.test.d.ts.map +1 -0
- package/dist/cli/git.test.js +125 -0
- package/dist/cli/git.test.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +337 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/schema-validation.d.ts +95 -0
- package/dist/cli/utils/schema-validation.d.ts.map +1 -0
- package/dist/cli/utils/schema-validation.js +175 -0
- package/dist/cli/utils/schema-validation.js.map +1 -0
- package/dist/cli/utils/schema-validation.test.d.ts +5 -0
- package/dist/cli/utils/schema-validation.test.d.ts.map +1 -0
- package/dist/cli/utils/schema-validation.test.js +173 -0
- package/dist/cli/utils/schema-validation.test.js.map +1 -0
- package/dist/client/base.d.ts +116 -0
- package/dist/client/base.d.ts.map +1 -0
- package/dist/client/base.js +328 -0
- package/dist/client/base.js.map +1 -0
- package/dist/client/types.d.ts +137 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +43 -0
- package/dist/client/types.js.map +1 -0
- package/dist/generator/client.d.ts +44 -0
- package/dist/generator/client.d.ts.map +1 -0
- package/dist/generator/client.js +144 -0
- package/dist/generator/client.js.map +1 -0
- package/dist/generator/datasource.d.ts +57 -0
- package/dist/generator/datasource.d.ts.map +1 -0
- package/dist/generator/datasource.js +169 -0
- package/dist/generator/datasource.js.map +1 -0
- package/dist/generator/datasource.test.d.ts +2 -0
- package/dist/generator/datasource.test.d.ts.map +1 -0
- package/dist/generator/datasource.test.js +254 -0
- package/dist/generator/datasource.test.js.map +1 -0
- package/dist/generator/index.d.ts +131 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +121 -0
- package/dist/generator/index.js.map +1 -0
- package/dist/generator/index.test.d.ts +2 -0
- package/dist/generator/index.test.d.ts.map +1 -0
- package/dist/generator/index.test.js +175 -0
- package/dist/generator/index.test.js.map +1 -0
- package/dist/generator/loader.d.ts +156 -0
- package/dist/generator/loader.d.ts.map +1 -0
- package/dist/generator/loader.js +295 -0
- package/dist/generator/loader.js.map +1 -0
- package/dist/generator/pipe.d.ts +72 -0
- package/dist/generator/pipe.d.ts.map +1 -0
- package/dist/generator/pipe.js +174 -0
- package/dist/generator/pipe.js.map +1 -0
- package/dist/generator/pipe.test.d.ts +2 -0
- package/dist/generator/pipe.test.d.ts.map +1 -0
- package/dist/generator/pipe.test.js +393 -0
- package/dist/generator/pipe.test.js.map +1 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/infer/index.d.ts +202 -0
- package/dist/infer/index.d.ts.map +1 -0
- package/dist/infer/index.js +5 -0
- package/dist/infer/index.js.map +1 -0
- package/dist/schema/datasource.d.ts +135 -0
- package/dist/schema/datasource.d.ts.map +1 -0
- package/dist/schema/datasource.js +105 -0
- package/dist/schema/datasource.js.map +1 -0
- package/dist/schema/datasource.test.d.ts +2 -0
- package/dist/schema/datasource.test.d.ts.map +1 -0
- package/dist/schema/datasource.test.js +142 -0
- package/dist/schema/datasource.test.js.map +1 -0
- package/dist/schema/engines.d.ts +157 -0
- package/dist/schema/engines.d.ts.map +1 -0
- package/dist/schema/engines.js +155 -0
- package/dist/schema/engines.js.map +1 -0
- package/dist/schema/engines.test.d.ts +2 -0
- package/dist/schema/engines.test.d.ts.map +1 -0
- package/dist/schema/engines.test.js +221 -0
- package/dist/schema/engines.test.js.map +1 -0
- package/dist/schema/params.d.ts +106 -0
- package/dist/schema/params.d.ts.map +1 -0
- package/dist/schema/params.js +138 -0
- package/dist/schema/params.js.map +1 -0
- package/dist/schema/params.test.d.ts +2 -0
- package/dist/schema/params.test.d.ts.map +1 -0
- package/dist/schema/params.test.js +175 -0
- package/dist/schema/params.test.js.map +1 -0
- package/dist/schema/pipe.d.ts +436 -0
- package/dist/schema/pipe.d.ts.map +1 -0
- package/dist/schema/pipe.js +484 -0
- package/dist/schema/pipe.js.map +1 -0
- package/dist/schema/pipe.test.d.ts +2 -0
- package/dist/schema/pipe.test.d.ts.map +1 -0
- package/dist/schema/pipe.test.js +488 -0
- package/dist/schema/pipe.test.js.map +1 -0
- package/dist/schema/project.d.ts +202 -0
- package/dist/schema/project.d.ts.map +1 -0
- package/dist/schema/project.js +188 -0
- package/dist/schema/project.js.map +1 -0
- package/dist/schema/project.test.d.ts +2 -0
- package/dist/schema/project.test.d.ts.map +1 -0
- package/dist/schema/project.test.js +180 -0
- package/dist/schema/project.test.js.map +1 -0
- package/dist/schema/types.d.ts +140 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +174 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/schema/types.test.d.ts +2 -0
- package/dist/schema/types.test.d.ts.map +1 -0
- package/dist/schema/types.test.js +176 -0
- package/dist/schema/types.test.js.map +1 -0
- package/dist/test/handlers.d.ts +58 -0
- package/dist/test/handlers.d.ts.map +1 -0
- package/dist/test/handlers.js +62 -0
- package/dist/test/handlers.js.map +1 -0
- package/dist/test/setup.d.ts +5 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +11 -0
- package/dist/test/setup.js.map +1 -0
- package/package.json +57 -0
- package/src/api/branches.test.ts +377 -0
- package/src/api/branches.ts +334 -0
- package/src/api/build.test.ts +216 -0
- package/src/api/build.ts +266 -0
- package/src/api/deploy.test.ts +193 -0
- package/src/api/deploy.ts +163 -0
- package/src/api/workspaces.test.ts +81 -0
- package/src/api/workspaces.ts +77 -0
- package/src/cli/auth.ts +358 -0
- package/src/cli/branch-store.test.ts +139 -0
- package/src/cli/branch-store.ts +137 -0
- package/src/cli/commands/branch.ts +306 -0
- package/src/cli/commands/build.ts +183 -0
- package/src/cli/commands/dev.ts +334 -0
- package/src/cli/commands/init.test.ts +249 -0
- package/src/cli/commands/init.ts +323 -0
- package/src/cli/commands/login.ts +98 -0
- package/src/cli/config.test.ts +359 -0
- package/src/cli/config.ts +335 -0
- package/src/cli/env.ts +86 -0
- package/src/cli/git.test.ts +147 -0
- package/src/cli/git.ts +125 -0
- package/src/cli/index.ts +382 -0
- package/src/cli/utils/schema-validation.test.ts +222 -0
- package/src/cli/utils/schema-validation.ts +272 -0
- package/src/client/base.ts +414 -0
- package/src/client/types.ts +165 -0
- package/src/generator/client.ts +194 -0
- package/src/generator/datasource.test.ts +297 -0
- package/src/generator/datasource.ts +217 -0
- package/src/generator/index.test.ts +209 -0
- package/src/generator/index.ts +203 -0
- package/src/generator/loader.ts +406 -0
- package/src/generator/pipe.test.ts +441 -0
- package/src/generator/pipe.ts +220 -0
- package/src/index.ts +191 -0
- package/src/infer/index.ts +247 -0
- package/src/schema/datasource.test.ts +187 -0
- package/src/schema/datasource.ts +195 -0
- package/src/schema/engines.test.ts +247 -0
- package/src/schema/engines.ts +271 -0
- package/src/schema/params.test.ts +208 -0
- package/src/schema/params.ts +249 -0
- package/src/schema/pipe.test.ts +588 -0
- package/src/schema/pipe.ts +832 -0
- package/src/schema/project.test.ts +236 -0
- package/src/schema/project.ts +394 -0
- package/src/schema/types.test.ts +212 -0
- package/src/schema/types.ts +366 -0
- package/src/test/handlers.ts +79 -0
- package/src/test/setup.ts +13 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipe content generator
|
|
3
|
+
* Converts PipeDefinition to native .pipe file format
|
|
4
|
+
*/
|
|
5
|
+
import type { PipeDefinition } from "../schema/pipe.js";
|
|
6
|
+
/**
|
|
7
|
+
* Generated pipe content
|
|
8
|
+
*/
|
|
9
|
+
export interface GeneratedPipe {
|
|
10
|
+
/** Pipe name */
|
|
11
|
+
name: string;
|
|
12
|
+
/** The generated .pipe file content */
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate a .pipe file content from a PipeDefinition
|
|
17
|
+
*
|
|
18
|
+
* @param pipe - The pipe definition
|
|
19
|
+
* @returns Generated pipe content
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const topEvents = definePipe('top_events', {
|
|
24
|
+
* description: 'Get top events by count',
|
|
25
|
+
* params: {
|
|
26
|
+
* start_date: p.dateTime(),
|
|
27
|
+
* limit: p.int32().optional(10),
|
|
28
|
+
* },
|
|
29
|
+
* nodes: [
|
|
30
|
+
* node({
|
|
31
|
+
* name: 'endpoint',
|
|
32
|
+
* sql: `
|
|
33
|
+
* SELECT event_type, count() as count
|
|
34
|
+
* FROM events
|
|
35
|
+
* WHERE timestamp >= {{DateTime(start_date)}}
|
|
36
|
+
* ORDER BY count DESC
|
|
37
|
+
* LIMIT {{Int32(limit, 10)}}
|
|
38
|
+
* `,
|
|
39
|
+
* }),
|
|
40
|
+
* ],
|
|
41
|
+
* output: {
|
|
42
|
+
* event_type: t.string(),
|
|
43
|
+
* count: t.uint64(),
|
|
44
|
+
* },
|
|
45
|
+
* endpoint: true,
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* const { content } = generatePipe(topEvents);
|
|
49
|
+
* // Returns:
|
|
50
|
+
* // DESCRIPTION >
|
|
51
|
+
* // Get top events by count
|
|
52
|
+
* //
|
|
53
|
+
* // NODE endpoint
|
|
54
|
+
* // SQL >
|
|
55
|
+
* // SELECT event_type, count() as count
|
|
56
|
+
* // FROM events
|
|
57
|
+
* // WHERE timestamp >= {{DateTime(start_date)}}
|
|
58
|
+
* // ORDER BY count DESC
|
|
59
|
+
* // LIMIT {{Int32(limit, 10)}}
|
|
60
|
+
* //
|
|
61
|
+
* // TYPE endpoint
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function generatePipe(pipe: PipeDefinition): GeneratedPipe;
|
|
65
|
+
/**
|
|
66
|
+
* Generate .pipe files for all pipes in a project
|
|
67
|
+
*
|
|
68
|
+
* @param pipes - Record of pipe definitions
|
|
69
|
+
* @returns Array of generated pipe content
|
|
70
|
+
*/
|
|
71
|
+
export declare function generateAllPipes(pipes: Record<string, PipeDefinition>): GeneratedPipe[];
|
|
72
|
+
//# sourceMappingURL=pipe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipe.d.ts","sourceRoot":"","sources":["../../src/generator/pipe.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,mBAAmB,CAAC;AAG3B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AA6FD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,aAAa,CA2ChE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GACpC,aAAa,EAAE,CAEjB"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipe content generator
|
|
3
|
+
* Converts PipeDefinition to native .pipe file format
|
|
4
|
+
*/
|
|
5
|
+
import { getEndpointConfig, getMaterializedConfig, getCopyConfig } from "../schema/pipe.js";
|
|
6
|
+
/**
|
|
7
|
+
* Check if SQL contains template parameters like {{...}}
|
|
8
|
+
*/
|
|
9
|
+
function hasDynamicParameters(sql) {
|
|
10
|
+
return /\{\{[^}]+\}\}/.test(sql);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Generate a NODE section for the pipe
|
|
14
|
+
*/
|
|
15
|
+
function generateNode(node) {
|
|
16
|
+
const parts = [];
|
|
17
|
+
parts.push(`NODE ${node._name}`);
|
|
18
|
+
if (node.description) {
|
|
19
|
+
parts.push(`DESCRIPTION >`);
|
|
20
|
+
parts.push(` ${node.description}`);
|
|
21
|
+
}
|
|
22
|
+
parts.push(`SQL >`);
|
|
23
|
+
// Check if SQL has dynamic parameters - if so, add % on its own line
|
|
24
|
+
const isDynamic = hasDynamicParameters(node.sql);
|
|
25
|
+
if (isDynamic) {
|
|
26
|
+
parts.push(` %`);
|
|
27
|
+
}
|
|
28
|
+
const sqlLines = node.sql.trim().split("\n");
|
|
29
|
+
sqlLines.forEach((line) => {
|
|
30
|
+
parts.push(` ${line}`);
|
|
31
|
+
});
|
|
32
|
+
return parts.join("\n");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate the TYPE endpoint section
|
|
36
|
+
*/
|
|
37
|
+
function generateEndpoint(endpoint) {
|
|
38
|
+
const parts = ["TYPE endpoint"];
|
|
39
|
+
if (endpoint.cache?.enabled) {
|
|
40
|
+
if (endpoint.cache.ttl !== undefined) {
|
|
41
|
+
parts.push(`CACHE ${endpoint.cache.ttl}`);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
parts.push("CACHE 60"); // Default cache TTL
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return parts.join("\n");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Generate the TYPE MATERIALIZED section
|
|
51
|
+
*/
|
|
52
|
+
function generateMaterialized(config) {
|
|
53
|
+
const parts = ["TYPE MATERIALIZED"];
|
|
54
|
+
// The config is normalized by definePipe to always have `datasource` set.
|
|
55
|
+
// Use non-null assertion since we know it's always present after normalization.
|
|
56
|
+
const datasourceName = config.datasource._name;
|
|
57
|
+
parts.push(`DATASOURCE ${datasourceName}`);
|
|
58
|
+
if (config.deploymentMethod === "alter") {
|
|
59
|
+
parts.push("DEPLOYMENT_METHOD alter");
|
|
60
|
+
}
|
|
61
|
+
return parts.join("\n");
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Generate the TYPE COPY section
|
|
65
|
+
*/
|
|
66
|
+
function generateCopy(config) {
|
|
67
|
+
const parts = ["TYPE COPY"];
|
|
68
|
+
const datasourceName = config.datasource._name;
|
|
69
|
+
parts.push(`TARGET_DATASOURCE ${datasourceName}`);
|
|
70
|
+
if (config.copy_schedule) {
|
|
71
|
+
parts.push(`COPY_SCHEDULE ${config.copy_schedule}`);
|
|
72
|
+
}
|
|
73
|
+
if (config.copy_mode) {
|
|
74
|
+
parts.push(`COPY_MODE ${config.copy_mode}`);
|
|
75
|
+
}
|
|
76
|
+
return parts.join("\n");
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Generate a .pipe file content from a PipeDefinition
|
|
80
|
+
*
|
|
81
|
+
* @param pipe - The pipe definition
|
|
82
|
+
* @returns Generated pipe content
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* const topEvents = definePipe('top_events', {
|
|
87
|
+
* description: 'Get top events by count',
|
|
88
|
+
* params: {
|
|
89
|
+
* start_date: p.dateTime(),
|
|
90
|
+
* limit: p.int32().optional(10),
|
|
91
|
+
* },
|
|
92
|
+
* nodes: [
|
|
93
|
+
* node({
|
|
94
|
+
* name: 'endpoint',
|
|
95
|
+
* sql: `
|
|
96
|
+
* SELECT event_type, count() as count
|
|
97
|
+
* FROM events
|
|
98
|
+
* WHERE timestamp >= {{DateTime(start_date)}}
|
|
99
|
+
* ORDER BY count DESC
|
|
100
|
+
* LIMIT {{Int32(limit, 10)}}
|
|
101
|
+
* `,
|
|
102
|
+
* }),
|
|
103
|
+
* ],
|
|
104
|
+
* output: {
|
|
105
|
+
* event_type: t.string(),
|
|
106
|
+
* count: t.uint64(),
|
|
107
|
+
* },
|
|
108
|
+
* endpoint: true,
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* const { content } = generatePipe(topEvents);
|
|
112
|
+
* // Returns:
|
|
113
|
+
* // DESCRIPTION >
|
|
114
|
+
* // Get top events by count
|
|
115
|
+
* //
|
|
116
|
+
* // NODE endpoint
|
|
117
|
+
* // SQL >
|
|
118
|
+
* // SELECT event_type, count() as count
|
|
119
|
+
* // FROM events
|
|
120
|
+
* // WHERE timestamp >= {{DateTime(start_date)}}
|
|
121
|
+
* // ORDER BY count DESC
|
|
122
|
+
* // LIMIT {{Int32(limit, 10)}}
|
|
123
|
+
* //
|
|
124
|
+
* // TYPE endpoint
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function generatePipe(pipe) {
|
|
128
|
+
const parts = [];
|
|
129
|
+
// Add description if present
|
|
130
|
+
if (pipe.options.description) {
|
|
131
|
+
parts.push(`DESCRIPTION >\n ${pipe.options.description}`);
|
|
132
|
+
parts.push("");
|
|
133
|
+
}
|
|
134
|
+
// Add all nodes
|
|
135
|
+
pipe.options.nodes.forEach((node, index) => {
|
|
136
|
+
parts.push(generateNode(node));
|
|
137
|
+
// Add empty line between nodes
|
|
138
|
+
if (index < pipe.options.nodes.length - 1) {
|
|
139
|
+
parts.push("");
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
// Add endpoint configuration if this is an endpoint
|
|
143
|
+
const endpointConfig = getEndpointConfig(pipe);
|
|
144
|
+
if (endpointConfig) {
|
|
145
|
+
parts.push("");
|
|
146
|
+
parts.push(generateEndpoint(endpointConfig));
|
|
147
|
+
}
|
|
148
|
+
// Add materialized view configuration if this is a materialized view
|
|
149
|
+
const materializedConfig = getMaterializedConfig(pipe);
|
|
150
|
+
if (materializedConfig) {
|
|
151
|
+
parts.push("");
|
|
152
|
+
parts.push(generateMaterialized(materializedConfig));
|
|
153
|
+
}
|
|
154
|
+
// Add copy pipe configuration if this is a copy pipe
|
|
155
|
+
const copyConfig = getCopyConfig(pipe);
|
|
156
|
+
if (copyConfig) {
|
|
157
|
+
parts.push("");
|
|
158
|
+
parts.push(generateCopy(copyConfig));
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
name: pipe._name,
|
|
162
|
+
content: parts.join("\n"),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Generate .pipe files for all pipes in a project
|
|
167
|
+
*
|
|
168
|
+
* @param pipes - Record of pipe definitions
|
|
169
|
+
* @returns Array of generated pipe content
|
|
170
|
+
*/
|
|
171
|
+
export function generateAllPipes(pipes) {
|
|
172
|
+
return Object.values(pipes).map(generatePipe);
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=pipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipe.js","sourceRoot":"","sources":["../../src/generator/pipe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipe.test.d.ts","sourceRoot":"","sources":["../../src/generator/pipe.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generatePipe, generateAllPipes } from './pipe.js';
|
|
3
|
+
import { definePipe, defineMaterializedView, node } from '../schema/pipe.js';
|
|
4
|
+
import { defineDatasource } from '../schema/datasource.js';
|
|
5
|
+
import { t } from '../schema/types.js';
|
|
6
|
+
import { p } from '../schema/params.js';
|
|
7
|
+
import { engine } from '../schema/engines.js';
|
|
8
|
+
// Helper to create a simple output schema for tests
|
|
9
|
+
const simpleOutput = { result: t.int32() };
|
|
10
|
+
describe('Pipe Generator', () => {
|
|
11
|
+
describe('generatePipe', () => {
|
|
12
|
+
it('generates basic pipe with node', () => {
|
|
13
|
+
const pipe = definePipe('test_pipe', {
|
|
14
|
+
nodes: [
|
|
15
|
+
node({
|
|
16
|
+
name: 'endpoint',
|
|
17
|
+
sql: 'SELECT * FROM table',
|
|
18
|
+
}),
|
|
19
|
+
],
|
|
20
|
+
output: simpleOutput,
|
|
21
|
+
endpoint: true,
|
|
22
|
+
});
|
|
23
|
+
const result = generatePipe(pipe);
|
|
24
|
+
expect(result.name).toBe('test_pipe');
|
|
25
|
+
expect(result.content).toContain('NODE endpoint');
|
|
26
|
+
expect(result.content).toContain('SQL >');
|
|
27
|
+
expect(result.content).toContain('SELECT * FROM table');
|
|
28
|
+
});
|
|
29
|
+
it('includes description when provided', () => {
|
|
30
|
+
const pipe = definePipe('test_pipe', {
|
|
31
|
+
description: 'Test pipe description',
|
|
32
|
+
nodes: [node({ name: 'endpoint', sql: 'SELECT 1' })],
|
|
33
|
+
output: simpleOutput,
|
|
34
|
+
endpoint: true,
|
|
35
|
+
});
|
|
36
|
+
const result = generatePipe(pipe);
|
|
37
|
+
expect(result.content).toContain('DESCRIPTION >');
|
|
38
|
+
expect(result.content).toContain('Test pipe description');
|
|
39
|
+
});
|
|
40
|
+
it('includes TYPE endpoint when endpoint is true', () => {
|
|
41
|
+
const pipe = definePipe('test_pipe', {
|
|
42
|
+
nodes: [node({ name: 'endpoint', sql: 'SELECT 1' })],
|
|
43
|
+
output: simpleOutput,
|
|
44
|
+
endpoint: true,
|
|
45
|
+
});
|
|
46
|
+
const result = generatePipe(pipe);
|
|
47
|
+
expect(result.content).toContain('TYPE endpoint');
|
|
48
|
+
});
|
|
49
|
+
it('does not include TYPE endpoint when endpoint is false', () => {
|
|
50
|
+
const pipe = definePipe('test_pipe', {
|
|
51
|
+
nodes: [node({ name: 'endpoint', sql: 'SELECT 1' })],
|
|
52
|
+
output: simpleOutput,
|
|
53
|
+
endpoint: false,
|
|
54
|
+
});
|
|
55
|
+
const result = generatePipe(pipe);
|
|
56
|
+
expect(result.content).not.toContain('TYPE endpoint');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('Dynamic SQL detection', () => {
|
|
60
|
+
it('adds % on its own line for SQL with template parameters', () => {
|
|
61
|
+
const pipe = definePipe('test_pipe', {
|
|
62
|
+
nodes: [
|
|
63
|
+
node({
|
|
64
|
+
name: 'endpoint',
|
|
65
|
+
sql: 'SELECT * FROM table WHERE id = {{Int32(id)}}',
|
|
66
|
+
}),
|
|
67
|
+
],
|
|
68
|
+
output: simpleOutput,
|
|
69
|
+
endpoint: true,
|
|
70
|
+
});
|
|
71
|
+
const result = generatePipe(pipe);
|
|
72
|
+
expect(result.content).toContain('SQL >\n %\n SELECT');
|
|
73
|
+
});
|
|
74
|
+
it('adds % for SQL with DateTime parameter', () => {
|
|
75
|
+
const pipe = definePipe('test_pipe', {
|
|
76
|
+
nodes: [
|
|
77
|
+
node({
|
|
78
|
+
name: 'endpoint',
|
|
79
|
+
sql: 'SELECT * FROM table WHERE timestamp >= {{DateTime(start_date)}}',
|
|
80
|
+
}),
|
|
81
|
+
],
|
|
82
|
+
output: simpleOutput,
|
|
83
|
+
endpoint: true,
|
|
84
|
+
});
|
|
85
|
+
const result = generatePipe(pipe);
|
|
86
|
+
expect(result.content).toContain(' %\n');
|
|
87
|
+
});
|
|
88
|
+
it('does not add % for SQL without template parameters', () => {
|
|
89
|
+
const pipe = definePipe('test_pipe', {
|
|
90
|
+
nodes: [
|
|
91
|
+
node({
|
|
92
|
+
name: 'endpoint',
|
|
93
|
+
sql: 'SELECT * FROM table',
|
|
94
|
+
}),
|
|
95
|
+
],
|
|
96
|
+
output: simpleOutput,
|
|
97
|
+
endpoint: true,
|
|
98
|
+
});
|
|
99
|
+
const result = generatePipe(pipe);
|
|
100
|
+
expect(result.content).not.toContain('%');
|
|
101
|
+
});
|
|
102
|
+
it('does not add % for SQL with curly braces that are not parameters', () => {
|
|
103
|
+
const pipe = definePipe('test_pipe', {
|
|
104
|
+
nodes: [
|
|
105
|
+
node({
|
|
106
|
+
name: 'endpoint',
|
|
107
|
+
sql: "SELECT JSONExtract(data, 'field', 'String') FROM table",
|
|
108
|
+
}),
|
|
109
|
+
],
|
|
110
|
+
output: simpleOutput,
|
|
111
|
+
endpoint: true,
|
|
112
|
+
});
|
|
113
|
+
const result = generatePipe(pipe);
|
|
114
|
+
expect(result.content).not.toContain('%');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
describe('Multiple nodes', () => {
|
|
118
|
+
it('generates all nodes with separation', () => {
|
|
119
|
+
const pipe = definePipe('test_pipe', {
|
|
120
|
+
nodes: [
|
|
121
|
+
node({ name: 'first', sql: 'SELECT * FROM table1' }),
|
|
122
|
+
node({ name: 'second', sql: 'SELECT * FROM first' }),
|
|
123
|
+
],
|
|
124
|
+
output: simpleOutput,
|
|
125
|
+
endpoint: true,
|
|
126
|
+
});
|
|
127
|
+
const result = generatePipe(pipe);
|
|
128
|
+
expect(result.content).toContain('NODE first');
|
|
129
|
+
expect(result.content).toContain('NODE second');
|
|
130
|
+
expect(result.content).toContain('SELECT * FROM table1');
|
|
131
|
+
expect(result.content).toContain('SELECT * FROM first');
|
|
132
|
+
});
|
|
133
|
+
it('includes node descriptions', () => {
|
|
134
|
+
const pipe = definePipe('test_pipe', {
|
|
135
|
+
nodes: [
|
|
136
|
+
node({
|
|
137
|
+
name: 'endpoint',
|
|
138
|
+
description: 'This is a test node',
|
|
139
|
+
sql: 'SELECT 1',
|
|
140
|
+
}),
|
|
141
|
+
],
|
|
142
|
+
output: simpleOutput,
|
|
143
|
+
endpoint: true,
|
|
144
|
+
});
|
|
145
|
+
const result = generatePipe(pipe);
|
|
146
|
+
expect(result.content).toContain('NODE endpoint');
|
|
147
|
+
expect(result.content).toContain('DESCRIPTION >');
|
|
148
|
+
expect(result.content).toContain('This is a test node');
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe('Endpoint configuration', () => {
|
|
152
|
+
it('includes cache when enabled', () => {
|
|
153
|
+
const pipe = definePipe('test_pipe', {
|
|
154
|
+
nodes: [node({ name: 'endpoint', sql: 'SELECT 1' })],
|
|
155
|
+
output: simpleOutput,
|
|
156
|
+
endpoint: {
|
|
157
|
+
enabled: true,
|
|
158
|
+
cache: { enabled: true, ttl: 300 },
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
const result = generatePipe(pipe);
|
|
162
|
+
expect(result.content).toContain('TYPE endpoint');
|
|
163
|
+
expect(result.content).toContain('CACHE 300');
|
|
164
|
+
});
|
|
165
|
+
it('uses default cache TTL when not specified', () => {
|
|
166
|
+
const pipe = definePipe('test_pipe', {
|
|
167
|
+
nodes: [node({ name: 'endpoint', sql: 'SELECT 1' })],
|
|
168
|
+
output: simpleOutput,
|
|
169
|
+
endpoint: {
|
|
170
|
+
enabled: true,
|
|
171
|
+
cache: { enabled: true },
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
const result = generatePipe(pipe);
|
|
175
|
+
expect(result.content).toContain('CACHE 60');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
describe('generateAllPipes', () => {
|
|
179
|
+
it('generates all pipes', () => {
|
|
180
|
+
const pipe1 = definePipe('pipe1', {
|
|
181
|
+
nodes: [node({ name: 'endpoint', sql: 'SELECT 1' })],
|
|
182
|
+
output: simpleOutput,
|
|
183
|
+
endpoint: true,
|
|
184
|
+
});
|
|
185
|
+
const pipe2 = definePipe('pipe2', {
|
|
186
|
+
nodes: [node({ name: 'endpoint', sql: 'SELECT 2' })],
|
|
187
|
+
output: simpleOutput,
|
|
188
|
+
endpoint: true,
|
|
189
|
+
});
|
|
190
|
+
const results = generateAllPipes({ pipe1, pipe2 });
|
|
191
|
+
expect(results).toHaveLength(2);
|
|
192
|
+
expect(results.map(r => r.name).sort()).toEqual(['pipe1', 'pipe2']);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
describe('Full integration', () => {
|
|
196
|
+
it('generates complete pipe file', () => {
|
|
197
|
+
const pipe = definePipe('top_pages', {
|
|
198
|
+
description: 'Get the most visited pages',
|
|
199
|
+
params: {
|
|
200
|
+
start_date: p.dateTime(),
|
|
201
|
+
end_date: p.dateTime(),
|
|
202
|
+
limit: p.int32().optional(10),
|
|
203
|
+
},
|
|
204
|
+
nodes: [
|
|
205
|
+
node({
|
|
206
|
+
name: 'aggregated',
|
|
207
|
+
sql: `
|
|
208
|
+
SELECT
|
|
209
|
+
pathname,
|
|
210
|
+
count() AS views
|
|
211
|
+
FROM page_views
|
|
212
|
+
WHERE timestamp >= {{DateTime(start_date)}}
|
|
213
|
+
AND timestamp <= {{DateTime(end_date)}}
|
|
214
|
+
GROUP BY pathname
|
|
215
|
+
ORDER BY views DESC
|
|
216
|
+
LIMIT {{Int32(limit, 10)}}
|
|
217
|
+
`.trim(),
|
|
218
|
+
}),
|
|
219
|
+
],
|
|
220
|
+
output: {
|
|
221
|
+
pathname: t.string(),
|
|
222
|
+
views: t.uint64(),
|
|
223
|
+
},
|
|
224
|
+
endpoint: true,
|
|
225
|
+
});
|
|
226
|
+
const result = generatePipe(pipe);
|
|
227
|
+
expect(result.name).toBe('top_pages');
|
|
228
|
+
expect(result.content).toContain('DESCRIPTION >');
|
|
229
|
+
expect(result.content).toContain('Get the most visited pages');
|
|
230
|
+
expect(result.content).toContain('NODE aggregated');
|
|
231
|
+
expect(result.content).toContain('SQL >');
|
|
232
|
+
expect(result.content).toContain(' %\n');
|
|
233
|
+
expect(result.content).toContain('pathname');
|
|
234
|
+
expect(result.content).toContain('{{DateTime(start_date)}}');
|
|
235
|
+
expect(result.content).toContain('{{Int32(limit, 10)}}');
|
|
236
|
+
expect(result.content).toContain('TYPE endpoint');
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
describe('Materialized Views', () => {
|
|
240
|
+
const salesByHour = defineDatasource('sales_by_hour', {
|
|
241
|
+
schema: {
|
|
242
|
+
day: t.date(),
|
|
243
|
+
country: t.string().lowCardinality(),
|
|
244
|
+
total_sales: t.simpleAggregateFunction('sum', t.uint64()),
|
|
245
|
+
},
|
|
246
|
+
engine: engine.aggregatingMergeTree({
|
|
247
|
+
sortingKey: ['day', 'country'],
|
|
248
|
+
}),
|
|
249
|
+
});
|
|
250
|
+
it('generates TYPE MATERIALIZED and DATASOURCE with datasource', () => {
|
|
251
|
+
const pipe = definePipe('sales_by_hour_mv', {
|
|
252
|
+
nodes: [
|
|
253
|
+
node({
|
|
254
|
+
name: 'daily_sales',
|
|
255
|
+
sql: 'SELECT toStartOfDay(date) as day, country, sum(sales) as total_sales FROM teams GROUP BY day, country',
|
|
256
|
+
}),
|
|
257
|
+
],
|
|
258
|
+
output: {
|
|
259
|
+
day: t.date(),
|
|
260
|
+
country: t.string().lowCardinality(),
|
|
261
|
+
total_sales: t.simpleAggregateFunction('sum', t.uint64()),
|
|
262
|
+
},
|
|
263
|
+
materialized: {
|
|
264
|
+
datasource: salesByHour,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
const result = generatePipe(pipe);
|
|
268
|
+
expect(result.content).toContain('TYPE MATERIALIZED');
|
|
269
|
+
expect(result.content).toContain('DATASOURCE sales_by_hour');
|
|
270
|
+
expect(result.content).not.toContain('TYPE endpoint');
|
|
271
|
+
});
|
|
272
|
+
it('generates DEPLOYMENT_METHOD alter when specified', () => {
|
|
273
|
+
const pipe = definePipe('sales_by_hour_mv', {
|
|
274
|
+
nodes: [
|
|
275
|
+
node({
|
|
276
|
+
name: 'daily_sales',
|
|
277
|
+
sql: 'SELECT toStartOfDay(date) as day, country, sum(sales) as total_sales FROM teams GROUP BY day, country',
|
|
278
|
+
}),
|
|
279
|
+
],
|
|
280
|
+
output: {
|
|
281
|
+
day: t.date(),
|
|
282
|
+
country: t.string().lowCardinality(),
|
|
283
|
+
total_sales: t.simpleAggregateFunction('sum', t.uint64()),
|
|
284
|
+
},
|
|
285
|
+
materialized: {
|
|
286
|
+
datasource: salesByHour,
|
|
287
|
+
deploymentMethod: 'alter',
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
const result = generatePipe(pipe);
|
|
291
|
+
expect(result.content).toContain('TYPE MATERIALIZED');
|
|
292
|
+
expect(result.content).toContain('DATASOURCE sales_by_hour');
|
|
293
|
+
expect(result.content).toContain('DEPLOYMENT_METHOD alter');
|
|
294
|
+
});
|
|
295
|
+
it('does not include DEPLOYMENT_METHOD when not specified', () => {
|
|
296
|
+
const pipe = definePipe('sales_by_hour_mv', {
|
|
297
|
+
nodes: [
|
|
298
|
+
node({
|
|
299
|
+
name: 'daily_sales',
|
|
300
|
+
sql: 'SELECT toStartOfDay(date) as day, country, sum(sales) as total_sales FROM teams GROUP BY day, country',
|
|
301
|
+
}),
|
|
302
|
+
],
|
|
303
|
+
output: {
|
|
304
|
+
day: t.date(),
|
|
305
|
+
country: t.string().lowCardinality(),
|
|
306
|
+
total_sales: t.simpleAggregateFunction('sum', t.uint64()),
|
|
307
|
+
},
|
|
308
|
+
materialized: {
|
|
309
|
+
datasource: salesByHour,
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
const result = generatePipe(pipe);
|
|
313
|
+
expect(result.content).not.toContain('DEPLOYMENT_METHOD');
|
|
314
|
+
});
|
|
315
|
+
it('generates complete materialized view pipe file', () => {
|
|
316
|
+
const pipe = definePipe('sales_by_hour_mv', {
|
|
317
|
+
description: 'Aggregate sales per hour',
|
|
318
|
+
nodes: [
|
|
319
|
+
node({
|
|
320
|
+
name: 'daily_sales',
|
|
321
|
+
sql: `
|
|
322
|
+
SELECT
|
|
323
|
+
toStartOfDay(starting_date) as day,
|
|
324
|
+
country,
|
|
325
|
+
sum(sales) as total_sales
|
|
326
|
+
FROM teams
|
|
327
|
+
GROUP BY day, country
|
|
328
|
+
`.trim(),
|
|
329
|
+
}),
|
|
330
|
+
],
|
|
331
|
+
output: {
|
|
332
|
+
day: t.date(),
|
|
333
|
+
country: t.string().lowCardinality(),
|
|
334
|
+
total_sales: t.simpleAggregateFunction('sum', t.uint64()),
|
|
335
|
+
},
|
|
336
|
+
materialized: {
|
|
337
|
+
datasource: salesByHour,
|
|
338
|
+
deploymentMethod: 'alter',
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
const result = generatePipe(pipe);
|
|
342
|
+
expect(result.name).toBe('sales_by_hour_mv');
|
|
343
|
+
expect(result.content).toContain('DESCRIPTION >');
|
|
344
|
+
expect(result.content).toContain('Aggregate sales per hour');
|
|
345
|
+
expect(result.content).toContain('NODE daily_sales');
|
|
346
|
+
expect(result.content).toContain('SQL >');
|
|
347
|
+
expect(result.content).toContain('toStartOfDay(starting_date) as day');
|
|
348
|
+
expect(result.content).toContain('TYPE MATERIALIZED');
|
|
349
|
+
expect(result.content).toContain('DATASOURCE sales_by_hour');
|
|
350
|
+
expect(result.content).toContain('DEPLOYMENT_METHOD alter');
|
|
351
|
+
});
|
|
352
|
+
it('works with defineMaterializedView helper using datasource', () => {
|
|
353
|
+
const pipe = defineMaterializedView('sales_mv', {
|
|
354
|
+
description: 'Sales materialized view',
|
|
355
|
+
datasource: salesByHour,
|
|
356
|
+
nodes: [
|
|
357
|
+
node({
|
|
358
|
+
name: 'daily_sales',
|
|
359
|
+
sql: 'SELECT toStartOfDay(date) as day, country, sum(sales) as total_sales FROM events GROUP BY day, country',
|
|
360
|
+
}),
|
|
361
|
+
],
|
|
362
|
+
deploymentMethod: 'alter',
|
|
363
|
+
});
|
|
364
|
+
const result = generatePipe(pipe);
|
|
365
|
+
expect(result.name).toBe('sales_mv');
|
|
366
|
+
expect(result.content).toContain('TYPE MATERIALIZED');
|
|
367
|
+
expect(result.content).toContain('DATASOURCE sales_by_hour');
|
|
368
|
+
expect(result.content).toContain('DEPLOYMENT_METHOD alter');
|
|
369
|
+
});
|
|
370
|
+
it('generates DATASOURCE correctly with datasource field', () => {
|
|
371
|
+
const pipe = definePipe('sales_by_hour_mv_2', {
|
|
372
|
+
nodes: [
|
|
373
|
+
node({
|
|
374
|
+
name: 'daily_sales',
|
|
375
|
+
sql: 'SELECT toStartOfDay(date) as day, country, sum(sales) as total_sales FROM teams GROUP BY day, country',
|
|
376
|
+
}),
|
|
377
|
+
],
|
|
378
|
+
output: {
|
|
379
|
+
day: t.date(),
|
|
380
|
+
country: t.string().lowCardinality(),
|
|
381
|
+
total_sales: t.simpleAggregateFunction('sum', t.uint64()),
|
|
382
|
+
},
|
|
383
|
+
materialized: {
|
|
384
|
+
datasource: salesByHour,
|
|
385
|
+
},
|
|
386
|
+
});
|
|
387
|
+
const result = generatePipe(pipe);
|
|
388
|
+
expect(result.content).toContain('TYPE MATERIALIZED');
|
|
389
|
+
expect(result.content).toContain('DATASOURCE sales_by_hour');
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
//# sourceMappingURL=pipe.test.js.map
|