@hazeljs/data 0.2.0-alpha.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.
Files changed (171) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +308 -0
  3. package/dist/connectors/connector.interface.d.ts +29 -0
  4. package/dist/connectors/connector.interface.d.ts.map +1 -0
  5. package/dist/connectors/connector.interface.js +6 -0
  6. package/dist/connectors/csv.connector.d.ts +63 -0
  7. package/dist/connectors/csv.connector.d.ts.map +1 -0
  8. package/dist/connectors/csv.connector.js +147 -0
  9. package/dist/connectors/http.connector.d.ts +68 -0
  10. package/dist/connectors/http.connector.d.ts.map +1 -0
  11. package/dist/connectors/http.connector.js +131 -0
  12. package/dist/connectors/index.d.ts +7 -0
  13. package/dist/connectors/index.d.ts.map +1 -0
  14. package/dist/connectors/index.js +12 -0
  15. package/dist/connectors/memory.connector.d.ts +38 -0
  16. package/dist/connectors/memory.connector.d.ts.map +1 -0
  17. package/dist/connectors/memory.connector.js +56 -0
  18. package/dist/connectors/memory.connector.test.d.ts +2 -0
  19. package/dist/connectors/memory.connector.test.d.ts.map +1 -0
  20. package/dist/connectors/memory.connector.test.js +43 -0
  21. package/dist/data.module.d.ts +30 -0
  22. package/dist/data.module.d.ts.map +1 -0
  23. package/dist/data.module.js +120 -0
  24. package/dist/data.module.test.d.ts +2 -0
  25. package/dist/data.module.test.d.ts.map +1 -0
  26. package/dist/data.module.test.js +28 -0
  27. package/dist/data.types.d.ts +67 -0
  28. package/dist/data.types.d.ts.map +1 -0
  29. package/dist/data.types.js +5 -0
  30. package/dist/decorators/index.d.ts +6 -0
  31. package/dist/decorators/index.d.ts.map +1 -0
  32. package/dist/decorators/index.js +24 -0
  33. package/dist/decorators/pii.decorator.d.ts +59 -0
  34. package/dist/decorators/pii.decorator.d.ts.map +1 -0
  35. package/dist/decorators/pii.decorator.js +197 -0
  36. package/dist/decorators/pii.decorator.test.d.ts +2 -0
  37. package/dist/decorators/pii.decorator.test.d.ts.map +1 -0
  38. package/dist/decorators/pii.decorator.test.js +150 -0
  39. package/dist/decorators/pipeline.decorator.d.ts +22 -0
  40. package/dist/decorators/pipeline.decorator.d.ts.map +1 -0
  41. package/dist/decorators/pipeline.decorator.js +42 -0
  42. package/dist/decorators/pipeline.decorator.test.d.ts +2 -0
  43. package/dist/decorators/pipeline.decorator.test.d.ts.map +1 -0
  44. package/dist/decorators/pipeline.decorator.test.js +104 -0
  45. package/dist/decorators/stream.decorator.d.ts +31 -0
  46. package/dist/decorators/stream.decorator.d.ts.map +1 -0
  47. package/dist/decorators/stream.decorator.js +48 -0
  48. package/dist/decorators/transform.decorator.d.ts +29 -0
  49. package/dist/decorators/transform.decorator.d.ts.map +1 -0
  50. package/dist/decorators/transform.decorator.js +41 -0
  51. package/dist/decorators/validate.decorator.d.ts +34 -0
  52. package/dist/decorators/validate.decorator.d.ts.map +1 -0
  53. package/dist/decorators/validate.decorator.js +49 -0
  54. package/dist/flink.service.d.ts +80 -0
  55. package/dist/flink.service.d.ts.map +1 -0
  56. package/dist/flink.service.js +134 -0
  57. package/dist/flink.service.test.d.ts +2 -0
  58. package/dist/flink.service.test.d.ts.map +1 -0
  59. package/dist/flink.service.test.js +60 -0
  60. package/dist/index.d.ts +32 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +96 -0
  63. package/dist/pipelines/etl.service.d.ts +59 -0
  64. package/dist/pipelines/etl.service.d.ts.map +1 -0
  65. package/dist/pipelines/etl.service.js +223 -0
  66. package/dist/pipelines/etl.service.test.d.ts +2 -0
  67. package/dist/pipelines/etl.service.test.d.ts.map +1 -0
  68. package/dist/pipelines/etl.service.test.js +319 -0
  69. package/dist/pipelines/pipeline.base.d.ts +24 -0
  70. package/dist/pipelines/pipeline.base.d.ts.map +1 -0
  71. package/dist/pipelines/pipeline.base.js +29 -0
  72. package/dist/pipelines/pipeline.base.test.d.ts +2 -0
  73. package/dist/pipelines/pipeline.base.test.d.ts.map +1 -0
  74. package/dist/pipelines/pipeline.base.test.js +38 -0
  75. package/dist/pipelines/pipeline.builder.d.ts +95 -0
  76. package/dist/pipelines/pipeline.builder.d.ts.map +1 -0
  77. package/dist/pipelines/pipeline.builder.js +212 -0
  78. package/dist/pipelines/pipeline.builder.test.d.ts +2 -0
  79. package/dist/pipelines/pipeline.builder.test.d.ts.map +1 -0
  80. package/dist/pipelines/pipeline.builder.test.js +185 -0
  81. package/dist/pipelines/stream.service.d.ts +12 -0
  82. package/dist/pipelines/stream.service.d.ts.map +1 -0
  83. package/dist/pipelines/stream.service.js +58 -0
  84. package/dist/pipelines/stream.service.test.d.ts +2 -0
  85. package/dist/pipelines/stream.service.test.d.ts.map +1 -0
  86. package/dist/pipelines/stream.service.test.js +103 -0
  87. package/dist/quality/quality.service.d.ts +87 -0
  88. package/dist/quality/quality.service.d.ts.map +1 -0
  89. package/dist/quality/quality.service.js +326 -0
  90. package/dist/quality/quality.service.test.d.ts +2 -0
  91. package/dist/quality/quality.service.test.d.ts.map +1 -0
  92. package/dist/quality/quality.service.test.js +128 -0
  93. package/dist/schema/schema.d.ts +127 -0
  94. package/dist/schema/schema.d.ts.map +1 -0
  95. package/dist/schema/schema.js +487 -0
  96. package/dist/schema/schema.test.d.ts +2 -0
  97. package/dist/schema/schema.test.d.ts.map +1 -0
  98. package/dist/schema/schema.test.js +411 -0
  99. package/dist/streaming/flink/flink.client.d.ts +96 -0
  100. package/dist/streaming/flink/flink.client.d.ts.map +1 -0
  101. package/dist/streaming/flink/flink.client.js +267 -0
  102. package/dist/streaming/flink/flink.client.test.d.ts +2 -0
  103. package/dist/streaming/flink/flink.client.test.d.ts.map +1 -0
  104. package/dist/streaming/flink/flink.client.test.js +59 -0
  105. package/dist/streaming/flink/flink.job.d.ts +29 -0
  106. package/dist/streaming/flink/flink.job.d.ts.map +1 -0
  107. package/dist/streaming/flink/flink.job.js +27 -0
  108. package/dist/streaming/flink/flink.job.test.d.ts +2 -0
  109. package/dist/streaming/flink/flink.job.test.d.ts.map +1 -0
  110. package/dist/streaming/flink/flink.job.test.js +37 -0
  111. package/dist/streaming/flink/flink.operators.d.ts +35 -0
  112. package/dist/streaming/flink/flink.operators.d.ts.map +1 -0
  113. package/dist/streaming/flink/flink.operators.js +43 -0
  114. package/dist/streaming/flink/flink.operators.test.d.ts +2 -0
  115. package/dist/streaming/flink/flink.operators.test.d.ts.map +1 -0
  116. package/dist/streaming/flink/flink.operators.test.js +38 -0
  117. package/dist/streaming/stream.builder.d.ts +22 -0
  118. package/dist/streaming/stream.builder.d.ts.map +1 -0
  119. package/dist/streaming/stream.builder.js +50 -0
  120. package/dist/streaming/stream.builder.test.d.ts +2 -0
  121. package/dist/streaming/stream.builder.test.d.ts.map +1 -0
  122. package/dist/streaming/stream.builder.test.js +59 -0
  123. package/dist/streaming/stream.processor.d.ts +66 -0
  124. package/dist/streaming/stream.processor.d.ts.map +1 -0
  125. package/dist/streaming/stream.processor.js +178 -0
  126. package/dist/streaming/stream.processor.test.d.ts +2 -0
  127. package/dist/streaming/stream.processor.test.d.ts.map +1 -0
  128. package/dist/streaming/stream.processor.test.js +151 -0
  129. package/dist/streaming/stream.processor.windowing.test.d.ts +2 -0
  130. package/dist/streaming/stream.processor.windowing.test.d.ts.map +1 -0
  131. package/dist/streaming/stream.processor.windowing.test.js +69 -0
  132. package/dist/telemetry/telemetry.d.ts +124 -0
  133. package/dist/telemetry/telemetry.d.ts.map +1 -0
  134. package/dist/telemetry/telemetry.js +259 -0
  135. package/dist/telemetry/telemetry.test.d.ts +2 -0
  136. package/dist/telemetry/telemetry.test.d.ts.map +1 -0
  137. package/dist/telemetry/telemetry.test.js +51 -0
  138. package/dist/testing/index.d.ts +12 -0
  139. package/dist/testing/index.d.ts.map +1 -0
  140. package/dist/testing/index.js +18 -0
  141. package/dist/testing/pipeline-test-harness.d.ts +40 -0
  142. package/dist/testing/pipeline-test-harness.d.ts.map +1 -0
  143. package/dist/testing/pipeline-test-harness.js +55 -0
  144. package/dist/testing/pipeline-test-harness.test.d.ts +2 -0
  145. package/dist/testing/pipeline-test-harness.test.d.ts.map +1 -0
  146. package/dist/testing/pipeline-test-harness.test.js +102 -0
  147. package/dist/testing/schema-faker.d.ts +32 -0
  148. package/dist/testing/schema-faker.d.ts.map +1 -0
  149. package/dist/testing/schema-faker.js +91 -0
  150. package/dist/testing/schema-faker.test.d.ts +2 -0
  151. package/dist/testing/schema-faker.test.d.ts.map +1 -0
  152. package/dist/testing/schema-faker.test.js +66 -0
  153. package/dist/transformers/built-in.transformers.d.ts +12 -0
  154. package/dist/transformers/built-in.transformers.d.ts.map +1 -0
  155. package/dist/transformers/built-in.transformers.js +75 -0
  156. package/dist/transformers/built-in.transformers.test.d.ts +2 -0
  157. package/dist/transformers/built-in.transformers.test.d.ts.map +1 -0
  158. package/dist/transformers/built-in.transformers.test.js +85 -0
  159. package/dist/transformers/transformer.service.d.ts +14 -0
  160. package/dist/transformers/transformer.service.d.ts.map +1 -0
  161. package/dist/transformers/transformer.service.js +65 -0
  162. package/dist/transformers/transformer.service.test.d.ts +2 -0
  163. package/dist/transformers/transformer.service.test.d.ts.map +1 -0
  164. package/dist/transformers/transformer.service.test.js +42 -0
  165. package/dist/validators/schema.validator.d.ts +21 -0
  166. package/dist/validators/schema.validator.d.ts.map +1 -0
  167. package/dist/validators/schema.validator.js +40 -0
  168. package/dist/validators/schema.validator.test.d.ts +2 -0
  169. package/dist/validators/schema.validator.test.d.ts.map +1 -0
  170. package/dist/validators/schema.validator.test.js +42 -0
  171. package/package.json +53 -0
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FlinkClient = void 0;
37
+ /**
38
+ * Flink Client - REST API client for Apache Flink clusters
39
+ * Interacts with Flink JobManager REST API
40
+ */
41
+ class FlinkClient {
42
+ constructor(config) {
43
+ this.config = {
44
+ timeout: 30000,
45
+ retries: 3,
46
+ ...config,
47
+ };
48
+ this.url = this.config.url.replace(/\/$/, '');
49
+ }
50
+ async request(method, path, body) {
51
+ const url = `${this.url}${path}`;
52
+ const headers = {
53
+ 'Content-Type': 'application/json',
54
+ };
55
+ if (this.config.auth?.type === 'basic' && this.config.auth.username) {
56
+ const credentials = Buffer.from(`${this.config.auth.username}:${this.config.auth.password || ''}`).toString('base64');
57
+ headers['Authorization'] = `Basic ${credentials}`;
58
+ }
59
+ else if (this.config.auth?.type === 'token' && this.config.auth.token) {
60
+ headers['Authorization'] = `Bearer ${this.config.auth.token}`;
61
+ }
62
+ const controller = new AbortController();
63
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
64
+ try {
65
+ const response = await fetch(url, {
66
+ method,
67
+ headers,
68
+ body: body ? JSON.stringify(body) : undefined,
69
+ signal: controller.signal,
70
+ });
71
+ clearTimeout(timeoutId);
72
+ if (!response.ok) {
73
+ const text = await response.text();
74
+ throw new Error(`Flink API error ${response.status}: ${text}`);
75
+ }
76
+ const text = await response.text();
77
+ return text ? JSON.parse(text) : {};
78
+ }
79
+ catch (error) {
80
+ clearTimeout(timeoutId);
81
+ if (error instanceof Error) {
82
+ throw new Error(`Flink request failed: ${error.message}`);
83
+ }
84
+ throw error;
85
+ }
86
+ }
87
+ async listJobs() {
88
+ const result = await this.request('GET', '/jobs/overview');
89
+ const jobs = result.jobs ?? [];
90
+ return jobs.map((j) => ({
91
+ id: j.id,
92
+ status: j.status,
93
+ startTime: j['start-time'],
94
+ endTime: j['end-time'],
95
+ duration: j.duration,
96
+ }));
97
+ }
98
+ async getJobStatus(jobId) {
99
+ const result = await this.request('GET', `/jobs/${jobId}`);
100
+ return {
101
+ state: result.state,
102
+ startTime: result['start-time'],
103
+ duration: result.duration,
104
+ };
105
+ }
106
+ async cancelJob(jobId) {
107
+ await this.request('PATCH', `/jobs/${jobId}?mode=cancel`);
108
+ }
109
+ async stopJob(jobId, savepointPath) {
110
+ const body = savepointPath ? { targetDirectory: savepointPath } : {};
111
+ return this.request('PATCH', `/jobs/${jobId}?mode=stop`, body);
112
+ }
113
+ async createSavepoint(jobId, savepointPath) {
114
+ const body = savepointPath ? { targetDirectory: savepointPath } : {};
115
+ return this.request('POST', `/jobs/${jobId}/savepoints`, body);
116
+ }
117
+ async getClusterInfo() {
118
+ return this.request('GET', '/overview');
119
+ }
120
+ async getTaskManagers() {
121
+ const result = await this.request('GET', '/taskmanagers');
122
+ return result.taskmanagers ?? [];
123
+ }
124
+ /**
125
+ * Upload a JAR file to the Flink cluster.
126
+ * @param jarPath Local filesystem path to the JAR file
127
+ * @returns The Flink JAR ID for subsequent run calls
128
+ */
129
+ async uploadJar(jarPath) {
130
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
131
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
132
+ if (!fs.existsSync(jarPath)) {
133
+ throw new Error(`JAR file not found: ${jarPath}`);
134
+ }
135
+ const url = `${this.url}/jars/upload`;
136
+ const fileName = path.basename(jarPath);
137
+ const fileBuffer = fs.readFileSync(jarPath);
138
+ const headers = {};
139
+ if (this.config.auth?.type === 'basic' && this.config.auth.username) {
140
+ headers['Authorization'] =
141
+ `Basic ${Buffer.from(`${this.config.auth.username}:${this.config.auth.password ?? ''}`).toString('base64')}`;
142
+ }
143
+ else if (this.config.auth?.type === 'token' && this.config.auth.token) {
144
+ headers['Authorization'] = `Bearer ${this.config.auth.token}`;
145
+ }
146
+ const boundary = `----HazelJSFormBoundary${Date.now()}`;
147
+ const bodyParts = [
148
+ Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="jarfile"; filename="${fileName}"\r\nContent-Type: application/java-archive\r\n\r\n`),
149
+ fileBuffer,
150
+ Buffer.from(`\r\n--${boundary}--\r\n`),
151
+ ];
152
+ const body = Buffer.concat(bodyParts);
153
+ const controller = new AbortController();
154
+ const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
155
+ try {
156
+ const response = await fetch(url, {
157
+ method: 'POST',
158
+ headers: {
159
+ ...headers,
160
+ 'Content-Type': `multipart/form-data; boundary=${boundary}`,
161
+ 'Content-Length': String(body.length),
162
+ },
163
+ body,
164
+ signal: controller.signal,
165
+ });
166
+ clearTimeout(timeoutId);
167
+ if (!response.ok) {
168
+ throw new Error(`Flink JAR upload failed ${response.status}: ${await response.text()}`);
169
+ }
170
+ const result = (await response.json());
171
+ const filename = result.filename ?? '';
172
+ const match = /\/jars\/([^/]+)$/.exec(filename);
173
+ if (!match)
174
+ throw new Error(`Could not extract JAR ID from Flink response: ${filename}`);
175
+ return match[1];
176
+ }
177
+ catch (error) {
178
+ clearTimeout(timeoutId);
179
+ throw error instanceof Error ? error : new Error(String(error));
180
+ }
181
+ }
182
+ /**
183
+ * Run a previously uploaded JAR as a Flink job.
184
+ * @param jarId The JAR ID returned by uploadJar()
185
+ * @param request Optional run parameters (entry class, parallelism, args, etc.)
186
+ * @returns The Flink job ID
187
+ */
188
+ async runJar(jarId, request = {}) {
189
+ const body = {};
190
+ if (request.jobName)
191
+ body['job-name'] = request.jobName;
192
+ if (request.parallelism)
193
+ body['parallelism'] = request.parallelism;
194
+ if (request.entryClass)
195
+ body['entry-class'] = request.entryClass;
196
+ if (request.programArgs)
197
+ body['program-args'] = request.programArgs;
198
+ if (request.savepointPath)
199
+ body['savepointPath'] = request.savepointPath;
200
+ if (request.allowNonRestoredState)
201
+ body['allowNonRestoredState'] = request.allowNonRestoredState;
202
+ const result = await this.request('POST', `/jars/${jarId}/run`, body);
203
+ if (!result.jobid)
204
+ throw new Error('Flink did not return a job ID');
205
+ return result.jobid;
206
+ }
207
+ /**
208
+ * Submit a SQL statement to the Flink SQL Gateway.
209
+ * Requires Flink SQL Gateway to be running.
210
+ * @param sql The SQL statement to execute (CREATE TABLE, INSERT INTO, etc.)
211
+ * @param sessionId An existing Flink SQL Gateway session ID
212
+ */
213
+ async submitSql(sql, sessionId) {
214
+ const result = await this.request('POST', `/sessions/${sessionId}/statements`, { statement: sql });
215
+ const id = result.operationHandle?.identifier;
216
+ if (!id)
217
+ throw new Error('Flink SQL Gateway did not return an operation handle');
218
+ return id;
219
+ }
220
+ /**
221
+ * Create a new SQL Gateway session.
222
+ * @returns The session ID
223
+ */
224
+ async createSqlSession(properties) {
225
+ const result = await this.request('POST', '/sessions', { properties: properties ?? {} });
226
+ const id = result.sessionHandle?.identifier;
227
+ if (!id)
228
+ throw new Error('Flink SQL Gateway did not return a session handle');
229
+ return id;
230
+ }
231
+ /**
232
+ * Submit a job to Flink cluster.
233
+ * Requires `jarFile` path in the jobConfig or uses FlinkJobSubmitRequest.jarFile.
234
+ *
235
+ * Workflow: upload JAR → run JAR → return job ID.
236
+ */
237
+ async submitJob(jobConfig, request = {}) {
238
+ const jarFile = request.jarFile;
239
+ if (!jarFile) {
240
+ throw new Error('submitJob requires a JAR file path. Set request.jarFile to the path of your pipeline JAR, ' +
241
+ 'or use submitSql() for Flink SQL-based pipelines.');
242
+ }
243
+ const jarId = await this.uploadJar(jarFile);
244
+ return this.runJar(jarId, {
245
+ jobName: request.jobName ?? jobConfig.jobName,
246
+ parallelism: request.parallelism ?? jobConfig.parallelism,
247
+ entryClass: request.entryClass,
248
+ programArgs: request.programArgs,
249
+ savepointPath: request.savepointPath,
250
+ allowNonRestoredState: request.allowNonRestoredState,
251
+ });
252
+ }
253
+ /**
254
+ * List uploaded JARs on the cluster.
255
+ */
256
+ async listJars() {
257
+ const result = await this.request('GET', '/jars');
258
+ return result.files ?? [];
259
+ }
260
+ /**
261
+ * Delete an uploaded JAR from the cluster.
262
+ */
263
+ async deleteJar(jarId) {
264
+ await this.request('DELETE', `/jars/${jarId}`);
265
+ }
266
+ }
267
+ exports.FlinkClient = FlinkClient;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flink.client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flink.client.test.d.ts","sourceRoot":"","sources":["../../../src/streaming/flink/flink.client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const flink_client_1 = require("./flink.client");
4
+ // Mock fetch for Flink REST API
5
+ const mockFetch = jest.fn();
6
+ describe('FlinkClient', () => {
7
+ let client;
8
+ beforeAll(() => {
9
+ global.fetch = mockFetch;
10
+ });
11
+ beforeEach(() => {
12
+ mockFetch.mockReset();
13
+ client = new flink_client_1.FlinkClient({ url: 'http://localhost:8081' });
14
+ });
15
+ it('listJobs fetches job overview', async () => {
16
+ mockFetch.mockResolvedValue({
17
+ ok: true,
18
+ text: () => Promise.resolve(JSON.stringify({
19
+ jobs: [
20
+ { id: 'j1', status: 'RUNNING', 'start-time': 123, 'end-time': 456, duration: 100 },
21
+ ],
22
+ })),
23
+ });
24
+ const jobs = await client.listJobs();
25
+ expect(jobs).toHaveLength(1);
26
+ expect(jobs[0].id).toBe('j1');
27
+ expect(jobs[0].status).toBe('RUNNING');
28
+ expect(mockFetch).toHaveBeenCalledWith('http://localhost:8081/jobs/overview', expect.any(Object));
29
+ });
30
+ it('getJobStatus fetches job details', async () => {
31
+ mockFetch.mockResolvedValue({
32
+ ok: true,
33
+ text: () => Promise.resolve(JSON.stringify({ state: 'RUNNING', 'start-time': 123, duration: 500 })),
34
+ });
35
+ const status = await client.getJobStatus('j1');
36
+ expect(status.state).toBe('RUNNING');
37
+ expect(status.duration).toBe(500);
38
+ });
39
+ it('throws on API error', async () => {
40
+ mockFetch.mockResolvedValue({ ok: false, text: () => Promise.resolve('Not found') });
41
+ await expect(client.getJobStatus('bad')).rejects.toThrow('Flink API error');
42
+ });
43
+ it('submitJob requires a jarFile path', async () => {
44
+ await expect(client.submitJob({})).rejects.toThrow('submitJob requires a JAR file path');
45
+ });
46
+ it('getClusterInfo', async () => {
47
+ mockFetch.mockResolvedValue({
48
+ ok: true,
49
+ text: () => Promise.resolve(JSON.stringify({ taskmanagers: 2, 'slots-total': 8 })),
50
+ });
51
+ const info = await client.getClusterInfo();
52
+ expect(info.taskmanagers).toBe(2);
53
+ });
54
+ it('cancelJob', async () => {
55
+ mockFetch.mockResolvedValue({ ok: true, text: () => Promise.resolve('') });
56
+ await client.cancelJob('j1');
57
+ expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('mode=cancel'), expect.any(Object));
58
+ });
59
+ });
@@ -0,0 +1,29 @@
1
+ import { FlinkClient } from './flink.client';
2
+ import type { FlinkJobConfig } from '../../data.types';
3
+ import type { FlinkJobSubmitRequest } from './flink.client';
4
+ export interface FlinkJobResult {
5
+ jobId: string;
6
+ status: string;
7
+ webUI?: string;
8
+ }
9
+ /**
10
+ * Flink Job - Job management operations
11
+ */
12
+ export declare class FlinkJob {
13
+ private readonly client;
14
+ constructor(client: FlinkClient);
15
+ getStatus(jobId: string): Promise<{
16
+ state: string;
17
+ startTime?: number;
18
+ duration?: number;
19
+ }>;
20
+ cancel(jobId: string): Promise<void>;
21
+ createSavepoint(jobId: string, savepointPath?: string): Promise<{
22
+ 'request-id': string;
23
+ }>;
24
+ stop(jobId: string, savepointPath?: string): Promise<{
25
+ 'request-id': string;
26
+ }>;
27
+ submit(config: FlinkJobConfig, request?: FlinkJobSubmitRequest): Promise<string>;
28
+ }
29
+ //# sourceMappingURL=flink.job.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flink.job.d.ts","sourceRoot":"","sources":["../../../src/streaming/flink/flink.job.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,QAAQ;IACP,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,WAAW;IAE1C,SAAS,CACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAI9D,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpC,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAIzF,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAI9E,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC;CAGvF"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlinkJob = void 0;
4
+ /**
5
+ * Flink Job - Job management operations
6
+ */
7
+ class FlinkJob {
8
+ constructor(client) {
9
+ this.client = client;
10
+ }
11
+ async getStatus(jobId) {
12
+ return this.client.getJobStatus(jobId);
13
+ }
14
+ async cancel(jobId) {
15
+ return this.client.cancelJob(jobId);
16
+ }
17
+ async createSavepoint(jobId, savepointPath) {
18
+ return this.client.createSavepoint(jobId, savepointPath);
19
+ }
20
+ async stop(jobId, savepointPath) {
21
+ return this.client.stopJob(jobId, savepointPath);
22
+ }
23
+ async submit(config, request) {
24
+ return this.client.submitJob(config, request);
25
+ }
26
+ }
27
+ exports.FlinkJob = FlinkJob;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flink.job.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flink.job.test.d.ts","sourceRoot":"","sources":["../../../src/streaming/flink/flink.job.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const flink_client_1 = require("./flink.client");
4
+ const flink_job_1 = require("./flink.job");
5
+ const mockFetch = jest.fn();
6
+ describe('FlinkJob', () => {
7
+ let job;
8
+ beforeAll(() => {
9
+ global.fetch = mockFetch;
10
+ });
11
+ beforeEach(() => {
12
+ mockFetch.mockReset();
13
+ const client = new flink_client_1.FlinkClient({ url: 'http://localhost:8081' });
14
+ job = new flink_job_1.FlinkJob(client);
15
+ });
16
+ it('getStatus fetches job status', async () => {
17
+ mockFetch.mockResolvedValue({
18
+ ok: true,
19
+ text: () => Promise.resolve(JSON.stringify({ state: 'RUNNING', duration: 100 })),
20
+ });
21
+ const status = await job.getStatus('j1');
22
+ expect(status.state).toBe('RUNNING');
23
+ });
24
+ it('cancel calls cancel endpoint', async () => {
25
+ mockFetch.mockResolvedValue({ ok: true, text: () => Promise.resolve('') });
26
+ await job.cancel('j1');
27
+ expect(mockFetch).toHaveBeenCalledWith(expect.stringContaining('/jobs/j1'), expect.any(Object));
28
+ });
29
+ it('createSavepoint', async () => {
30
+ mockFetch.mockResolvedValue({
31
+ ok: true,
32
+ text: () => Promise.resolve(JSON.stringify({ 'request-id': 'req-1' })),
33
+ });
34
+ const result = await job.createSavepoint('j1', '/path');
35
+ expect(result['request-id']).toBe('req-1');
36
+ });
37
+ });
@@ -0,0 +1,35 @@
1
+ import type { PipelineStep } from '../../pipelines/etl.service';
2
+ export type FlinkOperatorType = 'map' | 'filter' | 'flatMap' | 'window' | 'keyBy';
3
+ export interface FlinkOperator {
4
+ type: FlinkOperatorType;
5
+ step: number;
6
+ name: string;
7
+ function?: (value: unknown) => unknown | Promise<unknown>;
8
+ predicate?: (value: unknown) => boolean;
9
+ windowType?: 'tumbling' | 'sliding' | 'session';
10
+ windowSize?: string;
11
+ aggregator?: (value: unknown) => unknown;
12
+ }
13
+ /**
14
+ * Maps HazelJS pipeline steps to Flink operators
15
+ * Note: Actual function/predicate execution happens in deployed Flink job
16
+ */
17
+ export declare function mapToFlinkOperator(step: PipelineStep): FlinkOperator;
18
+ export declare function createFlinkJobGraph(steps: PipelineStep[], sourceConfig: {
19
+ type: string;
20
+ topic?: string;
21
+ properties?: Record<string, string>;
22
+ }, sinkConfig: {
23
+ type: string;
24
+ topic?: string;
25
+ properties?: Record<string, string>;
26
+ }): {
27
+ source: typeof sourceConfig;
28
+ transformations: Array<{
29
+ step: number;
30
+ name: string;
31
+ operator: FlinkOperator;
32
+ }>;
33
+ sink: typeof sinkConfig;
34
+ };
35
+ //# sourceMappingURL=flink.operators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flink.operators.d.ts","sourceRoot":"","sources":["../../../src/streaming/flink/flink.operators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAElF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;IACxC,UAAU,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;CAC1C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,aAAa,CAuBpE;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,YAAY,EAAE,EACrB,YAAY,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACnF,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAChF;IACD,MAAM,EAAE,OAAO,YAAY,CAAC;IAC5B,eAAe,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IAChF,IAAI,EAAE,OAAO,UAAU,CAAC;CACzB,CAUA"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapToFlinkOperator = mapToFlinkOperator;
4
+ exports.createFlinkJobGraph = createFlinkJobGraph;
5
+ /**
6
+ * Maps HazelJS pipeline steps to Flink operators
7
+ * Note: Actual function/predicate execution happens in deployed Flink job
8
+ */
9
+ function mapToFlinkOperator(step) {
10
+ switch (step.type) {
11
+ case 'transform':
12
+ return {
13
+ type: 'map',
14
+ step: step.step,
15
+ name: step.name,
16
+ // Method name for job graph - actual handler in deployed job
17
+ };
18
+ case 'validate':
19
+ return {
20
+ type: 'filter',
21
+ step: step.step,
22
+ name: step.name,
23
+ predicate: () => true,
24
+ };
25
+ default:
26
+ return {
27
+ type: 'map',
28
+ step: step.step,
29
+ name: step.name,
30
+ };
31
+ }
32
+ }
33
+ function createFlinkJobGraph(steps, sourceConfig, sinkConfig) {
34
+ return {
35
+ source: sourceConfig,
36
+ transformations: steps.map((step) => ({
37
+ step: step.step,
38
+ name: step.name,
39
+ operator: mapToFlinkOperator(step),
40
+ })),
41
+ sink: sinkConfig,
42
+ };
43
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flink.operators.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flink.operators.test.d.ts","sourceRoot":"","sources":["../../../src/streaming/flink/flink.operators.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const flink_operators_1 = require("./flink.operators");
4
+ describe('flink operators', () => {
5
+ describe('mapToFlinkOperator', () => {
6
+ it('maps transform step to map operator', () => {
7
+ const step = {
8
+ step: 1,
9
+ name: 'parse',
10
+ type: 'transform',
11
+ method: 'parse',
12
+ };
13
+ const op = (0, flink_operators_1.mapToFlinkOperator)(step);
14
+ expect(op.type).toBe('map');
15
+ expect(op.step).toBe(1);
16
+ expect(op.name).toBe('parse');
17
+ });
18
+ it('maps validate step to filter operator', () => {
19
+ const step = {
20
+ step: 2,
21
+ name: 'validate',
22
+ type: 'validate',
23
+ method: 'validate',
24
+ };
25
+ const op = (0, flink_operators_1.mapToFlinkOperator)(step);
26
+ expect(op.type).toBe('filter');
27
+ });
28
+ });
29
+ describe('createFlinkJobGraph', () => {
30
+ it('creates job graph with source, sink, transformations', () => {
31
+ const steps = [{ step: 1, name: 't1', type: 'transform', method: 't1' }];
32
+ const graph = (0, flink_operators_1.createFlinkJobGraph)(steps, { type: 'kafka', topic: 'in', properties: {} }, { type: 'kafka', topic: 'out', properties: {} });
33
+ expect(graph.source.topic).toBe('in');
34
+ expect(graph.sink.topic).toBe('out');
35
+ expect(graph.transformations).toHaveLength(1);
36
+ });
37
+ });
38
+ });
@@ -0,0 +1,22 @@
1
+ import { ETLService } from '../pipelines/etl.service';
2
+ import type { FlinkJobConfig } from '../data.types';
3
+ import type { PipelineStep } from '../pipelines/etl.service';
4
+ import { createFlinkJobGraph } from './flink/flink.operators';
5
+ export interface StreamPipelineConfig {
6
+ source: string;
7
+ sink: string;
8
+ parallelism?: number;
9
+ steps: PipelineStep[];
10
+ }
11
+ /**
12
+ * Stream Builder - Builds Flink job config from @Stream decorated pipeline
13
+ */
14
+ export declare class StreamBuilder {
15
+ private readonly etlService;
16
+ constructor(etlService: ETLService);
17
+ buildConfig(pipelineInstance: object, overrideConfig?: Partial<FlinkJobConfig>): {
18
+ jobConfig: FlinkJobConfig;
19
+ jobGraph: ReturnType<typeof createFlinkJobGraph>;
20
+ };
21
+ }
22
+ //# sourceMappingURL=stream.builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.builder.d.ts","sourceRoot":"","sources":["../../src/streaming/stream.builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAED;;GAEG;AACH,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAEnD,WAAW,CACT,gBAAgB,EAAE,MAAM,EACxB,cAAc,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GACvC;QACD,SAAS,EAAE,cAAc,CAAC;QAC1B,QAAQ,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;KAClD;CA6CF"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StreamBuilder = void 0;
4
+ const decorators_1 = require("../decorators");
5
+ const flink_operators_1 = require("./flink/flink.operators");
6
+ /**
7
+ * Stream Builder - Builds Flink job config from @Stream decorated pipeline
8
+ */
9
+ class StreamBuilder {
10
+ constructor(etlService) {
11
+ this.etlService = etlService;
12
+ }
13
+ buildConfig(pipelineInstance, overrideConfig) {
14
+ const metadata = (0, decorators_1.getStreamMetadata)(pipelineInstance.constructor);
15
+ if (!metadata) {
16
+ throw new Error('Pipeline must be decorated with @Stream');
17
+ }
18
+ const steps = this.etlService.extractSteps(pipelineInstance);
19
+ const sourceTopic = metadata.source.replace('kafka://', '');
20
+ const sinkTopic = metadata.sink.replace('kafka://', '');
21
+ const jobConfig = {
22
+ jobName: pipelineInstance.constructor.name,
23
+ parallelism: metadata.parallelism ?? 4,
24
+ checkpointInterval: 60000,
25
+ restartStrategy: {
26
+ type: 'fixed-delay',
27
+ attempts: 3,
28
+ delay: 10000,
29
+ },
30
+ ...overrideConfig,
31
+ };
32
+ const jobGraph = (0, flink_operators_1.createFlinkJobGraph)(steps, {
33
+ type: 'kafka',
34
+ topic: sourceTopic,
35
+ properties: {
36
+ 'bootstrap.servers': process.env.KAFKA_BOOTSTRAP_SERVERS ?? 'localhost:9092',
37
+ 'group.id': `hazeljs-${metadata.name}`,
38
+ 'auto.offset.reset': 'latest',
39
+ },
40
+ }, {
41
+ type: 'kafka',
42
+ topic: sinkTopic,
43
+ properties: {
44
+ 'bootstrap.servers': process.env.KAFKA_BOOTSTRAP_SERVERS ?? 'localhost:9092',
45
+ },
46
+ });
47
+ return { jobConfig, jobGraph };
48
+ }
49
+ }
50
+ exports.StreamBuilder = StreamBuilder;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stream.builder.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.builder.test.d.ts","sourceRoot":"","sources":["../../src/streaming/stream.builder.test.ts"],"names":[],"mappings":""}