create-feltdb 0.7.2 → 0.7.4

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.
Files changed (36) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/create.js +30 -6
  3. package/dist/managed-account.js +14 -7
  4. package/dist/package-versions.js +1 -1
  5. package/dist/server-source/crates/feltdb/src/application.rs +31 -1
  6. package/dist/server-source/crates/feltdb/src/authority_failover.rs +847 -0
  7. package/dist/server-source/crates/feltdb/src/bin/feltdb_node.rs +338 -29
  8. package/dist/server-source/crates/feltdb/src/distributed_transactions.rs +72 -34
  9. package/dist/server-source/crates/feltdb/src/lib.rs +1 -0
  10. package/dist/server-source/crates/feltdb-server/src/identity.rs +46 -3
  11. package/dist/server-source/crates/feltdb-server/src/main.rs +315 -16
  12. package/dist/server-source/crates/feltdb-server/src/tenancy.rs +20 -0
  13. package/dist/template/default-project/README.md +57 -0
  14. package/dist/template/default-project/agents/activity-assistant.ts +18 -0
  15. package/dist/template/default-project/agents/project-assistant.ts +19 -0
  16. package/dist/template/default-project/capabilities/activity-summary.ts +14 -0
  17. package/dist/template/default-project/capabilities/project-search.ts +16 -0
  18. package/dist/template/default-project/capabilities/project-summary.ts +15 -0
  19. package/dist/template/default-project/feltdb.flow +169 -0
  20. package/dist/template/default-project/src/App.tsx +20 -0
  21. package/dist/template/default-project/src/context/AuthContext.tsx +27 -0
  22. package/dist/template/default-project/src/feltdb.ts +87 -0
  23. package/dist/template/default-project/src/index.tsx +14 -0
  24. package/dist/template/default-project/src/pages/Activity.tsx +7 -0
  25. package/dist/template/default-project/src/pages/Agents.tsx +15 -0
  26. package/dist/template/default-project/src/pages/Dashboard.tsx +17 -0
  27. package/dist/template/default-project/src/pages/Invitations.tsx +11 -0
  28. package/dist/template/default-project/src/pages/Projects.tsx +11 -0
  29. package/dist/template/default-project/src/pages/SignIn.tsx +8 -0
  30. package/dist/template/default-project/src/pages/SignUp.tsx +8 -0
  31. package/dist/template/default-project/src/styles-application.css +21 -0
  32. package/dist/template/default-project/src/styles.css +1 -0
  33. package/dist/template/default-project/workflows/agent-assisted-summary.ts +1 -0
  34. package/dist/template/default-project/workflows/invitation.ts +30 -0
  35. package/dist/template/default-project/workflows/project-created.ts +2 -0
  36. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -308,7 +308,7 @@ Learn more: https://github.com/rkendel1/feltdb`);
308
308
  await createProject({
309
309
  projectName,
310
310
  autoYes: shouldAutoYes,
311
- templatesDir: path.join(__dirname, '../template'),
311
+ templatesDir: path.join(__dirname, 'template'),
312
312
  runtime: options.runtime,
313
313
  framework: options.framework,
314
314
  distributed: options.distributed,
package/dist/create.js CHANGED
@@ -49,9 +49,12 @@ export async function createProject(options) {
49
49
  test: 'node --test',
50
50
  feltdb: 'feltdb',
51
51
  'feltdb:server': 'feltdb server',
52
- 'feltdb:connect': 'feltdb connect http://localhost:7700',
52
+ 'feltdb:connect': 'feltdb connect',
53
53
  'feltdb:status': 'feltdb status',
54
54
  'feltdb:studio': 'feltdb studio',
55
+ 'feltdb:publish': 'feltdb publish',
56
+ 'feltdb:ai': 'feltdb ai',
57
+ 'feltdb:sync': 'feltdb sync',
55
58
  'feltdb:validate': 'feltdb validate',
56
59
  'feltdb:diff': 'feltdb diff',
57
60
  'feltdb:deploy': 'feltdb deploy',
@@ -176,25 +179,26 @@ ${hasAgents ? ` agent WorkspaceAssistant {
176
179
  lib: ['ES2020', 'DOM'],
177
180
  declaration: true,
178
181
  outDir: './dist',
179
- rootDir: './src',
182
+ rootDir: '.',
180
183
  strict: true,
181
184
  esModuleInterop: true,
182
185
  skipLibCheck: true,
183
186
  forceConsistentCasingInFileNames: true,
184
- moduleResolution: 'node',
187
+ moduleResolution: 'bundler',
185
188
  },
186
- include: ['src/**/*'],
189
+ include: framework === 'react' ? ['src/**/*', 'agents/**/*', 'capabilities/**/*', 'workflows/**/*'] : ['src/**/*'],
187
190
  exclude: ['node_modules'],
188
191
  };
189
192
  if (framework === 'react') {
190
193
  tsconfig.compilerOptions.jsx = 'react-jsx';
194
+ tsconfig.compilerOptions.types = ['vite/client'];
191
195
  }
192
196
  fs.writeFileSync(path.join(projectDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
193
197
  // Create main application files
194
198
  const runtimeOptions = runtime === 'browser'
195
199
  ? "{ namespace: import.meta.env.VITE_FELTDB_NAMESPACE || '" + applicationName + "', browser: true }"
196
200
  : runtime === 'managed'
197
- ? "{ namespace: import.meta.env.VITE_FELTDB_MANAGED_NAMESPACE || import.meta.env.VITE_FELTDB_NAMESPACE || '" + applicationName + "', server: { url: import.meta.env.VITE_FELTDB_MANAGED_URL || import.meta.env.VITE_FELTDB_URL || '', token: import.meta.env.VITE_FELTDB_MANAGED_API_KEY || import.meta.env.VITE_FELTDB_API_KEY || '' } }"
201
+ ? "{ namespace: import.meta.env.VITE_FELTDB_MANAGED_NAMESPACE || import.meta.env.VITE_FELTDB_NAMESPACE || '" + applicationName + "', server: { url: import.meta.env.VITE_FELTDB_MANAGED_URL || import.meta.env.VITE_FELTDB_URL || '', token: import.meta.env.VITE_FELTDB_MANAGED_API_KEY || import.meta.env.VITE_FELTDB_API_KEY || '', applicationId: import.meta.env.VITE_FELTDB_MANAGED_APPLICATION_ID, environment: import.meta.env.VITE_FELTDB_MANAGED_ENVIRONMENT || 'production' } }"
198
202
  : runtime === 'self-hosted'
199
203
  ? "{ namespace: import.meta.env.VITE_FELTDB_NAMESPACE || '" + applicationName + "', server: { url: import.meta.env.VITE_FELTDB_URL || 'http://localhost:7700', token: import.meta.env.VITE_FELTDB_API_KEY || '' } }"
200
204
  : "{ namespace: import.meta.env.VITE_FELTDB_NAMESPACE || '" + applicationName + "', memory: true }";
@@ -365,7 +369,7 @@ async function logActivity(event: Omit<ActivityEvent, 'id' | 'timestamp'>): Prom
365
369
  `;
366
370
  fs.writeFileSync(path.join(srcDir, 'feltdb.ts'), feltdbTs);
367
371
  // Create a real local-inference agent for browser projects.
368
- if (hasAgents && hasWebLLM) {
372
+ if (hasAgents && hasWebLLM && framework !== 'react') {
369
373
  const agentTs = `import { WebLLMProvider } from '@feltdb/webllm';
370
374
  import { db, reports } from '../../src/feltdb';
371
375
 
@@ -1185,6 +1189,17 @@ root.render(
1185
1189
  );
1186
1190
  `;
1187
1191
  fs.writeFileSync(path.join(srcDir, 'index.tsx'), indexTsx);
1192
+ const canonicalTemplate = path.join(templatesDir, 'default-project');
1193
+ if (fs.existsSync(canonicalTemplate)) {
1194
+ fs.cpSync(path.join(canonicalTemplate, 'src'), srcDir, { recursive: true });
1195
+ for (const directory of ['agents', 'capabilities', 'workflows']) {
1196
+ fs.cpSync(path.join(canonicalTemplate, directory), path.join(projectDir, directory), { recursive: true });
1197
+ fs.rmSync(path.join(feltdbDir, directory), { recursive: true, force: true });
1198
+ }
1199
+ fs.writeFileSync(path.join(projectDir, 'feltdb.flow'), fs.readFileSync(path.join(canonicalTemplate, 'feltdb.flow'), 'utf8').replace('app FeltDBStarter', `app ${appName}`));
1200
+ const databasePath = path.join(srcDir, 'feltdb.ts');
1201
+ fs.writeFileSync(databasePath, fs.readFileSync(databasePath, 'utf8').replace(/export const managedRuntime[\s\S]*?\n\} : \{ namespace: import\.meta\.env\.VITE_FELTDB_NAMESPACE \|\| 'feltdb-starter', browser: true \}\);/, `export const managedRuntime = ${runtime === 'managed'};\nexport const db = createFeltDB(${runtimeOptions});`));
1202
+ }
1188
1203
  }
1189
1204
  else {
1190
1205
  // Create vanilla JS app
@@ -1229,6 +1244,8 @@ VITE_FELTDB_URL=http://localhost:7700
1229
1244
  # Managed example: https://runtime.your-app.feltdb.com
1230
1245
  VITE_FELTDB_MANAGED_URL=
1231
1246
  VITE_FELTDB_MANAGED_API_KEY=
1247
+ # Server/control-plane only. Never rename with a VITE_ prefix.
1248
+ FELTDB_MANAGED_CONTROL_API_KEY=
1232
1249
  VITE_FELTDB_MANAGED_TENANT_ID=
1233
1250
  VITE_FELTDB_MANAGED_APPLICATION_ID=
1234
1251
  VITE_FELTDB_MANAGED_NAMESPACE=
@@ -1524,6 +1541,7 @@ build/
1524
1541
  *.log
1525
1542
  .DS_Store
1526
1543
  .feltdb/
1544
+ .feltdb-data/
1527
1545
  `;
1528
1546
  fs.writeFileSync(path.join(projectDir, '.gitignore'), gitignore);
1529
1547
  // Create README
@@ -1795,6 +1813,12 @@ If port 5173 is in use:
1795
1813
  MIT
1796
1814
  `;
1797
1815
  fs.writeFileSync(path.join(projectDir, 'README.md'), readme);
1816
+ if (framework === 'react') {
1817
+ const canonicalReadme = path.join(templatesDir, 'default-project', 'README.md');
1818
+ if (fs.existsSync(canonicalReadme)) {
1819
+ fs.writeFileSync(path.join(projectDir, 'README.md'), fs.readFileSync(canonicalReadme, 'utf8').replace('# My FeltDB App', `# ${applicationName}`));
1820
+ }
1821
+ }
1798
1822
  // Initialize Development Workspace
1799
1823
  // This creates .feltdb/workspace.json which enables all FeltDB-aware tools
1800
1824
  // (CLI, IDE, agents, browser extensions) to discover and connect to the
@@ -43,20 +43,27 @@ export async function configureManagedAccount(options) {
43
43
  // Provision through the account origin that issued the human session. The
44
44
  // resulting key is still used against apiUrl, but setup must not require a
45
45
  // browser session to be trusted by every managed data-plane hostname.
46
- const key = await responseJson(await request(`${sessionOrigin}/api/keys`, {
46
+ const controlKey = await responseJson(await request(`${sessionOrigin}/api/keys`, {
47
47
  method: 'POST', headers,
48
48
  body: JSON.stringify({
49
- id: `${options.applicationName}-cli`,
49
+ id: `${options.applicationName}-control`,
50
50
  namespace: options.namespace,
51
- scopes: ['state:read', 'state:write', 'events:read'],
51
+ scopes: [
52
+ 'application:read', 'application:write', 'application:revision:create',
53
+ 'application:revision:read', 'application:revision:promote',
54
+ 'application:environment:read', 'application:environment:write',
55
+ ],
52
56
  }),
53
- }), 'Could not create the managed application key');
54
- if (!key.secret)
55
- throw new Error('Managed key service did not return an application secret');
57
+ }), 'Could not create the managed control-plane key');
58
+ if (!controlKey.secret)
59
+ throw new Error('Managed key service did not return the control-plane credential');
56
60
  const environment = [
57
61
  '# Generated by create-feltdb managed setup. Do not commit this file.',
58
62
  `VITE_FELTDB_MANAGED_URL=${apiUrl}`,
59
- `VITE_FELTDB_MANAGED_API_KEY=${key.secret}`,
63
+ '# Browser authentication uses short-lived actor sessions; no long-lived data key is embedded.',
64
+ 'VITE_FELTDB_MANAGED_API_KEY=',
65
+ '# Control-plane credential. Never prefix this with VITE_ or expose it to browser code.',
66
+ `FELTDB_MANAGED_CONTROL_API_KEY=${controlKey.secret}`,
60
67
  `VITE_FELTDB_MANAGED_TENANT_ID=${tenant.id}`,
61
68
  `VITE_FELTDB_MANAGED_APPLICATION_ID=${application.id}`,
62
69
  `VITE_FELTDB_MANAGED_NAMESPACE=${application.namespace || options.namespace}`,
@@ -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.4';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
@@ -132,7 +132,10 @@ pub struct CapabilityBinding {
132
132
  pub name: String,
133
133
  #[serde(default)]
134
134
  pub provider: Option<String>,
135
+ #[serde(default = "public_capability_visibility")]
136
+ pub visibility: String,
135
137
  }
138
+ fn public_capability_visibility() -> String { "public".into() }
136
139
  #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
137
140
  pub struct SecretReference {
138
141
  pub binding: String,
@@ -368,7 +371,14 @@ pub struct ValidationReport {
368
371
  }
369
372
 
370
373
  fn is_valid_policy_subject(subject: &str) -> bool {
371
- matches!(subject, "authenticated" | "owner" | "member")
374
+ subject.split('|').map(str::trim).all(|term| {
375
+ matches!(term, "authenticated" | "owner" | "member")
376
+ || term.strip_prefix("self(").and_then(|value| value.strip_suffix(')')).is_some_and(|field| {
377
+ let mut characters = field.chars();
378
+ characters.next().is_some_and(|value| value == '_' || value.is_ascii_alphabetic())
379
+ && characters.all(|value| value == '_' || value.is_ascii_alphanumeric())
380
+ })
381
+ })
372
382
  }
373
383
 
374
384
  fn unique(
@@ -540,6 +550,11 @@ pub fn validate_manifest(
540
550
  .iter()
541
551
  .map(|v| v.name.as_str())
542
552
  .collect();
553
+ for capability in &manifest.capabilities {
554
+ if !matches!(capability.visibility.as_str(), "public" | "agent" | "workflow" | "internal") {
555
+ issues.push(issue("semantic", format!("capabilities.{}.visibility", capability.name), "visibility must be public, agent, workflow, or internal"));
556
+ }
557
+ }
543
558
  let workflows: BTreeSet<_> = manifest.workflows.iter().map(|v| v.name.as_str()).collect();
544
559
  let queries: BTreeSet<_> = manifest.queries.iter().map(|v| v.name.as_str()).collect();
545
560
  let bindings: BTreeSet<_> = manifest
@@ -2468,6 +2483,21 @@ mod tests {
2468
2483
  );
2469
2484
  }
2470
2485
 
2486
+ #[test]
2487
+ fn policy_with_identity_and_membership_composition_is_valid() {
2488
+ let mut manifest = ApplicationManifest::empty("tenant", "app", "test");
2489
+ manifest.policies.push(PolicyDefinition {
2490
+ name: "InvitationPolicy".into(),
2491
+ resource: "Invitation".into(),
2492
+ read: Some("self(user) | member".into()),
2493
+ write: Some("member".into()),
2494
+ capabilities: vec![],
2495
+ });
2496
+
2497
+ let report = validate_manifest(&manifest, "tenant", "app", None);
2498
+ assert!(report.valid, "composed identity policy should be valid: {:?}", report.issues);
2499
+ }
2500
+
2471
2501
  #[test]
2472
2502
  fn policy_with_invalid_subject_rejected() {
2473
2503
  let mut manifest = ApplicationManifest::empty("tenant", "app", "test");