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.
- package/dist/cli.js +1 -1
- package/dist/create.js +30 -6
- package/dist/managed-account.js +14 -7
- package/dist/package-versions.js +1 -1
- package/dist/server-source/crates/feltdb/src/application.rs +31 -1
- package/dist/server-source/crates/feltdb/src/authority_failover.rs +847 -0
- package/dist/server-source/crates/feltdb/src/bin/feltdb_node.rs +338 -29
- package/dist/server-source/crates/feltdb/src/distributed_transactions.rs +72 -34
- package/dist/server-source/crates/feltdb/src/lib.rs +1 -0
- package/dist/server-source/crates/feltdb-server/src/identity.rs +46 -3
- package/dist/server-source/crates/feltdb-server/src/main.rs +315 -16
- package/dist/server-source/crates/feltdb-server/src/tenancy.rs +20 -0
- package/dist/template/default-project/README.md +57 -0
- package/dist/template/default-project/agents/activity-assistant.ts +18 -0
- package/dist/template/default-project/agents/project-assistant.ts +19 -0
- package/dist/template/default-project/capabilities/activity-summary.ts +14 -0
- package/dist/template/default-project/capabilities/project-search.ts +16 -0
- package/dist/template/default-project/capabilities/project-summary.ts +15 -0
- package/dist/template/default-project/feltdb.flow +169 -0
- package/dist/template/default-project/src/App.tsx +20 -0
- package/dist/template/default-project/src/context/AuthContext.tsx +27 -0
- package/dist/template/default-project/src/feltdb.ts +87 -0
- package/dist/template/default-project/src/index.tsx +14 -0
- package/dist/template/default-project/src/pages/Activity.tsx +7 -0
- package/dist/template/default-project/src/pages/Agents.tsx +15 -0
- package/dist/template/default-project/src/pages/Dashboard.tsx +17 -0
- package/dist/template/default-project/src/pages/Invitations.tsx +11 -0
- package/dist/template/default-project/src/pages/Projects.tsx +11 -0
- package/dist/template/default-project/src/pages/SignIn.tsx +8 -0
- package/dist/template/default-project/src/pages/SignUp.tsx +8 -0
- package/dist/template/default-project/src/styles-application.css +21 -0
- package/dist/template/default-project/src/styles.css +1 -0
- package/dist/template/default-project/workflows/agent-assisted-summary.ts +1 -0
- package/dist/template/default-project/workflows/invitation.ts +30 -0
- package/dist/template/default-project/workflows/project-created.ts +2 -0
- 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, '
|
|
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
|
|
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: '
|
|
182
|
+
rootDir: '.',
|
|
180
183
|
strict: true,
|
|
181
184
|
esModuleInterop: true,
|
|
182
185
|
skipLibCheck: true,
|
|
183
186
|
forceConsistentCasingInFileNames: true,
|
|
184
|
-
moduleResolution: '
|
|
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
|
package/dist/managed-account.js
CHANGED
|
@@ -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
|
|
46
|
+
const controlKey = await responseJson(await request(`${sessionOrigin}/api/keys`, {
|
|
47
47
|
method: 'POST', headers,
|
|
48
48
|
body: JSON.stringify({
|
|
49
|
-
id: `${options.applicationName}-
|
|
49
|
+
id: `${options.applicationName}-control`,
|
|
50
50
|
namespace: options.namespace,
|
|
51
|
-
scopes: [
|
|
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
|
|
54
|
-
if (!
|
|
55
|
-
throw new Error('Managed key service did not return
|
|
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
|
-
|
|
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}`,
|
package/dist/package-versions.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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");
|