create-feltdb 0.4.1 → 0.4.3

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.
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Application identity management
3
+ *
4
+ * Handles:
5
+ * - FELTDB_APP_NAME: Human-readable application name
6
+ * - FELTDB_APP_ID: UUID-based stable identifier
7
+ * - FELTDB_VERSION: Semantic versioning
8
+ * - Version matching between components
9
+ */
10
+ import crypto from 'crypto';
11
+ /**
12
+ * Generate a stable UUID from application name
13
+ * Ensures same name always produces same ID
14
+ */
15
+ export function generateAppId(appName) {
16
+ const hash = crypto
17
+ .createHash('sha256')
18
+ .update(appName)
19
+ .digest('hex');
20
+ // Convert to UUID v5-like format
21
+ const uuid = [
22
+ hash.substring(0, 8),
23
+ hash.substring(8, 12),
24
+ '5' + hash.substring(13, 16),
25
+ ((parseInt(hash.substring(16, 18), 16) & 0x3f) | 0x80).toString(16).padStart(2, '0') +
26
+ hash.substring(18, 20),
27
+ hash.substring(20, 32),
28
+ ].join('-');
29
+ return uuid;
30
+ }
31
+ /**
32
+ * Sanitize application name for use in identifiers
33
+ */
34
+ export function sanitizeAppName(name) {
35
+ return (name
36
+ // Replace spaces with hyphens
37
+ .replace(/\s+/g, '-')
38
+ // Remove non-alphanumeric except hyphens
39
+ .replace(/[^a-z0-9-]/gi, '')
40
+ // Remove leading/trailing hyphens
41
+ .replace(/^-+|-+$/g, '')
42
+ // Limit to 50 chars
43
+ .substring(0, 50)
44
+ .toLowerCase());
45
+ }
46
+ /**
47
+ * Get current FeltDB version from package.json
48
+ */
49
+ export function getCurrentFeltDBVersion() {
50
+ // This would be replaced at build time with the actual version
51
+ return '0.4.2';
52
+ }
53
+ /**
54
+ * Create application identity manifest
55
+ */
56
+ export function createApplicationIdentity(appName, feltdbVersion) {
57
+ const version = feltdbVersion || getCurrentFeltDBVersion();
58
+ const sanitized = sanitizeAppName(appName);
59
+ const appId = generateAppId(sanitized);
60
+ return {
61
+ name: sanitized,
62
+ id: appId,
63
+ version,
64
+ createdAt: new Date().toISOString(),
65
+ components: {
66
+ app: version,
67
+ feltdb: version,
68
+ studio: version,
69
+ },
70
+ };
71
+ }
72
+ /**
73
+ * Generate version manifest for Docker image metadata
74
+ */
75
+ export function generateVersionLabels(identity) {
76
+ return {
77
+ 'app.feltdb/app-name': identity.name,
78
+ 'app.feltdb/app-id': identity.id,
79
+ 'app.feltdb/version': identity.version,
80
+ 'app.feltdb/created': identity.createdAt,
81
+ 'app.feltdb/component.app': identity.components.app,
82
+ 'app.feltdb/component.feltdb': identity.components.feltdb,
83
+ 'app.feltdb/component.studio': identity.components.studio,
84
+ };
85
+ }
86
+ /**
87
+ * Validate version compatibility
88
+ */
89
+ export function validateVersionCompatibility(appVersion, feltdbVersion, studioVersion) {
90
+ const warnings = [];
91
+ // All components should be same major.minor version for safety
92
+ const extractMajorMinor = (v) => v.split('.').slice(0, 2).join('.');
93
+ const appMajorMinor = extractMajorMinor(appVersion);
94
+ const feltdbMajorMinor = extractMajorMinor(feltdbVersion);
95
+ const studioMajorMinor = extractMajorMinor(studioVersion);
96
+ if (appMajorMinor !== feltdbMajorMinor) {
97
+ warnings.push(`App version ${appVersion} differs from FeltDB ${feltdbVersion}`);
98
+ }
99
+ if (feltdbMajorMinor !== studioMajorMinor) {
100
+ warnings.push(`FeltDB version ${feltdbVersion} differs from Studio ${studioVersion}`);
101
+ }
102
+ return {
103
+ compatible: warnings.length === 0,
104
+ warnings,
105
+ };
106
+ }
107
+ /**
108
+ * Generate .feltdb/identity.json manifest
109
+ */
110
+ export function generateIdentityManifest(identity) {
111
+ return JSON.stringify(identity, null, 2);
112
+ }
113
+ /**
114
+ * Generate environment file with version information
115
+ */
116
+ export function generateVersionEnv(identity) {
117
+ return `# Application Identity
118
+ # Generated at: ${identity.createdAt}
119
+
120
+ FELTDB_APP_NAME=${identity.name}
121
+ FELTDB_APP_ID=${identity.id}
122
+ FELTDB_VERSION=${identity.version}
123
+
124
+ # Component Versions
125
+ FELTDB_APP_VERSION=${identity.components.app}
126
+ FELTDB_CORE_VERSION=${identity.components.feltdb}
127
+ FELTDB_STUDIO_VERSION=${identity.components.studio}
128
+
129
+ # Build Info
130
+ FELTDB_BUILT_AT=${new Date().toISOString()}
131
+ `;
132
+ }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * CLI lifecycle scripts for FeltDB applications
3
+ *
4
+ * Generates scripts for common operations:
5
+ * - feltdb:up - Start services
6
+ * - feltdb:down - Stop services
7
+ * - feltdb:logs - View logs
8
+ * - feltdb:status - Check health
9
+ * - feltdb:reset - Reset data
10
+ * - feltdb:ps - List running services
11
+ */
12
+ export function generateUpScript() {
13
+ return `#!/bin/bash
14
+
15
+ # Start FeltDB application stack
16
+ # Usage: npm run feltdb:up
17
+
18
+ set -e
19
+
20
+ echo "🚀 Starting FeltDB application..."
21
+
22
+ # Load environment
23
+ if [ -f .env.local ]; then
24
+ export \$(cat .env.local | grep -v '^#' | xargs)
25
+ fi
26
+
27
+ # Build and start containers
28
+ docker-compose up -d
29
+
30
+ echo "⏳ Waiting for services to be healthy..."
31
+
32
+ # Wait for FeltDB
33
+ TIMEOUT=60
34
+ ELAPSED=0
35
+ while [ \$ELAPSED -lt \$TIMEOUT ]; do
36
+ if curl -s -f http://localhost:7700/health > /dev/null 2>&1; then
37
+ STATE=\$(curl -s http://localhost:7700/health | grep -o '"state":"[^"]*' | cut -d'"' -f4)
38
+ if [ "\$STATE" = "ready" ] || [ "\$STATE" = "recovering" ]; then
39
+ echo "✅ FeltDB is \$STATE"
40
+ break
41
+ fi
42
+ fi
43
+ sleep 2
44
+ ELAPSED=\$((ELAPSED + 2))
45
+ done
46
+
47
+ # Wait for application
48
+ ELAPSED=0
49
+ while [ \$ELAPSED -lt \$TIMEOUT ]; do
50
+ if curl -s -f http://localhost:3000/health > /dev/null 2>&1; then
51
+ echo "✅ Application is ready"
52
+ break
53
+ fi
54
+ sleep 2
55
+ ELAPSED=\$((ELAPSED + 2))
56
+ done
57
+
58
+ echo ""
59
+ echo "✨ FeltDB stack is running!"
60
+ echo ""
61
+ echo "Services:"
62
+ echo " FeltDB: http://localhost:7700"
63
+ echo " App: http://localhost:3000"
64
+ echo " Studio: http://localhost:8000"
65
+ echo ""
66
+ echo "Next: npm run feltdb:logs"
67
+ `;
68
+ }
69
+ export function generateDownScript() {
70
+ return `#!/bin/bash
71
+
72
+ # Stop FeltDB application stack
73
+ # Usage: npm run feltdb:down
74
+
75
+ echo "🛑 Stopping FeltDB application..."
76
+
77
+ docker-compose down
78
+
79
+ echo "✅ Services stopped"
80
+ `;
81
+ }
82
+ export function generateLogsScript() {
83
+ return `#!/bin/bash
84
+
85
+ # View logs from FeltDB stack
86
+ # Usage: npm run feltdb:logs [service]
87
+ # npm run feltdb:logs feltdb
88
+ # npm run feltdb:logs app
89
+ # npm run feltdb:logs studio
90
+
91
+ SERVICE=\$1
92
+
93
+ if [ -z "\$SERVICE" ]; then
94
+ echo "📋 Logs from all services (Ctrl+C to exit):"
95
+ docker-compose logs -f
96
+ else
97
+ echo "📋 Logs from \$SERVICE (Ctrl+C to exit):"
98
+ docker-compose logs -f \$SERVICE
99
+ fi
100
+ `;
101
+ }
102
+ export function generateStatusScript() {
103
+ return `#!/bin/bash
104
+
105
+ # Check health and status of FeltDB stack
106
+ # Usage: npm run feltdb:status
107
+
108
+ echo "🔍 FeltDB Application Status"
109
+ echo ""
110
+
111
+ # Docker Compose status
112
+ echo "Containers:"
113
+ docker-compose ps
114
+
115
+ echo ""
116
+
117
+ # FeltDB health
118
+ echo "FeltDB Health:"
119
+ if RESPONSE=\$(curl -s -m 5 http://localhost:7700/health 2>/dev/null); then
120
+ STATE=\$(echo \$RESPONSE | grep -o '"state":"[^"]*' | cut -d'"' -f4)
121
+ VERSION=\$(echo \$RESPONSE | grep -o '"version":"[^"]*' | cut -d'"' -f4)
122
+ echo " State: \$STATE"
123
+ echo " Version: \$VERSION"
124
+ else
125
+ echo " ❌ No response"
126
+ fi
127
+
128
+ # Application health
129
+ echo ""
130
+ echo "Application Health:"
131
+ if RESPONSE=\$(curl -s -m 5 http://localhost:3000/health 2>/dev/null); then
132
+ echo " ✅ Responding"
133
+ else
134
+ echo " ❌ No response"
135
+ fi
136
+
137
+ echo ""
138
+ echo "Log tail: npm run feltdb:logs"
139
+ `;
140
+ }
141
+ export function generateResetScript() {
142
+ return `#!/bin/bash
143
+
144
+ # Reset FeltDB data (WARNING: destructive)
145
+ # Usage: npm run feltdb:reset
146
+
147
+ echo "⚠️ This will delete all FeltDB data!"
148
+ read -p "Are you sure? (type 'yes' to confirm): " CONFIRM
149
+
150
+ if [ "\$CONFIRM" != "yes" ]; then
151
+ echo "Cancelled."
152
+ exit 1
153
+ fi
154
+
155
+ echo "🧹 Resetting FeltDB data..."
156
+
157
+ # Stop containers
158
+ docker-compose down
159
+
160
+ # Remove volumes
161
+ docker volume rm \$(docker volume ls -q | grep feltdb_data) 2>/dev/null || true
162
+
163
+ # Restart
164
+ echo "🚀 Restarting services..."
165
+ docker-compose up -d
166
+
167
+ echo "✅ Data reset complete"
168
+ `;
169
+ }
170
+ export function generatePsScript() {
171
+ return `#!/bin/bash
172
+
173
+ # List running FeltDB services
174
+ # Usage: npm run feltdb:ps
175
+
176
+ docker-compose ps
177
+ `;
178
+ }
179
+ export function generatePackageJsonScripts(runtime) {
180
+ const scripts = {
181
+ 'feltdb:up': 'bash scripts/feltdb-up.sh',
182
+ 'feltdb:down': 'bash scripts/feltdb-down.sh',
183
+ 'feltdb:logs': 'bash scripts/feltdb-logs.sh',
184
+ 'feltdb:status': 'bash scripts/feltdb-status.sh',
185
+ 'feltdb:reset': 'bash scripts/feltdb-reset.sh',
186
+ 'feltdb:ps': 'bash scripts/feltdb-ps.sh',
187
+ };
188
+ if (runtime === 'browser') {
189
+ // Browser doesn't need container scripts
190
+ return {
191
+ dev: 'vite',
192
+ build: 'tsc && vite build',
193
+ };
194
+ }
195
+ if (runtime === 'node') {
196
+ return {
197
+ dev: 'tsx src/index.ts',
198
+ build: 'tsc',
199
+ start: 'node dist/index.js',
200
+ ...scripts,
201
+ };
202
+ }
203
+ if (runtime === 'self-hosted') {
204
+ return {
205
+ dev: 'docker-compose up -d --build',
206
+ build: 'docker-compose build',
207
+ ...scripts,
208
+ };
209
+ }
210
+ return scripts;
211
+ }
package/dist/cli.js CHANGED
@@ -124,11 +124,11 @@ async function promptForOptions(defaults) {
124
124
  async function main() {
125
125
  const args = process.argv.slice(2);
126
126
  if (args.includes('--help') || args.includes('-h')) {
127
- console.log(`create-feltdb 0.4.1\n\nUsage: create-feltdb [project-name] [options]\n\nOptions:\n --runtime <browser|node|self-hosted>\n --framework <react|vanilla>\n --no-distributed\n --no-agents\n --capabilities <list>\n --no-install\n --no-start\n -y, --yes\n -h, --help\n --version`);
127
+ console.log(`create-feltdb 0.4.2\n\nUsage: create-feltdb [project-name] [options]\n\nOptions:\n --runtime <browser|node|self-hosted>\n --framework <react|vanilla>\n --no-distributed\n --no-agents\n --capabilities <list>\n --no-install\n --no-start\n -y, --yes\n -h, --help\n --version`);
128
128
  return;
129
129
  }
130
130
  if (args.includes('--version')) {
131
- console.log('0.4.1');
131
+ console.log('0.4.2');
132
132
  return;
133
133
  }
134
134
  // Find project name (first non-flag argument)
package/dist/create.js CHANGED
@@ -219,7 +219,7 @@ export async function runResearcher(
219
219
  // Create capabilities index
220
220
  const capabilitiesTs = `/**
221
221
  * Capabilities
222
- *
222
+ *
223
223
  * Define capabilities that can be executed by agents
224
224
  * across distributed peers
225
225
  */
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Docker Compose generator for self-hosted FeltDB deployments
3
+ *
4
+ * Generates:
5
+ * - docker-compose.yml with three services (FeltDB, App, Studio)
6
+ * - Dockerfile for application container
7
+ * - .dockerignore for optimized builds
8
+ * - Environment configuration
9
+ */
10
+ export function generateDockerCompose(config) {
11
+ return `version: '3.9'
12
+
13
+ services:
14
+ feltdb:
15
+ image: rkendel1/feltdb:latest
16
+ container_name: \${COMPOSE_PROJECT_NAME}-feltdb
17
+ environment:
18
+ FELTDB_APP_NAME: \${FELTDB_APP_NAME:-${config.appName}}
19
+ FELTDB_APP_ID: \${FELTDB_APP_ID:-${config.appId}}
20
+ FELTDB_VERSION: \${FELTDB_VERSION:-${config.version}}
21
+ FELTDB_PEERS: \${FELTDB_PEERS:-}
22
+ FELTDB_REPLICATION_PORT: ${config.replicationPort}
23
+ NODE_ENV: \${NODE_ENV:-production}
24
+ ports:
25
+ - "\${FELTDB_API_PORT:-${config.port}}:7700"
26
+ - "\${FELTDB_REPLICATION_PORT:-${config.replicationPort}}:${config.replicationPort}"
27
+ volumes:
28
+ - feltdb_data:/data
29
+ - ./feltdb.flow:/app/feltdb.flow:ro
30
+ healthcheck:
31
+ test: ["CMD", "curl", "-f", "http://localhost:7700/health"]
32
+ interval: 10s
33
+ timeout: 5s
34
+ retries: 3
35
+ start_period: 30s
36
+ restart: unless-stopped
37
+ networks:
38
+ - feltdb_network
39
+ labels:
40
+ app.feltdb/component: "database"
41
+ app.feltdb/version: "${config.version}"
42
+
43
+ app:
44
+ build:
45
+ context: .
46
+ dockerfile: Dockerfile
47
+ args:
48
+ NODE_ENV: production
49
+ container_name: \${COMPOSE_PROJECT_NAME}-app
50
+ environment:
51
+ FELTDB_API_URL: http://feltdb:7700
52
+ FELTDB_APP_NAME: \${FELTDB_APP_NAME:-${config.appName}}
53
+ NODE_ENV: \${NODE_ENV:-production}
54
+ ports:
55
+ - "\${APP_PORT:-3000}:3000"
56
+ depends_on:
57
+ feltdb:
58
+ condition: service_healthy
59
+ volumes:
60
+ - ./src:/app/src
61
+ - app_node_modules:/app/node_modules
62
+ healthcheck:
63
+ test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
64
+ interval: 10s
65
+ timeout: 5s
66
+ retries: 3
67
+ start_period: 30s
68
+ restart: unless-stopped
69
+ networks:
70
+ - feltdb_network
71
+ labels:
72
+ app.feltdb/component: "application"
73
+ app.feltdb/version: "${config.version}"
74
+
75
+ studio:
76
+ image: rkendel1/feltdb-studio:latest
77
+ container_name: \${COMPOSE_PROJECT_NAME}-studio
78
+ environment:
79
+ FELTDB_API_URL: http://feltdb:7700
80
+ APP_URL: http://app:3000
81
+ ports:
82
+ - "\${STUDIO_PORT:-${config.studioPort}}:8000"
83
+ depends_on:
84
+ - feltdb
85
+ - app
86
+ networks:
87
+ - feltdb_network
88
+ labels:
89
+ app.feltdb/component: "studio"
90
+
91
+ networks:
92
+ feltdb_network:
93
+ driver: bridge
94
+ labels:
95
+ app.feltdb/network: "internal"
96
+
97
+ volumes:
98
+ feltdb_data:
99
+ driver: local
100
+ labels:
101
+ app.feltdb/data: "durable-operations-log"
102
+ app_node_modules:
103
+ driver: local
104
+ `;
105
+ }
106
+ export function generateDockerfile(config, framework) {
107
+ const buildCommand = framework === 'react' ? 'npm run build' : 'npm run build';
108
+ return `# FeltDB Application Container
109
+ # Multi-stage build for optimized production image
110
+
111
+ FROM node:18-alpine AS builder
112
+
113
+ WORKDIR /app
114
+
115
+ # Install dependencies
116
+ COPY package.json package-lock.json ./
117
+ RUN npm ci --only=production && npm cache clean --force
118
+
119
+ # Copy source
120
+ COPY . .
121
+
122
+ # Build application
123
+ RUN ${buildCommand}
124
+
125
+ # Final stage
126
+ FROM node:18-alpine
127
+
128
+ WORKDIR /app
129
+
130
+ # Install health check utility
131
+ RUN apk add --no-cache curl
132
+
133
+ # Copy built application
134
+ COPY --from=builder /app/node_modules ./node_modules
135
+ COPY --from=builder /app/dist ./dist
136
+ COPY --from=builder /app/package.json ./package.json
137
+ COPY --from=builder /app/feltdb.flow ./feltdb.flow
138
+
139
+ # Create data directory for Node runtime
140
+ RUN mkdir -p /data && chmod 755 /data
141
+
142
+ # Non-root user for security
143
+ RUN addgroup -g 1001 -S nodejs
144
+ RUN adduser -S nodejs -u 1001
145
+ USER nodejs
146
+
147
+ # Health check
148
+ HEALTHCHECK --interval=10s --timeout=5s --retries=3 --start-period=30s \\
149
+ CMD curl -f http://localhost:3000/health || exit 1
150
+
151
+ # Startup
152
+ ENV NODE_ENV=production
153
+ EXPOSE 3000
154
+
155
+ CMD ["node", "dist/index.js"]
156
+ `;
157
+ }
158
+ export function generateDockerIgnore() {
159
+ return `node_modules
160
+ npm-debug.log
161
+ dist
162
+ build
163
+ .git
164
+ .gitignore
165
+ .env
166
+ .env.local
167
+ .env.*.local
168
+ .DS_Store
169
+ *.log
170
+ .feltdb/keys.json
171
+ .feltdb/connection.json
172
+ .nextc
173
+ .next
174
+ coverage
175
+ .nyc_output
176
+ .cache
177
+ .turbo
178
+ `;
179
+ }
180
+ export function generateDotEnvLocal(config) {
181
+ return `# Docker Compose Environment
182
+ # Generated by create-feltdb for \${config.appName}
183
+
184
+ COMPOSE_PROJECT_NAME=${config.appName.toLowerCase().replace(/[^a-z0-9]/g, '-')}
185
+
186
+ # FeltDB Configuration
187
+ FELTDB_APP_NAME=${config.appName}
188
+ FELTDB_APP_ID=${config.appId}
189
+ FELTDB_VERSION=${config.version}
190
+ FELTDB_API_PORT=${config.port}
191
+ FELTDB_REPLICATION_PORT=${config.replicationPort}
192
+ FELTDB_PEERS=
193
+
194
+ # Application Configuration
195
+ APP_PORT=3000
196
+ FELTDB_API_URL=http://feltdb:7700
197
+
198
+ # Studio Configuration
199
+ STUDIO_PORT=${config.studioPort}
200
+
201
+ # Runtime
202
+ NODE_ENV=production
203
+ `;
204
+ }
205
+ export function generateHealthCheckScript() {
206
+ return `#!/bin/bash
207
+
208
+ # Multi-service health check orchestration
209
+ # Ensures all services are ready before marking deployment as healthy
210
+
211
+ set -e
212
+
213
+ TIMEOUT=60
214
+ ELAPSED=0
215
+ INTERVAL=2
216
+
217
+ echo "[Health Check] Starting orchestration check (timeout: \${TIMEOUT}s)"
218
+
219
+ # Wait for FeltDB
220
+ echo "[Health Check] Checking FeltDB service..."
221
+ while [ \$ELAPSED -lt \$TIMEOUT ]; do
222
+ if curl -s -f http://feltdb:7700/health > /dev/null 2>&1; then
223
+ STATE=\$(curl -s http://feltdb:7700/health | grep -o '"state":"[^"]*' | cut -d'"' -f4)
224
+ if [ "\$STATE" = "ready" ] || [ "\$STATE" = "recovering" ]; then
225
+ echo "[Health Check] FeltDB is \$STATE"
226
+ break
227
+ fi
228
+ fi
229
+ sleep \$INTERVAL
230
+ ELAPSED=\$((ELAPSED + INTERVAL))
231
+ done
232
+
233
+ if [ \$ELAPSED -ge \$TIMEOUT ]; then
234
+ echo "[Health Check] FAILED: FeltDB did not respond within timeout"
235
+ exit 1
236
+ fi
237
+
238
+ # Wait for application
239
+ echo "[Health Check] Checking application service..."
240
+ ELAPSED=0
241
+ while [ \$ELAPSED -lt \$TIMEOUT ]; do
242
+ if curl -s -f http://app:3000/health > /dev/null 2>&1; then
243
+ echo "[Health Check] Application is ready"
244
+ break
245
+ fi
246
+ sleep \$INTERVAL
247
+ ELAPSED=\$((ELAPSED + INTERVAL))
248
+ done
249
+
250
+ if [ \$ELAPSED -ge \$TIMEOUT ]; then
251
+ echo "[Health Check] FAILED: Application did not respond within timeout"
252
+ exit 1
253
+ fi
254
+
255
+ echo "[Health Check] All services healthy - deployment ready"
256
+ exit 0
257
+ `;
258
+ }
@@ -1,4 +1,4 @@
1
1
  // One release train keeps generated applications installable. The repository
2
2
  // validation script checks these values against every workspace manifest.
3
- export const FELTDB_PACKAGE_VERSION = '0.4.1';
3
+ export const FELTDB_PACKAGE_VERSION = '0.4.3';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Runtime-specific templates and configuration
3
+ *
4
+ * Generates runtime-appropriate file structure and configuration
5
+ * for Browser, Node, and Self-hosted deployment modes.
6
+ */
7
+ export function generateBrowserRuntime(config) {
8
+ return {
9
+ feltdbInit: `import { createFeltDB } from '@feltdb/core';
10
+
11
+ /**
12
+ * Browser Runtime
13
+ *
14
+ * Local-first FeltDB with durable IndexedDB storage.
15
+ * No server required. Full offline capability.
16
+ *
17
+ * AppID: ${config.appId}
18
+ */
19
+ export const db = createFeltDB({
20
+ namespace: '${config.appName}',
21
+ runtime: 'browser',
22
+ storage: {
23
+ type: 'indexeddb',
24
+ durable: true,
25
+ },
26
+ ${config.distributed ? `
27
+ replication: {
28
+ enabled: false, // Browser cannot initiate replication
29
+ },` : ''}
30
+ health: {
31
+ onStateChange: (state: 'starting' | 'recovering' | 'ready' | 'failed') => {
32
+ console.log(\`[FeltDB Health] Browser state changed: \${state}\`);
33
+ window.dispatchEvent(new CustomEvent('feltdb:health', { detail: { state } }));
34
+ },
35
+ },
36
+ });
37
+
38
+ // Collections
39
+ export const documents = db.collection('documents');
40
+ export const reports = db.collection('reports');
41
+ `,
42
+ envExample: `# Browser Runtime
43
+ # No environment configuration needed.
44
+ # Data persists locally in IndexedDB.
45
+ NODE_ENV=development
46
+ `,
47
+ };
48
+ }
49
+ export function generateNodeRuntime(config) {
50
+ return {
51
+ feltdbInit: `import { createFeltDB } from '@feltdb/core';
52
+ import path from 'path';
53
+ import { fileURLToPath } from 'url';
54
+ import fs from 'fs';
55
+
56
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
57
+
58
+ /**
59
+ * Node Runtime
60
+ *
61
+ * Server-side FeltDB with durable file-based storage.
62
+ * Can participate in distributed replication.
63
+ * Persistent operation log to /data directory.
64
+ *
65
+ * AppID: ${config.appId}
66
+ */
67
+
68
+ const dataDir = process.env.FELTDB_DATA_DIR || path.join(__dirname, '..', 'data');
69
+ if (!fs.existsSync(dataDir)) {
70
+ fs.mkdirSync(dataDir, { recursive: true });
71
+ }
72
+
73
+ export const db = createFeltDB({
74
+ namespace: '${config.appName}',
75
+ runtime: 'node',
76
+ storage: {
77
+ type: 'file',
78
+ path: dataDir,
79
+ durable: true,
80
+ fsync: true,
81
+ },
82
+ ${config.distributed ? `
83
+ replication: {
84
+ enabled: true,
85
+ port: parseInt(process.env.FELTDB_REPLICATION_PORT || '9000'),
86
+ },` : ''}
87
+ health: {
88
+ onStateChange: (state: 'starting' | 'recovering' | 'ready' | 'failed') => {
89
+ console.log(\`[FeltDB Health] Node state changed: \${state}\`);
90
+ // Emit to monitoring system
91
+ process.emit('feltdb:health', { state, timestamp: Date.now() });
92
+ },
93
+ },
94
+ });
95
+
96
+ // Collections
97
+ export const documents = db.collection('documents');
98
+ export const reports = db.collection('reports');
99
+
100
+ // Startup hook
101
+ export async function startup() {
102
+ console.log(\`[FeltDB] Starting Node runtime\`);
103
+ console.log(\`[FeltDB] App: ${config.appName} (${config.appId})\`);
104
+ console.log(\`[FeltDB] Version: ${config.version}\`);
105
+ console.log(\`[FeltDB] Data directory: \${dataDir}\`);
106
+ ${config.distributed ? `console.log(\`[FeltDB] Replication enabled on port \${process.env.FELTDB_REPLICATION_PORT || 9000}\`);` : ''}
107
+ await db.ready();
108
+ console.log(\`[FeltDB] Ready\`);
109
+ }
110
+ `,
111
+ envExample: `# Node Runtime
112
+ FELTDB_DATA_DIR=./data
113
+ FELTDB_REPLICATION_PORT=9000
114
+ NODE_ENV=development
115
+ `,
116
+ startupScript: `#!/usr/bin/env node
117
+
118
+ /**
119
+ * Node runtime startup orchestration
120
+ */
121
+
122
+ import { db, startup } from './feltdb.js';
123
+
124
+ async function main() {
125
+ try {
126
+ await startup();
127
+
128
+ // Start HTTP server for API
129
+ const { createServer } = await import('./server.js');
130
+ const server = createServer(db);
131
+ const port = process.env.FELTDB_API_PORT || 7700;
132
+ server.listen(port, () => {
133
+ console.log(\`[HTTP] API server listening on port \${port}\`);
134
+ });
135
+ } catch (err) {
136
+ console.error('[FeltDB] Startup failed:', err);
137
+ process.exit(1);
138
+ }
139
+ }
140
+
141
+ main();
142
+ `,
143
+ };
144
+ }
145
+ export function generateSelfHostedRuntime(config) {
146
+ return {
147
+ feltdbInit: `import { createFeltDB } from '@feltdb/core';
148
+ import path from 'path';
149
+ import { fileURLToPath } from 'url';
150
+ import fs from 'fs';
151
+
152
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
153
+
154
+ /**
155
+ * Self-hosted Runtime
156
+ *
157
+ * Containerized FeltDB with Docker Compose orchestration.
158
+ * Durable file-based storage with persistent volumes.
159
+ * Full replication topology with multi-node support.
160
+ *
161
+ * AppID: ${config.appId}
162
+ * Version: ${config.version}
163
+ */
164
+
165
+ const dataDir = process.env.FELTDB_DATA_DIR || '/data';
166
+ if (!fs.existsSync(dataDir)) {
167
+ fs.mkdirSync(dataDir, { recursive: true });
168
+ }
169
+
170
+ export const db = createFeltDB({
171
+ namespace: '${config.appName}',
172
+ runtime: 'self-hosted',
173
+ storage: {
174
+ type: 'file',
175
+ path: dataDir,
176
+ durable: true,
177
+ fsync: true,
178
+ },
179
+ replication: {
180
+ enabled: true,
181
+ port: 9000,
182
+ peers: (process.env.FELTDB_PEERS || '').split(',').filter(Boolean),
183
+ },
184
+ health: {
185
+ onStateChange: (state: 'starting' | 'recovering' | 'ready' | 'failed') => {
186
+ console.log(\`[\${new Date().toISOString()}] [FeltDB] \${state.toUpperCase()}\`);
187
+ // Structured logging for container orchestration
188
+ process.emit('feltdb:health', { state, timestamp: Date.now() });
189
+ },
190
+ },
191
+ });
192
+
193
+ // Collections
194
+ export const documents = db.collection('documents');
195
+ export const reports = db.collection('reports');
196
+
197
+ // Startup with health check loop
198
+ export async function startup() {
199
+ console.log(\`[\${new Date().toISOString()}] [FeltDB] Container starting\`);
200
+ console.log(\`[FeltDB] App: ${config.appName} (${config.appId})\`);
201
+ console.log(\`[FeltDB] Version: ${config.version}\`);
202
+ console.log(\`[FeltDB] Runtime: self-hosted\`);
203
+ console.log(\`[FeltDB] Data directory: \${dataDir}\`);
204
+
205
+ await db.ready();
206
+ console.log(\`[\${new Date().toISOString()}] [FeltDB] READY\`);
207
+ }
208
+ `,
209
+ envExample: `# Self-hosted Docker Runtime
210
+ FELTDB_DATA_DIR=/data
211
+ FELTDB_APP_NAME=${config.appName}
212
+ FELTDB_APP_ID=${config.appId}
213
+ FELTDB_VERSION=${config.version}
214
+ FELTDB_REPLICATION_PORT=9000
215
+ FELTDB_PEERS=
216
+ NODE_ENV=production
217
+ `,
218
+ healthCheck: `#!/bin/sh
219
+
220
+ # Health check for Docker container
221
+ # Verifies FeltDB is responding and not in RECOVERING state
222
+
223
+ HEALTH_URL="http://localhost:7700/health"
224
+ TIMEOUT=5
225
+
226
+ # Query health endpoint
227
+ RESPONSE=\$(curl -s -m \${TIMEOUT} \${HEALTH_URL} 2>/dev/null)
228
+
229
+ if [ -z "\$RESPONSE" ]; then
230
+ echo "UNHEALTHY: No response from health endpoint"
231
+ exit 1
232
+ fi
233
+
234
+ STATE=\$(echo \$RESPONSE | grep -o '"state":"[^"]*' | cut -d'"' -f4)
235
+
236
+ case "\$STATE" in
237
+ ready)
238
+ echo "HEALTHY: State is ready"
239
+ exit 0
240
+ ;;
241
+ recovering)
242
+ echo "STARTING: Recovering from disk"
243
+ exit 0
244
+ ;;
245
+ starting)
246
+ echo "STARTING: Initializing"
247
+ exit 0
248
+ ;;
249
+ failed)
250
+ echo "UNHEALTHY: State is failed"
251
+ exit 1
252
+ ;;
253
+ *)
254
+ echo "UNKNOWN: State is \$STATE"
255
+ exit 2
256
+ ;;
257
+ esac
258
+ `,
259
+ };
260
+ }
261
+ export function getTemplate(config) {
262
+ switch (config.runtime) {
263
+ case 'browser':
264
+ return generateBrowserRuntime(config);
265
+ case 'node':
266
+ return generateNodeRuntime(config);
267
+ case 'self-hosted':
268
+ return generateSelfHostedRuntime(config);
269
+ default:
270
+ throw new Error(`Unknown runtime: ${config.runtime}`);
271
+ }
272
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-feltdb",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Create a new FeltDB application with one command",
5
5
  "license": "MIT",
6
6
  "bin": {