@gftdcojp/gftd-orm
Version:
Enterprise-grade real-time data platform with ksqlDB, inspired by Supabase architecture
269 lines • 8.72 kB
JavaScript
/**
* Next.js環境のブラウザ専用クライアント
* Client ComponentsやPages Routerのクライアントサイドで使用
*/
// Mock DatabaseClient implementation
// @todo [P2] Replace with actual DatabaseClient implementation
class DatabaseClient {
constructor(config) {
console.log('[MOCK] DatabaseClient created:', config);
}
async initialize() {
console.log('[MOCK] DatabaseClient initialized');
}
from(table) {
return {
select: () => ({
eq: () => ({
execute: () => Promise.resolve({ data: [], error: null })
})
}),
insert: (data) => Promise.resolve({ data: [], error: null }),
update: (data) => Promise.resolve({ data: [], error: null }),
delete: () => Promise.resolve({ data: [], error: null }),
execute: () => Promise.resolve({ data: [], error: null })
};
}
async sql(query, params) {
return { data: [], queryId: 'mock-query' };
}
async health() {
return { status: 'ok' };
}
}
import { RealtimeClient } from '../realtime-client';
import { isBrowser } from '../utils/env';
import { log } from '../utils/logger';
/**
* ブラウザ専用のGFTD-ORMクライアント
*/
export class BrowserClient {
constructor(config) {
this._database = null;
this._realtime = null;
this.initialized = false;
this._isConnected = false;
this.validateBrowserEnvironment();
this.config = config;
}
/**
* ブラウザ環境の検証
*/
validateBrowserEnvironment() {
if (!isBrowser()) {
throw new Error('BrowserClient can only be used in browser environment. ' +
'Use ServerClient for server-side rendering or API routes.');
}
}
/**
* データベースクライアントを取得
*/
get database() {
if (!this._database) {
throw new Error('Database not initialized. Call initialize() first.');
}
return this._database;
}
/**
* リアルタイムクライアントを取得
*/
get realtime() {
if (!this._realtime) {
throw new Error('Realtime not initialized or not configured.');
}
return this._realtime;
}
/**
* クライアントを初期化
*/
async initialize() {
if (this.initialized) {
return;
}
try {
log.info('Initializing browser client...');
// Database初期化(ブラウザ版)
this._database = new DatabaseClient({
ksql: {
url: this.config.database.ksql.url,
apiKey: this.config.database.ksql.apiKey,
headers: {
'User-Agent': 'gftd-orm-browser-client',
'X-Client-Type': 'browser',
...this.config.database.ksql.headers,
},
},
schemaRegistry: {
url: this.config.database.schemaRegistry.url,
apiKey: this.config.database.schemaRegistry.apiKey,
},
});
await this._database.initialize();
// Realtime初期化(ブラウザ版)
if (this.config.realtime) {
this._realtime = new RealtimeClient({
url: this.config.realtime.url,
apiKey: this.config.realtime.apiKey,
autoReconnect: this.config.realtime.autoReconnect ?? true,
reconnectInterval: this.config.realtime.reconnectInterval ?? 3000,
maxReconnectAttempts: this.config.realtime.maxReconnectAttempts ?? 5,
});
}
this.initialized = true;
this._isConnected = true;
log.info('Browser client initialized successfully');
}
catch (error) {
log.error(`Failed to initialize browser client: ${error}`);
this._isConnected = false;
throw error;
}
}
/**
* Supabaseライクなテーブルアクセス
*/
from(table) {
return this.database.from(table);
}
/**
* SQL クエリを実行
*/
async query(sql, params) {
try {
const startTime = Date.now();
const result = await this.database.sql(sql, params);
const executionTime = Date.now() - startTime;
return {
data: result.data,
error: null,
metadata: {
rowCount: result.data.length,
executionTime,
queryId: result.queryId,
},
};
}
catch (error) {
log.error(`Query execution failed: ${error}`);
return {
data: [],
error: error,
};
}
}
/**
* ストリーミングクエリを実行
*/
async stream(sql, onData, onError) {
try {
// TODO: DatabaseClientのstream機能が実装されるまでの暫定実装
const mockTerminate = () => {
log.info('Stream terminated');
};
// 実際の実装では、this.database.stream(sql, onData, onError) を使用
// const { terminate } = await this.database.stream(sql, onData, onError);
return {
terminate: mockTerminate,
pause: () => {
// ブラウザクライアントでは一時停止機能は制限的
log.warn('Pause functionality is limited in browser client');
},
resume: () => {
// ブラウザクライアントでは再開機能は制限的
log.warn('Resume functionality is limited in browser client');
},
isActive: () => true, // 基本的なステータス管理
};
}
catch (error) {
log.error(`Stream execution failed: ${error}`);
throw error;
}
}
/**
* リアルタイムチャンネル
*/
channel(name) {
if (!this._realtime) {
throw new Error('Realtime not configured. Please provide realtime config when creating the client.');
}
return this._realtime.channel(name);
}
/**
* ヘルスチェック
*/
async health() {
const connections = {
ksqldb: this._database ? 'connected' : 'disconnected',
schemaRegistry: this._database ? 'connected' : 'disconnected',
realtime: this._realtime ? 'connected' : 'disconnected',
};
const features = [
'database',
...(this._realtime ? ['realtime'] : []),
'rate-limit',
];
try {
// データベースの健康状態をチェック
if (this._database) {
await this._database.health();
}
return {
status: 'ok',
version: '25.07.6',
features,
connections,
environment: 'browser',
};
}
catch (error) {
log.error(`Health check failed: ${error}`);
return {
status: 'error',
version: '25.07.6',
features,
connections: {
...connections,
ksqldb: 'error',
},
environment: 'browser',
};
}
}
/**
* 接続状態を確認
*/
isConnected() {
return this._isConnected && this.initialized;
}
/**
* すべての接続を閉じる
*/
async disconnect() {
try {
if (this._realtime) {
this._realtime.disconnect();
}
this._isConnected = false;
this.initialized = false;
log.info('Browser client disconnected');
}
catch (error) {
log.error(`Error during disconnect: ${error}`);
throw error;
}
}
}
/**
* ブラウザ専用クライアント作成関数
*/
export function createBrowserClient(config) {
return new BrowserClient(config);
}
/**
* Next.js Client Components用のクライアント作成関数
*/
export function createNextBrowserClient(config) {
return createBrowserClient(config);
}
//# sourceMappingURL=browser.js.map