@gftdcojp/ksqldb-orm
Version:
ksqldb-orm - Server-Side TypeScript ORM for ksqlDB with enterprise security extensions
435 lines • 15.1 kB
JavaScript
;
/**
* クライアントサイド用HTTPクライアント(ブラウザ環境)
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.KsqlDbClientServer = exports.KsqlDbClientBrowser = exports.HttpClient = void 0;
exports.createKsqlDbClientBrowser = createKsqlDbClientBrowser;
exports.createKsqlDbClientServer = createKsqlDbClientServer;
const env_1 = require("./utils/env");
const ksqldb_client_1 = require("./ksqldb-client");
const logger_1 = require("./utils/logger");
/**
* URLのHTTPS検証を行う
* @param url 検証対象のURL
* @param enforceHttps HTTPSを強制するかどうか
*/
function validateHttpsUrl(url, enforceHttps = true) {
try {
const urlObj = new URL(url);
// テスト環境ではHTTPを許可
const allowHttp = typeof process !== 'undefined' &&
(process.env?.GFTD_ALLOW_HTTP === 'true' || process.env?.NODE_ENV === 'test');
if (urlObj.protocol === 'http:' && !allowHttp) {
const message = `Insecure HTTP connection detected: ${url}. HTTPS is required for security.`;
if (enforceHttps) {
throw new Error(`SECURITY ERROR: ${message}`);
}
else {
logger_1.log.warn(`SECURITY WARNING: ${message}`);
}
}
}
catch (error) {
if (error instanceof TypeError) {
throw new Error(`Invalid URL format: ${url}`);
}
throw error;
}
}
/**
* ブラウザ環境用HTTPクライアント
*/
class HttpClient {
constructor(config) {
if (!(0, env_1.isBrowser)()) {
throw new Error('HttpClient can only be used in browser environment');
}
// デフォルトでHTTPSを強制
const enforceHttps = config.enforceHttps !== false;
// URLのHTTPS検証
validateHttpsUrl(config.url, enforceHttps);
this.config = {
...config,
enforceHttps
};
this.defaultHeaders = {
'Content-Type': 'application/json',
...config.headers,
};
// 認証情報がある場合は Authorization ヘッダーを追加
if (config.apiKey && config.apiSecret) {
const credentials = btoa(`${config.apiKey}:${config.apiSecret}`);
this.defaultHeaders['Authorization'] = `Basic ${credentials}`;
}
// セキュリティヘッダーを追加
this.defaultHeaders['X-Content-Type-Options'] = 'nosniff';
this.defaultHeaders['X-Frame-Options'] = 'DENY';
this.defaultHeaders['X-XSS-Protection'] = '1; mode=block';
}
/**
* GET リクエストを送信
*/
async get(path, params) {
const url = new URL(path, this.config.url);
if (params) {
Object.keys(params).forEach(key => {
url.searchParams.append(key, params[key]);
});
}
const response = await this.fetch(url.toString(), {
method: 'GET',
headers: this.defaultHeaders,
});
return this.handleResponse(response);
}
/**
* POST リクエストを送信
*/
async post(path, data) {
const url = new URL(path, this.config.url);
const response = await this.fetch(url.toString(), {
method: 'POST',
headers: this.defaultHeaders,
body: data ? JSON.stringify(data) : undefined,
});
return this.handleResponse(response);
}
/**
* PUT リクエストを送信
*/
async put(path, data) {
const url = new URL(path, this.config.url);
const response = await this.fetch(url.toString(), {
method: 'PUT',
headers: this.defaultHeaders,
body: data ? JSON.stringify(data) : undefined,
});
return this.handleResponse(response);
}
/**
* DELETE リクエストを送信
*/
async delete(path) {
const url = new URL(path, this.config.url);
const response = await this.fetch(url.toString(), {
method: 'DELETE',
headers: this.defaultHeaders,
});
return this.handleResponse(response);
}
/**
* ksqlDB クエリを実行
*/
async executeQuery(sql) {
const response = await this.post('/ksql', {
ksql: sql,
streamsProperties: {},
});
return response;
}
/**
* Pull Query を実行(ブラウザ用)
* @param sql - 実行するSQL文
* @param options - Pull Queryのオプション
*/
async executePullQuery(sql, options = {}) {
const { format = 'object' } = options;
const response = await this.post('/query-stream', {
sql,
properties: {},
});
// ブラウザ環境では、レスポンスの形式が異なる可能性があるため、
// 基本的な変換処理を実装
if (format === 'object' && response && Array.isArray(response.data) && response.columnNames) {
const transformedData = (0, ksqldb_client_1.transformArrayRowsToObjects)(response.data, response.columnNames);
return {
...response,
data: transformedData
};
}
return response;
}
async fetch(url, options) {
// URLのHTTPS検証
validateHttpsUrl(url, this.config.enforceHttps);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout || 30000);
// セキュリティ設定を追加
const secureOptions = {
...options,
signal: controller.signal,
mode: 'cors',
credentials: 'same-origin',
referrerPolicy: 'strict-origin-when-cross-origin',
};
try {
const response = await fetch(url, secureOptions);
clearTimeout(timeoutId);
return response;
}
catch (error) {
clearTimeout(timeoutId);
if (error instanceof Error && error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
async handleResponse(response) {
if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP ${response.status}: ${errorText}`);
}
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return response.json();
}
return response.text();
}
}
exports.HttpClient = HttpClient;
/**
* ブラウザ環境用ksqlDBクライアント
*/
class KsqlDbClientBrowser {
constructor(config) {
this.config = config;
this.httpClient = new HttpClient({
url: config.url,
apiKey: config.apiKey,
apiSecret: config.apiSecret,
headers: config.headers,
timeout: 30000,
});
}
/**
* クエリを実行
*/
async executeQuery(sql) {
try {
const response = await this.httpClient.executeQuery(sql);
return response;
}
catch (error) {
throw new Error(`ksqlDB query failed: ${error.message}`);
}
}
/**
* Pull Query を実行
* @param sql - 実行するSQL文
* @param options - Pull Queryのオプション(デフォルトでオブジェクト形式を返す)
*/
async executePullQuery(sql, options = {}) {
console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - SQL: ${sql}`);
console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - Options:`, options);
console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - Config:`, this.config);
try {
const response = await this.httpClient.executePullQuery(sql, options);
console.log(`[DEBUG] KsqlDbClientBrowser.executePullQuery - Response:`, response);
return response;
}
catch (error) {
console.error(`[ERROR] KsqlDbClientBrowser.executePullQuery failed:`, error);
// ストリーム vs テーブルの問題を特定しやすくする
if (error.message && (error.message.includes('stream') || error.message.includes('table'))) {
console.error(`[ERROR] KsqlDbClientBrowser.executePullQuery - Possible stream/table issue. SQL: ${sql}`);
console.error(`[ERROR] KsqlDbClientBrowser.executePullQuery - Remember: Pull queries work only on TABLES, not STREAMS`);
}
throw new Error(`ksqlDB pull query failed: ${error.message || 'Unknown error'}`);
}
}
/**
* DDL文を実行
*/
async executeDDL(ddl) {
try {
const response = await this.httpClient.executeQuery(ddl);
if (response && response[0] && response[0].errorMessage) {
throw new Error(`DDL execution failed: ${response[0].errorMessage.message}`);
}
return response;
}
catch (error) {
throw new Error(`ksqlDB DDL failed: ${error.message}`);
}
}
/**
* ストリーム/テーブル一覧を取得
*/
async listStreams() {
return this.executeQuery('LIST STREAMS;');
}
async listTables() {
return this.executeQuery('LIST TABLES;');
}
/**
* トピック一覧を取得
*/
async listTopics() {
return this.executeQuery('LIST TOPICS;');
}
/**
* スキーマ情報を取得
*/
async describeStream(streamName) {
return this.executeQuery(`DESCRIBE ${streamName};`);
}
async describeTable(tableName) {
return this.executeQuery(`DESCRIBE ${tableName};`);
}
/**
* 接続状態を確認
*/
async isConnected() {
try {
await this.executeQuery('SHOW QUERIES;');
return true;
}
catch {
return false;
}
}
/**
* クライアント設定を取得
*/
getConfig() {
return this.config;
}
}
exports.KsqlDbClientBrowser = KsqlDbClientBrowser;
/**
* ブラウザ環境用ksqlDBクライアントを作成
*/
function createKsqlDbClientBrowser(config) {
return new KsqlDbClientBrowser(config);
}
/**
* サーバー環境用ksqlDBクライアント
*/
class KsqlDbClientServer {
constructor(config) {
this.config = config;
}
/**
* クエリを実行
*/
async executeQuery(sql) {
// サーバー環境では、ksqldb-clientの関数を直接使用
const { executeQuery } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return executeQuery(sql);
}
/**
* Pull Query を実行
* @param sql - 実行するSQL文
* @param options - Pull Queryのオプション(デフォルトでオブジェクト形式を返す)
*/
async executePullQuery(sql, options = {}) {
const { executePullQuery } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return executePullQuery(sql, options);
}
/**
* Push Query を実行
*/
async executePushQuery(sql, onData, onError, onComplete) {
const { executePushQuery } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return executePushQuery(sql, onData, onError, onComplete);
}
/**
* DDL文を実行
*/
async executeDDL(ddl) {
const { executeDDL } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return executeDDL(ddl);
}
/**
* ストリーム/テーブル一覧を取得
*/
async listStreams() {
const { listStreams } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return listStreams();
}
async listTables() {
const { listTables } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return listTables();
}
/**
* トピック一覧を取得
*/
async listTopics() {
const { listTopics } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return listTopics();
}
/**
* スキーマ情報を取得
*/
async describeStream(streamName) {
const { describeStream } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return describeStream(streamName);
}
async describeTable(tableName) {
const { describeTable } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return describeTable(tableName);
}
/**
* 接続状態を確認
*/
async isConnected() {
const { isConnected } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
return isConnected();
}
/**
* クライアント設定を取得
*/
getConfig() {
return this.config;
}
/**
* クライアントを初期化
*/
async initialize() {
const { initializeKsqlDbClient } = await Promise.resolve().then(() => __importStar(require('./ksqldb-client')));
initializeKsqlDbClient(this.config);
}
}
exports.KsqlDbClientServer = KsqlDbClientServer;
/**
* サーバー環境用ksqlDBクライアントを作成
*/
function createKsqlDbClientServer(config) {
return new KsqlDbClientServer(config);
}
//# sourceMappingURL=http-client.js.map