mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 07:12:54 +01:00
Refactored dev-docs folder after multiple additions to organise correctly
This commit is contained in:
57
dev-docs/tasks/phase-6-uba-system/PROGRESS.md
Normal file
57
dev-docs/tasks/phase-6-uba-system/PROGRESS.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Phase 6: UBA System (Universal Backend Adapter) - Progress Tracker
|
||||
|
||||
**Last Updated:** 2026-01-07
|
||||
**Overall Status:** 🔴 Not Started
|
||||
|
||||
---
|
||||
|
||||
## Quick Summary
|
||||
|
||||
| Metric | Value |
|
||||
| ------------ | ------ |
|
||||
| Total Tasks | 6 |
|
||||
| Completed | 0 |
|
||||
| In Progress | 0 |
|
||||
| Not Started | 6 |
|
||||
| **Progress** | **0%** |
|
||||
|
||||
---
|
||||
|
||||
## Task Status
|
||||
|
||||
| Task | Name | Status | Notes |
|
||||
| ------- | ----------------- | -------------- | -------------------------- |
|
||||
| UBA-001 | Foundation | 🔴 Not Started | Schema parser, basic UI |
|
||||
| UBA-002 | Field Types | 🔴 Not Started | Complex field type support |
|
||||
| UBA-003 | Debug System | 🔴 Not Started | Debug streaming for panels |
|
||||
| UBA-004 | Polish | 🔴 Not Started | UX improvements |
|
||||
| UBA-005 | Reference Backend | 🔴 Not Started | Reference implementation |
|
||||
| UBA-006 | Community | 🔴 Not Started | Community adapter sharing |
|
||||
|
||||
---
|
||||
|
||||
## Status Legend
|
||||
|
||||
- 🔴 **Not Started** - Work has not begun
|
||||
- 🟡 **In Progress** - Actively being worked on
|
||||
- 🟢 **Complete** - Finished and verified
|
||||
|
||||
---
|
||||
|
||||
## Recent Updates
|
||||
|
||||
| Date | Update |
|
||||
| ---------- | ---------------------------------------- |
|
||||
| 2026-01-07 | Moved from Phase 3 TASK-008 to own phase |
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
Depends on: Phase 3 (Editor UX foundation)
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
This phase was previously TASK-008 in Phase 3. Moved to its own phase for clarity. UBA enables configuring any backend (Directus, Supabase, etc.) through a unified adapter interface.
|
||||
96
dev-docs/tasks/phase-6-uba-system/README.md
Normal file
96
dev-docs/tasks/phase-6-uba-system/README.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# Phase 6: Universal Backend Adapter (UBA) System
|
||||
|
||||
**Phase:** 6
|
||||
**Status:** 🔴 Not Started
|
||||
**Effort:** 8-12 weeks
|
||||
**Priority:** HIGH
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Universal Backend Adapter (UBA) system enables Noodl to connect to any backend service (Directus, Supabase, Pocketbase, Firebase, custom APIs) through a unified configuration interface. Instead of hardcoding backend integrations, users configure adapters that translate between Noodl's data model and the target backend.
|
||||
|
||||
---
|
||||
|
||||
## Problem Statement
|
||||
|
||||
Currently, Noodl users who want to use external backends must:
|
||||
|
||||
1. Manually configure HTTP requests for each endpoint
|
||||
2. Handle authentication, pagination, and error handling themselves
|
||||
3. Build custom UI for CRUD operations
|
||||
4. No schema introspection - everything is manual
|
||||
|
||||
---
|
||||
|
||||
## Solution: UBA System
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ NOODL DATA LAYER │
|
||||
│ │
|
||||
│ Query Node → UBA Adapter → Backend API → Response │
|
||||
│ │
|
||||
│ The adapter handles: │
|
||||
│ • Authentication (Bearer, Basic, API Key) │
|
||||
│ • Schema introspection │
|
||||
│ • Query translation │
|
||||
│ • Pagination │
|
||||
│ • Error normalization │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task Breakdown
|
||||
|
||||
| Task | Name | Description | Effort |
|
||||
| ------- | ----------------- | --------------------------------- | --------- |
|
||||
| UBA-001 | Foundation | Schema parser, basic field types | 2-3 weeks |
|
||||
| UBA-002 | Field Types | Complex fields (relations, JSON) | 1-2 weeks |
|
||||
| UBA-003 | Debug System | Live debugging panel | 1-2 weeks |
|
||||
| UBA-004 | Polish | UX improvements, error handling | 1 week |
|
||||
| UBA-005 | Reference Backend | Example implementation (Directus) | 1-2 weeks |
|
||||
| UBA-006 | Community | Adapter sharing, marketplace | 2-3 weeks |
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
See individual task files for detailed architecture:
|
||||
|
||||
- [UBA-001-FOUNDATION.md](./UBA-001-FOUNDATION.md) - Core system architecture
|
||||
- [UBA-002-FIELD-TYPES.md](./UBA-002-FIELD-TYPES.md) - Field type system
|
||||
- [UBA-003-DEBUG-SYSTEM.md](./UBA-003-DEBUG-SYSTEM.md) - Debug infrastructure
|
||||
- [UBA-004-POLISH.md](./UBA-004-POLISH.md) - UX polish tasks
|
||||
- [UBA-005-REFERENCE-BACKEND.md](./UBA-005-REFERENCE-BACKEND.md) - Directus adapter
|
||||
- [UBA-006-COMMUNITY.md](./UBA-006-COMMUNITY.md) - Community features
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Phase 3 Editor UX (property panel infrastructure)
|
||||
- HTTP Node improvements (Phase 2/3)
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] Users can configure Directus/Supabase connection via UI
|
||||
- [ ] Schema introspection populates dropdowns automatically
|
||||
- [ ] CRUD operations work with configured adapters
|
||||
- [ ] Debug panel shows live request/response data
|
||||
- [ ] At least 3 backend adapters available
|
||||
|
||||
---
|
||||
|
||||
## History
|
||||
|
||||
- **2026-01-07**: Moved from Phase 3 TASK-008 to dedicated Phase 6
|
||||
- Previously named "granular-deployment" which was misleading
|
||||
|
||||
---
|
||||
|
||||
_Last Updated: January 2026_
|
||||
1798
dev-docs/tasks/phase-6-uba-system/UBA-001-FOUNDATION.md
Normal file
1798
dev-docs/tasks/phase-6-uba-system/UBA-001-FOUNDATION.md
Normal file
File diff suppressed because it is too large
Load Diff
1792
dev-docs/tasks/phase-6-uba-system/UBA-002-FIELD-TYPES.md
Normal file
1792
dev-docs/tasks/phase-6-uba-system/UBA-002-FIELD-TYPES.md
Normal file
File diff suppressed because it is too large
Load Diff
1701
dev-docs/tasks/phase-6-uba-system/UBA-003-DEBUG-SYSTEM.md
Normal file
1701
dev-docs/tasks/phase-6-uba-system/UBA-003-DEBUG-SYSTEM.md
Normal file
File diff suppressed because it is too large
Load Diff
871
dev-docs/tasks/phase-6-uba-system/UBA-004-POLISH.md
Normal file
871
dev-docs/tasks/phase-6-uba-system/UBA-004-POLISH.md
Normal file
@@ -0,0 +1,871 @@
|
||||
# Phase 6D: UBA Polish & Documentation
|
||||
|
||||
## Error Handling, Performance, and Developer Documentation
|
||||
|
||||
**Phase:** 6D of 6
|
||||
**Duration:** 2 weeks (10 working days)
|
||||
**Priority:** HIGH
|
||||
**Status:** NOT STARTED
|
||||
**Depends On:** Phase 6A, 6B, 6C complete
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Phase 6D focuses on production readiness: comprehensive error handling, performance optimization, accessibility compliance, and complete documentation for both users and backend developers.
|
||||
|
||||
This phase transforms the UBA system from "it works" to "it works reliably and is well-documented."
|
||||
|
||||
### Key Outcomes
|
||||
|
||||
1. **Robust Error Handling** - Graceful failures, helpful messages, recovery paths
|
||||
2. **Performance** - Smooth UI even with complex schemas and many events
|
||||
3. **Accessibility** - Full keyboard navigation, screen reader support
|
||||
4. **Documentation** - Schema reference, backend guide, tutorials
|
||||
|
||||
---
|
||||
|
||||
## Goals
|
||||
|
||||
1. **Comprehensive error handling** - Every failure mode handled gracefully
|
||||
2. **Performance optimization** - Lazy loading, virtualization, caching
|
||||
3. **Backend Services integration** - UBA seamlessly integrated into existing panel
|
||||
4. **Complete documentation** - Reference docs, tutorials, examples
|
||||
5. **Example schemas** - Templates for common backend types
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Phase 6A complete ✅
|
||||
- Phase 6B complete ✅
|
||||
- Phase 6C complete ✅
|
||||
|
||||
---
|
||||
|
||||
## Task Breakdown
|
||||
|
||||
### UBA-018: Error Handling
|
||||
**Effort:** 3 days
|
||||
**Assignee:** TBD
|
||||
**Branch:** `feature/uba-018-error-handling`
|
||||
|
||||
#### Description
|
||||
|
||||
Implement comprehensive error handling for all UBA operations with user-friendly messages, recovery suggestions, and graceful degradation.
|
||||
|
||||
#### Error Categories
|
||||
|
||||
| Category | Examples | User Impact |
|
||||
|----------|----------|-------------|
|
||||
| **Network** | Backend unreachable, timeout, DNS failure | Can't fetch schema or push config |
|
||||
| **Schema** | Invalid YAML, unsupported version, missing fields | Can't render config panel |
|
||||
| **Auth** | Token expired, invalid credentials, 403 | Can't communicate with backend |
|
||||
| **Validation** | Required field missing, pattern mismatch | Can't save config |
|
||||
| **Runtime** | Debug stream disconnected, event parse error | Degraded debugging |
|
||||
|
||||
#### Files to Create
|
||||
|
||||
```
|
||||
packages/noodl-editor/src/editor/src/models/UBA/
|
||||
├── errors/
|
||||
│ ├── UBAError.ts
|
||||
│ ├── NetworkError.ts
|
||||
│ ├── SchemaError.ts
|
||||
│ ├── AuthError.ts
|
||||
│ ├── ValidationError.ts
|
||||
│ └── index.ts
|
||||
└── ErrorRecovery.ts
|
||||
|
||||
packages/noodl-editor/src/editor/src/views/UBA/
|
||||
├── ErrorBoundary.tsx
|
||||
├── ErrorDisplay.tsx
|
||||
└── ErrorRecoveryActions.tsx
|
||||
```
|
||||
|
||||
#### Implementation
|
||||
|
||||
```typescript
|
||||
// UBAError.ts
|
||||
export abstract class UBAError extends Error {
|
||||
abstract readonly code: string;
|
||||
abstract readonly category: 'network' | 'schema' | 'auth' | 'validation' | 'runtime';
|
||||
abstract readonly recoverable: boolean;
|
||||
abstract readonly userMessage: string;
|
||||
abstract readonly recoveryActions: RecoveryAction[];
|
||||
|
||||
readonly timestamp: Date = new Date();
|
||||
readonly context: Record<string, any> = {};
|
||||
|
||||
constructor(message: string, context?: Record<string, any>) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
if (context) {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
code: this.code,
|
||||
category: this.category,
|
||||
message: this.message,
|
||||
userMessage: this.userMessage,
|
||||
recoverable: this.recoverable,
|
||||
context: this.context,
|
||||
timestamp: this.timestamp.toISOString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface RecoveryAction {
|
||||
label: string;
|
||||
action: () => void | Promise<void>;
|
||||
primary?: boolean;
|
||||
}
|
||||
|
||||
// NetworkError.ts
|
||||
export class BackendUnreachableError extends UBAError {
|
||||
readonly code = 'BACKEND_UNREACHABLE';
|
||||
readonly category = 'network' as const;
|
||||
readonly recoverable = true;
|
||||
|
||||
get userMessage(): string {
|
||||
return `Cannot connect to backend at ${this.context.url}. Please check that the backend is running and accessible.`;
|
||||
}
|
||||
|
||||
get recoveryActions(): RecoveryAction[] {
|
||||
return [
|
||||
{
|
||||
label: 'Retry Connection',
|
||||
action: this.context.retry,
|
||||
primary: true
|
||||
},
|
||||
{
|
||||
label: 'Check Backend Status',
|
||||
action: () => window.open(this.context.healthUrl, '_blank')
|
||||
},
|
||||
{
|
||||
label: 'Use Cached Schema',
|
||||
action: this.context.useCached
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class RequestTimeoutError extends UBAError {
|
||||
readonly code = 'REQUEST_TIMEOUT';
|
||||
readonly category = 'network' as const;
|
||||
readonly recoverable = true;
|
||||
|
||||
get userMessage(): string {
|
||||
return `Request to backend timed out after ${this.context.timeout}ms. The backend may be overloaded.`;
|
||||
}
|
||||
|
||||
get recoveryActions(): RecoveryAction[] {
|
||||
return [
|
||||
{ label: 'Retry', action: this.context.retry, primary: true },
|
||||
{ label: 'Increase Timeout', action: this.context.increaseTimeout }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// SchemaError.ts
|
||||
export class SchemaParseError extends UBAError {
|
||||
readonly code = 'SCHEMA_PARSE_ERROR';
|
||||
readonly category = 'schema' as const;
|
||||
readonly recoverable = false;
|
||||
|
||||
get userMessage(): string {
|
||||
const location = this.context.line ? ` at line ${this.context.line}` : '';
|
||||
return `Failed to parse backend schema${location}: ${this.context.parseError}`;
|
||||
}
|
||||
|
||||
get recoveryActions(): RecoveryAction[] {
|
||||
return [
|
||||
{ label: 'View Raw Schema', action: this.context.viewRaw },
|
||||
{ label: 'Report Issue', action: () => this.context.reportIssue() }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
export class UnsupportedSchemaVersionError extends UBAError {
|
||||
readonly code = 'UNSUPPORTED_SCHEMA_VERSION';
|
||||
readonly category = 'schema' as const;
|
||||
readonly recoverable = false;
|
||||
|
||||
get userMessage(): string {
|
||||
return `Schema version ${this.context.schemaVersion} is not supported. Nodegx supports up to version ${this.context.supportedVersion}.`;
|
||||
}
|
||||
|
||||
get recoveryActions(): RecoveryAction[] {
|
||||
return [
|
||||
{ label: 'Check for Updates', action: this.context.checkUpdates, primary: true }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// AuthError.ts
|
||||
export class AuthenticationError extends UBAError {
|
||||
readonly code = 'AUTH_FAILED';
|
||||
readonly category = 'auth' as const;
|
||||
readonly recoverable = true;
|
||||
|
||||
get userMessage(): string {
|
||||
if (this.context.status === 401) {
|
||||
return 'Authentication failed. Your credentials may be invalid or expired.';
|
||||
}
|
||||
if (this.context.status === 403) {
|
||||
return 'Access denied. You may not have permission to access this backend.';
|
||||
}
|
||||
return 'Authentication error occurred.';
|
||||
}
|
||||
|
||||
get recoveryActions(): RecoveryAction[] {
|
||||
return [
|
||||
{ label: 'Update Credentials', action: this.context.openAuthDialog, primary: true },
|
||||
{ label: 'Remove Backend', action: this.context.removeBackend }
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorBoundary.tsx
|
||||
export class UBAErrorBoundary extends React.Component<
|
||||
{ children: React.ReactNode; fallback?: React.ReactNode },
|
||||
{ error: Error | null; errorInfo: React.ErrorInfo | null }
|
||||
> {
|
||||
state = { error: null, errorInfo: null };
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
||||
this.setState({ errorInfo });
|
||||
console.error('UBA Error Boundary caught error:', error, errorInfo);
|
||||
}
|
||||
|
||||
handleRetry = () => {
|
||||
this.setState({ error: null, errorInfo: null });
|
||||
};
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return this.props.fallback || (
|
||||
<ErrorDisplay
|
||||
error={this.state.error}
|
||||
errorInfo={this.state.errorInfo}
|
||||
onRetry={this.handleRetry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorDisplay.tsx
|
||||
export function ErrorDisplay({ error, errorInfo, onRetry, compact }: ErrorDisplayProps) {
|
||||
const isUBAError = error instanceof UBAError;
|
||||
|
||||
if (compact) {
|
||||
return (
|
||||
<div className={css['error-compact']}>
|
||||
<IconAlertCircle />
|
||||
<span>{isUBAError ? error.userMessage : error.message}</span>
|
||||
{onRetry && <button onClick={onRetry}>Retry</button>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={css['error-display']}>
|
||||
<div className={css['error-header']}>
|
||||
<IconAlertCircle />
|
||||
<h3>Something went wrong</h3>
|
||||
</div>
|
||||
|
||||
<p className={css['error-message']}>
|
||||
{isUBAError ? error.userMessage : error.message}
|
||||
</p>
|
||||
|
||||
{isUBAError && error.recoveryActions.length > 0 && (
|
||||
<div className={css['recovery-actions']}>
|
||||
{error.recoveryActions.map((action, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => action.action()}
|
||||
className={cn(css['action-button'], action.primary && css['primary'])}
|
||||
>
|
||||
{action.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<details className={css['error-details']}>
|
||||
<summary>Technical Details</summary>
|
||||
<pre>{isUBAError ? JSON.stringify(error.toJSON(), null, 2) : error.stack}</pre>
|
||||
</details>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Error Scenarios
|
||||
|
||||
| Scenario | Error Class | Recovery |
|
||||
|----------|-------------|----------|
|
||||
| Backend URL returns 404 | `BackendUnreachableError` | Retry, check URL |
|
||||
| Schema YAML syntax error | `SchemaParseError` | View raw, report |
|
||||
| Schema version too new | `UnsupportedSchemaVersionError` | Update Nodegx |
|
||||
| API key expired | `AuthenticationError` | Update credentials |
|
||||
| Config push rejected | `ConfigRejectedError` | Show field errors |
|
||||
| WebSocket disconnected | `DebugDisconnectedError` | Auto-reconnect |
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
- [ ] All error classes implemented
|
||||
- [ ] User-friendly messages for all errors
|
||||
- [ ] Recovery actions work correctly
|
||||
- [ ] Error boundary catches React errors
|
||||
- [ ] Development mode shows details
|
||||
- [ ] Production mode hides internals
|
||||
|
||||
---
|
||||
|
||||
### UBA-019: Performance Optimization
|
||||
**Effort:** 3 days
|
||||
**Assignee:** TBD
|
||||
**Branch:** `feature/uba-019-performance`
|
||||
|
||||
#### Description
|
||||
|
||||
Optimize UBA system performance through lazy loading, memoization, virtualization, and efficient caching.
|
||||
|
||||
#### Performance Goals
|
||||
|
||||
| Metric | Target | Method |
|
||||
|--------|--------|--------|
|
||||
| Schema parse time | < 50ms | Optimize parser |
|
||||
| Config panel render | < 100ms | Lazy load fields |
|
||||
| Field type switch | < 16ms | Memoization |
|
||||
| Debug event render | < 5ms | Virtualization |
|
||||
| Memory (1000 events) | < 50MB | Ring buffer |
|
||||
|
||||
#### Key Optimizations
|
||||
|
||||
##### 1. Lazy Loading Field Renderers
|
||||
|
||||
```typescript
|
||||
// fields/index.ts
|
||||
const fieldComponents: Record<string, React.LazyExoticComponent<any>> = {
|
||||
string: React.lazy(() => import('./StringField')),
|
||||
text: React.lazy(() => import('./TextField')),
|
||||
number: React.lazy(() => import('./NumberField')),
|
||||
// Complex types loaded only when needed
|
||||
field_mapping: React.lazy(() => import('./FieldMappingField')),
|
||||
prompt: React.lazy(() => import('./PromptField')),
|
||||
code: React.lazy(() => import('./CodeField')),
|
||||
};
|
||||
|
||||
export function FieldRenderer({ field, ...props }: FieldRendererProps) {
|
||||
const Component = fieldComponents[field.type] || fieldComponents.string;
|
||||
|
||||
return (
|
||||
<React.Suspense fallback={<FieldSkeleton />}>
|
||||
<Component field={field} {...props} />
|
||||
</React.Suspense>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
##### 2. Memoized Components
|
||||
|
||||
```typescript
|
||||
// ConfigSection.tsx
|
||||
export const ConfigSection = React.memo(function ConfigSection({
|
||||
section, values, errors, onChange, disabled
|
||||
}: ConfigSectionProps) {
|
||||
const visibility = useMemo(
|
||||
() => calculateVisibility(section.fields, values),
|
||||
[section.fields, values]
|
||||
);
|
||||
|
||||
const handleFieldChange = useCallback(
|
||||
(fieldId: string) => (value: any) => onChange(`${section.id}.${fieldId}`, value),
|
||||
[section.id, onChange]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={css['section']}>
|
||||
{section.fields.map(field => (
|
||||
visibility.get(field.id)?.visible && (
|
||||
<MemoizedFieldRenderer
|
||||
key={field.id}
|
||||
field={field}
|
||||
value={getNestedValue(values, `${section.id}.${field.id}`)}
|
||||
onChange={handleFieldChange(field.id)}
|
||||
error={errors[`${section.id}.${field.id}`]}
|
||||
disabled={disabled}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
##### 3. Virtualized Debug Event List
|
||||
|
||||
```typescript
|
||||
// DebugEventTree.tsx
|
||||
import { VariableSizeList } from 'react-window';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
|
||||
export function DebugEventTree({ events, eventSchema }: DebugEventTreeProps) {
|
||||
const [expandedEvents, setExpandedEvents] = useState<Set<string>>(new Set());
|
||||
|
||||
const getItemSize = useCallback((index: number) => {
|
||||
const event = events[index];
|
||||
return expandedEvents.has(event.id) ? 200 : 48;
|
||||
}, [events, expandedEvents]);
|
||||
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<VariableSizeList
|
||||
height={height}
|
||||
width={width}
|
||||
itemCount={events.length}
|
||||
itemSize={getItemSize}
|
||||
overscanCount={5}
|
||||
>
|
||||
{({ index, style }) => (
|
||||
<div style={style}>
|
||||
<DebugEventItem
|
||||
event={events[index]}
|
||||
expanded={expandedEvents.has(events[index].id)}
|
||||
onToggle={() => toggleExpand(events[index].id)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</VariableSizeList>
|
||||
)}
|
||||
</AutoSizer>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
##### 4. Schema Caching with ETag
|
||||
|
||||
```typescript
|
||||
// SchemaCache.ts
|
||||
export class SchemaCache {
|
||||
private cache: Map<string, CacheEntry> = new Map();
|
||||
private ttl = 24 * 60 * 60 * 1000; // 24 hours
|
||||
|
||||
async fetchWithCache(url: string, auth?: AuthConfig): Promise<UBASchema> {
|
||||
const cached = this.cache.get(url);
|
||||
|
||||
const headers: HeadersInit = { ...buildAuthHeaders(auth) };
|
||||
if (cached?.etag) {
|
||||
headers['If-None-Match'] = cached.etag;
|
||||
}
|
||||
|
||||
const response = await fetch(url, { headers });
|
||||
|
||||
if (response.status === 304 && cached) {
|
||||
return cached.schema; // Not modified
|
||||
}
|
||||
|
||||
const rawYaml = await response.text();
|
||||
const schema = parseSchema(rawYaml);
|
||||
const etag = response.headers.get('ETag') || undefined;
|
||||
|
||||
this.cache.set(url, { schema, fetchedAt: Date.now(), etag });
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Performance Tests
|
||||
|
||||
```typescript
|
||||
describe('UBA Performance', () => {
|
||||
it('parses complex schema in < 50ms', async () => {
|
||||
const start = performance.now();
|
||||
await parseSchema(complexSchemaYaml);
|
||||
expect(performance.now() - start).toBeLessThan(50);
|
||||
});
|
||||
|
||||
it('renders config panel in < 100ms', async () => {
|
||||
const start = performance.now();
|
||||
render(<ConfigPanel schema={complexSchema} />);
|
||||
await waitFor(() => screen.getByRole('tablist'));
|
||||
expect(performance.now() - start).toBeLessThan(100);
|
||||
});
|
||||
|
||||
it('handles 1000 debug events without memory leak', () => {
|
||||
const store = new DebugStore({ maxEvents: 1000 });
|
||||
for (let i = 0; i < 2000; i++) {
|
||||
store.addEvent(generateMockEvent(i));
|
||||
}
|
||||
expect(store.getAllEvents().length).toBe(1000);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
- [ ] Lazy loading implemented
|
||||
- [ ] Component memoization applied
|
||||
- [ ] Debug list virtualized
|
||||
- [ ] Schema caching with ETag
|
||||
- [ ] Performance benchmarks pass
|
||||
|
||||
---
|
||||
|
||||
### UBA-020: Backend Services Integration
|
||||
**Effort:** 2 days
|
||||
**Assignee:** TBD
|
||||
**Branch:** `feature/uba-020-backend-integration`
|
||||
|
||||
#### Description
|
||||
|
||||
Integrate UBA backends into the existing Backend Services panel for a unified experience.
|
||||
|
||||
#### Integration Points
|
||||
|
||||
1. **Backend List** - UBA backends appear alongside Directus, Supabase
|
||||
2. **Add Backend Dialog** - "Schema-Configured Backend" option
|
||||
3. **Config Button** - Opens UBA Config Panel
|
||||
4. **Debug Button** - Opens Debug Panel (if supported)
|
||||
5. **Health Status** - Show connection status
|
||||
|
||||
#### Implementation
|
||||
|
||||
```typescript
|
||||
// BackendPanel.tsx (updated)
|
||||
export function BackendPanel() {
|
||||
const { directusBackends, supabaseBackends } = useBYOBBackends();
|
||||
const { ubaBackends } = useUBABackends();
|
||||
|
||||
return (
|
||||
<div className={css['backend-panel']}>
|
||||
<section className={css['section']}>
|
||||
<h3>Data Backends</h3>
|
||||
<BackendList backends={[...directusBackends, ...supabaseBackends]} />
|
||||
</section>
|
||||
|
||||
<section className={css['section']}>
|
||||
<h3>Schema-Configured Backends</h3>
|
||||
{ubaBackends.length === 0 ? (
|
||||
<EmptyState message="No schema-configured backends" />
|
||||
) : (
|
||||
ubaBackends.map(backend => (
|
||||
<UBABackendItem
|
||||
key={backend.id}
|
||||
backend={backend}
|
||||
onConfigure={() => openConfigPanel(backend)}
|
||||
onDebug={() => openDebugPanel(backend)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</section>
|
||||
|
||||
<Button onClick={openAddDialog}>
|
||||
<IconPlus /> Add Backend
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// UBABackendItem.tsx
|
||||
export function UBABackendItem({ backend, onConfigure, onDebug, onRemove }) {
|
||||
const [healthStatus, setHealthStatus] = useState<'healthy' | 'unhealthy' | 'checking'>('checking');
|
||||
|
||||
useEffect(() => {
|
||||
checkHealth(backend).then(setHealthStatus);
|
||||
}, [backend]);
|
||||
|
||||
return (
|
||||
<div className={css['backend-item']}>
|
||||
<div className={css['backend-info']}>
|
||||
<h4>{backend.schema.backend.name}</h4>
|
||||
<p>{backend.url}</p>
|
||||
<HealthBadge status={healthStatus} />
|
||||
</div>
|
||||
|
||||
<div className={css['actions']}>
|
||||
<Button onClick={onConfigure}><IconSettings /></Button>
|
||||
{backend.schema.debug?.enabled && (
|
||||
<Button onClick={onDebug}><IconBug /></Button>
|
||||
)}
|
||||
<Button onClick={onRemove}><IconTrash /></Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
- [ ] UBA backends appear in panel
|
||||
- [ ] Add dialog includes UBA option
|
||||
- [ ] Configure opens Config Panel
|
||||
- [ ] Debug opens Debug Panel
|
||||
- [ ] Health status displayed
|
||||
|
||||
---
|
||||
|
||||
### UBA-021: Documentation
|
||||
**Effort:** 4 days
|
||||
**Assignee:** TBD
|
||||
**Branch:** `feature/uba-021-documentation`
|
||||
|
||||
#### Description
|
||||
|
||||
Create comprehensive documentation for UBA.
|
||||
|
||||
#### Documentation Structure
|
||||
|
||||
```
|
||||
docs/uba/
|
||||
├── README.md # Overview and quick start
|
||||
├── schema-specification.md # Complete schema reference
|
||||
├── field-types.md # All field types with examples
|
||||
├── backend-implementation.md # How to make backends UBA-compatible
|
||||
├── tutorials/
|
||||
│ ├── first-backend.md # Create your first UBA backend
|
||||
│ ├── field-mapping.md # Using field mappings
|
||||
│ ├── debug-integration.md # Adding debug support
|
||||
│ └── advanced-validation.md # Custom validation
|
||||
├── api-reference/
|
||||
│ ├── endpoints.md # Required and optional endpoints
|
||||
│ ├── events.md # Debug event format
|
||||
│ └── responses.md # Response formats
|
||||
└── troubleshooting.md # Common issues and solutions
|
||||
```
|
||||
|
||||
#### Key Content
|
||||
|
||||
##### Quick Start (README.md)
|
||||
|
||||
```markdown
|
||||
# Universal Backend Adapter (UBA)
|
||||
|
||||
Connect any backend to Nodegx with a simple YAML schema.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Create Schema
|
||||
|
||||
```yaml
|
||||
schema_version: "1.0"
|
||||
backend:
|
||||
id: "my-backend"
|
||||
name: "My Backend"
|
||||
version: "1.0.0"
|
||||
endpoints:
|
||||
config: "/nodegx/config"
|
||||
sections:
|
||||
- id: "settings"
|
||||
fields:
|
||||
- id: "api_key"
|
||||
type: "secret"
|
||||
name: "API Key"
|
||||
required: true
|
||||
```
|
||||
|
||||
### 2. Serve Schema
|
||||
|
||||
```python
|
||||
@app.get("/.well-known/nodegx-schema.yaml")
|
||||
async def get_schema():
|
||||
return FileResponse("nodegx-schema.yaml")
|
||||
```
|
||||
|
||||
### 3. Implement Config Endpoint
|
||||
|
||||
```python
|
||||
@app.post("/nodegx/config")
|
||||
async def apply_config(request: ConfigRequest):
|
||||
# Apply configuration
|
||||
return {"success": True, "applied_at": datetime.utcnow().isoformat()}
|
||||
```
|
||||
|
||||
[Full Guide →](./backend-implementation.md)
|
||||
```
|
||||
|
||||
##### Backend Implementation Guide
|
||||
|
||||
- Python/FastAPI example
|
||||
- Node.js/Express example
|
||||
- Go example
|
||||
- Health endpoint
|
||||
- Debug stream implementation
|
||||
- Testing your integration
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
- [ ] README with quick start
|
||||
- [ ] Schema specification complete
|
||||
- [ ] All field types documented
|
||||
- [ ] Backend guide with examples
|
||||
- [ ] 4+ tutorials
|
||||
- [ ] API reference
|
||||
- [ ] Troubleshooting guide
|
||||
|
||||
---
|
||||
|
||||
### UBA-022: Example Schemas
|
||||
**Effort:** 2 days
|
||||
**Assignee:** TBD
|
||||
**Branch:** `feature/uba-022-examples`
|
||||
|
||||
#### Description
|
||||
|
||||
Create example schemas for common backend types.
|
||||
|
||||
#### Examples
|
||||
|
||||
1. **Minimal** - Simplest possible schema
|
||||
2. **AI Agent** - Full-featured with debug
|
||||
3. **Analytics** - Event tracking backend
|
||||
4. **Webhook Handler** - With code field
|
||||
|
||||
```yaml
|
||||
# examples/minimal.yaml
|
||||
schema_version: "1.0"
|
||||
backend:
|
||||
id: "minimal"
|
||||
name: "Minimal Backend"
|
||||
version: "1.0.0"
|
||||
endpoints:
|
||||
config: "/config"
|
||||
sections:
|
||||
- id: "settings"
|
||||
name: "Settings"
|
||||
fields:
|
||||
- id: "enabled"
|
||||
type: "boolean"
|
||||
name: "Enabled"
|
||||
default: true
|
||||
```
|
||||
|
||||
```yaml
|
||||
# examples/ai-agent.yaml
|
||||
schema_version: "1.0"
|
||||
backend:
|
||||
id: "ai-agent"
|
||||
name: "AI Agent"
|
||||
version: "1.0.0"
|
||||
endpoints:
|
||||
config: "/nodegx/config"
|
||||
health: "/health"
|
||||
debug_stream: "/nodegx/debug"
|
||||
capabilities:
|
||||
hot_reload: true
|
||||
debug: true
|
||||
sections:
|
||||
- id: "llm"
|
||||
name: "Language Model"
|
||||
fields:
|
||||
- id: "provider"
|
||||
type: "select"
|
||||
options:
|
||||
- value: "anthropic"
|
||||
label: "Anthropic"
|
||||
- value: "openai"
|
||||
label: "OpenAI"
|
||||
- id: "api_key"
|
||||
type: "secret"
|
||||
required: true
|
||||
- id: "temperature"
|
||||
type: "slider"
|
||||
min: 0
|
||||
max: 2
|
||||
default: 0.7
|
||||
- id: "tools"
|
||||
name: "Tools"
|
||||
fields:
|
||||
- id: "web_search"
|
||||
type: "tool_toggle"
|
||||
name: "Web Search"
|
||||
default: true
|
||||
- id: "prompts"
|
||||
name: "Prompts"
|
||||
fields:
|
||||
- id: "system"
|
||||
type: "prompt"
|
||||
variables:
|
||||
- name: "agent_name"
|
||||
source: "project.name"
|
||||
debug:
|
||||
enabled: true
|
||||
event_schema:
|
||||
- id: "agent_step"
|
||||
fields:
|
||||
- id: "step"
|
||||
type: "string"
|
||||
- id: "duration_ms"
|
||||
type: "number"
|
||||
format: "duration"
|
||||
```
|
||||
|
||||
#### Acceptance Criteria
|
||||
|
||||
- [ ] Minimal example
|
||||
- [ ] AI Agent example
|
||||
- [ ] Analytics example
|
||||
- [ ] Webhook example
|
||||
- [ ] All validate correctly
|
||||
|
||||
---
|
||||
|
||||
## Phase 6D Checklist
|
||||
|
||||
### UBA-018: Error Handling
|
||||
- [ ] Error class hierarchy
|
||||
- [ ] All error types
|
||||
- [ ] Error boundary
|
||||
- [ ] Error display
|
||||
- [ ] Recovery actions
|
||||
|
||||
### UBA-019: Performance
|
||||
- [ ] Lazy loading
|
||||
- [ ] Memoization
|
||||
- [ ] Virtualization
|
||||
- [ ] Caching
|
||||
- [ ] Benchmarks pass
|
||||
|
||||
### UBA-020: Integration
|
||||
- [ ] Backend panel updated
|
||||
- [ ] UBA backend item
|
||||
- [ ] Add dialog option
|
||||
- [ ] Panel connections
|
||||
|
||||
### UBA-021: Documentation
|
||||
- [ ] README
|
||||
- [ ] Schema spec
|
||||
- [ ] Field types
|
||||
- [ ] Backend guide
|
||||
- [ ] Tutorials
|
||||
- [ ] API reference
|
||||
|
||||
### UBA-022: Examples
|
||||
- [ ] Minimal
|
||||
- [ ] AI Agent
|
||||
- [ ] Analytics
|
||||
- [ ] Webhook
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] All errors handled gracefully
|
||||
- [ ] Performance benchmarks pass
|
||||
- [ ] Documentation complete
|
||||
- [ ] Examples validated
|
||||
1910
dev-docs/tasks/phase-6-uba-system/UBA-005-REFERENCE-BACKEND.md
Normal file
1910
dev-docs/tasks/phase-6-uba-system/UBA-005-REFERENCE-BACKEND.md
Normal file
File diff suppressed because it is too large
Load Diff
1016
dev-docs/tasks/phase-6-uba-system/UBA-006-COMMUNITY.md
Normal file
1016
dev-docs/tasks/phase-6-uba-system/UBA-006-COMMUNITY.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user