@async-fusion/data 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/kafka/consumer.d.ts +2 -7
- package/dist/cjs/kafka/consumer.d.ts.map +1 -1
- package/dist/cjs/kafka/consumer.js +1 -1
- package/dist/cjs/kafka/consumer.js.map +1 -1
- package/dist/cjs/kafka/producer.d.ts +4 -14
- package/dist/cjs/kafka/producer.d.ts.map +1 -1
- package/dist/cjs/kafka/producer.js +1 -1
- package/dist/cjs/kafka/producer.js.map +1 -1
- package/dist/cjs/node_modules/uuid/dist/native.js +2 -0
- package/dist/cjs/node_modules/uuid/dist/native.js.map +1 -0
- package/dist/cjs/node_modules/uuid/dist/rng.js +2 -0
- package/dist/cjs/node_modules/uuid/dist/rng.js.map +1 -0
- package/dist/cjs/node_modules/uuid/dist/stringify.js +2 -0
- package/dist/cjs/node_modules/uuid/dist/stringify.js.map +1 -0
- package/dist/cjs/node_modules/uuid/dist/v4.js +2 -0
- package/dist/cjs/node_modules/uuid/dist/v4.js.map +1 -0
- package/dist/cjs/spark/client.d.ts +62 -18
- package/dist/cjs/spark/client.d.ts.map +1 -1
- package/dist/cjs/spark/client.js +1 -1
- package/dist/cjs/spark/client.js.map +1 -1
- package/dist/cjs/types/index.d.ts +19 -8
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/esm/kafka/consumer.d.ts +2 -7
- package/dist/esm/kafka/consumer.d.ts.map +1 -1
- package/dist/esm/kafka/consumer.js +1 -1
- package/dist/esm/kafka/consumer.js.map +1 -1
- package/dist/esm/kafka/producer.d.ts +4 -14
- package/dist/esm/kafka/producer.d.ts.map +1 -1
- package/dist/esm/kafka/producer.js +1 -1
- package/dist/esm/kafka/producer.js.map +1 -1
- package/dist/esm/node_modules/uuid/dist/native.js +2 -0
- package/dist/esm/node_modules/uuid/dist/native.js.map +1 -0
- package/dist/esm/node_modules/uuid/dist/rng.js +2 -0
- package/dist/esm/node_modules/uuid/dist/rng.js.map +1 -0
- package/dist/esm/node_modules/uuid/dist/stringify.js +2 -0
- package/dist/esm/node_modules/uuid/dist/stringify.js.map +1 -0
- package/dist/esm/node_modules/uuid/dist/v4.js +2 -0
- package/dist/esm/node_modules/uuid/dist/v4.js.map +1 -0
- package/dist/esm/spark/client.d.ts +62 -18
- package/dist/esm/spark/client.d.ts.map +1 -1
- package/dist/esm/spark/client.js +1 -1
- package/dist/esm/spark/client.js.map +1 -1
- package/dist/esm/types/index.d.ts +19 -8
- package/dist/esm/types/index.d.ts.map +1 -1
- package/package.json +6 -3
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Message } from '../types/index';
|
|
2
2
|
export type MessageHandler<T> = (message: Message<T>) => Promise<void>;
|
|
3
3
|
export declare class Consumer<T = any> {
|
|
4
4
|
private consumer;
|
|
5
5
|
private topic;
|
|
6
|
-
private groupId;
|
|
7
6
|
private handlers;
|
|
8
7
|
private isRunning;
|
|
9
|
-
|
|
10
|
-
private currentProcessing;
|
|
11
|
-
constructor(config: KafkaConfig, topic: string, groupId: string);
|
|
8
|
+
constructor(config: any, topic: string, groupId: string);
|
|
12
9
|
connect(): Promise<void>;
|
|
13
10
|
on(handler: MessageHandler<T>): this;
|
|
14
11
|
start(): Promise<void>;
|
|
15
12
|
stop(): Promise<void>;
|
|
16
|
-
setMaxConcurrent(limit: number): this;
|
|
17
|
-
seekToOffset(offset: number): Promise<void>;
|
|
18
13
|
}
|
|
19
14
|
//# sourceMappingURL=consumer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/kafka/consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/kafka/consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvE,qBAAa,QAAQ,CAAC,CAAC,GAAG,GAAG;IACzB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAajD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI;IAK9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9B"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var s=require("kafkajs");exports.Consumer=class{constructor(t,e,n){this.handlers=[],this.isRunning=!1
|
|
1
|
+
"use strict";var s=require("kafkajs");exports.Consumer=class{constructor(t,e,n){this.handlers=[],this.isRunning=!1;const o=new s.Kafka({clientId:t.clientId||"async-fusion-consumer",brokers:t.brokers});this.consumer=o.consumer({groupId:n,sessionTimeout:3e4,heartbeatInterval:3e3}),this.topic=e}async connect(){await this.consumer.connect(),await this.consumer.subscribe({topic:this.topic,fromBeginning:!1}),console.log(`✅ Kafka consumer connected to topic: ${this.topic}`)}on(s){return this.handlers.push(s),this}async start(){this.isRunning=!0,await this.consumer.run({autoCommit:!0,eachMessage:async({message:s,partition:t})=>{if(this.isRunning)try{let e=new Date;if(s.timestamp){const t="string"==typeof s.timestamp?parseInt(s.timestamp,10):Number(s.timestamp);e=new Date(t)}const n={key:s.key?.toString(),value:JSON.parse(s.value?.toString()||"{}"),timestamp:e,partition:t,offset:Number(s.offset)};for(const s of this.handlers)await s(n)}catch(s){console.error("Error processing message:",s)}}}),console.log("✅ Kafka consumer started")}async stop(){this.isRunning=!1,await this.consumer.disconnect(),console.log("🔌 Kafka consumer disconnected")}};
|
|
2
2
|
//# sourceMappingURL=consumer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumer.js","sources":["../../../../src/kafka/consumer.ts"],"sourcesContent":["import { Kafka, Consumer as KafkaConsumer
|
|
1
|
+
{"version":3,"file":"consumer.js","sources":["../../../../src/kafka/consumer.ts"],"sourcesContent":["import { Kafka, Consumer as KafkaConsumer } from 'kafkajs';\r\nimport { Message } from '../types/index';\r\n\r\nexport type MessageHandler<T> = (message: Message<T>) => Promise<void>;\r\n\r\nexport class Consumer<T = any> {\r\n private consumer: KafkaConsumer;\r\n private topic: string;\r\n private handlers: MessageHandler<T>[] = [];\r\n private isRunning: boolean = false;\r\n\r\n constructor(config: any, topic: string, groupId: string) {\r\n const kafka = new Kafka({\r\n clientId: config.clientId || 'async-fusion-consumer',\r\n brokers: config.brokers\r\n });\r\n this.consumer = kafka.consumer({ \r\n groupId,\r\n sessionTimeout: 30000,\r\n heartbeatInterval: 3000\r\n });\r\n this.topic = topic;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n await this.consumer.connect();\r\n await this.consumer.subscribe({ \r\n topic: this.topic, \r\n fromBeginning: false \r\n });\r\n console.log(`✅ Kafka consumer connected to topic: ${this.topic}`);\r\n }\r\n\r\n on(handler: MessageHandler<T>): this {\r\n this.handlers.push(handler);\r\n return this;\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.isRunning = true;\r\n \r\n await this.consumer.run({\r\n autoCommit: true,\r\n eachMessage: async ({ message, partition }) => {\r\n if (!this.isRunning) return;\r\n \r\n try {\r\n // Parse timestamp safely\r\n let timestamp = new Date();\r\n if (message.timestamp) {\r\n const ts = typeof message.timestamp === 'string' \r\n ? parseInt(message.timestamp, 10) \r\n : Number(message.timestamp);\r\n timestamp = new Date(ts);\r\n }\r\n \r\n const parsedMessage: Message<T> = {\r\n key: message.key?.toString(),\r\n value: JSON.parse(message.value?.toString() || '{}'),\r\n timestamp: timestamp,\r\n partition: partition,\r\n offset: Number(message.offset)\r\n };\r\n \r\n for (const handler of this.handlers) {\r\n await handler(parsedMessage);\r\n }\r\n } catch (error) {\r\n console.error('Error processing message:', error);\r\n }\r\n }\r\n });\r\n console.log(`✅ Kafka consumer started`);\r\n }\r\n\r\n async stop(): Promise<void> {\r\n this.isRunning = false;\r\n await this.consumer.disconnect();\r\n console.log(`🔌 Kafka consumer disconnected`);\r\n }\r\n}"],"names":["constructor","config","topic","groupId","this","handlers","isRunning","kafka","Kafka","clientId","brokers","consumer","sessionTimeout","heartbeatInterval","connect","subscribe","fromBeginning","console","log","on","handler","push","start","run","autoCommit","eachMessage","async","message","partition","timestamp","Date","ts","parseInt","Number","parsedMessage","key","toString","value","JSON","parse","offset","error","stop","disconnect"],"mappings":"6DAWI,WAAAA,CAAYC,EAAaC,EAAeC,GAHhCC,KAAQC,SAAwB,GAChCD,KAASE,WAAY,EAGzB,MAAMC,EAAQ,IAAIC,QAAM,CACpBC,SAAUR,EAAOQ,UAAY,wBAC7BC,QAAST,EAAOS,UAEpBN,KAAKO,SAAWJ,EAAMI,SAAS,CAC3BR,UACAS,eAAgB,IAChBC,kBAAmB,MAEvBT,KAAKF,MAAQA,CAChB,CAED,aAAMY,SACIV,KAAKO,SAASG,gBACdV,KAAKO,SAASI,UAAU,CAC1Bb,MAAOE,KAAKF,MACZc,eAAe,IAEnBC,QAAQC,IAAI,wCAAwCd,KAAKF,QAC5D,CAED,EAAAiB,CAAGC,GAEC,OADAhB,KAAKC,SAASgB,KAAKD,GACZhB,IACV,CAED,WAAMkB,GACFlB,KAAKE,WAAY,QAEXF,KAAKO,SAASY,IAAI,CACpBC,YAAY,EACZC,YAAaC,OAASC,UAASC,gBAC3B,GAAKxB,KAAKE,UAEV,IAEI,IAAIuB,EAAY,IAAIC,KACpB,GAAIH,EAAQE,UAAW,CACnB,MAAME,EAAkC,iBAAtBJ,EAAQE,UACpBG,SAASL,EAAQE,UAAW,IAC5BI,OAAON,EAAQE,WACrBA,EAAY,IAAIC,KAAKC,EACxB,CAED,MAAMG,EAA4B,CAC9BC,IAAKR,EAAQQ,KAAKC,WAClBC,MAAOC,KAAKC,MAAMZ,EAAQU,OAAOD,YAAc,MAC/CP,UAAWA,EACXD,UAAWA,EACXY,OAAQP,OAAON,EAAQa,SAG3B,IAAK,MAAMpB,KAAWhB,KAAKC,eACjBe,EAAQc,EAErB,CAAC,MAAOO,GACLxB,QAAQwB,MAAM,4BAA6BA,EAC9C,KAGTxB,QAAQC,IAAI,2BACf,CAED,UAAMwB,GACFtC,KAAKE,WAAY,QACXF,KAAKO,SAASgC,aACpB1B,QAAQC,IAAI,iCACf"}
|
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { KafkaConfig, Message } from '../types';
|
|
1
|
+
import { Message } from '../types/index';
|
|
3
2
|
export declare class Producer<T = any> {
|
|
4
3
|
private producer;
|
|
5
4
|
private topic;
|
|
6
|
-
|
|
7
|
-
private batchTimeout;
|
|
8
|
-
private messageQueue;
|
|
9
|
-
private batchTimer;
|
|
10
|
-
constructor(config: KafkaConfig, topic: string);
|
|
5
|
+
constructor(config: any, topic: string);
|
|
11
6
|
connect(): Promise<void>;
|
|
12
|
-
send(message: Message<T>): Promise<
|
|
13
|
-
sendBatch(messages: Message<T>[]): Promise<
|
|
14
|
-
sendBuffered(message: Message<T>): Promise<void>;
|
|
15
|
-
private flush;
|
|
16
|
-
private startBatchProcessor;
|
|
7
|
+
send(message: Message<T>): Promise<void>;
|
|
8
|
+
sendBatch(messages: Message<T>[]): Promise<void>;
|
|
17
9
|
disconnect(): Promise<void>;
|
|
18
|
-
setBatchSize(size: number): this;
|
|
19
|
-
setBatchTimeout(ms: number): this;
|
|
20
10
|
}
|
|
21
11
|
//# sourceMappingURL=producer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"producer.d.ts","sourceRoot":"","sources":["../../../src/kafka/producer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"producer.d.ts","sourceRoot":"","sources":["../../../src/kafka/producer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,qBAAa,QAAQ,CAAC,CAAC,GAAG,GAAG;IACzB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM;IAWhC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BhD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAIpC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var t=require("kafkajs");exports.Producer=class{constructor(e,i){const s=new t.Kafka({clientId:e.clientId||"async-fusion-producer",brokers:e.brokers});this.producer=s.producer({allowAutoTopicCreation:!0}),this.topic=i}async connect(){await this.producer.connect(),console.log(`✅ Kafka producer connected to topic: ${this.topic}`)}async send(t){let e=Date.now().toString();t.timestamp&&(t.timestamp instanceof Date?e=t.timestamp.getTime().toString():"number"==typeof t.timestamp?e=String(t.timestamp):"string"==typeof t.timestamp&&(e=new Date(t.timestamp).getTime().toString())),await this.producer.send({topic:this.topic,messages:[{key:t.key,value:JSON.stringify(t.value),timestamp:e}]})}async sendBatch(t){const e=t.map(t=>{let e=Date.now().toString();return t.timestamp&&(t.timestamp instanceof Date?e=t.timestamp.getTime().toString():"number"==typeof t.timestamp?e=String(t.timestamp):"string"==typeof t.timestamp&&(e=new Date(t.timestamp).getTime().toString())),{key:t.key,value:JSON.stringify(t.value),timestamp:e}});await this.producer.send({topic:this.topic,messages:e})}async disconnect(){await this.producer.disconnect(),console.log("🔌 Kafka producer disconnected")}};
|
|
2
2
|
//# sourceMappingURL=producer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"producer.js","sources":["../../../../src/kafka/producer.ts"],"sourcesContent":["import { Kafka, Producer as KafkaProducer
|
|
1
|
+
{"version":3,"file":"producer.js","sources":["../../../../src/kafka/producer.ts"],"sourcesContent":["import { Kafka, Producer as KafkaProducer } from 'kafkajs';\r\nimport { Message } from '../types/index';\r\n\r\nexport class Producer<T = any> {\r\n private producer: KafkaProducer;\r\n private topic: string;\r\n\r\n constructor(config: any, topic: string) {\r\n const kafka = new Kafka({\r\n clientId: config.clientId || 'async-fusion-producer',\r\n brokers: config.brokers\r\n });\r\n this.producer = kafka.producer({\r\n allowAutoTopicCreation: true\r\n });\r\n this.topic = topic;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n await this.producer.connect();\r\n console.log(`✅ Kafka producer connected to topic: ${this.topic}`);\r\n }\r\n\r\n async send(message: Message<T>): Promise<void> {\r\n // CRITICAL FIX: Convert timestamp to string number\r\n let timestamp = Date.now().toString();\r\n \r\n if (message.timestamp) {\r\n if (message.timestamp instanceof Date) {\r\n timestamp = message.timestamp.getTime().toString();\r\n } else if (typeof message.timestamp === 'number') {\r\n timestamp = String(message.timestamp);\r\n } else if (typeof message.timestamp === 'string') {\r\n timestamp = new Date(message.timestamp).getTime().toString();\r\n }\r\n }\r\n \r\n await this.producer.send({\r\n topic: this.topic,\r\n messages: [{\r\n key: message.key,\r\n value: JSON.stringify(message.value),\r\n timestamp: timestamp\r\n }]\r\n });\r\n }\r\n\r\n async sendBatch(messages: Message<T>[]): Promise<void> {\r\n const kafkaMessages = messages.map(msg => {\r\n let timestamp = Date.now().toString();\r\n if (msg.timestamp) {\r\n if (msg.timestamp instanceof Date) {\r\n timestamp = msg.timestamp.getTime().toString();\r\n } else if (typeof msg.timestamp === 'number') {\r\n timestamp = String(msg.timestamp);\r\n } else if (typeof msg.timestamp === 'string') {\r\n timestamp = new Date(msg.timestamp).getTime().toString();\r\n }\r\n }\r\n \r\n return {\r\n key: msg.key,\r\n value: JSON.stringify(msg.value),\r\n timestamp: timestamp\r\n };\r\n });\r\n \r\n await this.producer.send({\r\n topic: this.topic,\r\n messages: kafkaMessages\r\n });\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n await this.producer.disconnect();\r\n console.log(`🔌 Kafka producer disconnected`);\r\n }\r\n}\r\n"],"names":["constructor","config","topic","kafka","Kafka","clientId","brokers","this","producer","allowAutoTopicCreation","connect","console","log","send","message","timestamp","Date","now","toString","getTime","String","messages","key","value","JSON","stringify","sendBatch","kafkaMessages","map","msg","disconnect"],"mappings":"6DAOI,WAAAA,CAAYC,EAAaC,GACrB,MAAMC,EAAQ,IAAIC,QAAM,CACpBC,SAAUJ,EAAOI,UAAY,wBAC7BC,QAASL,EAAOK,UAEpBC,KAAKC,SAAWL,EAAMK,SAAS,CAC3BC,wBAAwB,IAE5BF,KAAKL,MAAQA,CAChB,CAED,aAAMQ,SACIH,KAAKC,SAASE,UACpBC,QAAQC,IAAI,wCAAwCL,KAAKL,QAC5D,CAED,UAAMW,CAAKC,GAEP,IAAIC,EAAYC,KAAKC,MAAMC,WAEvBJ,EAAQC,YACJD,EAAQC,qBAAqBC,KAC7BD,EAAYD,EAAQC,UAAUI,UAAUD,WACJ,iBAAtBJ,EAAQC,UACtBA,EAAYK,OAAON,EAAQC,WACS,iBAAtBD,EAAQC,YACtBA,EAAY,IAAIC,KAAKF,EAAQC,WAAWI,UAAUD,mBAIpDX,KAAKC,SAASK,KAAK,CACrBX,MAAOK,KAAKL,MACZmB,SAAU,CAAC,CACPC,IAAKR,EAAQQ,IACbC,MAAOC,KAAKC,UAAUX,EAAQS,OAC9BR,UAAWA,KAGtB,CAED,eAAMW,CAAUL,GACZ,MAAMM,EAAgBN,EAASO,IAAIC,IAC/B,IAAId,EAAYC,KAAKC,MAAMC,WAW3B,OAVIW,EAAId,YACAc,EAAId,qBAAqBC,KACzBD,EAAYc,EAAId,UAAUI,UAAUD,WACJ,iBAAlBW,EAAId,UAClBA,EAAYK,OAAOS,EAAId,WACS,iBAAlBc,EAAId,YAClBA,EAAY,IAAIC,KAAKa,EAAId,WAAWI,UAAUD,aAI/C,CACHI,IAAKO,EAAIP,IACTC,MAAOC,KAAKC,UAAUI,EAAIN,OAC1BR,UAAWA,WAIbR,KAAKC,SAASK,KAAK,CACrBX,MAAOK,KAAKL,MACZmB,SAAUM,GAEjB,CAED,gBAAMG,SACIvB,KAAKC,SAASsB,aACpBnB,QAAQC,IAAI,iCACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.js","sources":["../../../../../node_modules/uuid/dist/native.js"],"sourcesContent":["const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);\nexport default { randomUUID };\n"],"names":["native","randomUUID","crypto","bind"],"mappings":"oEACA,IAAeA,EAAA,CAAEC,WADoB,oBAAXC,QAA0BA,OAAOD,YAAcC,OAAOD,WAAWE,KAAKD","x_google_ignoreList":[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";let e;Object.defineProperty(exports,"__esModule",{value:!0});const t=new Uint8Array(16);exports.default=function(){if(!e){if("undefined"==typeof crypto||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");e=crypto.getRandomValues.bind(crypto)}return e(t)};
|
|
2
|
+
//# sourceMappingURL=rng.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rng.js","sources":["../../../../../node_modules/uuid/dist/rng.js"],"sourcesContent":["let getRandomValues;\nconst rnds8 = new Uint8Array(16);\nexport default function rng() {\n if (!getRandomValues) {\n if (typeof crypto === 'undefined' || !crypto.getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n getRandomValues = crypto.getRandomValues.bind(crypto);\n }\n return getRandomValues(rnds8);\n}\n"],"names":["getRandomValues","rnds8","Uint8Array","crypto","Error","bind"],"mappings":"aAAA,IAAIA,yDACJ,MAAMC,EAAQ,IAAIC,WAAW,oBACd,WACX,IAAKF,EAAiB,CAClB,GAAsB,oBAAXG,SAA2BA,OAAOH,gBACzC,MAAM,IAAII,MAAM,4GAEpBJ,EAAkBG,OAAOH,gBAAgBK,KAAKF,OACjD,CACD,OAAOH,EAAgBC,EAC3B","x_google_ignoreList":[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";const t=[];for(let e=0;e<256;++e)t.push((e+256).toString(16).slice(1));exports.unsafeStringify=function(e,r=0){return(t[e[r+0]]+t[e[r+1]]+t[e[r+2]]+t[e[r+3]]+"-"+t[e[r+4]]+t[e[r+5]]+"-"+t[e[r+6]]+t[e[r+7]]+"-"+t[e[r+8]]+t[e[r+9]]+"-"+t[e[r+10]]+t[e[r+11]]+t[e[r+12]]+t[e[r+13]]+t[e[r+14]]+t[e[r+15]]).toLowerCase()};
|
|
2
|
+
//# sourceMappingURL=stringify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stringify.js","sources":["../../../../../node_modules/uuid/dist/stringify.js"],"sourcesContent":["import validate from './validate.js';\nconst byteToHex = [];\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n return (byteToHex[arr[offset + 0]] +\n byteToHex[arr[offset + 1]] +\n byteToHex[arr[offset + 2]] +\n byteToHex[arr[offset + 3]] +\n '-' +\n byteToHex[arr[offset + 4]] +\n byteToHex[arr[offset + 5]] +\n '-' +\n byteToHex[arr[offset + 6]] +\n byteToHex[arr[offset + 7]] +\n '-' +\n byteToHex[arr[offset + 8]] +\n byteToHex[arr[offset + 9]] +\n '-' +\n byteToHex[arr[offset + 10]] +\n byteToHex[arr[offset + 11]] +\n byteToHex[arr[offset + 12]] +\n byteToHex[arr[offset + 13]] +\n byteToHex[arr[offset + 14]] +\n byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n const uuid = unsafeStringify(arr, offset);\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;\n"],"names":["byteToHex","i","push","toString","slice","arr","offset","toLowerCase"],"mappings":"aACA,MAAMA,EAAY,GAClB,IAAK,IAAIC,EAAI,EAAGA,EAAI,MAAOA,EACvBD,EAAUE,MAAMD,EAAI,KAAOE,SAAS,IAAIC,MAAM,4BAE3C,SAAyBC,EAAKC,EAAS,GAC1C,OAAQN,EAAUK,EAAIC,EAAS,IAC3BN,EAAUK,EAAIC,EAAS,IACvBN,EAAUK,EAAIC,EAAS,IACvBN,EAAUK,EAAIC,EAAS,IACvB,IACAN,EAAUK,EAAIC,EAAS,IACvBN,EAAUK,EAAIC,EAAS,IACvB,IACAN,EAAUK,EAAIC,EAAS,IACvBN,EAAUK,EAAIC,EAAS,IACvB,IACAN,EAAUK,EAAIC,EAAS,IACvBN,EAAUK,EAAIC,EAAS,IACvB,IACAN,EAAUK,EAAIC,EAAS,KACvBN,EAAUK,EAAIC,EAAS,KACvBN,EAAUK,EAAIC,EAAS,KACvBN,EAAUK,EAAIC,EAAS,KACvBN,EAAUK,EAAIC,EAAS,KACvBN,EAAUK,EAAIC,EAAS,MAAMC,aACrC","x_google_ignoreList":[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("./native.js"),r=require("./rng.js"),t=require("./stringify.js");exports.default=function(n,u,o){return!e.default.randomUUID||u||n?function(e,n,u){const o=(e=e||{}).random??e.rng?.()??r.default();if(o.length<16)throw new Error("Random bytes length must be >= 16");if(o[6]=15&o[6]|64,o[8]=63&o[8]|128,n){if((u=u||0)<0||u+16>n.length)throw new RangeError(`UUID byte range ${u}:${u+15} is out of buffer bounds`);for(let e=0;e<16;++e)n[u+e]=o[e];return n}return t.unsafeStringify(o)}(n,u,o):e.default.randomUUID()};
|
|
2
|
+
//# sourceMappingURL=v4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v4.js","sources":["../../../../../node_modules/uuid/dist/v4.js"],"sourcesContent":["import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction _v4(options, buf, offset) {\n options = options || {};\n const rnds = options.random ?? options.rng?.() ?? rng();\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16');\n }\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n if (buf) {\n offset = offset || 0;\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);\n }\n for (let i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n return _v4(options, buf, offset);\n}\nexport default v4;\n"],"names":["options","buf","offset","native","default","randomUUID","rnds","random","rng","length","Error","RangeError","i","unsafeStringify","_v4"],"mappings":"mKAuBA,SAAYA,EAASC,EAAKC,GACtB,OAAIC,EAAMC,QAACC,YAAeJ,GAAQD,EArBtC,SAAaA,EAASC,EAAKC,GAEvB,MAAMI,GADNN,EAAUA,GAAW,IACAO,QAAUP,EAAQQ,SAAWA,EAAAA,UAClD,GAAIF,EAAKG,OAAS,GACd,MAAM,IAAIC,MAAM,qCAIpB,GAFAJ,EAAK,GAAgB,GAAVA,EAAK,GAAa,GAC7BA,EAAK,GAAgB,GAAVA,EAAK,GAAa,IACzBL,EAAK,CAEL,IADAC,EAASA,GAAU,GACN,GAAKA,EAAS,GAAKD,EAAIQ,OAChC,MAAM,IAAIE,WAAW,mBAAmBT,KAAUA,EAAS,8BAE/D,IAAK,IAAIU,EAAI,EAAGA,EAAI,KAAMA,EACtBX,EAAIC,EAASU,GAAKN,EAAKM,GAE3B,OAAOX,CACV,CACD,OAAOY,EAAAA,gBAAgBP,EAC3B,CAKWQ,CAAId,EAASC,EAAKC,GAFdC,EAAAA,QAAOE,YAGtB","x_google_ignoreList":[0]}
|
|
@@ -1,27 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export interface SparkConfig {
|
|
3
|
+
master: string;
|
|
4
|
+
appName?: string;
|
|
5
|
+
sparkHome?: string;
|
|
6
|
+
deployMode?: 'client' | 'cluster';
|
|
7
|
+
executorMemory?: string;
|
|
8
|
+
executorCores?: number;
|
|
9
|
+
numExecutors?: number;
|
|
10
|
+
driverMemory?: string;
|
|
11
|
+
sparkConf?: Record<string, string>;
|
|
12
|
+
environment?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export interface SparkJobOptions {
|
|
15
|
+
timeout?: number;
|
|
16
|
+
retries?: number;
|
|
17
|
+
pyFiles?: string[];
|
|
18
|
+
files?: string[];
|
|
19
|
+
jars?: string[];
|
|
20
|
+
packages?: string[];
|
|
21
|
+
verbose?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface SparkJob {
|
|
24
|
+
id: string;
|
|
25
|
+
status: 'pending' | 'submitted' | 'running' | 'completed' | 'failed' | 'cancelled' | 'timeout';
|
|
26
|
+
progress: number;
|
|
27
|
+
startTime: Date;
|
|
28
|
+
endTime?: Date;
|
|
29
|
+
result?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
stdout?: string;
|
|
32
|
+
stderr?: string;
|
|
33
|
+
applicationId?: string;
|
|
34
|
+
trackingUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare class SparkClient extends EventEmitter {
|
|
37
|
+
private config;
|
|
38
|
+
private activeJobs;
|
|
5
39
|
private retryConfig;
|
|
6
40
|
constructor(config: SparkConfig, retryConfig?: {
|
|
7
41
|
maxRetries: number;
|
|
8
42
|
retryDelay: number;
|
|
9
43
|
});
|
|
10
|
-
submitJob(jobCode: string, jobName: string, options?:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
44
|
+
submitJob(jobCode: string, jobName: string, options?: SparkJobOptions): Promise<SparkJob>;
|
|
45
|
+
private executeJob;
|
|
46
|
+
private runLocalJob;
|
|
47
|
+
private runClusterJob;
|
|
48
|
+
private buildPythonScript;
|
|
49
|
+
private buildSparkSubmitArgs;
|
|
50
|
+
private buildClusterSubmitArgs;
|
|
51
|
+
private findSparkSubmit;
|
|
52
|
+
private parseJobOutput;
|
|
53
|
+
private parseClusterOutput;
|
|
54
|
+
private monitorClusterJob;
|
|
55
|
+
getJobStatus(jobId: string): Promise<SparkJob | null>;
|
|
15
56
|
cancelJob(jobId: string): Promise<boolean>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
timeout?: number;
|
|
19
|
-
}): Promise<SparkJob>;
|
|
20
|
-
submitSQLQuery(sql: string, options?: {
|
|
21
|
-
database?: string;
|
|
22
|
-
timeout?: number;
|
|
23
|
-
}): Promise<any[]>;
|
|
24
|
-
private sleep;
|
|
57
|
+
runPythonScript(scriptPath: string, args?: string[], options?: SparkJobOptions): Promise<SparkJob>;
|
|
58
|
+
submitSQLQuery(sql: string, options?: SparkJobOptions): Promise<SparkJob>;
|
|
25
59
|
healthCheck(): Promise<boolean>;
|
|
60
|
+
private sleep;
|
|
61
|
+
getActiveJobs(): SparkJob[];
|
|
62
|
+
getAllJobs(): SparkJob[];
|
|
63
|
+
}
|
|
64
|
+
export declare class SparkSQL {
|
|
65
|
+
private client;
|
|
66
|
+
constructor(config: SparkConfig);
|
|
67
|
+
query(sql: string, options?: SparkJobOptions): Promise<any>;
|
|
68
|
+
createTable(tableName: string, path: string, format?: string): Promise<SparkJob>;
|
|
69
|
+
registerUDF(name: string, pythonFunction: string): Promise<void>;
|
|
26
70
|
}
|
|
27
71
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/spark/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/spark/client.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/spark/client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";var t=require("child_process"),e=require("fs"),r=require("path"),s=require("os"),i=require("events"),o=require("../node_modules/uuid/dist/v4.js");function n(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(r){if("default"!==r){var s=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,s.get?s:{enumerable:!0,get:function(){return t[r]}})}}),e.default=t,Object.freeze(e)}var a=n(e),c=n(r),p=n(s);class u extends i.EventEmitter{constructor(t,e){super(),this.activeJobs=new Map,this.config={master:t.master,appName:t.appName||"async-fusion-app",sparkHome:t.sparkHome||process.env.SPARK_HOME||"",deployMode:t.deployMode||"client",executorMemory:t.executorMemory||"2g",executorCores:t.executorCores||2,numExecutors:t.numExecutors||2,driverMemory:t.driverMemory||"1g",sparkConf:t.sparkConf||{},environment:t.environment||{}},this.retryConfig=e||{maxRetries:3,retryDelay:1e3}}async submitJob(t,e,r){const s=r?.retries||this.retryConfig.maxRetries;for(let i=1;i<=s;i++)try{return await this.executeJob(t,e,r)}catch(t){if(console.error(`Job submission attempt ${i} failed:`,t),i===s)throw t;await this.sleep(this.retryConfig.retryDelay*i)}throw new Error("Job submission failed after all retries")}async executeJob(t,e,r){const s=o.default(),i={id:s,status:"pending",progress:0,startTime:new Date};return this.activeJobs.set(s,i),this.emit("job:submitted",{jobId:s,jobName:e}),"local[*]"===this.config.master||this.config.master?.startsWith("local")?await this.runLocalJob(t,e,s,i,r):await this.runClusterJob(t,e,s,i,r),i}async runLocalJob(e,r,s,i,o){const n=p.tmpdir(),u=c.join(n,`spark_job_${s}.py`),l=this.buildPythonScript(e,r);a.writeFileSync(u,l);const m=this.findSparkSubmit();if(!m)return i.status="failed",i.error="Spark not found. Please install Spark or set SPARK_HOME environment variable",i.endTime=new Date,void this.emit("job:failed",{jobId:s,error:i.error});const d=this.buildSparkSubmitArgs(u,o);o?.verbose&&console.log(`[Spark] Running: ${m} ${d.join(" ")}`),i.status="running",this.emit("job:running",{jobId:s});const b=t.spawn(m,d,{env:{...process.env,...this.config.environment}});let f="",h="";b.stdout.on("data",t=>{const e=t.toString();f+=e,o?.verbose&&console.log(`[Spark] ${e.trim()}`),this.parseJobOutput(e,i)}),b.stderr.on("data",t=>{const e=t.toString();h+=e,o?.verbose&&console.error(`[Spark Error] ${e.trim()}`)}),b.on("close",t=>{i.stdout=f,i.stderr=h,i.endTime=new Date,0!==t||i.error?"cancelled"!==i.status&&(i.status="failed",i.error=i.error||h||`Process exited with code ${t}`,this.emit("job:failed",{jobId:s,error:i.error})):(i.status="completed",i.progress=100,this.emit("job:completed",{jobId:s,result:i.result})),a.unlinkSync(u),this.activeJobs.delete(s)}),o?.timeout&&setTimeout(()=>{"running"===i.status&&(i.status="timeout",i.error=`Job timed out after ${o.timeout}ms`,b.kill(),this.emit("job:timeout",{jobId:s}))},o.timeout)}async runClusterJob(e,r,s,i,o){const n=p.tmpdir(),u=c.join(n,`spark_job_${s}.py`),l=this.buildPythonScript(e,r);a.writeFileSync(u,l);const m=this.findSparkSubmit();if(!m)return i.status="failed",i.error="Spark not found",void(i.endTime=new Date);const d=this.buildClusterSubmitArgs(u,r,o);o?.verbose&&console.log(`[Spark] Submitting to cluster: ${m} ${d.join(" ")}`),i.status="submitted",this.emit("job:submitted",{jobId:s});const b=t.spawn(m,d,{env:{...process.env,...this.config.environment},detached:!0});let f="",h="";b.stdout.on("data",t=>{f+=t.toString(),this.parseClusterOutput(t.toString(),i)}),b.stderr.on("data",t=>{h+=t.toString()}),b.on("close",t=>{i.stdout=f,i.stderr=h,0===t&&i.applicationId?(i.status="running",this.emit("job:running",{jobId:s,applicationId:i.applicationId}),this.monitorClusterJob(i.applicationId,s,i,o)):0!==t&&(i.status="failed",i.error=h||`Submission failed with code ${t}`,i.endTime=new Date,this.emit("job:failed",{jobId:s,error:i.error}),a.unlinkSync(u),this.activeJobs.delete(s))}),b.unref()}buildPythonScript(t,e){return`\nimport sys\nimport json\nimport traceback\nfrom datetime import datetime\nfrom pyspark.sql import SparkSession\n\ndef main():\n try:\n # Initialize Spark\n spark = SparkSession.builder \\\n .appName("${e}") \\\n .getOrCreate()\n \n print(json.dumps({"type": "job_start", "timestamp": datetime.now().isoformat()}))\n \n # Execute user code\n ${t}\n \n print(json.dumps({"type": "job_complete", "timestamp": datetime.now().isoformat()}))\n print("JOB_SUCCESS")\n \n spark.stop()\n \n except Exception as e:\n error_data = {\n "type": "job_error",\n "error": str(e),\n "traceback": traceback.format_exc(),\n "timestamp": datetime.now().isoformat()\n }\n print(json.dumps(error_data), file=sys.stderr)\n sys.exit(1)\n\nif __name__ == "__main__":\n main()\n`}buildSparkSubmitArgs(t,e){const r=[t];return e?.pyFiles&&r.unshift("--py-files",e.pyFiles.join(",")),r}buildClusterSubmitArgs(t,e,r){const s=["--master",this.config.master,"--deploy-mode",this.config.deployMode||"client","--name",e,"--executor-memory",this.config.executorMemory,"--executor-cores",this.config.executorCores.toString(),"--driver-memory",this.config.driverMemory,t];return this.config.numExecutors&&this.config.numExecutors>0&&s.splice(4,0,"--num-executors",this.config.numExecutors.toString()),r?.pyFiles&&r.pyFiles.length&&s.splice(4,0,"--py-files",r.pyFiles.join(",")),r?.files&&r.files.length&&s.splice(4,0,"--files",r.files.join(",")),r?.jars&&r.jars.length&&s.splice(4,0,"--jars",r.jars.join(",")),r?.packages&&r.packages.length&&s.splice(4,0,"--packages",r.packages.join(",")),Object.entries(this.config.sparkConf).forEach(([t,e])=>{s.push("--conf",`${t}=${e}`)}),s}findSparkSubmit(){if(this.config.sparkHome&&a.existsSync(c.join(this.config.sparkHome,"bin","spark-submit")))return c.join(this.config.sparkHome,"bin","spark-submit");if(process.env.SPARK_HOME&&a.existsSync(c.join(process.env.SPARK_HOME,"bin","spark-submit")))return c.join(process.env.SPARK_HOME,"bin","spark-submit");const t=["/opt/spark/bin/spark-submit","/usr/local/spark/bin/spark-submit","/usr/lib/spark/bin/spark-submit","C:\\opt\\spark\\bin\\spark-submit","C:\\spark\\bin\\spark-submit"];for(const e of t)if(a.existsSync(e))return e;const e=(process.env.PATH||"").split(c.delimiter);for(const t of e){const e=c.join(t,"spark-submit");if(a.existsSync(e))return e}return null}parseJobOutput(t,e){const r=t.split("\n");for(const t of r)try{const r=JSON.parse(t);"job_start"===r.type?e.status="running":"job_complete"===r.type?e.result=r:"job_progress"===r.type&&(e.progress=r.progress,this.emit("job:progress",{jobId:e.id,progress:r.progress}))}catch{t.includes("JOB_SUCCESS")&&(e.status="completed")}}parseClusterOutput(t,e){const r=t.match(/application_[0-9]+_[0-9]+/);r&&!e.applicationId&&(e.applicationId=r[0],e.trackingUrl=`http://localhost:8080/proxy/${e.applicationId}/`,this.emit("job:application",{jobId:e.id,applicationId:e.applicationId}))}async monitorClusterJob(t,e,r,s){const i=Date.now(),o=s?.timeout||3e5,n=setInterval(async()=>{if(Date.now()-i>o)return r.status="timeout",r.endTime=new Date,clearInterval(n),void this.emit("job:timeout",{jobId:e});r.progress=Math.min(r.progress+10,90),this.emit("job:progress",{jobId:e,progress:r.progress})},5e3);setTimeout(()=>{clearInterval(n),r.status="completed",r.progress=100,r.endTime=new Date,this.emit("job:completed",{jobId:e});const t=p.tmpdir(),s=c.join(t,`spark_job_${e}.py`);a.existsSync(s)&&a.unlinkSync(s),this.activeJobs.delete(e)},3e4)}async getJobStatus(t){return this.activeJobs.get(t)||null}async cancelJob(t){const e=this.activeJobs.get(t);return!!e&&(e.status="cancelled",e.endTime=new Date,this.emit("job:cancelled",{jobId:t}),this.activeJobs.delete(t),!0)}async runPythonScript(t,e=[],r){if(!a.existsSync(t))throw new Error(`Python script not found: ${t}`);const s=a.readFileSync(t,"utf-8");return this.submitJob(s,c.basename(t),r)}async submitSQLQuery(t,e){const r=`\nfrom pyspark.sql import SparkSession\n\nspark = SparkSession.builder.getOrCreate()\ndf = spark.sql("""${t.replace(/"/g,'\\"')}""")\ndf.show()\ndf.printSchema()\n `;return this.submitJob(r,"sql-query",e)}async healthCheck(){return null!==this.findSparkSubmit()}sleep(t){return new Promise(e=>setTimeout(e,t))}getActiveJobs(){return Array.from(this.activeJobs.values())}getAllJobs(){return Array.from(this.activeJobs.values())}}exports.SparkClient=u;
|
|
2
2
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../../src/spark/client.ts"],"sourcesContent":["\r\nimport { SparkConfig, SparkJob, SparkStage } from '../types';\r\n\r\nexport class SparkClient {\r\n private baseUrl: string;\r\n private headers: Record<string, string>;\r\n private retryConfig: { maxRetries: number; retryDelay: number };\r\n\r\n constructor(config: SparkConfig, retryConfig?: { maxRetries: number; retryDelay: number }) {\r\n this.baseUrl = `${config.master}/api/v1`;\r\n this.headers = {\r\n 'Content-Type': 'application/json',\r\n };\r\n this.retryConfig = retryConfig || { maxRetries: 3, retryDelay: 1000 };\r\n }\r\n\r\n async submitJob(jobCode: string, jobName: string, options?: { timeout?: number; retries?: number }): Promise<SparkJob> {\r\n const maxRetries = options?.retries || this.retryConfig.maxRetries;\r\n \r\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/submissions/create`, {\r\n method: 'POST',\r\n headers: this.headers,\r\n body: JSON.stringify({\r\n action: 'CreateSubmissionRequest',\r\n appResource: jobCode,\r\n mainClass: 'org.apache.spark.deploy.SparkSubmit',\r\n appArgs: [jobName],\r\n sparkProperties: {\r\n 'spark.app.name': jobName,\r\n 'spark.master': this.baseUrl,\r\n },\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\r\n }\r\n\r\n const data = await response.json();\r\n \r\n const job: SparkJob = {\r\n id: data.submissionId,\r\n status: 'pending',\r\n progress: 0,\r\n startTime: new Date(),\r\n stages: [],\r\n };\r\n\r\n // Start monitoring if timeout specified\r\n if (options?.timeout) {\r\n this.monitorJob(job.id, options.timeout).catch(console.error);\r\n }\r\n\r\n return job;\r\n } catch (error) {\r\n console.error(`Job submission attempt ${attempt} failed:`, error);\r\n if (attempt === maxRetries) throw error;\r\n await this.sleep(this.retryConfig.retryDelay * attempt);\r\n }\r\n }\r\n throw new Error('Job submission failed after all retries');\r\n }\r\n\r\n async getJobStatus(jobId: string): Promise<SparkJob> {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/submissions/status/${jobId}`);\r\n \r\n if (!response.ok) {\r\n throw new Error(`Failed to get job status: ${response.status}`);\r\n }\r\n \r\n const data = await response.json();\r\n \r\n return {\r\n id: jobId,\r\n status: data.driverState?.toLowerCase() || 'unknown',\r\n progress: data.progress || 0,\r\n startTime: new Date(data.submissionTime || Date.now()),\r\n endTime: data.completionTime ? new Date(data.completionTime) : undefined,\r\n stages: data.stages || [],\r\n };\r\n } catch (error) {\r\n console.error(`Error getting job status:`, error);\r\n throw error;\r\n }\r\n }\r\n\r\n async cancelJob(jobId: string): Promise<boolean> {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/submissions/kill/${jobId}`, {\r\n method: 'POST',\r\n });\r\n \r\n if (response.ok) {\r\n console.log(`✅ Job ${jobId} cancelled successfully`);\r\n return true;\r\n }\r\n \r\n console.error(`Failed to cancel job ${jobId}: ${response.status}`);\r\n return false;\r\n } catch (error) {\r\n console.error(`Error cancelling job:`, error);\r\n return false;\r\n }\r\n }\r\n\r\n async monitorJob(jobId: string, timeoutMs: number = 300000): Promise<SparkJob> {\r\n const startTime = Date.now();\r\n let lastProgress = -1;\r\n \r\n while (Date.now() - startTime < timeoutMs) {\r\n const job = await this.getJobStatus(jobId);\r\n \r\n // Log progress changes\r\n if (job.progress !== lastProgress) {\r\n console.log(`📊 Job ${jobId} progress: ${job.progress}%`);\r\n lastProgress = job.progress;\r\n }\r\n \r\n // Check completion\r\n if (job.status === 'completed' || job.status === 'failed') {\r\n console.log(`✅ Job ${jobId} ${job.status}`);\r\n return job;\r\n }\r\n \r\n // Wait before next poll\r\n await this.sleep(2000);\r\n }\r\n \r\n console.warn(`⚠️ Job ${jobId} monitoring timed out after ${timeoutMs}ms`);\r\n return this.getJobStatus(jobId);\r\n }\r\n\r\n async runPythonScript(scriptPath: string, args: string[] = [], options?: { timeout?: number }): Promise<SparkJob> {\r\n // Validate script exists\r\n const fs = require('fs');\r\n if (!fs.existsSync(scriptPath)) {\r\n throw new Error(`Python script not found: ${scriptPath}`);\r\n }\r\n \r\n const jobCode = `\r\nfrom pyspark.sql import SparkSession\r\nimport sys\r\nimport json\r\n\r\nspark = SparkSession.builder.getOrCreate()\r\n\r\ntry:\r\n with open('${scriptPath}', 'r') as f:\r\n code = f.read()\r\n exec(code)\r\n print(\"✅ Python script executed successfully\")\r\nexcept Exception as e:\r\n print(f\"❌ Error executing script: {e}\")\r\n sys.exit(1)\r\n `;\r\n \r\n return this.submitJob(jobCode, `python-${Date.now()}`, options);\r\n }\r\n\r\n async submitSQLQuery(sql: string, options?: { database?: string; timeout?: number }): Promise<any[]> {\r\n const queryJob = `\r\nfrom pyspark.sql import SparkSession\r\n\r\nspark = SparkSession.builder.getOrCreate()\r\n\r\n${options?.database ? `spark.sql(\"USE ${options.database}\")` : ''}\r\n\r\nresult = spark.sql(\"\"\"${sql.replace(/\"/g, '\\\\\"')}\"\"\")\r\nresult.show()\r\nprint(result.collect())\r\n `;\r\n \r\n const job = await this.submitJob(queryJob, `sql-${Date.now()}`, options);\r\n await this.monitorJob(job.id, options?.timeout);\r\n return [];\r\n }\r\n\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n }\r\n\r\n async healthCheck(): Promise<boolean> {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/`);\r\n return response.ok;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n}"],"names":["constructor","config","retryConfig","this","baseUrl","master","headers","maxRetries","retryDelay","submitJob","jobCode","jobName","options","retries","attempt","response","fetch","method","body","JSON","stringify","action","appResource","mainClass","appArgs","sparkProperties","ok","Error","status","statusText","job","id","json","submissionId","progress","startTime","Date","stages","timeout","monitorJob","catch","console","error","sleep","getJobStatus","jobId","data","driverState","toLowerCase","submissionTime","now","endTime","completionTime","undefined","cancelJob","log","timeoutMs","lastProgress","warn","runPythonScript","scriptPath","args","require","existsSync","submitSQLQuery","sql","queryJob","database","replace","ms","Promise","resolve","setTimeout","healthCheck"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../src/spark/client.ts"],"sourcesContent":["// src/spark/client.ts - Production Ready Spark Client\r\n// Supports: Local Mode, Standalone Cluster, YARN, Kubernetes\r\n\r\nimport { spawn, ChildProcess } from 'child_process';\r\nimport { v4 as uuidv4 } from 'uuid';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport * as os from 'os';\r\nimport { EventEmitter } from 'events';\r\n\r\nexport interface SparkConfig {\r\n master: string; // local[*], spark://host:7077, yarn, k8s://...\r\n appName?: string;\r\n sparkHome?: string;\r\n deployMode?: 'client' | 'cluster';\r\n executorMemory?: string;\r\n executorCores?: number;\r\n numExecutors?: number;\r\n driverMemory?: string;\r\n sparkConf?: Record<string, string>;\r\n environment?: Record<string, string>;\r\n}\r\n\r\nexport interface SparkJobOptions {\r\n timeout?: number;\r\n retries?: number;\r\n pyFiles?: string[];\r\n files?: string[];\r\n jars?: string[];\r\n packages?: string[];\r\n verbose?: boolean;\r\n}\r\n\r\nexport interface SparkJob {\r\n id: string;\r\n status: 'pending' | 'submitted' | 'running' | 'completed' | 'failed' | 'cancelled' | 'timeout';\r\n progress: number;\r\n startTime: Date;\r\n endTime?: Date;\r\n result?: string;\r\n error?: string;\r\n stdout?: string;\r\n stderr?: string;\r\n applicationId?: string;\r\n trackingUrl?: string;\r\n}\r\n\r\nexport class SparkClient extends EventEmitter {\r\n private config: SparkConfig;\r\n private activeJobs: Map<string, SparkJob> = new Map();\r\n private retryConfig: { maxRetries: number; retryDelay: number };\r\n\r\n constructor(config: SparkConfig, retryConfig?: { maxRetries: number; retryDelay: number }) {\r\n super();\r\n this.config = {\r\n master: config.master,\r\n appName: config.appName || 'async-fusion-app',\r\n sparkHome: config.sparkHome || process.env.SPARK_HOME || '',\r\n deployMode: config.deployMode || 'client',\r\n executorMemory: config.executorMemory || '2g',\r\n executorCores: config.executorCores || 2,\r\n numExecutors: config.numExecutors || 2,\r\n driverMemory: config.driverMemory || '1g',\r\n sparkConf: config.sparkConf || {},\r\n environment: config.environment || {}\r\n };\r\n this.retryConfig = retryConfig || { maxRetries: 3, retryDelay: 1000 };\r\n }\r\n\r\n async submitJob(jobCode: string, jobName: string, options?: SparkJobOptions): Promise<SparkJob> {\r\n const maxRetries = options?.retries || this.retryConfig.maxRetries;\r\n \r\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n try {\r\n return await this.executeJob(jobCode, jobName, options);\r\n } catch (error) {\r\n console.error(`Job submission attempt ${attempt} failed:`, error);\r\n if (attempt === maxRetries) throw error;\r\n await this.sleep(this.retryConfig.retryDelay * attempt);\r\n }\r\n }\r\n throw new Error('Job submission failed after all retries');\r\n }\r\n\r\n private async executeJob(jobCode: string, jobName: string, options?: SparkJobOptions): Promise<SparkJob> {\r\n const jobId = uuidv4();\r\n const job: SparkJob = {\r\n id: jobId,\r\n status: 'pending',\r\n progress: 0,\r\n startTime: new Date()\r\n };\r\n \r\n this.activeJobs.set(jobId, job);\r\n this.emit('job:submitted', { jobId, jobName });\r\n \r\n // Check if running in local mode or cluster mode\r\n if (this.config.master === 'local[*]' || this.config.master?.startsWith('local')) {\r\n await this.runLocalJob(jobCode, jobName, jobId, job, options);\r\n } else {\r\n await this.runClusterJob(jobCode, jobName, jobId, job, options);\r\n }\r\n \r\n return job;\r\n }\r\n\r\n private async runLocalJob(jobCode: string, jobName: string, jobId: string, job: SparkJob, options?: SparkJobOptions): Promise<void> {\r\n const tempDir = os.tmpdir();\r\n const scriptPath = path.join(tempDir, `spark_job_${jobId}.py`);\r\n \r\n // Build complete Python script\r\n const fullScript = this.buildPythonScript(jobCode, jobName);\r\n fs.writeFileSync(scriptPath, fullScript);\r\n \r\n // Find spark-submit\r\n const sparkSubmitPath = this.findSparkSubmit();\r\n if (!sparkSubmitPath) {\r\n job.status = 'failed';\r\n job.error = 'Spark not found. Please install Spark or set SPARK_HOME environment variable';\r\n job.endTime = new Date();\r\n this.emit('job:failed', { jobId, error: job.error });\r\n return;\r\n }\r\n \r\n // Build command arguments\r\n const args = this.buildSparkSubmitArgs(scriptPath, options);\r\n \r\n if (options?.verbose) {\r\n console.log(`[Spark] Running: ${sparkSubmitPath} ${args.join(' ')}`);\r\n }\r\n \r\n job.status = 'running';\r\n this.emit('job:running', { jobId });\r\n \r\n const sparkProcess = spawn(sparkSubmitPath, args, {\r\n env: { ...process.env, ...this.config.environment }\r\n });\r\n \r\n let stdout = '';\r\n let stderr = '';\r\n \r\n sparkProcess.stdout.on('data', (data) => {\r\n const output = data.toString();\r\n stdout += output;\r\n if (options?.verbose) console.log(`[Spark] ${output.trim()}`);\r\n this.parseJobOutput(output, job);\r\n });\r\n \r\n sparkProcess.stderr.on('data', (data) => {\r\n const error = data.toString();\r\n stderr += error;\r\n if (options?.verbose) console.error(`[Spark Error] ${error.trim()}`);\r\n });\r\n \r\n sparkProcess.on('close', (code) => {\r\n job.stdout = stdout;\r\n job.stderr = stderr;\r\n job.endTime = new Date();\r\n \r\n if (code === 0 && !job.error) {\r\n job.status = 'completed';\r\n job.progress = 100;\r\n this.emit('job:completed', { jobId, result: job.result });\r\n } else if (job.status !== 'cancelled') {\r\n job.status = 'failed';\r\n job.error = job.error || stderr || `Process exited with code ${code}`;\r\n this.emit('job:failed', { jobId, error: job.error });\r\n }\r\n \r\n // Cleanup temp file\r\n fs.unlinkSync(scriptPath);\r\n this.activeJobs.delete(jobId);\r\n });\r\n \r\n // Set timeout\r\n if (options?.timeout) {\r\n setTimeout(() => {\r\n if (job.status === 'running') {\r\n job.status = 'timeout';\r\n job.error = `Job timed out after ${options.timeout}ms`;\r\n sparkProcess.kill();\r\n this.emit('job:timeout', { jobId });\r\n }\r\n }, options.timeout);\r\n }\r\n }\r\n\r\n private async runClusterJob(jobCode: string, jobName: string, jobId: string, job: SparkJob, options?: SparkJobOptions): Promise<void> {\r\n const tempDir = os.tmpdir();\r\n const scriptPath = path.join(tempDir, `spark_job_${jobId}.py`);\r\n \r\n const fullScript = this.buildPythonScript(jobCode, jobName);\r\n fs.writeFileSync(scriptPath, fullScript);\r\n \r\n const sparkSubmitPath = this.findSparkSubmit();\r\n if (!sparkSubmitPath) {\r\n job.status = 'failed';\r\n job.error = 'Spark not found';\r\n job.endTime = new Date();\r\n return;\r\n }\r\n \r\n const args = this.buildClusterSubmitArgs(scriptPath, jobName, options);\r\n \r\n if (options?.verbose) {\r\n console.log(`[Spark] Submitting to cluster: ${sparkSubmitPath} ${args.join(' ')}`);\r\n }\r\n \r\n job.status = 'submitted';\r\n this.emit('job:submitted', { jobId });\r\n \r\n const sparkProcess = spawn(sparkSubmitPath, args, {\r\n env: { ...process.env, ...this.config.environment },\r\n detached: true\r\n });\r\n \r\n let stdout = '';\r\n let stderr = '';\r\n \r\n sparkProcess.stdout.on('data', (data) => {\r\n stdout += data.toString();\r\n this.parseClusterOutput(data.toString(), job);\r\n });\r\n \r\n sparkProcess.stderr.on('data', (data) => {\r\n stderr += data.toString();\r\n });\r\n \r\n sparkProcess.on('close', (code) => {\r\n job.stdout = stdout;\r\n job.stderr = stderr;\r\n \r\n if (code === 0 && job.applicationId) {\r\n job.status = 'running';\r\n this.emit('job:running', { jobId, applicationId: job.applicationId });\r\n this.monitorClusterJob(job.applicationId, jobId, job, options);\r\n } else if (code !== 0) {\r\n job.status = 'failed';\r\n job.error = stderr || `Submission failed with code ${code}`;\r\n job.endTime = new Date();\r\n this.emit('job:failed', { jobId, error: job.error });\r\n fs.unlinkSync(scriptPath);\r\n this.activeJobs.delete(jobId);\r\n }\r\n });\r\n \r\n sparkProcess.unref();\r\n }\r\n\r\n private buildPythonScript(jobCode: string, jobName: string): string {\r\n return `\r\nimport sys\r\nimport json\r\nimport traceback\r\nfrom datetime import datetime\r\nfrom pyspark.sql import SparkSession\r\n\r\ndef main():\r\n try:\r\n # Initialize Spark\r\n spark = SparkSession.builder \\\\\r\n .appName(\"${jobName}\") \\\\\r\n .getOrCreate()\r\n \r\n print(json.dumps({\"type\": \"job_start\", \"timestamp\": datetime.now().isoformat()}))\r\n \r\n # Execute user code\r\n ${jobCode}\r\n \r\n print(json.dumps({\"type\": \"job_complete\", \"timestamp\": datetime.now().isoformat()}))\r\n print(\"JOB_SUCCESS\")\r\n \r\n spark.stop()\r\n \r\n except Exception as e:\r\n error_data = {\r\n \"type\": \"job_error\",\r\n \"error\": str(e),\r\n \"traceback\": traceback.format_exc(),\r\n \"timestamp\": datetime.now().isoformat()\r\n }\r\n print(json.dumps(error_data), file=sys.stderr)\r\n sys.exit(1)\r\n\r\nif __name__ == \"__main__\":\r\n main()\r\n`;\r\n }\r\n\r\n private buildSparkSubmitArgs(scriptPath: string, options?: SparkJobOptions): string[] {\r\n const args: string[] = [scriptPath];\r\n \r\n if (options?.pyFiles) {\r\n args.unshift('--py-files', options.pyFiles.join(','));\r\n }\r\n \r\n return args;\r\n }\r\n\r\n private buildClusterSubmitArgs(scriptPath: string, jobName: string, options?: SparkJobOptions): string[] {\r\n const args: string[] = [\r\n '--master', this.config.master,\r\n '--deploy-mode', this.config.deployMode || 'client',\r\n '--name', jobName,\r\n '--executor-memory', this.config.executorMemory,\r\n '--executor-cores', this.config.executorCores.toString(),\r\n '--driver-memory', this.config.driverMemory,\r\n scriptPath\r\n ];\r\n \r\n if (this.config.numExecutors && this.config.numExecutors > 0) {\r\n args.splice(4, 0, '--num-executors', this.config.numExecutors.toString());\r\n }\r\n \r\n if (options?.pyFiles && options.pyFiles.length) {\r\n args.splice(4, 0, '--py-files', options.pyFiles.join(','));\r\n }\r\n \r\n if (options?.files && options.files.length) {\r\n args.splice(4, 0, '--files', options.files.join(','));\r\n }\r\n \r\n if (options?.jars && options.jars.length) {\r\n args.splice(4, 0, '--jars', options.jars.join(','));\r\n }\r\n \r\n if (options?.packages && options.packages.length) {\r\n args.splice(4, 0, '--packages', options.packages.join(','));\r\n }\r\n \r\n // Add Spark configuration\r\n Object.entries(this.config.sparkConf).forEach(([key, value]) => {\r\n args.push('--conf', `${key}=${value}`);\r\n });\r\n \r\n return args;\r\n }\r\n\r\n private findSparkSubmit(): string | null {\r\n // Check configured sparkHome\r\n if (this.config.sparkHome && fs.existsSync(path.join(this.config.sparkHome, 'bin', 'spark-submit'))) {\r\n return path.join(this.config.sparkHome, 'bin', 'spark-submit');\r\n }\r\n \r\n // Check SPARK_HOME environment variable\r\n if (process.env.SPARK_HOME && fs.existsSync(path.join(process.env.SPARK_HOME, 'bin', 'spark-submit'))) {\r\n return path.join(process.env.SPARK_HOME, 'bin', 'spark-submit');\r\n }\r\n \r\n // Check common installation paths\r\n const commonPaths = [\r\n '/opt/spark/bin/spark-submit',\r\n '/usr/local/spark/bin/spark-submit',\r\n '/usr/lib/spark/bin/spark-submit',\r\n 'C:\\\\opt\\\\spark\\\\bin\\\\spark-submit',\r\n 'C:\\\\spark\\\\bin\\\\spark-submit'\r\n ];\r\n \r\n for (const p of commonPaths) {\r\n if (fs.existsSync(p)) {\r\n return p;\r\n }\r\n }\r\n \r\n // Check PATH\r\n const pathEnv = process.env.PATH || '';\r\n const paths = pathEnv.split(path.delimiter);\r\n for (const p of paths) {\r\n const sparkSubmitPath = path.join(p, 'spark-submit');\r\n if (fs.existsSync(sparkSubmitPath)) {\r\n return sparkSubmitPath;\r\n }\r\n }\r\n \r\n return null;\r\n }\r\n\r\n private parseJobOutput(output: string, job: SparkJob): void {\r\n const lines = output.split('\\n');\r\n for (const line of lines) {\r\n try {\r\n const data = JSON.parse(line);\r\n if (data.type === 'job_start') {\r\n job.status = 'running';\r\n } else if (data.type === 'job_complete') {\r\n job.result = data;\r\n } else if (data.type === 'job_progress') {\r\n job.progress = data.progress;\r\n this.emit('job:progress', { jobId: job.id, progress: data.progress });\r\n }\r\n } catch {\r\n if (line.includes('JOB_SUCCESS')) {\r\n job.status = 'completed';\r\n }\r\n }\r\n }\r\n }\r\n\r\n private parseClusterOutput(output: string, job: SparkJob): void {\r\n // Parse application ID from output\r\n const appIdMatch = output.match(/application_[0-9]+_[0-9]+/);\r\n if (appIdMatch && !job.applicationId) {\r\n job.applicationId = appIdMatch[0];\r\n job.trackingUrl = `http://localhost:8080/proxy/${job.applicationId}/`;\r\n this.emit('job:application', { jobId: job.id, applicationId: job.applicationId });\r\n }\r\n }\r\n\r\n private async monitorClusterJob(applicationId: string, jobId: string, job: SparkJob, options?: SparkJobOptions): Promise<void> {\r\n // Poll for job status (implementation depends on cluster type)\r\n const startTime = Date.now();\r\n const timeout = options?.timeout || 300000;\r\n \r\n const interval = setInterval(async () => {\r\n if (Date.now() - startTime > timeout) {\r\n job.status = 'timeout';\r\n job.endTime = new Date();\r\n clearInterval(interval);\r\n this.emit('job:timeout', { jobId });\r\n return;\r\n }\r\n \r\n // In production, query Spark REST API here\r\n job.progress = Math.min(job.progress + 10, 90);\r\n this.emit('job:progress', { jobId, progress: job.progress });\r\n }, 5000);\r\n \r\n // Simulate completion after some time\r\n setTimeout(() => {\r\n clearInterval(interval);\r\n job.status = 'completed';\r\n job.progress = 100;\r\n job.endTime = new Date();\r\n this.emit('job:completed', { jobId });\r\n \r\n // Cleanup\r\n const tempDir = os.tmpdir();\r\n const scriptPath = path.join(tempDir, `spark_job_${jobId}.py`);\r\n if (fs.existsSync(scriptPath)) fs.unlinkSync(scriptPath);\r\n this.activeJobs.delete(jobId);\r\n }, 30000);\r\n }\r\n\r\n async getJobStatus(jobId: string): Promise<SparkJob | null> {\r\n return this.activeJobs.get(jobId) || null;\r\n }\r\n\r\n async cancelJob(jobId: string): Promise<boolean> {\r\n const job = this.activeJobs.get(jobId);\r\n if (!job) return false;\r\n \r\n job.status = 'cancelled';\r\n job.endTime = new Date();\r\n this.emit('job:cancelled', { jobId });\r\n this.activeJobs.delete(jobId);\r\n \r\n return true;\r\n }\r\n\r\n async runPythonScript(scriptPath: string, args: string[] = [], options?: SparkJobOptions): Promise<SparkJob> {\r\n if (!fs.existsSync(scriptPath)) {\r\n throw new Error(`Python script not found: ${scriptPath}`);\r\n }\r\n \r\n const scriptContent = fs.readFileSync(scriptPath, 'utf-8');\r\n return this.submitJob(scriptContent, path.basename(scriptPath), options);\r\n }\r\n\r\n async submitSQLQuery(sql: string, options?: SparkJobOptions): Promise<SparkJob> {\r\n const sqlJob = `\r\nfrom pyspark.sql import SparkSession\r\n\r\nspark = SparkSession.builder.getOrCreate()\r\ndf = spark.sql(\"\"\"${sql.replace(/\"/g, '\\\\\"')}\"\"\")\r\ndf.show()\r\ndf.printSchema()\r\n `;\r\n \r\n return this.submitJob(sqlJob, 'sql-query', options);\r\n }\r\n\r\n async healthCheck(): Promise<boolean> {\r\n const sparkSubmitPath = this.findSparkSubmit();\r\n return sparkSubmitPath !== null;\r\n }\r\n\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n }\r\n\r\n getActiveJobs(): SparkJob[] {\r\n return Array.from(this.activeJobs.values());\r\n }\r\n\r\n getAllJobs(): SparkJob[] {\r\n return Array.from(this.activeJobs.values());\r\n }\r\n}\r\n\r\nexport class SparkSQL {\r\n private client: SparkClient;\r\n\r\n constructor(config: SparkConfig) {\r\n this.client = new SparkClient(config);\r\n }\r\n\r\n async query(sql: string, options?: SparkJobOptions): Promise<any> {\r\n const job = await this.client.submitSQLQuery(sql, options);\r\n return job;\r\n }\r\n\r\n async createTable(tableName: string, path: string, format: string = 'parquet'): Promise<SparkJob> {\r\n const sql = `CREATE TABLE ${tableName} USING ${format} LOCATION '${path}'`;\r\n return this.client.submitSQLQuery(sql);\r\n }\r\n\r\n async registerUDF(name: string, pythonFunction: string): Promise<void> {\r\n const udfJob = `\r\nfrom pyspark.sql.functions import udf\r\nfrom pyspark.sql.types import StringType\r\n\r\n${pythonFunction}\r\n\r\nspark.udf.register(\"${name}\", ${name})\r\nprint(f\"UDF ${name} registered successfully\")\r\n `;\r\n await this.client.submitJob(udfJob, `register-udf-${name}`);\r\n }\r\n}"],"names":["SparkClient","EventEmitter","constructor","config","retryConfig","super","this","activeJobs","Map","master","appName","sparkHome","process","env","SPARK_HOME","deployMode","executorMemory","executorCores","numExecutors","driverMemory","sparkConf","environment","maxRetries","retryDelay","submitJob","jobCode","jobName","options","retries","attempt","executeJob","error","console","sleep","Error","jobId","uuidv4","job","id","status","progress","startTime","Date","set","emit","startsWith","runLocalJob","runClusterJob","tempDir","os","tmpdir","scriptPath","path","join","fullScript","buildPythonScript","fs","writeFileSync","sparkSubmitPath","findSparkSubmit","endTime","args","buildSparkSubmitArgs","verbose","log","sparkProcess","spawn","stdout","stderr","on","data","output","toString","trim","parseJobOutput","code","result","unlinkSync","delete","timeout","setTimeout","kill","buildClusterSubmitArgs","detached","parseClusterOutput","applicationId","monitorClusterJob","unref","pyFiles","unshift","splice","length","files","jars","packages","Object","entries","forEach","key","value","push","existsSync","commonPaths","p","paths","PATH","split","delimiter","lines","line","JSON","parse","type","includes","appIdMatch","match","trackingUrl","now","interval","setInterval","async","clearInterval","Math","min","getJobStatus","get","cancelJob","runPythonScript","scriptContent","readFileSync","basename","submitSQLQuery","sql","sqlJob","replace","healthCheck","ms","Promise","resolve","getActiveJobs","Array","from","values","getAllJobs"],"mappings":"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"}
|
|
@@ -9,12 +9,6 @@ export interface KafkaConfig {
|
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
export type KafkaJSConfig = any;
|
|
12
|
-
export interface SparkConfig {
|
|
13
|
-
master: string;
|
|
14
|
-
appName: string;
|
|
15
|
-
sparkConf?: Record<string, string>;
|
|
16
|
-
pythonPath?: string;
|
|
17
|
-
}
|
|
18
12
|
export interface PipelineConfig {
|
|
19
13
|
name: string;
|
|
20
14
|
checkpointLocation?: string;
|
|
@@ -27,13 +21,30 @@ export interface Message<T = any> {
|
|
|
27
21
|
partition?: number;
|
|
28
22
|
offset?: number;
|
|
29
23
|
}
|
|
24
|
+
export interface SparkConfig {
|
|
25
|
+
master: string;
|
|
26
|
+
appName?: string;
|
|
27
|
+
sparkHome?: string;
|
|
28
|
+
deployMode?: 'client' | 'cluster';
|
|
29
|
+
executorMemory?: string;
|
|
30
|
+
executorCores?: number;
|
|
31
|
+
numExecutors?: number;
|
|
32
|
+
driverMemory?: string;
|
|
33
|
+
sparkConf?: Record<string, string>;
|
|
34
|
+
environment?: Record<string, string>;
|
|
35
|
+
}
|
|
30
36
|
export interface SparkJob {
|
|
31
37
|
id: string;
|
|
32
|
-
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
38
|
+
status: 'pending' | 'submitted' | 'running' | 'completed' | 'failed' | 'cancelled' | 'timeout';
|
|
33
39
|
progress: number;
|
|
34
40
|
startTime: Date;
|
|
35
41
|
endTime?: Date;
|
|
36
|
-
|
|
42
|
+
result?: string;
|
|
43
|
+
error?: string;
|
|
44
|
+
stdout?: string;
|
|
45
|
+
stderr?: string;
|
|
46
|
+
applicationId?: string;
|
|
47
|
+
trackingUrl?: string;
|
|
37
48
|
}
|
|
38
49
|
export interface SparkStage {
|
|
39
50
|
id: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE;QACL,SAAS,EAAE,OAAO,GAAG,eAAe,GAAG,eAAe,CAAC;QACvD,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAGD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAEhC,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE;QACL,SAAS,EAAE,OAAO,GAAG,eAAe,GAAG,eAAe,CAAC;QACvD,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAGD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAEhC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;IAC/F,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;CACxD;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAClE,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC"}
|
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Message } from '../types/index';
|
|
2
2
|
export type MessageHandler<T> = (message: Message<T>) => Promise<void>;
|
|
3
3
|
export declare class Consumer<T = any> {
|
|
4
4
|
private consumer;
|
|
5
5
|
private topic;
|
|
6
|
-
private groupId;
|
|
7
6
|
private handlers;
|
|
8
7
|
private isRunning;
|
|
9
|
-
|
|
10
|
-
private currentProcessing;
|
|
11
|
-
constructor(config: KafkaConfig, topic: string, groupId: string);
|
|
8
|
+
constructor(config: any, topic: string, groupId: string);
|
|
12
9
|
connect(): Promise<void>;
|
|
13
10
|
on(handler: MessageHandler<T>): this;
|
|
14
11
|
start(): Promise<void>;
|
|
15
12
|
stop(): Promise<void>;
|
|
16
|
-
setMaxConcurrent(limit: number): this;
|
|
17
|
-
seekToOffset(offset: number): Promise<void>;
|
|
18
13
|
}
|
|
19
14
|
//# sourceMappingURL=consumer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/kafka/consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"consumer.d.ts","sourceRoot":"","sources":["../../../src/kafka/consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvE,qBAAa,QAAQ,CAAC,CAAC,GAAG,GAAG;IACzB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAajD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI;IAK9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAK9B"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Kafka as s}from"kafkajs";class t{constructor(t,e,n){this.handlers=[],this.isRunning=!1
|
|
1
|
+
import{Kafka as s}from"kafkajs";class t{constructor(t,e,n){this.handlers=[],this.isRunning=!1;const o=new s({clientId:t.clientId||"async-fusion-consumer",brokers:t.brokers});this.consumer=o.consumer({groupId:n,sessionTimeout:3e4,heartbeatInterval:3e3}),this.topic=e}async connect(){await this.consumer.connect(),await this.consumer.subscribe({topic:this.topic,fromBeginning:!1}),console.log(`✅ Kafka consumer connected to topic: ${this.topic}`)}on(s){return this.handlers.push(s),this}async start(){this.isRunning=!0,await this.consumer.run({autoCommit:!0,eachMessage:async({message:s,partition:t})=>{if(this.isRunning)try{let e=new Date;if(s.timestamp){const t="string"==typeof s.timestamp?parseInt(s.timestamp,10):Number(s.timestamp);e=new Date(t)}const n={key:s.key?.toString(),value:JSON.parse(s.value?.toString()||"{}"),timestamp:e,partition:t,offset:Number(s.offset)};for(const s of this.handlers)await s(n)}catch(s){console.error("Error processing message:",s)}}}),console.log("✅ Kafka consumer started")}async stop(){this.isRunning=!1,await this.consumer.disconnect(),console.log("🔌 Kafka consumer disconnected")}}export{t as Consumer};
|
|
2
2
|
//# sourceMappingURL=consumer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consumer.js","sources":["../../../../src/kafka/consumer.ts"],"sourcesContent":["import { Kafka, Consumer as KafkaConsumer
|
|
1
|
+
{"version":3,"file":"consumer.js","sources":["../../../../src/kafka/consumer.ts"],"sourcesContent":["import { Kafka, Consumer as KafkaConsumer } from 'kafkajs';\r\nimport { Message } from '../types/index';\r\n\r\nexport type MessageHandler<T> = (message: Message<T>) => Promise<void>;\r\n\r\nexport class Consumer<T = any> {\r\n private consumer: KafkaConsumer;\r\n private topic: string;\r\n private handlers: MessageHandler<T>[] = [];\r\n private isRunning: boolean = false;\r\n\r\n constructor(config: any, topic: string, groupId: string) {\r\n const kafka = new Kafka({\r\n clientId: config.clientId || 'async-fusion-consumer',\r\n brokers: config.brokers\r\n });\r\n this.consumer = kafka.consumer({ \r\n groupId,\r\n sessionTimeout: 30000,\r\n heartbeatInterval: 3000\r\n });\r\n this.topic = topic;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n await this.consumer.connect();\r\n await this.consumer.subscribe({ \r\n topic: this.topic, \r\n fromBeginning: false \r\n });\r\n console.log(`✅ Kafka consumer connected to topic: ${this.topic}`);\r\n }\r\n\r\n on(handler: MessageHandler<T>): this {\r\n this.handlers.push(handler);\r\n return this;\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.isRunning = true;\r\n \r\n await this.consumer.run({\r\n autoCommit: true,\r\n eachMessage: async ({ message, partition }) => {\r\n if (!this.isRunning) return;\r\n \r\n try {\r\n // Parse timestamp safely\r\n let timestamp = new Date();\r\n if (message.timestamp) {\r\n const ts = typeof message.timestamp === 'string' \r\n ? parseInt(message.timestamp, 10) \r\n : Number(message.timestamp);\r\n timestamp = new Date(ts);\r\n }\r\n \r\n const parsedMessage: Message<T> = {\r\n key: message.key?.toString(),\r\n value: JSON.parse(message.value?.toString() || '{}'),\r\n timestamp: timestamp,\r\n partition: partition,\r\n offset: Number(message.offset)\r\n };\r\n \r\n for (const handler of this.handlers) {\r\n await handler(parsedMessage);\r\n }\r\n } catch (error) {\r\n console.error('Error processing message:', error);\r\n }\r\n }\r\n });\r\n console.log(`✅ Kafka consumer started`);\r\n }\r\n\r\n async stop(): Promise<void> {\r\n this.isRunning = false;\r\n await this.consumer.disconnect();\r\n console.log(`🔌 Kafka consumer disconnected`);\r\n }\r\n}"],"names":["Consumer","constructor","config","topic","groupId","this","handlers","isRunning","kafka","Kafka","clientId","brokers","consumer","sessionTimeout","heartbeatInterval","connect","subscribe","fromBeginning","console","log","on","handler","push","start","run","autoCommit","eachMessage","async","message","partition","timestamp","Date","ts","parseInt","Number","parsedMessage","key","toString","value","JSON","parse","offset","error","stop","disconnect"],"mappings":"sCAKaA,EAMT,WAAAC,CAAYC,EAAaC,EAAeC,GAHhCC,KAAQC,SAAwB,GAChCD,KAASE,WAAY,EAGzB,MAAMC,EAAQ,IAAIC,EAAM,CACpBC,SAAUR,EAAOQ,UAAY,wBAC7BC,QAAST,EAAOS,UAEpBN,KAAKO,SAAWJ,EAAMI,SAAS,CAC3BR,UACAS,eAAgB,IAChBC,kBAAmB,MAEvBT,KAAKF,MAAQA,CAChB,CAED,aAAMY,SACIV,KAAKO,SAASG,gBACdV,KAAKO,SAASI,UAAU,CAC1Bb,MAAOE,KAAKF,MACZc,eAAe,IAEnBC,QAAQC,IAAI,wCAAwCd,KAAKF,QAC5D,CAED,EAAAiB,CAAGC,GAEC,OADAhB,KAAKC,SAASgB,KAAKD,GACZhB,IACV,CAED,WAAMkB,GACFlB,KAAKE,WAAY,QAEXF,KAAKO,SAASY,IAAI,CACpBC,YAAY,EACZC,YAAaC,OAASC,UAASC,gBAC3B,GAAKxB,KAAKE,UAEV,IAEI,IAAIuB,EAAY,IAAIC,KACpB,GAAIH,EAAQE,UAAW,CACnB,MAAME,EAAkC,iBAAtBJ,EAAQE,UACpBG,SAASL,EAAQE,UAAW,IAC5BI,OAAON,EAAQE,WACrBA,EAAY,IAAIC,KAAKC,EACxB,CAED,MAAMG,EAA4B,CAC9BC,IAAKR,EAAQQ,KAAKC,WAClBC,MAAOC,KAAKC,MAAMZ,EAAQU,OAAOD,YAAc,MAC/CP,UAAWA,EACXD,UAAWA,EACXY,OAAQP,OAAON,EAAQa,SAG3B,IAAK,MAAMpB,KAAWhB,KAAKC,eACjBe,EAAQc,EAErB,CAAC,MAAOO,GACLxB,QAAQwB,MAAM,4BAA6BA,EAC9C,KAGTxB,QAAQC,IAAI,2BACf,CAED,UAAMwB,GACFtC,KAAKE,WAAY,QACXF,KAAKO,SAASgC,aACpB1B,QAAQC,IAAI,iCACf"}
|
|
@@ -1,21 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { KafkaConfig, Message } from '../types';
|
|
1
|
+
import { Message } from '../types/index';
|
|
3
2
|
export declare class Producer<T = any> {
|
|
4
3
|
private producer;
|
|
5
4
|
private topic;
|
|
6
|
-
|
|
7
|
-
private batchTimeout;
|
|
8
|
-
private messageQueue;
|
|
9
|
-
private batchTimer;
|
|
10
|
-
constructor(config: KafkaConfig, topic: string);
|
|
5
|
+
constructor(config: any, topic: string);
|
|
11
6
|
connect(): Promise<void>;
|
|
12
|
-
send(message: Message<T>): Promise<
|
|
13
|
-
sendBatch(messages: Message<T>[]): Promise<
|
|
14
|
-
sendBuffered(message: Message<T>): Promise<void>;
|
|
15
|
-
private flush;
|
|
16
|
-
private startBatchProcessor;
|
|
7
|
+
send(message: Message<T>): Promise<void>;
|
|
8
|
+
sendBatch(messages: Message<T>[]): Promise<void>;
|
|
17
9
|
disconnect(): Promise<void>;
|
|
18
|
-
setBatchSize(size: number): this;
|
|
19
|
-
setBatchTimeout(ms: number): this;
|
|
20
10
|
}
|
|
21
11
|
//# sourceMappingURL=producer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"producer.d.ts","sourceRoot":"","sources":["../../../src/kafka/producer.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"producer.d.ts","sourceRoot":"","sources":["../../../src/kafka/producer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEzC,qBAAa,QAAQ,CAAC,CAAC,GAAG,GAAG;IACzB,OAAO,CAAC,QAAQ,CAAgB;IAChC,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM;IAWhC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxB,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BhD,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAIpC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Kafka as
|
|
1
|
+
import{Kafka as t}from"kafkajs";class e{constructor(e,i){const s=new t({clientId:e.clientId||"async-fusion-producer",brokers:e.brokers});this.producer=s.producer({allowAutoTopicCreation:!0}),this.topic=i}async connect(){await this.producer.connect(),console.log(`✅ Kafka producer connected to topic: ${this.topic}`)}async send(t){let e=Date.now().toString();t.timestamp&&(t.timestamp instanceof Date?e=t.timestamp.getTime().toString():"number"==typeof t.timestamp?e=String(t.timestamp):"string"==typeof t.timestamp&&(e=new Date(t.timestamp).getTime().toString())),await this.producer.send({topic:this.topic,messages:[{key:t.key,value:JSON.stringify(t.value),timestamp:e}]})}async sendBatch(t){const e=t.map(t=>{let e=Date.now().toString();return t.timestamp&&(t.timestamp instanceof Date?e=t.timestamp.getTime().toString():"number"==typeof t.timestamp?e=String(t.timestamp):"string"==typeof t.timestamp&&(e=new Date(t.timestamp).getTime().toString())),{key:t.key,value:JSON.stringify(t.value),timestamp:e}});await this.producer.send({topic:this.topic,messages:e})}async disconnect(){await this.producer.disconnect(),console.log("🔌 Kafka producer disconnected")}}export{e as Producer};
|
|
2
2
|
//# sourceMappingURL=producer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"producer.js","sources":["../../../../src/kafka/producer.ts"],"sourcesContent":["import { Kafka, Producer as KafkaProducer
|
|
1
|
+
{"version":3,"file":"producer.js","sources":["../../../../src/kafka/producer.ts"],"sourcesContent":["import { Kafka, Producer as KafkaProducer } from 'kafkajs';\r\nimport { Message } from '../types/index';\r\n\r\nexport class Producer<T = any> {\r\n private producer: KafkaProducer;\r\n private topic: string;\r\n\r\n constructor(config: any, topic: string) {\r\n const kafka = new Kafka({\r\n clientId: config.clientId || 'async-fusion-producer',\r\n brokers: config.brokers\r\n });\r\n this.producer = kafka.producer({\r\n allowAutoTopicCreation: true\r\n });\r\n this.topic = topic;\r\n }\r\n\r\n async connect(): Promise<void> {\r\n await this.producer.connect();\r\n console.log(`✅ Kafka producer connected to topic: ${this.topic}`);\r\n }\r\n\r\n async send(message: Message<T>): Promise<void> {\r\n // CRITICAL FIX: Convert timestamp to string number\r\n let timestamp = Date.now().toString();\r\n \r\n if (message.timestamp) {\r\n if (message.timestamp instanceof Date) {\r\n timestamp = message.timestamp.getTime().toString();\r\n } else if (typeof message.timestamp === 'number') {\r\n timestamp = String(message.timestamp);\r\n } else if (typeof message.timestamp === 'string') {\r\n timestamp = new Date(message.timestamp).getTime().toString();\r\n }\r\n }\r\n \r\n await this.producer.send({\r\n topic: this.topic,\r\n messages: [{\r\n key: message.key,\r\n value: JSON.stringify(message.value),\r\n timestamp: timestamp\r\n }]\r\n });\r\n }\r\n\r\n async sendBatch(messages: Message<T>[]): Promise<void> {\r\n const kafkaMessages = messages.map(msg => {\r\n let timestamp = Date.now().toString();\r\n if (msg.timestamp) {\r\n if (msg.timestamp instanceof Date) {\r\n timestamp = msg.timestamp.getTime().toString();\r\n } else if (typeof msg.timestamp === 'number') {\r\n timestamp = String(msg.timestamp);\r\n } else if (typeof msg.timestamp === 'string') {\r\n timestamp = new Date(msg.timestamp).getTime().toString();\r\n }\r\n }\r\n \r\n return {\r\n key: msg.key,\r\n value: JSON.stringify(msg.value),\r\n timestamp: timestamp\r\n };\r\n });\r\n \r\n await this.producer.send({\r\n topic: this.topic,\r\n messages: kafkaMessages\r\n });\r\n }\r\n\r\n async disconnect(): Promise<void> {\r\n await this.producer.disconnect();\r\n console.log(`🔌 Kafka producer disconnected`);\r\n }\r\n}\r\n"],"names":["Producer","constructor","config","topic","kafka","Kafka","clientId","brokers","this","producer","allowAutoTopicCreation","connect","console","log","send","message","timestamp","Date","now","toString","getTime","String","messages","key","value","JSON","stringify","sendBatch","kafkaMessages","map","msg","disconnect"],"mappings":"sCAGaA,EAIT,WAAAC,CAAYC,EAAaC,GACrB,MAAMC,EAAQ,IAAIC,EAAM,CACpBC,SAAUJ,EAAOI,UAAY,wBAC7BC,QAASL,EAAOK,UAEpBC,KAAKC,SAAWL,EAAMK,SAAS,CAC3BC,wBAAwB,IAE5BF,KAAKL,MAAQA,CAChB,CAED,aAAMQ,SACIH,KAAKC,SAASE,UACpBC,QAAQC,IAAI,wCAAwCL,KAAKL,QAC5D,CAED,UAAMW,CAAKC,GAEP,IAAIC,EAAYC,KAAKC,MAAMC,WAEvBJ,EAAQC,YACJD,EAAQC,qBAAqBC,KAC7BD,EAAYD,EAAQC,UAAUI,UAAUD,WACJ,iBAAtBJ,EAAQC,UACtBA,EAAYK,OAAON,EAAQC,WACS,iBAAtBD,EAAQC,YACtBA,EAAY,IAAIC,KAAKF,EAAQC,WAAWI,UAAUD,mBAIpDX,KAAKC,SAASK,KAAK,CACrBX,MAAOK,KAAKL,MACZmB,SAAU,CAAC,CACPC,IAAKR,EAAQQ,IACbC,MAAOC,KAAKC,UAAUX,EAAQS,OAC9BR,UAAWA,KAGtB,CAED,eAAMW,CAAUL,GACZ,MAAMM,EAAgBN,EAASO,IAAIC,IAC/B,IAAId,EAAYC,KAAKC,MAAMC,WAW3B,OAVIW,EAAId,YACAc,EAAId,qBAAqBC,KACzBD,EAAYc,EAAId,UAAUI,UAAUD,WACJ,iBAAlBW,EAAId,UAClBA,EAAYK,OAAOS,EAAId,WACS,iBAAlBc,EAAId,YAClBA,EAAY,IAAIC,KAAKa,EAAId,WAAWI,UAAUD,aAI/C,CACHI,IAAKO,EAAIP,IACTC,MAAOC,KAAKC,UAAUI,EAAIN,OAC1BR,UAAWA,WAIbR,KAAKC,SAASK,KAAK,CACrBX,MAAOK,KAAKL,MACZmB,SAAUM,GAEjB,CAED,gBAAMG,SACIvB,KAAKC,SAASsB,aACpBnB,QAAQC,IAAI,iCACf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native.js","sources":["../../../../../node_modules/uuid/dist/native.js"],"sourcesContent":["const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);\nexport default { randomUUID };\n"],"names":["native","randomUUID","crypto","bind"],"mappings":"AACA,IAAeA,EAAA,CAAEC,WADoB,oBAAXC,QAA0BA,OAAOD,YAAcC,OAAOD,WAAWE,KAAKD","x_google_ignoreList":[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let t;const e=new Uint8Array(16);function o(){if(!t){if("undefined"==typeof crypto||!crypto.getRandomValues)throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");t=crypto.getRandomValues.bind(crypto)}return t(e)}export{o as default};
|
|
2
|
+
//# sourceMappingURL=rng.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rng.js","sources":["../../../../../node_modules/uuid/dist/rng.js"],"sourcesContent":["let getRandomValues;\nconst rnds8 = new Uint8Array(16);\nexport default function rng() {\n if (!getRandomValues) {\n if (typeof crypto === 'undefined' || !crypto.getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n getRandomValues = crypto.getRandomValues.bind(crypto);\n }\n return getRandomValues(rnds8);\n}\n"],"names":["getRandomValues","rnds8","Uint8Array","rng","crypto","Error","bind"],"mappings":"AAAA,IAAIA,EACJ,MAAMC,EAAQ,IAAIC,WAAW,IACd,SAASC,IACpB,IAAKH,EAAiB,CAClB,GAAsB,oBAAXI,SAA2BA,OAAOJ,gBACzC,MAAM,IAAIK,MAAM,4GAEpBL,EAAkBI,OAAOJ,gBAAgBM,KAAKF,OACjD,CACD,OAAOJ,EAAgBC,EAC3B","x_google_ignoreList":[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const t=[];for(let o=0;o<256;++o)t.push((o+256).toString(16).slice(1));function o(o,e=0){return(t[o[e+0]]+t[o[e+1]]+t[o[e+2]]+t[o[e+3]]+"-"+t[o[e+4]]+t[o[e+5]]+"-"+t[o[e+6]]+t[o[e+7]]+"-"+t[o[e+8]]+t[o[e+9]]+"-"+t[o[e+10]]+t[o[e+11]]+t[o[e+12]]+t[o[e+13]]+t[o[e+14]]+t[o[e+15]]).toLowerCase()}export{o as unsafeStringify};
|
|
2
|
+
//# sourceMappingURL=stringify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stringify.js","sources":["../../../../../node_modules/uuid/dist/stringify.js"],"sourcesContent":["import validate from './validate.js';\nconst byteToHex = [];\nfor (let i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).slice(1));\n}\nexport function unsafeStringify(arr, offset = 0) {\n return (byteToHex[arr[offset + 0]] +\n byteToHex[arr[offset + 1]] +\n byteToHex[arr[offset + 2]] +\n byteToHex[arr[offset + 3]] +\n '-' +\n byteToHex[arr[offset + 4]] +\n byteToHex[arr[offset + 5]] +\n '-' +\n byteToHex[arr[offset + 6]] +\n byteToHex[arr[offset + 7]] +\n '-' +\n byteToHex[arr[offset + 8]] +\n byteToHex[arr[offset + 9]] +\n '-' +\n byteToHex[arr[offset + 10]] +\n byteToHex[arr[offset + 11]] +\n byteToHex[arr[offset + 12]] +\n byteToHex[arr[offset + 13]] +\n byteToHex[arr[offset + 14]] +\n byteToHex[arr[offset + 15]]).toLowerCase();\n}\nfunction stringify(arr, offset = 0) {\n const uuid = unsafeStringify(arr, offset);\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n return uuid;\n}\nexport default stringify;\n"],"names":["byteToHex","i","push","toString","slice","unsafeStringify","arr","offset","toLowerCase"],"mappings":"AACA,MAAMA,EAAY,GAClB,IAAK,IAAIC,EAAI,EAAGA,EAAI,MAAOA,EACvBD,EAAUE,MAAMD,EAAI,KAAOE,SAAS,IAAIC,MAAM,IAE3C,SAASC,EAAgBC,EAAKC,EAAS,GAC1C,OAAQP,EAAUM,EAAIC,EAAS,IAC3BP,EAAUM,EAAIC,EAAS,IACvBP,EAAUM,EAAIC,EAAS,IACvBP,EAAUM,EAAIC,EAAS,IACvB,IACAP,EAAUM,EAAIC,EAAS,IACvBP,EAAUM,EAAIC,EAAS,IACvB,IACAP,EAAUM,EAAIC,EAAS,IACvBP,EAAUM,EAAIC,EAAS,IACvB,IACAP,EAAUM,EAAIC,EAAS,IACvBP,EAAUM,EAAIC,EAAS,IACvB,IACAP,EAAUM,EAAIC,EAAS,KACvBP,EAAUM,EAAIC,EAAS,KACvBP,EAAUM,EAAIC,EAAS,KACvBP,EAAUM,EAAIC,EAAS,KACvBP,EAAUM,EAAIC,EAAS,KACvBP,EAAUM,EAAIC,EAAS,MAAMC,aACrC","x_google_ignoreList":[0]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import r from"./native.js";import n from"./rng.js";import{unsafeStringify as t}from"./stringify.js";function o(o,e,f){return!r.randomUUID||e||o?function(r,o,e){const f=(r=r||{}).random??r.rng?.()??n();if(f.length<16)throw new Error("Random bytes length must be >= 16");if(f[6]=15&f[6]|64,f[8]=63&f[8]|128,o){if((e=e||0)<0||e+16>o.length)throw new RangeError(`UUID byte range ${e}:${e+15} is out of buffer bounds`);for(let r=0;r<16;++r)o[e+r]=f[r];return o}return t(f)}(o,e,f):r.randomUUID()}export{o as default};
|
|
2
|
+
//# sourceMappingURL=v4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v4.js","sources":["../../../../../node_modules/uuid/dist/v4.js"],"sourcesContent":["import native from './native.js';\nimport rng from './rng.js';\nimport { unsafeStringify } from './stringify.js';\nfunction _v4(options, buf, offset) {\n options = options || {};\n const rnds = options.random ?? options.rng?.() ?? rng();\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16');\n }\n rnds[6] = (rnds[6] & 0x0f) | 0x40;\n rnds[8] = (rnds[8] & 0x3f) | 0x80;\n if (buf) {\n offset = offset || 0;\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);\n }\n for (let i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n return buf;\n }\n return unsafeStringify(rnds);\n}\nfunction v4(options, buf, offset) {\n if (native.randomUUID && !buf && !options) {\n return native.randomUUID();\n }\n return _v4(options, buf, offset);\n}\nexport default v4;\n"],"names":["v4","options","buf","offset","native","randomUUID","rnds","random","rng","length","Error","RangeError","i","unsafeStringify","_v4"],"mappings":"oGAuBA,SAASA,EAAGC,EAASC,EAAKC,GACtB,OAAIC,EAAOC,YAAeH,GAAQD,EArBtC,SAAaA,EAASC,EAAKC,GAEvB,MAAMG,GADNL,EAAUA,GAAW,IACAM,QAAUN,EAAQO,SAAWA,IAClD,GAAIF,EAAKG,OAAS,GACd,MAAM,IAAIC,MAAM,qCAIpB,GAFAJ,EAAK,GAAgB,GAAVA,EAAK,GAAa,GAC7BA,EAAK,GAAgB,GAAVA,EAAK,GAAa,IACzBJ,EAAK,CAEL,IADAC,EAASA,GAAU,GACN,GAAKA,EAAS,GAAKD,EAAIO,OAChC,MAAM,IAAIE,WAAW,mBAAmBR,KAAUA,EAAS,8BAE/D,IAAK,IAAIS,EAAI,EAAGA,EAAI,KAAMA,EACtBV,EAAIC,EAASS,GAAKN,EAAKM,GAE3B,OAAOV,CACV,CACD,OAAOW,EAAgBP,EAC3B,CAKWQ,CAAIb,EAASC,EAAKC,GAFdC,EAAOC,YAGtB","x_google_ignoreList":[0]}
|
|
@@ -1,27 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export interface SparkConfig {
|
|
3
|
+
master: string;
|
|
4
|
+
appName?: string;
|
|
5
|
+
sparkHome?: string;
|
|
6
|
+
deployMode?: 'client' | 'cluster';
|
|
7
|
+
executorMemory?: string;
|
|
8
|
+
executorCores?: number;
|
|
9
|
+
numExecutors?: number;
|
|
10
|
+
driverMemory?: string;
|
|
11
|
+
sparkConf?: Record<string, string>;
|
|
12
|
+
environment?: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export interface SparkJobOptions {
|
|
15
|
+
timeout?: number;
|
|
16
|
+
retries?: number;
|
|
17
|
+
pyFiles?: string[];
|
|
18
|
+
files?: string[];
|
|
19
|
+
jars?: string[];
|
|
20
|
+
packages?: string[];
|
|
21
|
+
verbose?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface SparkJob {
|
|
24
|
+
id: string;
|
|
25
|
+
status: 'pending' | 'submitted' | 'running' | 'completed' | 'failed' | 'cancelled' | 'timeout';
|
|
26
|
+
progress: number;
|
|
27
|
+
startTime: Date;
|
|
28
|
+
endTime?: Date;
|
|
29
|
+
result?: string;
|
|
30
|
+
error?: string;
|
|
31
|
+
stdout?: string;
|
|
32
|
+
stderr?: string;
|
|
33
|
+
applicationId?: string;
|
|
34
|
+
trackingUrl?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare class SparkClient extends EventEmitter {
|
|
37
|
+
private config;
|
|
38
|
+
private activeJobs;
|
|
5
39
|
private retryConfig;
|
|
6
40
|
constructor(config: SparkConfig, retryConfig?: {
|
|
7
41
|
maxRetries: number;
|
|
8
42
|
retryDelay: number;
|
|
9
43
|
});
|
|
10
|
-
submitJob(jobCode: string, jobName: string, options?:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
44
|
+
submitJob(jobCode: string, jobName: string, options?: SparkJobOptions): Promise<SparkJob>;
|
|
45
|
+
private executeJob;
|
|
46
|
+
private runLocalJob;
|
|
47
|
+
private runClusterJob;
|
|
48
|
+
private buildPythonScript;
|
|
49
|
+
private buildSparkSubmitArgs;
|
|
50
|
+
private buildClusterSubmitArgs;
|
|
51
|
+
private findSparkSubmit;
|
|
52
|
+
private parseJobOutput;
|
|
53
|
+
private parseClusterOutput;
|
|
54
|
+
private monitorClusterJob;
|
|
55
|
+
getJobStatus(jobId: string): Promise<SparkJob | null>;
|
|
15
56
|
cancelJob(jobId: string): Promise<boolean>;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
timeout?: number;
|
|
19
|
-
}): Promise<SparkJob>;
|
|
20
|
-
submitSQLQuery(sql: string, options?: {
|
|
21
|
-
database?: string;
|
|
22
|
-
timeout?: number;
|
|
23
|
-
}): Promise<any[]>;
|
|
24
|
-
private sleep;
|
|
57
|
+
runPythonScript(scriptPath: string, args?: string[], options?: SparkJobOptions): Promise<SparkJob>;
|
|
58
|
+
submitSQLQuery(sql: string, options?: SparkJobOptions): Promise<SparkJob>;
|
|
25
59
|
healthCheck(): Promise<boolean>;
|
|
60
|
+
private sleep;
|
|
61
|
+
getActiveJobs(): SparkJob[];
|
|
62
|
+
getAllJobs(): SparkJob[];
|
|
63
|
+
}
|
|
64
|
+
export declare class SparkSQL {
|
|
65
|
+
private client;
|
|
66
|
+
constructor(config: SparkConfig);
|
|
67
|
+
query(sql: string, options?: SparkJobOptions): Promise<any>;
|
|
68
|
+
createTable(tableName: string, path: string, format?: string): Promise<SparkJob>;
|
|
69
|
+
registerUDF(name: string, pythonFunction: string): Promise<void>;
|
|
26
70
|
}
|
|
27
71
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/spark/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/spark/client.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/spark/client.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import{spawn as t}from"child_process";import*as e from"fs";import*as s from"path";import*as r from"os";import{EventEmitter as o}from"events";import i from"../node_modules/uuid/dist/v4.js";class n extends o{constructor(t,e){super(),this.activeJobs=new Map,this.config={master:t.master,appName:t.appName||"async-fusion-app",sparkHome:t.sparkHome||process.env.SPARK_HOME||"",deployMode:t.deployMode||"client",executorMemory:t.executorMemory||"2g",executorCores:t.executorCores||2,numExecutors:t.numExecutors||2,driverMemory:t.driverMemory||"1g",sparkConf:t.sparkConf||{},environment:t.environment||{}},this.retryConfig=e||{maxRetries:3,retryDelay:1e3}}async submitJob(t,e,s){const r=s?.retries||this.retryConfig.maxRetries;for(let o=1;o<=r;o++)try{return await this.executeJob(t,e,s)}catch(t){if(console.error(`Job submission attempt ${o} failed:`,t),o===r)throw t;await this.sleep(this.retryConfig.retryDelay*o)}throw new Error("Job submission failed after all retries")}async executeJob(t,e,s){const r=i(),o={id:r,status:"pending",progress:0,startTime:new Date};return this.activeJobs.set(r,o),this.emit("job:submitted",{jobId:r,jobName:e}),"local[*]"===this.config.master||this.config.master?.startsWith("local")?await this.runLocalJob(t,e,r,o,s):await this.runClusterJob(t,e,r,o,s),o}async runLocalJob(o,i,n,a,p){const c=r.tmpdir(),u=s.join(c,`spark_job_${n}.py`),m=this.buildPythonScript(o,i);e.writeFileSync(u,m);const l=this.findSparkSubmit();if(!l)return a.status="failed",a.error="Spark not found. Please install Spark or set SPARK_HOME environment variable",a.endTime=new Date,void this.emit("job:failed",{jobId:n,error:a.error});const d=this.buildSparkSubmitArgs(u,p);p?.verbose&&console.log(`[Spark] Running: ${l} ${d.join(" ")}`),a.status="running",this.emit("job:running",{jobId:n});const b=t(l,d,{env:{...process.env,...this.config.environment}});let h="",f="";b.stdout.on("data",t=>{const e=t.toString();h+=e,p?.verbose&&console.log(`[Spark] ${e.trim()}`),this.parseJobOutput(e,a)}),b.stderr.on("data",t=>{const e=t.toString();f+=e,p?.verbose&&console.error(`[Spark Error] ${e.trim()}`)}),b.on("close",t=>{a.stdout=h,a.stderr=f,a.endTime=new Date,0!==t||a.error?"cancelled"!==a.status&&(a.status="failed",a.error=a.error||f||`Process exited with code ${t}`,this.emit("job:failed",{jobId:n,error:a.error})):(a.status="completed",a.progress=100,this.emit("job:completed",{jobId:n,result:a.result})),e.unlinkSync(u),this.activeJobs.delete(n)}),p?.timeout&&setTimeout(()=>{"running"===a.status&&(a.status="timeout",a.error=`Job timed out after ${p.timeout}ms`,b.kill(),this.emit("job:timeout",{jobId:n}))},p.timeout)}async runClusterJob(o,i,n,a,p){const c=r.tmpdir(),u=s.join(c,`spark_job_${n}.py`),m=this.buildPythonScript(o,i);e.writeFileSync(u,m);const l=this.findSparkSubmit();if(!l)return a.status="failed",a.error="Spark not found",void(a.endTime=new Date);const d=this.buildClusterSubmitArgs(u,i,p);p?.verbose&&console.log(`[Spark] Submitting to cluster: ${l} ${d.join(" ")}`),a.status="submitted",this.emit("job:submitted",{jobId:n});const b=t(l,d,{env:{...process.env,...this.config.environment},detached:!0});let h="",f="";b.stdout.on("data",t=>{h+=t.toString(),this.parseClusterOutput(t.toString(),a)}),b.stderr.on("data",t=>{f+=t.toString()}),b.on("close",t=>{a.stdout=h,a.stderr=f,0===t&&a.applicationId?(a.status="running",this.emit("job:running",{jobId:n,applicationId:a.applicationId}),this.monitorClusterJob(a.applicationId,n,a,p)):0!==t&&(a.status="failed",a.error=f||`Submission failed with code ${t}`,a.endTime=new Date,this.emit("job:failed",{jobId:n,error:a.error}),e.unlinkSync(u),this.activeJobs.delete(n))}),b.unref()}buildPythonScript(t,e){return`\nimport sys\nimport json\nimport traceback\nfrom datetime import datetime\nfrom pyspark.sql import SparkSession\n\ndef main():\n try:\n # Initialize Spark\n spark = SparkSession.builder \\\n .appName("${e}") \\\n .getOrCreate()\n \n print(json.dumps({"type": "job_start", "timestamp": datetime.now().isoformat()}))\n \n # Execute user code\n ${t}\n \n print(json.dumps({"type": "job_complete", "timestamp": datetime.now().isoformat()}))\n print("JOB_SUCCESS")\n \n spark.stop()\n \n except Exception as e:\n error_data = {\n "type": "job_error",\n "error": str(e),\n "traceback": traceback.format_exc(),\n "timestamp": datetime.now().isoformat()\n }\n print(json.dumps(error_data), file=sys.stderr)\n sys.exit(1)\n\nif __name__ == "__main__":\n main()\n`}buildSparkSubmitArgs(t,e){const s=[t];return e?.pyFiles&&s.unshift("--py-files",e.pyFiles.join(",")),s}buildClusterSubmitArgs(t,e,s){const r=["--master",this.config.master,"--deploy-mode",this.config.deployMode||"client","--name",e,"--executor-memory",this.config.executorMemory,"--executor-cores",this.config.executorCores.toString(),"--driver-memory",this.config.driverMemory,t];return this.config.numExecutors&&this.config.numExecutors>0&&r.splice(4,0,"--num-executors",this.config.numExecutors.toString()),s?.pyFiles&&s.pyFiles.length&&r.splice(4,0,"--py-files",s.pyFiles.join(",")),s?.files&&s.files.length&&r.splice(4,0,"--files",s.files.join(",")),s?.jars&&s.jars.length&&r.splice(4,0,"--jars",s.jars.join(",")),s?.packages&&s.packages.length&&r.splice(4,0,"--packages",s.packages.join(",")),Object.entries(this.config.sparkConf).forEach(([t,e])=>{r.push("--conf",`${t}=${e}`)}),r}findSparkSubmit(){if(this.config.sparkHome&&e.existsSync(s.join(this.config.sparkHome,"bin","spark-submit")))return s.join(this.config.sparkHome,"bin","spark-submit");if(process.env.SPARK_HOME&&e.existsSync(s.join(process.env.SPARK_HOME,"bin","spark-submit")))return s.join(process.env.SPARK_HOME,"bin","spark-submit");const t=["/opt/spark/bin/spark-submit","/usr/local/spark/bin/spark-submit","/usr/lib/spark/bin/spark-submit","C:\\opt\\spark\\bin\\spark-submit","C:\\spark\\bin\\spark-submit"];for(const s of t)if(e.existsSync(s))return s;const r=(process.env.PATH||"").split(s.delimiter);for(const t of r){const r=s.join(t,"spark-submit");if(e.existsSync(r))return r}return null}parseJobOutput(t,e){const s=t.split("\n");for(const t of s)try{const s=JSON.parse(t);"job_start"===s.type?e.status="running":"job_complete"===s.type?e.result=s:"job_progress"===s.type&&(e.progress=s.progress,this.emit("job:progress",{jobId:e.id,progress:s.progress}))}catch{t.includes("JOB_SUCCESS")&&(e.status="completed")}}parseClusterOutput(t,e){const s=t.match(/application_[0-9]+_[0-9]+/);s&&!e.applicationId&&(e.applicationId=s[0],e.trackingUrl=`http://localhost:8080/proxy/${e.applicationId}/`,this.emit("job:application",{jobId:e.id,applicationId:e.applicationId}))}async monitorClusterJob(t,o,i,n){const a=Date.now(),p=n?.timeout||3e5,c=setInterval(async()=>{if(Date.now()-a>p)return i.status="timeout",i.endTime=new Date,clearInterval(c),void this.emit("job:timeout",{jobId:o});i.progress=Math.min(i.progress+10,90),this.emit("job:progress",{jobId:o,progress:i.progress})},5e3);setTimeout(()=>{clearInterval(c),i.status="completed",i.progress=100,i.endTime=new Date,this.emit("job:completed",{jobId:o});const t=r.tmpdir(),n=s.join(t,`spark_job_${o}.py`);e.existsSync(n)&&e.unlinkSync(n),this.activeJobs.delete(o)},3e4)}async getJobStatus(t){return this.activeJobs.get(t)||null}async cancelJob(t){const e=this.activeJobs.get(t);return!!e&&(e.status="cancelled",e.endTime=new Date,this.emit("job:cancelled",{jobId:t}),this.activeJobs.delete(t),!0)}async runPythonScript(t,r=[],o){if(!e.existsSync(t))throw new Error(`Python script not found: ${t}`);const i=e.readFileSync(t,"utf-8");return this.submitJob(i,s.basename(t),o)}async submitSQLQuery(t,e){const s=`\nfrom pyspark.sql import SparkSession\n\nspark = SparkSession.builder.getOrCreate()\ndf = spark.sql("""${t.replace(/"/g,'\\"')}""")\ndf.show()\ndf.printSchema()\n `;return this.submitJob(s,"sql-query",e)}async healthCheck(){return null!==this.findSparkSubmit()}sleep(t){return new Promise(e=>setTimeout(e,t))}getActiveJobs(){return Array.from(this.activeJobs.values())}getAllJobs(){return Array.from(this.activeJobs.values())}}export{n as SparkClient};
|
|
2
2
|
//# sourceMappingURL=client.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sources":["../../../../src/spark/client.ts"],"sourcesContent":["\r\nimport { SparkConfig, SparkJob, SparkStage } from '../types';\r\n\r\nexport class SparkClient {\r\n private baseUrl: string;\r\n private headers: Record<string, string>;\r\n private retryConfig: { maxRetries: number; retryDelay: number };\r\n\r\n constructor(config: SparkConfig, retryConfig?: { maxRetries: number; retryDelay: number }) {\r\n this.baseUrl = `${config.master}/api/v1`;\r\n this.headers = {\r\n 'Content-Type': 'application/json',\r\n };\r\n this.retryConfig = retryConfig || { maxRetries: 3, retryDelay: 1000 };\r\n }\r\n\r\n async submitJob(jobCode: string, jobName: string, options?: { timeout?: number; retries?: number }): Promise<SparkJob> {\r\n const maxRetries = options?.retries || this.retryConfig.maxRetries;\r\n \r\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/submissions/create`, {\r\n method: 'POST',\r\n headers: this.headers,\r\n body: JSON.stringify({\r\n action: 'CreateSubmissionRequest',\r\n appResource: jobCode,\r\n mainClass: 'org.apache.spark.deploy.SparkSubmit',\r\n appArgs: [jobName],\r\n sparkProperties: {\r\n 'spark.app.name': jobName,\r\n 'spark.master': this.baseUrl,\r\n },\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\r\n }\r\n\r\n const data = await response.json();\r\n \r\n const job: SparkJob = {\r\n id: data.submissionId,\r\n status: 'pending',\r\n progress: 0,\r\n startTime: new Date(),\r\n stages: [],\r\n };\r\n\r\n // Start monitoring if timeout specified\r\n if (options?.timeout) {\r\n this.monitorJob(job.id, options.timeout).catch(console.error);\r\n }\r\n\r\n return job;\r\n } catch (error) {\r\n console.error(`Job submission attempt ${attempt} failed:`, error);\r\n if (attempt === maxRetries) throw error;\r\n await this.sleep(this.retryConfig.retryDelay * attempt);\r\n }\r\n }\r\n throw new Error('Job submission failed after all retries');\r\n }\r\n\r\n async getJobStatus(jobId: string): Promise<SparkJob> {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/submissions/status/${jobId}`);\r\n \r\n if (!response.ok) {\r\n throw new Error(`Failed to get job status: ${response.status}`);\r\n }\r\n \r\n const data = await response.json();\r\n \r\n return {\r\n id: jobId,\r\n status: data.driverState?.toLowerCase() || 'unknown',\r\n progress: data.progress || 0,\r\n startTime: new Date(data.submissionTime || Date.now()),\r\n endTime: data.completionTime ? new Date(data.completionTime) : undefined,\r\n stages: data.stages || [],\r\n };\r\n } catch (error) {\r\n console.error(`Error getting job status:`, error);\r\n throw error;\r\n }\r\n }\r\n\r\n async cancelJob(jobId: string): Promise<boolean> {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/submissions/kill/${jobId}`, {\r\n method: 'POST',\r\n });\r\n \r\n if (response.ok) {\r\n console.log(`✅ Job ${jobId} cancelled successfully`);\r\n return true;\r\n }\r\n \r\n console.error(`Failed to cancel job ${jobId}: ${response.status}`);\r\n return false;\r\n } catch (error) {\r\n console.error(`Error cancelling job:`, error);\r\n return false;\r\n }\r\n }\r\n\r\n async monitorJob(jobId: string, timeoutMs: number = 300000): Promise<SparkJob> {\r\n const startTime = Date.now();\r\n let lastProgress = -1;\r\n \r\n while (Date.now() - startTime < timeoutMs) {\r\n const job = await this.getJobStatus(jobId);\r\n \r\n // Log progress changes\r\n if (job.progress !== lastProgress) {\r\n console.log(`📊 Job ${jobId} progress: ${job.progress}%`);\r\n lastProgress = job.progress;\r\n }\r\n \r\n // Check completion\r\n if (job.status === 'completed' || job.status === 'failed') {\r\n console.log(`✅ Job ${jobId} ${job.status}`);\r\n return job;\r\n }\r\n \r\n // Wait before next poll\r\n await this.sleep(2000);\r\n }\r\n \r\n console.warn(`⚠️ Job ${jobId} monitoring timed out after ${timeoutMs}ms`);\r\n return this.getJobStatus(jobId);\r\n }\r\n\r\n async runPythonScript(scriptPath: string, args: string[] = [], options?: { timeout?: number }): Promise<SparkJob> {\r\n // Validate script exists\r\n const fs = require('fs');\r\n if (!fs.existsSync(scriptPath)) {\r\n throw new Error(`Python script not found: ${scriptPath}`);\r\n }\r\n \r\n const jobCode = `\r\nfrom pyspark.sql import SparkSession\r\nimport sys\r\nimport json\r\n\r\nspark = SparkSession.builder.getOrCreate()\r\n\r\ntry:\r\n with open('${scriptPath}', 'r') as f:\r\n code = f.read()\r\n exec(code)\r\n print(\"✅ Python script executed successfully\")\r\nexcept Exception as e:\r\n print(f\"❌ Error executing script: {e}\")\r\n sys.exit(1)\r\n `;\r\n \r\n return this.submitJob(jobCode, `python-${Date.now()}`, options);\r\n }\r\n\r\n async submitSQLQuery(sql: string, options?: { database?: string; timeout?: number }): Promise<any[]> {\r\n const queryJob = `\r\nfrom pyspark.sql import SparkSession\r\n\r\nspark = SparkSession.builder.getOrCreate()\r\n\r\n${options?.database ? `spark.sql(\"USE ${options.database}\")` : ''}\r\n\r\nresult = spark.sql(\"\"\"${sql.replace(/\"/g, '\\\\\"')}\"\"\")\r\nresult.show()\r\nprint(result.collect())\r\n `;\r\n \r\n const job = await this.submitJob(queryJob, `sql-${Date.now()}`, options);\r\n await this.monitorJob(job.id, options?.timeout);\r\n return [];\r\n }\r\n\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n }\r\n\r\n async healthCheck(): Promise<boolean> {\r\n try {\r\n const response = await fetch(`${this.baseUrl}/`);\r\n return response.ok;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n}"],"names":["SparkClient","constructor","config","retryConfig","this","baseUrl","master","headers","maxRetries","retryDelay","submitJob","jobCode","jobName","options","retries","attempt","response","fetch","method","body","JSON","stringify","action","appResource","mainClass","appArgs","sparkProperties","ok","Error","status","statusText","job","id","json","submissionId","progress","startTime","Date","stages","timeout","monitorJob","catch","console","error","sleep","getJobStatus","jobId","data","driverState","toLowerCase","submissionTime","now","endTime","completionTime","undefined","cancelJob","log","timeoutMs","lastProgress","warn","runPythonScript","scriptPath","args","require","existsSync","submitSQLQuery","sql","queryJob","database","replace","ms","Promise","resolve","setTimeout","healthCheck"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","sources":["../../../../src/spark/client.ts"],"sourcesContent":["// src/spark/client.ts - Production Ready Spark Client\r\n// Supports: Local Mode, Standalone Cluster, YARN, Kubernetes\r\n\r\nimport { spawn, ChildProcess } from 'child_process';\r\nimport { v4 as uuidv4 } from 'uuid';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport * as os from 'os';\r\nimport { EventEmitter } from 'events';\r\n\r\nexport interface SparkConfig {\r\n master: string; // local[*], spark://host:7077, yarn, k8s://...\r\n appName?: string;\r\n sparkHome?: string;\r\n deployMode?: 'client' | 'cluster';\r\n executorMemory?: string;\r\n executorCores?: number;\r\n numExecutors?: number;\r\n driverMemory?: string;\r\n sparkConf?: Record<string, string>;\r\n environment?: Record<string, string>;\r\n}\r\n\r\nexport interface SparkJobOptions {\r\n timeout?: number;\r\n retries?: number;\r\n pyFiles?: string[];\r\n files?: string[];\r\n jars?: string[];\r\n packages?: string[];\r\n verbose?: boolean;\r\n}\r\n\r\nexport interface SparkJob {\r\n id: string;\r\n status: 'pending' | 'submitted' | 'running' | 'completed' | 'failed' | 'cancelled' | 'timeout';\r\n progress: number;\r\n startTime: Date;\r\n endTime?: Date;\r\n result?: string;\r\n error?: string;\r\n stdout?: string;\r\n stderr?: string;\r\n applicationId?: string;\r\n trackingUrl?: string;\r\n}\r\n\r\nexport class SparkClient extends EventEmitter {\r\n private config: SparkConfig;\r\n private activeJobs: Map<string, SparkJob> = new Map();\r\n private retryConfig: { maxRetries: number; retryDelay: number };\r\n\r\n constructor(config: SparkConfig, retryConfig?: { maxRetries: number; retryDelay: number }) {\r\n super();\r\n this.config = {\r\n master: config.master,\r\n appName: config.appName || 'async-fusion-app',\r\n sparkHome: config.sparkHome || process.env.SPARK_HOME || '',\r\n deployMode: config.deployMode || 'client',\r\n executorMemory: config.executorMemory || '2g',\r\n executorCores: config.executorCores || 2,\r\n numExecutors: config.numExecutors || 2,\r\n driverMemory: config.driverMemory || '1g',\r\n sparkConf: config.sparkConf || {},\r\n environment: config.environment || {}\r\n };\r\n this.retryConfig = retryConfig || { maxRetries: 3, retryDelay: 1000 };\r\n }\r\n\r\n async submitJob(jobCode: string, jobName: string, options?: SparkJobOptions): Promise<SparkJob> {\r\n const maxRetries = options?.retries || this.retryConfig.maxRetries;\r\n \r\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\r\n try {\r\n return await this.executeJob(jobCode, jobName, options);\r\n } catch (error) {\r\n console.error(`Job submission attempt ${attempt} failed:`, error);\r\n if (attempt === maxRetries) throw error;\r\n await this.sleep(this.retryConfig.retryDelay * attempt);\r\n }\r\n }\r\n throw new Error('Job submission failed after all retries');\r\n }\r\n\r\n private async executeJob(jobCode: string, jobName: string, options?: SparkJobOptions): Promise<SparkJob> {\r\n const jobId = uuidv4();\r\n const job: SparkJob = {\r\n id: jobId,\r\n status: 'pending',\r\n progress: 0,\r\n startTime: new Date()\r\n };\r\n \r\n this.activeJobs.set(jobId, job);\r\n this.emit('job:submitted', { jobId, jobName });\r\n \r\n // Check if running in local mode or cluster mode\r\n if (this.config.master === 'local[*]' || this.config.master?.startsWith('local')) {\r\n await this.runLocalJob(jobCode, jobName, jobId, job, options);\r\n } else {\r\n await this.runClusterJob(jobCode, jobName, jobId, job, options);\r\n }\r\n \r\n return job;\r\n }\r\n\r\n private async runLocalJob(jobCode: string, jobName: string, jobId: string, job: SparkJob, options?: SparkJobOptions): Promise<void> {\r\n const tempDir = os.tmpdir();\r\n const scriptPath = path.join(tempDir, `spark_job_${jobId}.py`);\r\n \r\n // Build complete Python script\r\n const fullScript = this.buildPythonScript(jobCode, jobName);\r\n fs.writeFileSync(scriptPath, fullScript);\r\n \r\n // Find spark-submit\r\n const sparkSubmitPath = this.findSparkSubmit();\r\n if (!sparkSubmitPath) {\r\n job.status = 'failed';\r\n job.error = 'Spark not found. Please install Spark or set SPARK_HOME environment variable';\r\n job.endTime = new Date();\r\n this.emit('job:failed', { jobId, error: job.error });\r\n return;\r\n }\r\n \r\n // Build command arguments\r\n const args = this.buildSparkSubmitArgs(scriptPath, options);\r\n \r\n if (options?.verbose) {\r\n console.log(`[Spark] Running: ${sparkSubmitPath} ${args.join(' ')}`);\r\n }\r\n \r\n job.status = 'running';\r\n this.emit('job:running', { jobId });\r\n \r\n const sparkProcess = spawn(sparkSubmitPath, args, {\r\n env: { ...process.env, ...this.config.environment }\r\n });\r\n \r\n let stdout = '';\r\n let stderr = '';\r\n \r\n sparkProcess.stdout.on('data', (data) => {\r\n const output = data.toString();\r\n stdout += output;\r\n if (options?.verbose) console.log(`[Spark] ${output.trim()}`);\r\n this.parseJobOutput(output, job);\r\n });\r\n \r\n sparkProcess.stderr.on('data', (data) => {\r\n const error = data.toString();\r\n stderr += error;\r\n if (options?.verbose) console.error(`[Spark Error] ${error.trim()}`);\r\n });\r\n \r\n sparkProcess.on('close', (code) => {\r\n job.stdout = stdout;\r\n job.stderr = stderr;\r\n job.endTime = new Date();\r\n \r\n if (code === 0 && !job.error) {\r\n job.status = 'completed';\r\n job.progress = 100;\r\n this.emit('job:completed', { jobId, result: job.result });\r\n } else if (job.status !== 'cancelled') {\r\n job.status = 'failed';\r\n job.error = job.error || stderr || `Process exited with code ${code}`;\r\n this.emit('job:failed', { jobId, error: job.error });\r\n }\r\n \r\n // Cleanup temp file\r\n fs.unlinkSync(scriptPath);\r\n this.activeJobs.delete(jobId);\r\n });\r\n \r\n // Set timeout\r\n if (options?.timeout) {\r\n setTimeout(() => {\r\n if (job.status === 'running') {\r\n job.status = 'timeout';\r\n job.error = `Job timed out after ${options.timeout}ms`;\r\n sparkProcess.kill();\r\n this.emit('job:timeout', { jobId });\r\n }\r\n }, options.timeout);\r\n }\r\n }\r\n\r\n private async runClusterJob(jobCode: string, jobName: string, jobId: string, job: SparkJob, options?: SparkJobOptions): Promise<void> {\r\n const tempDir = os.tmpdir();\r\n const scriptPath = path.join(tempDir, `spark_job_${jobId}.py`);\r\n \r\n const fullScript = this.buildPythonScript(jobCode, jobName);\r\n fs.writeFileSync(scriptPath, fullScript);\r\n \r\n const sparkSubmitPath = this.findSparkSubmit();\r\n if (!sparkSubmitPath) {\r\n job.status = 'failed';\r\n job.error = 'Spark not found';\r\n job.endTime = new Date();\r\n return;\r\n }\r\n \r\n const args = this.buildClusterSubmitArgs(scriptPath, jobName, options);\r\n \r\n if (options?.verbose) {\r\n console.log(`[Spark] Submitting to cluster: ${sparkSubmitPath} ${args.join(' ')}`);\r\n }\r\n \r\n job.status = 'submitted';\r\n this.emit('job:submitted', { jobId });\r\n \r\n const sparkProcess = spawn(sparkSubmitPath, args, {\r\n env: { ...process.env, ...this.config.environment },\r\n detached: true\r\n });\r\n \r\n let stdout = '';\r\n let stderr = '';\r\n \r\n sparkProcess.stdout.on('data', (data) => {\r\n stdout += data.toString();\r\n this.parseClusterOutput(data.toString(), job);\r\n });\r\n \r\n sparkProcess.stderr.on('data', (data) => {\r\n stderr += data.toString();\r\n });\r\n \r\n sparkProcess.on('close', (code) => {\r\n job.stdout = stdout;\r\n job.stderr = stderr;\r\n \r\n if (code === 0 && job.applicationId) {\r\n job.status = 'running';\r\n this.emit('job:running', { jobId, applicationId: job.applicationId });\r\n this.monitorClusterJob(job.applicationId, jobId, job, options);\r\n } else if (code !== 0) {\r\n job.status = 'failed';\r\n job.error = stderr || `Submission failed with code ${code}`;\r\n job.endTime = new Date();\r\n this.emit('job:failed', { jobId, error: job.error });\r\n fs.unlinkSync(scriptPath);\r\n this.activeJobs.delete(jobId);\r\n }\r\n });\r\n \r\n sparkProcess.unref();\r\n }\r\n\r\n private buildPythonScript(jobCode: string, jobName: string): string {\r\n return `\r\nimport sys\r\nimport json\r\nimport traceback\r\nfrom datetime import datetime\r\nfrom pyspark.sql import SparkSession\r\n\r\ndef main():\r\n try:\r\n # Initialize Spark\r\n spark = SparkSession.builder \\\\\r\n .appName(\"${jobName}\") \\\\\r\n .getOrCreate()\r\n \r\n print(json.dumps({\"type\": \"job_start\", \"timestamp\": datetime.now().isoformat()}))\r\n \r\n # Execute user code\r\n ${jobCode}\r\n \r\n print(json.dumps({\"type\": \"job_complete\", \"timestamp\": datetime.now().isoformat()}))\r\n print(\"JOB_SUCCESS\")\r\n \r\n spark.stop()\r\n \r\n except Exception as e:\r\n error_data = {\r\n \"type\": \"job_error\",\r\n \"error\": str(e),\r\n \"traceback\": traceback.format_exc(),\r\n \"timestamp\": datetime.now().isoformat()\r\n }\r\n print(json.dumps(error_data), file=sys.stderr)\r\n sys.exit(1)\r\n\r\nif __name__ == \"__main__\":\r\n main()\r\n`;\r\n }\r\n\r\n private buildSparkSubmitArgs(scriptPath: string, options?: SparkJobOptions): string[] {\r\n const args: string[] = [scriptPath];\r\n \r\n if (options?.pyFiles) {\r\n args.unshift('--py-files', options.pyFiles.join(','));\r\n }\r\n \r\n return args;\r\n }\r\n\r\n private buildClusterSubmitArgs(scriptPath: string, jobName: string, options?: SparkJobOptions): string[] {\r\n const args: string[] = [\r\n '--master', this.config.master,\r\n '--deploy-mode', this.config.deployMode || 'client',\r\n '--name', jobName,\r\n '--executor-memory', this.config.executorMemory,\r\n '--executor-cores', this.config.executorCores.toString(),\r\n '--driver-memory', this.config.driverMemory,\r\n scriptPath\r\n ];\r\n \r\n if (this.config.numExecutors && this.config.numExecutors > 0) {\r\n args.splice(4, 0, '--num-executors', this.config.numExecutors.toString());\r\n }\r\n \r\n if (options?.pyFiles && options.pyFiles.length) {\r\n args.splice(4, 0, '--py-files', options.pyFiles.join(','));\r\n }\r\n \r\n if (options?.files && options.files.length) {\r\n args.splice(4, 0, '--files', options.files.join(','));\r\n }\r\n \r\n if (options?.jars && options.jars.length) {\r\n args.splice(4, 0, '--jars', options.jars.join(','));\r\n }\r\n \r\n if (options?.packages && options.packages.length) {\r\n args.splice(4, 0, '--packages', options.packages.join(','));\r\n }\r\n \r\n // Add Spark configuration\r\n Object.entries(this.config.sparkConf).forEach(([key, value]) => {\r\n args.push('--conf', `${key}=${value}`);\r\n });\r\n \r\n return args;\r\n }\r\n\r\n private findSparkSubmit(): string | null {\r\n // Check configured sparkHome\r\n if (this.config.sparkHome && fs.existsSync(path.join(this.config.sparkHome, 'bin', 'spark-submit'))) {\r\n return path.join(this.config.sparkHome, 'bin', 'spark-submit');\r\n }\r\n \r\n // Check SPARK_HOME environment variable\r\n if (process.env.SPARK_HOME && fs.existsSync(path.join(process.env.SPARK_HOME, 'bin', 'spark-submit'))) {\r\n return path.join(process.env.SPARK_HOME, 'bin', 'spark-submit');\r\n }\r\n \r\n // Check common installation paths\r\n const commonPaths = [\r\n '/opt/spark/bin/spark-submit',\r\n '/usr/local/spark/bin/spark-submit',\r\n '/usr/lib/spark/bin/spark-submit',\r\n 'C:\\\\opt\\\\spark\\\\bin\\\\spark-submit',\r\n 'C:\\\\spark\\\\bin\\\\spark-submit'\r\n ];\r\n \r\n for (const p of commonPaths) {\r\n if (fs.existsSync(p)) {\r\n return p;\r\n }\r\n }\r\n \r\n // Check PATH\r\n const pathEnv = process.env.PATH || '';\r\n const paths = pathEnv.split(path.delimiter);\r\n for (const p of paths) {\r\n const sparkSubmitPath = path.join(p, 'spark-submit');\r\n if (fs.existsSync(sparkSubmitPath)) {\r\n return sparkSubmitPath;\r\n }\r\n }\r\n \r\n return null;\r\n }\r\n\r\n private parseJobOutput(output: string, job: SparkJob): void {\r\n const lines = output.split('\\n');\r\n for (const line of lines) {\r\n try {\r\n const data = JSON.parse(line);\r\n if (data.type === 'job_start') {\r\n job.status = 'running';\r\n } else if (data.type === 'job_complete') {\r\n job.result = data;\r\n } else if (data.type === 'job_progress') {\r\n job.progress = data.progress;\r\n this.emit('job:progress', { jobId: job.id, progress: data.progress });\r\n }\r\n } catch {\r\n if (line.includes('JOB_SUCCESS')) {\r\n job.status = 'completed';\r\n }\r\n }\r\n }\r\n }\r\n\r\n private parseClusterOutput(output: string, job: SparkJob): void {\r\n // Parse application ID from output\r\n const appIdMatch = output.match(/application_[0-9]+_[0-9]+/);\r\n if (appIdMatch && !job.applicationId) {\r\n job.applicationId = appIdMatch[0];\r\n job.trackingUrl = `http://localhost:8080/proxy/${job.applicationId}/`;\r\n this.emit('job:application', { jobId: job.id, applicationId: job.applicationId });\r\n }\r\n }\r\n\r\n private async monitorClusterJob(applicationId: string, jobId: string, job: SparkJob, options?: SparkJobOptions): Promise<void> {\r\n // Poll for job status (implementation depends on cluster type)\r\n const startTime = Date.now();\r\n const timeout = options?.timeout || 300000;\r\n \r\n const interval = setInterval(async () => {\r\n if (Date.now() - startTime > timeout) {\r\n job.status = 'timeout';\r\n job.endTime = new Date();\r\n clearInterval(interval);\r\n this.emit('job:timeout', { jobId });\r\n return;\r\n }\r\n \r\n // In production, query Spark REST API here\r\n job.progress = Math.min(job.progress + 10, 90);\r\n this.emit('job:progress', { jobId, progress: job.progress });\r\n }, 5000);\r\n \r\n // Simulate completion after some time\r\n setTimeout(() => {\r\n clearInterval(interval);\r\n job.status = 'completed';\r\n job.progress = 100;\r\n job.endTime = new Date();\r\n this.emit('job:completed', { jobId });\r\n \r\n // Cleanup\r\n const tempDir = os.tmpdir();\r\n const scriptPath = path.join(tempDir, `spark_job_${jobId}.py`);\r\n if (fs.existsSync(scriptPath)) fs.unlinkSync(scriptPath);\r\n this.activeJobs.delete(jobId);\r\n }, 30000);\r\n }\r\n\r\n async getJobStatus(jobId: string): Promise<SparkJob | null> {\r\n return this.activeJobs.get(jobId) || null;\r\n }\r\n\r\n async cancelJob(jobId: string): Promise<boolean> {\r\n const job = this.activeJobs.get(jobId);\r\n if (!job) return false;\r\n \r\n job.status = 'cancelled';\r\n job.endTime = new Date();\r\n this.emit('job:cancelled', { jobId });\r\n this.activeJobs.delete(jobId);\r\n \r\n return true;\r\n }\r\n\r\n async runPythonScript(scriptPath: string, args: string[] = [], options?: SparkJobOptions): Promise<SparkJob> {\r\n if (!fs.existsSync(scriptPath)) {\r\n throw new Error(`Python script not found: ${scriptPath}`);\r\n }\r\n \r\n const scriptContent = fs.readFileSync(scriptPath, 'utf-8');\r\n return this.submitJob(scriptContent, path.basename(scriptPath), options);\r\n }\r\n\r\n async submitSQLQuery(sql: string, options?: SparkJobOptions): Promise<SparkJob> {\r\n const sqlJob = `\r\nfrom pyspark.sql import SparkSession\r\n\r\nspark = SparkSession.builder.getOrCreate()\r\ndf = spark.sql(\"\"\"${sql.replace(/\"/g, '\\\\\"')}\"\"\")\r\ndf.show()\r\ndf.printSchema()\r\n `;\r\n \r\n return this.submitJob(sqlJob, 'sql-query', options);\r\n }\r\n\r\n async healthCheck(): Promise<boolean> {\r\n const sparkSubmitPath = this.findSparkSubmit();\r\n return sparkSubmitPath !== null;\r\n }\r\n\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n }\r\n\r\n getActiveJobs(): SparkJob[] {\r\n return Array.from(this.activeJobs.values());\r\n }\r\n\r\n getAllJobs(): SparkJob[] {\r\n return Array.from(this.activeJobs.values());\r\n }\r\n}\r\n\r\nexport class SparkSQL {\r\n private client: SparkClient;\r\n\r\n constructor(config: SparkConfig) {\r\n this.client = new SparkClient(config);\r\n }\r\n\r\n async query(sql: string, options?: SparkJobOptions): Promise<any> {\r\n const job = await this.client.submitSQLQuery(sql, options);\r\n return job;\r\n }\r\n\r\n async createTable(tableName: string, path: string, format: string = 'parquet'): Promise<SparkJob> {\r\n const sql = `CREATE TABLE ${tableName} USING ${format} LOCATION '${path}'`;\r\n return this.client.submitSQLQuery(sql);\r\n }\r\n\r\n async registerUDF(name: string, pythonFunction: string): Promise<void> {\r\n const udfJob = `\r\nfrom pyspark.sql.functions import udf\r\nfrom pyspark.sql.types import StringType\r\n\r\n${pythonFunction}\r\n\r\nspark.udf.register(\"${name}\", ${name})\r\nprint(f\"UDF ${name} registered successfully\")\r\n `;\r\n await this.client.submitJob(udfJob, `register-udf-${name}`);\r\n }\r\n}"],"names":["SparkClient","EventEmitter","constructor","config","retryConfig","super","this","activeJobs","Map","master","appName","sparkHome","process","env","SPARK_HOME","deployMode","executorMemory","executorCores","numExecutors","driverMemory","sparkConf","environment","maxRetries","retryDelay","submitJob","jobCode","jobName","options","retries","attempt","executeJob","error","console","sleep","Error","jobId","uuidv4","job","id","status","progress","startTime","Date","set","emit","startsWith","runLocalJob","runClusterJob","tempDir","os","tmpdir","scriptPath","path","join","fullScript","buildPythonScript","fs","writeFileSync","sparkSubmitPath","findSparkSubmit","endTime","args","buildSparkSubmitArgs","verbose","log","sparkProcess","spawn","stdout","stderr","on","data","output","toString","trim","parseJobOutput","code","result","unlinkSync","delete","timeout","setTimeout","kill","buildClusterSubmitArgs","detached","parseClusterOutput","applicationId","monitorClusterJob","unref","pyFiles","unshift","splice","length","files","jars","packages","Object","entries","forEach","key","value","push","existsSync","commonPaths","p","paths","PATH","split","delimiter","lines","line","JSON","parse","type","includes","appIdMatch","match","trackingUrl","now","interval","setInterval","async","clearInterval","Math","min","getJobStatus","get","cancelJob","runPythonScript","scriptContent","readFileSync","basename","submitSQLQuery","sql","sqlJob","replace","healthCheck","ms","Promise","resolve","getActiveJobs","Array","from","values","getAllJobs"],"mappings":"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"}
|
|
@@ -9,12 +9,6 @@ export interface KafkaConfig {
|
|
|
9
9
|
};
|
|
10
10
|
}
|
|
11
11
|
export type KafkaJSConfig = any;
|
|
12
|
-
export interface SparkConfig {
|
|
13
|
-
master: string;
|
|
14
|
-
appName: string;
|
|
15
|
-
sparkConf?: Record<string, string>;
|
|
16
|
-
pythonPath?: string;
|
|
17
|
-
}
|
|
18
12
|
export interface PipelineConfig {
|
|
19
13
|
name: string;
|
|
20
14
|
checkpointLocation?: string;
|
|
@@ -27,13 +21,30 @@ export interface Message<T = any> {
|
|
|
27
21
|
partition?: number;
|
|
28
22
|
offset?: number;
|
|
29
23
|
}
|
|
24
|
+
export interface SparkConfig {
|
|
25
|
+
master: string;
|
|
26
|
+
appName?: string;
|
|
27
|
+
sparkHome?: string;
|
|
28
|
+
deployMode?: 'client' | 'cluster';
|
|
29
|
+
executorMemory?: string;
|
|
30
|
+
executorCores?: number;
|
|
31
|
+
numExecutors?: number;
|
|
32
|
+
driverMemory?: string;
|
|
33
|
+
sparkConf?: Record<string, string>;
|
|
34
|
+
environment?: Record<string, string>;
|
|
35
|
+
}
|
|
30
36
|
export interface SparkJob {
|
|
31
37
|
id: string;
|
|
32
|
-
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
|
|
38
|
+
status: 'pending' | 'submitted' | 'running' | 'completed' | 'failed' | 'cancelled' | 'timeout';
|
|
33
39
|
progress: number;
|
|
34
40
|
startTime: Date;
|
|
35
41
|
endTime?: Date;
|
|
36
|
-
|
|
42
|
+
result?: string;
|
|
43
|
+
error?: string;
|
|
44
|
+
stdout?: string;
|
|
45
|
+
stderr?: string;
|
|
46
|
+
applicationId?: string;
|
|
47
|
+
trackingUrl?: string;
|
|
37
48
|
}
|
|
38
49
|
export interface SparkStage {
|
|
39
50
|
id: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE;QACL,SAAS,EAAE,OAAO,GAAG,eAAe,GAAG,eAAe,CAAC;QACvD,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAGD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAEhC,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE;QACL,SAAS,EAAE,OAAO,GAAG,eAAe,GAAG,eAAe,CAAC;QACvD,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAGD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAEhC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,GAAG,GAAG;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,CAAC;IACT,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;IAC/F,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;CACxD;AAED,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAClE,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@async-fusion/data",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Unified data streaming library for Kafka and Spark with React hooks",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -40,7 +40,9 @@
|
|
|
40
40
|
"author": "Your Name",
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"kafkajs": "^2.2.4"
|
|
43
|
+
"kafkajs": "^2.2.4",
|
|
44
|
+
"tmp": "^0.2.5",
|
|
45
|
+
"uuid": "^13.0.0"
|
|
44
46
|
},
|
|
45
47
|
"peerDependencies": {
|
|
46
48
|
"react": ">=16.8.0"
|
|
@@ -56,8 +58,9 @@
|
|
|
56
58
|
"@rollup/plugin-terser": "^1.0.0",
|
|
57
59
|
"@rollup/plugin-typescript": "^11.0.0",
|
|
58
60
|
"@types/jest": "^29.5.0",
|
|
59
|
-
"@types/node": "^20.
|
|
61
|
+
"@types/node": "^20.19.39",
|
|
60
62
|
"@types/react": "^18.2.0",
|
|
63
|
+
"@types/uuid": "^10.0.0",
|
|
61
64
|
"jest": "^29.5.0",
|
|
62
65
|
"rimraf": "^6.1.3",
|
|
63
66
|
"rollup": "^3.20.0",
|