@feltdb/core 0.7.2 → 0.7.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.
@@ -8,7 +8,7 @@ import net from 'net';
8
8
  import { createRequire } from 'module';
9
9
  import { spawn, spawnSync } from 'child_process';
10
10
  import { randomBytes } from 'crypto';
11
- import { createFeltDB, diffFlowSpec, formatFlowSpec, InvestigationLifecycleManager, parseFlowSpec, planFlowSpecMigration, startLocalDevelopmentAuthority, validateFlowSpec } from '@feltdb/core';
11
+ import { createFeltDB, diffFlowSpec, emptyFlowSpec, formatFlowSpec, InvestigationLifecycleManager, parseFlowSpec, planFlowSpecMigration, startLocalDevelopmentAuthority, validateFlowSpec } from '@feltdb/core';
12
12
  import { discoverWorkspace, ensureWorkspaceGitIgnored, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace, startPairingDiscoveryServer } from './workspace-integration.js';
13
13
  import { applicationUrlCandidate, detectApplication, discoverApplicationUrl } from './application.js';
14
14
  import { resolveApplicationLifecycle, startManagedApplication, waitForManagedApplication } from './application-lifecycle.js';
@@ -77,6 +77,8 @@ export async function handleCommand(command, args) {
77
77
  return handleFlowDiff(args);
78
78
  case 'deploy':
79
79
  return handleFlowDeploy(args);
80
+ case 'publish':
81
+ return handleManagedPublish(args);
80
82
  case 'help':
81
83
  default:
82
84
  return handleHelp();
@@ -231,6 +233,85 @@ async function handleFlowDeploy(args) {
231
233
  await db.close();
232
234
  }
233
235
  }
236
+ async function handleManagedPublish(args) {
237
+ loadProjectEnvironment();
238
+ const configPath = path.resolve('feltdb.config.json');
239
+ if (!fs.existsSync(configPath))
240
+ throw new Error('feltdb.config.json is required for publish');
241
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
242
+ if (config.runtime !== 'managed')
243
+ throw new Error('feltdb publish requires a managed runtime in feltdb.config.json');
244
+ const url = process.env.VITE_FELTDB_MANAGED_URL;
245
+ const token = process.env.VITE_FELTDB_MANAGED_API_KEY;
246
+ const tenantId = process.env.VITE_FELTDB_MANAGED_TENANT_ID;
247
+ const applicationId = process.env.VITE_FELTDB_MANAGED_APPLICATION_ID;
248
+ const namespace = process.env.VITE_FELTDB_MANAGED_NAMESPACE || config.namespace;
249
+ const environment = process.env.VITE_FELTDB_MANAGED_ENVIRONMENT || 'production';
250
+ const missing = [
251
+ ['VITE_FELTDB_MANAGED_URL', url],
252
+ ['VITE_FELTDB_MANAGED_API_KEY', token],
253
+ ['VITE_FELTDB_MANAGED_TENANT_ID', tenantId],
254
+ ['VITE_FELTDB_MANAGED_APPLICATION_ID', applicationId],
255
+ ['VITE_FELTDB_MANAGED_NAMESPACE', namespace],
256
+ ].filter(([, value]) => !value).map(([name]) => name);
257
+ if (missing.length)
258
+ throw new Error(`Managed publish configuration is incomplete in .env.local: ${missing.join(', ')}`);
259
+ const file = flowFile(args);
260
+ const spec = readFlowSpec(file);
261
+ const diagnostics = validateFlowSpec(spec).filter(value => value.severity === 'error');
262
+ if (diagnostics.length)
263
+ throw new Error(diagnostics.map(value => value.message).join('; '));
264
+ console.log('FeltDB Publish');
265
+ console.log(`Application: ${spec.app}`);
266
+ console.log(`Environment: ${environment}`);
267
+ console.log(`Namespace: ${namespace}`);
268
+ console.log(`API: ${url}`);
269
+ console.log('✓ Configuration loaded');
270
+ if (!args.includes('--no-build')) {
271
+ const manifestPath = path.resolve('package.json');
272
+ if (!fs.existsSync(manifestPath))
273
+ throw new Error('package.json is required to build the application');
274
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
275
+ if (!manifest.scripts?.build)
276
+ throw new Error('package.json must define a build script for managed publish');
277
+ const build = spawnSync('npm', ['run', 'build'], { cwd: process.cwd(), stdio: 'inherit' });
278
+ if (build.error)
279
+ throw build.error;
280
+ if (build.status !== 0)
281
+ throw new Error(`Application build failed with exit code ${build.status}`);
282
+ }
283
+ console.log('✓ Build completed');
284
+ console.log('✓ Application validated');
285
+ const remote = createFeltDB({ namespace, server: { url: url, token: token } });
286
+ try {
287
+ const current = await remote.collection('_flow_apps').get(spec.app);
288
+ const migration = planFlowSpecMigration(current?.spec ?? emptyFlowSpec(spec.app), spec);
289
+ const destructive = migration.filter(value => value.safety === 'destructive');
290
+ if (destructive.length && !args.includes('--allow-destructive')) {
291
+ throw new Error(`Publish contains destructive changes (${destructive.map(value => value.target).join(', ')}); review them and rerun with --allow-destructive`);
292
+ }
293
+ const result = await remote.deployFlowSpec(spec, current?.version ?? 0, args.includes('--allow-destructive'));
294
+ const publishState = {
295
+ tenantId,
296
+ applicationId,
297
+ namespace,
298
+ environment,
299
+ deploymentId: `${applicationId}:${environment}:${result.version}`,
300
+ version: result.version,
301
+ publishedAt: new Date().toISOString(),
302
+ };
303
+ fs.mkdirSync(path.resolve('.feltdb'), { recursive: true });
304
+ fs.writeFileSync(path.resolve('.feltdb/last-published.json'), `${JSON.stringify(publishState, null, 2)}\n`, { mode: 0o600 });
305
+ fs.writeFileSync(path.resolve('.feltdb/last-deployed.flow'), formatFlowSpec(spec));
306
+ console.log('✓ Published');
307
+ console.log(`Deployment: ${publishState.deploymentId}`);
308
+ console.log(`Version: ${result.version}`);
309
+ console.log('Your app is live.');
310
+ }
311
+ finally {
312
+ await remote.close();
313
+ }
314
+ }
234
315
  async function handleServer(args) {
235
316
  const require = createRequire(import.meta.url);
236
317
  const packageRoot = path.dirname(require.resolve('@feltdb/core/package.json'));
@@ -1155,9 +1236,13 @@ async function handleDoctor() {
1155
1236
  console.log('\n✅ System healthy\n');
1156
1237
  }
1157
1238
  async function handleStudio(args, onReady) {
1239
+ loadProjectEnvironment();
1240
+ const projectConfigPath = path.resolve('feltdb.config.json');
1241
+ const projectConfig = fs.existsSync(projectConfigPath) ? JSON.parse(fs.readFileSync(projectConfigPath, 'utf8')) : {};
1242
+ const projectIsManaged = projectConfig.runtime === 'managed';
1158
1243
  const connectUrl = args.includes('--connect')
1159
1244
  ? args[args.indexOf('--connect') + 1]
1160
- : undefined;
1245
+ : projectIsManaged ? process.env.VITE_FELTDB_MANAGED_URL : undefined;
1161
1246
  const port = args.includes('--port')
1162
1247
  ? args[args.indexOf('--port') + 1] || '7701'
1163
1248
  : '7701';
@@ -1167,10 +1252,10 @@ async function handleStudio(args, onReady) {
1167
1252
  const open = !args.includes('--no-open');
1168
1253
  const namespace = args.includes('--namespace')
1169
1254
  ? args[args.indexOf('--namespace') + 1] || 'default'
1170
- : 'default';
1255
+ : projectIsManaged ? process.env.VITE_FELTDB_MANAGED_NAMESPACE || projectConfig.namespace || 'default' : 'default';
1171
1256
  const runtime = args.includes('--runtime')
1172
1257
  ? args[args.indexOf('--runtime') + 1] || 'browser'
1173
- : connectUrl ? 'remote' : 'browser';
1258
+ : projectIsManaged ? 'managed' : connectUrl ? 'remote' : 'browser';
1174
1259
  const appUrl = args.includes('--app-url')
1175
1260
  ? args[args.indexOf('--app-url') + 1]
1176
1261
  : undefined;
@@ -1211,7 +1296,15 @@ async function handleStudio(args, onReady) {
1211
1296
  response.setHeader('Content-Type', 'application/json; charset=utf-8');
1212
1297
  response.setHeader('Cache-Control', 'no-store');
1213
1298
  response.setHeader('Pragma', 'no-cache');
1214
- response.end(JSON.stringify({ token: connectUrl ? process.env.VITE_FELTDB_API_KEY || '' : '', session }));
1299
+ const managedPublish = runtime === 'managed' ? {
1300
+ url: process.env.VITE_FELTDB_MANAGED_URL || connectUrl || '',
1301
+ namespace: process.env.VITE_FELTDB_MANAGED_NAMESPACE || namespace,
1302
+ token: process.env.VITE_FELTDB_MANAGED_API_KEY || process.env.VITE_FELTDB_API_KEY || '',
1303
+ tenantId: process.env.VITE_FELTDB_MANAGED_TENANT_ID || '',
1304
+ applicationId: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || '',
1305
+ environment: process.env.VITE_FELTDB_MANAGED_ENVIRONMENT || 'production',
1306
+ } : undefined;
1307
+ response.end(JSON.stringify({ token: connectUrl ? process.env.VITE_FELTDB_API_KEY || process.env.VITE_FELTDB_MANAGED_API_KEY || '' : '', managedPublish, session }));
1215
1308
  return;
1216
1309
  }
1217
1310
  if (pathname === '/_feltdb/project') {
@@ -1312,6 +1405,7 @@ Commands:
1312
1405
  validate [file] Validate an entire FlowSpec application model
1313
1406
  diff [file] Diff FlowSpec against the last deployed model
1314
1407
  deploy [file] Validate, version, and deploy FlowSpec
1408
+ publish [file] Build and publish/update a managed application
1315
1409
  studio Launch FeltDB Studio (developer interface)
1316
1410
  server Start self-hosted FeltDB server
1317
1411
  keys Manage API keys
package/dist/cli/index.js CHANGED
@@ -23,7 +23,7 @@ import * as path from 'path';
23
23
  import * as readline from 'readline';
24
24
  import { getClient } from './api-client.js';
25
25
  import { loadFeltDBConfig, createDefaultConfig, validateModel, } from './config.js';
26
- const VERSION = '0.7.2';
26
+ const VERSION = '0.7.3';
27
27
  function prompt(question) {
28
28
  const rl = readline.createInterface({
29
29
  input: process.stdin,
@@ -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.7.2';
3
+ export const FELTDB_PACKAGE_VERSION = '0.7.3';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;