@feltdb/core 0.7.4 → 0.8.0

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 (106) hide show
  1. package/README.md +25 -0
  2. package/dist/application-development.d.ts +3 -1
  3. package/dist/application-development.d.ts.map +1 -1
  4. package/dist/application-development.js +16 -0
  5. package/dist/application-manifest.d.ts +1 -0
  6. package/dist/application-manifest.d.ts.map +1 -1
  7. package/dist/canonical-application.d.ts +1 -0
  8. package/dist/canonical-application.d.ts.map +1 -1
  9. package/dist/canonical-application.js +3 -2
  10. package/dist/cli/cli.js +1 -1
  11. package/dist/cli/commands.js +225 -42
  12. package/dist/cli/index.js +1 -1
  13. package/dist/cli/managed-environment.js +6 -0
  14. package/dist/cli/managed-project.js +6 -0
  15. package/dist/cli/proposal-client.js +12 -3
  16. package/dist/cli/publish-engine.js +7 -4
  17. package/dist/cli/source-sync.js +47 -3
  18. package/dist/create/cli.js +10 -1
  19. package/dist/create/create.js +10 -12
  20. package/dist/create/managed-account.js +71 -7
  21. package/dist/create/package-versions.js +1 -1
  22. package/dist/create/server-source/crates/feltdb/src/application.rs +10 -0
  23. package/dist/create/server-source/crates/feltdb/src/lib.rs +20 -2
  24. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +12 -24
  25. package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +11 -3
  26. package/dist/create/server-source/crates/feltdb-server/src/authenticated_principal.rs +0 -1
  27. package/dist/create/server-source/crates/feltdb-server/src/certification_harness.rs +23 -22
  28. package/dist/create/server-source/crates/feltdb-server/src/delegation_token.rs +5 -15
  29. package/dist/create/server-source/crates/feltdb-server/src/durable_operations.rs +13 -28
  30. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +79 -14
  31. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +28 -7
  32. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +5 -5
  33. package/dist/create/server-source/crates/feltdb-server/src/main.rs +1777 -259
  34. package/dist/create/server-source/crates/feltdb-server/src/managed_diagnostics.rs +10 -30
  35. package/dist/create/server-source/crates/feltdb-server/src/membership_policy.rs +12 -4
  36. package/dist/create/server-source/crates/feltdb-server/src/request_telemetry.rs +8 -6
  37. package/dist/create/server-source/crates/feltdb-server/src/snapshot_cursor.rs +10 -14
  38. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +183 -21
  39. package/dist/create/server-source/crates/feltdb-server/src/tenant_policies.rs +9 -21
  40. package/dist/create/server-source/crates/feltdb-server/src/transaction_idempotency.rs +5 -3
  41. package/dist/create/server-source/crates/feltdb-server/src/transaction_recovery.rs +9 -8
  42. package/dist/create/server-source/crates/feltdb-server/tests/revision_recovery_integration_test.rs +1 -4
  43. package/dist/create/template/default-project/feltdb.flow +5 -0
  44. package/dist/flowspec.d.ts +1 -0
  45. package/dist/flowspec.d.ts.map +1 -1
  46. package/dist/flowspec.js +46 -12
  47. package/dist/http-client.d.ts +135 -14
  48. package/dist/http-client.d.ts.map +1 -1
  49. package/dist/http-client.js +79 -31
  50. package/dist/index-core.d.ts +2 -0
  51. package/dist/index-core.d.ts.map +1 -1
  52. package/dist/index-core.js +2 -0
  53. package/dist/module-intent.d.ts +37 -0
  54. package/dist/module-intent.d.ts.map +1 -0
  55. package/dist/module-intent.js +81 -0
  56. package/dist/module.d.ts +76 -0
  57. package/dist/module.d.ts.map +1 -0
  58. package/dist/module.js +61 -0
  59. package/dist/proposal.d.ts +80 -4
  60. package/dist/proposal.d.ts.map +1 -1
  61. package/dist/proposal.js +132 -5
  62. package/dist/studio/app.d.ts +3 -1
  63. package/dist/studio/app.d.ts.map +1 -1
  64. package/dist/studio/components/AskFeltDB.d.ts +2 -1
  65. package/dist/studio/components/AskFeltDB.d.ts.map +1 -1
  66. package/dist/studio/components/ContractInspector.d.ts +7 -0
  67. package/dist/studio/components/ContractInspector.d.ts.map +1 -0
  68. package/dist/studio/components/ManagedInstancePanel.d.ts.map +1 -1
  69. package/dist/studio/components/ManagedInstancePanel.js +1 -5
  70. package/dist/studio/components/ModuleExplorer.d.ts +6 -0
  71. package/dist/studio/components/ModuleExplorer.d.ts.map +1 -0
  72. package/dist/studio/components/ProposalBrowser.d.ts +10 -1
  73. package/dist/studio/components/ProposalBrowser.d.ts.map +1 -1
  74. package/dist/studio/components/StateExplorer.d.ts +6 -7
  75. package/dist/studio/components/StateExplorer.d.ts.map +1 -1
  76. package/dist/studio/components/index.d.ts +2 -0
  77. package/dist/studio/components/index.d.ts.map +1 -1
  78. package/dist/studio/components/index.js +4 -4
  79. package/dist/studio/components-CNSNrmA9.js +2787 -0
  80. package/dist/studio/index.js +193 -147
  81. package/dist/studio/studio.css +1 -1
  82. package/dist/studio/utils/index.d.ts +2 -0
  83. package/dist/studio/utils/index.d.ts.map +1 -1
  84. package/dist/studio/utils/index.js +14 -12
  85. package/dist/studio/utils/managed-instance.d.ts +0 -4
  86. package/dist/studio/utils/managed-instance.d.ts.map +1 -1
  87. package/dist/studio/utils/managed-instance.js +0 -4
  88. package/dist/studio/utils/proposal-api.d.ts +53 -0
  89. package/dist/studio/utils/proposal-api.d.ts.map +1 -0
  90. package/dist/studio/utils/proposal-api.js +91 -0
  91. package/dist/studio/utils/service-api.d.ts +81 -0
  92. package/dist/studio/utils/service-api.d.ts.map +1 -0
  93. package/dist/studio/utils/service-api.js +51 -0
  94. package/dist/studio-app/assets/{dist-DA1MYSzp.js → dist-CCOk39Uc.js} +1 -1
  95. package/dist/studio-app/assets/{feltdb_wasm-B-Ccgccb.js → feltdb_wasm-CSB6KVgw.js} +1 -1
  96. package/dist/studio-app/assets/feltdb_wasm_bg-Dd1fnO9U.wasm +0 -0
  97. package/dist/studio-app/assets/index-Bncji3aN.js +43 -0
  98. package/dist/studio-app/assets/{index-BRrmvaKw.css → index-yazaWMUN.css} +1 -1
  99. package/dist/studio-app/index.html +2 -2
  100. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  101. package/dist/workspace/local-development-authority.d.ts.map +1 -1
  102. package/dist/workspace/local-development-authority.js +64 -3
  103. package/package.json +1 -1
  104. package/dist/studio/components-Bm4sjhcX.js +0 -2162
  105. package/dist/studio-app/assets/feltdb_wasm_bg-wH3enJIA.wasm +0 -0
  106. package/dist/studio-app/assets/index-CEIqowCh.js +0 -42
@@ -1,6 +1,11 @@
1
1
  flow_version 1
2
2
 
3
3
  app FeltDBStarter {
4
+ // Optional billing semantics. Uncomment to include the Stripe-compatible module:
5
+ // module stripe {
6
+ // provider stripe-compatible
7
+ // version 1.0.0
8
+ // }
4
9
  collection User {
5
10
  email: text
6
11
  name: text
@@ -38,6 +38,7 @@ export interface FlowSpec {
38
38
  triggers: FlowTrigger[];
39
39
  policies: FlowBlock[];
40
40
  schedules: FlowBlock[];
41
+ modules?: import('./module.js').FlowModuleDeclaration[];
41
42
  }
42
43
  export interface FlowDiagnostic {
43
44
  severity: 'error' | 'warning';
@@ -1 +1 @@
1
- {"version":3,"file":"flowspec.d.ts","sourceRoot":"","sources":["../src/flowspec.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE;AAC5E,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AAC7E,MAAM,WAAW,cAAc;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,EAAE,CAAC;IAAC,OAAO,EAAE,SAAS,EAAE,CAAA;CAAE;AAC3F,MAAM,WAAW,SAAS;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE;AACjE,MAAM,WAAW,QAAS,SAAQ,SAAS;CAAG;AAC9C,MAAM,WAAW,YAAY;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,EAAE,CAAA;CAAE;AACrF,MAAM,WAAW,WAAW;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE;AACpE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,SAAS,EAAE,SAAS,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAAG,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AACjG,MAAM,WAAW,YAAY;IAAG,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE;AACvF,MAAM,WAAW,sBAAsB;IAAG,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAEpK,eAAO,MAAM,oBAAoB,yLAA0L,CAAC;AAC5N,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAQ1D;AAED,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,sHAAsH;AACtH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,mBAAmB,CAY/G;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,QAAQ,CAG1E;AAqGD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAA4C;AAEnG,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,EAAE,CAkBjE;AAGD,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAUrD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,YAAY,CAQ5E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,GAAG,sBAAsB,EAAE,CAsBjG;AAED,wBAAgB,aAAa,CAAC,GAAG,SAAU,GAAG,QAAQ,CAAyI"}
1
+ {"version":3,"file":"flowspec.d.ts","sourceRoot":"","sources":["../src/flowspec.ts"],"names":[],"mappings":"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"}
package/dist/flowspec.js CHANGED
@@ -3,7 +3,7 @@ export function isValidFlowFieldType(type) {
3
3
  const value = type.trim();
4
4
  if (FLOW_PRIMITIVE_TYPES.includes(value))
5
5
  return true;
6
- if (/^ref [A-Za-z_][A-Za-z0-9_-]*$/.test(value))
6
+ if (/^ref [A-Za-z_][A-Za-z0-9_-]*(?:\.[A-Za-z_][A-Za-z0-9_-]*)?$/.test(value))
7
7
  return true;
8
8
  if (/^enum\([^)]+\)$/.test(value))
9
9
  return value.slice(5, -1).split(',').every(item => item.trim().length > 0);
@@ -12,23 +12,25 @@ export function isValidFlowFieldType(type) {
12
12
  const container = /^(array|map)<(.+)>$/.exec(value);
13
13
  return Boolean(container && isValidFlowFieldType(container[2]));
14
14
  }
15
+ import { resolveFeltDBModules } from './module.js';
15
16
  /** FlowSpec is a portable source format; the returned Rust manifest is authoritative only after server validation. */
16
17
  export function flowSpecToManifest(spec, tenantId, applicationId) {
17
- const capabilities = spec.capabilities.map(value => ({
18
- name: value.name,
19
- visibility: (value.statements.find(statement => statement.startsWith('visibility '))?.slice('visibility '.length) || 'public'),
20
- }));
18
+ const modules = resolveFeltDBModules(spec.modules);
19
+ const capabilities = [...spec.capabilities.map(value => ({
20
+ name: value.name,
21
+ visibility: (value.statements.find(statement => statement.startsWith('visibility '))?.slice('visibility '.length) || 'public'),
22
+ })), ...modules.flatMap(module => module.capabilities.map(name => ({ name, provider: module.id, visibility: 'internal' })))];
21
23
  const policySubject = (statements, operation) => statements.map(value => new RegExp(`^${operation}\\s*:\\s*(.+)$`).exec(value)?.[1]).find(Boolean);
22
24
  const manifestField = (field) => {
23
25
  const reference = field.type.startsWith('ref ') ? field.type.slice(4) : undefined;
24
26
  const enumValues = field.type.startsWith('enum(') && field.type.endsWith(')') ? field.type.slice(5, -1).split(',').map(value => value.trim()) : undefined;
25
27
  return { name: field.name, type: reference ? 'reference' : enumValues ? 'enum' : field.type, required: !field.optional, ...(reference ? { reference } : {}), ...(enumValues ? { enum_values: enumValues } : {}) };
26
28
  };
27
- return { application_id: applicationId, tenant_id: tenantId, schema: 1, metadata: { name: spec.app, labels: {} }, collections: spec.collections.map(value => ({ name: value.name, fields: value.fields.map(manifestField) })), indexes: spec.collections.flatMap(collection => collection.indexes.map(index => ({ name: index.name, collection: collection.name, fields: [index.expression], unique: index.kind === 'unique' }))), references: [], queries: [], actions: [], workflows: spec.workflows.map(value => ({ name: value.name, steps: value.steps.map(step => ({ name: step.name, capability: step.statements.find(statement => statement.startsWith('capability '))?.slice('capability '.length) })) })), triggers: spec.triggers.map((value, index) => ({ name: `trigger-${index + 1}`, event: value.event, workflow: spec.workflows[0]?.name || '' })), schedules: spec.schedules.map(value => ({ name: value.name, expression: value.statements[0] || '', workflow: spec.workflows[0]?.name || '' })), agents: spec.agents.map(value => ({ name: value.name, capabilities: capabilities.map(capability => capability.name).filter(name => value.statements.some(statement => statement.includes(name))) })), capabilities, connections: [], policies: spec.policies.map(value => ({ name: value.name, resource: value.name, read: policySubject(value.statements, 'read'), write: policySubject(value.statements, 'write'), capabilities: [] })), ui: { routes: [], bindings: [], forms: [], dashboards: [] }, environments: ['development', 'preview', 'staging', 'production'].map(name => ({ name, configuration: {} })), artifacts: [], deployment: { environments: ['development', 'preview', 'staging', 'production'] }, runtime: { contract: 1, minimum_engine: '0.2.0', maximum_engine_exclusive: '2.0.0' } };
29
+ return { application_id: applicationId, tenant_id: tenantId, schema: 1, metadata: { name: spec.app, labels: {} }, collections: spec.collections.map(value => ({ name: value.name, fields: value.fields.map(manifestField) })), indexes: spec.collections.flatMap(collection => collection.indexes.map(index => ({ name: index.name, collection: collection.name, fields: [index.expression], unique: index.kind === 'unique' }))), references: [], queries: [], actions: [], workflows: spec.workflows.map(value => ({ name: value.name, steps: value.steps.map(step => ({ name: step.name, capability: step.statements.find(statement => statement.startsWith('capability '))?.slice('capability '.length) })) })), triggers: spec.triggers.map((value, index) => ({ name: `trigger-${index + 1}`, event: value.event, workflow: spec.workflows[0]?.name || '' })), schedules: spec.schedules.map(value => ({ name: value.name, expression: value.statements[0] || '', workflow: spec.workflows[0]?.name || '' })), agents: spec.agents.map(value => ({ name: value.name, capabilities: capabilities.map(capability => capability.name).filter(name => value.statements.some(statement => statement.includes(name))) })), capabilities, modules: resolveFeltDBModules(spec.modules), connections: [], policies: spec.policies.map(value => ({ name: value.name, resource: value.name, read: policySubject(value.statements, 'read'), write: policySubject(value.statements, 'write'), capabilities: [] })), ui: { routes: [], bindings: [], forms: [], dashboards: [] }, environments: ['development', 'preview', 'staging', 'production'].map(name => ({ name, configuration: {} })), artifacts: [], deployment: { environments: ['development', 'preview', 'staging', 'production'] }, runtime: { contract: 1, minimum_engine: '0.2.0', maximum_engine_exclusive: '2.0.0' } };
28
30
  }
29
31
  export function manifestToFlowSpec(manifest) {
30
32
  const fieldType = (field) => field.reference ? `ref ${field.reference}` : field.enum_values?.length ? `enum(${field.enum_values.join(',')})` : field.type;
31
- return { version: 1, app: manifest.metadata.name, collections: manifest.collections.map(value => ({ name: value.name, fields: value.fields.map(field => ({ name: field.name, type: fieldType(field), optional: !field.required })), indexes: manifest.indexes.filter(index => index.collection === value.name).map(index => ({ name: index.name, kind: index.unique ? 'unique' : 'index', expression: index.fields.join(', ') })) })), capabilities: manifest.capabilities.map(value => ({ name: value.name, statements: [...(value.provider ? [`provider ${value.provider}`] : []), `visibility ${value.visibility || 'public'}`] })), agents: manifest.agents.map(value => ({ name: value.name, statements: value.capabilities.map(capability => `capability ${capability}`) })), workflows: manifest.workflows.map(value => ({ name: value.name, parameters: '', steps: value.steps.map(step => ({ name: step.name, statements: step.capability ? [`capability ${step.capability}`] : [] })) })), triggers: manifest.triggers.map(value => ({ event: value.event, statements: [`run ${value.workflow}`] })), policies: manifest.policies.map(value => ({ name: value.name, statements: [...(value.read ? [`read: ${value.read}`] : []), ...(value.write ? [`write: ${value.write}`] : []), ...value.capabilities] })), schedules: manifest.schedules.map(value => ({ name: value.name, statements: [value.expression, `run ${value.workflow}`] })) };
33
+ return { version: 1, app: manifest.metadata.name, collections: manifest.collections.map(value => ({ name: value.name, fields: value.fields.map(field => ({ name: field.name, type: fieldType(field), optional: !field.required })), indexes: manifest.indexes.filter(index => index.collection === value.name).map(index => ({ name: index.name, kind: index.unique ? 'unique' : 'index', expression: index.fields.join(', ') })) })), capabilities: manifest.capabilities.map(value => ({ name: value.name, statements: [...(value.provider ? [`provider ${value.provider}`] : []), `visibility ${value.visibility || 'public'}`] })), agents: manifest.agents.map(value => ({ name: value.name, statements: value.capabilities.map(capability => `capability ${capability}`) })), workflows: manifest.workflows.map(value => ({ name: value.name, parameters: '', steps: value.steps.map(step => ({ name: step.name, statements: step.capability ? [`capability ${step.capability}`] : [] })) })), triggers: manifest.triggers.map(value => ({ event: value.event, statements: [`run ${value.workflow}`] })), policies: manifest.policies.map(value => ({ name: value.name, statements: [...(value.read ? [`read: ${value.read}`] : []), ...(value.write ? [`write: ${value.write}`] : []), ...value.capabilities] })), schedules: manifest.schedules.map(value => ({ name: value.name, statements: [value.expression, `run ${value.workflow}`] })), modules: (manifest.modules ?? []).map(value => ({ name: value.id, provider: value.provider, version: value.version })) };
32
34
  }
33
35
  function lex(source) {
34
36
  const tokens = [];
@@ -135,11 +137,20 @@ class Parser {
135
137
  const app = this.identifier();
136
138
  this.take('{');
137
139
  this.newlines();
138
- const spec = { version, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [] };
140
+ const spec = { version, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [], modules: [] };
139
141
  while (!this.peek('}')) {
140
142
  const kind = this.take().value;
141
143
  if (kind === 'collection')
142
144
  spec.collections.push(this.collection());
145
+ else if (kind === 'module') {
146
+ const name = this.identifier();
147
+ const statements = this.statements();
148
+ const provider = statements.find(value => value.startsWith('provider '))?.slice(9);
149
+ const version = statements.find(value => value.startsWith('version '))?.slice(8);
150
+ if (!provider)
151
+ throw new Error(`Module ${name} requires provider`);
152
+ spec.modules.push({ name, provider: provider.replace(/^"|"$/g, ''), version: version?.replace(/^"|"$/g, '') });
153
+ }
143
154
  else if (kind === 'workflow')
144
155
  spec.workflows.push(this.workflow());
145
156
  else if (kind === 'trigger')
@@ -238,6 +249,14 @@ export function validateFlowSpec(spec) {
238
249
  duplicate('capability', spec.capabilities.map(value => value.name));
239
250
  duplicate('agent', spec.agents.map(value => value.name));
240
251
  duplicate('workflow', spec.workflows.map(value => value.name));
252
+ duplicate('module', (spec.modules ?? []).map(value => value.name));
253
+ let resolvedModules = [];
254
+ try {
255
+ resolvedModules = resolveFeltDBModules(spec.modules);
256
+ }
257
+ catch (error) {
258
+ diagnostics.push({ severity: 'error', path: 'modules', message: error instanceof Error ? error.message : String(error) });
259
+ }
241
260
  for (const capability of spec.capabilities) {
242
261
  const visibility = capability.statements.find(statement => statement.startsWith('visibility '))?.slice('visibility '.length);
243
262
  if (visibility && !['public', 'agent', 'workflow', 'internal'].includes(visibility))
@@ -249,8 +268,13 @@ export function validateFlowSpec(spec) {
249
268
  for (const field of collection.fields) {
250
269
  if (!isValidFlowFieldType(field.type))
251
270
  diagnostics.push({ severity: 'error', path: `${collection.name}.${field.name}`, message: `Unsupported field type ${field.type}` });
252
- if (field.type.startsWith('ref ') && !collections.has(field.type.slice(4)))
253
- diagnostics.push({ severity: 'error', path: `${collection.name}.${field.name}`, message: `Unknown referenced collection ${field.type.slice(4)}` });
271
+ if (field.type.startsWith('ref ')) {
272
+ const target = field.type.slice(4);
273
+ const [moduleId, model] = target.split('.');
274
+ const moduleReference = model && resolvedModules.some(module => module.id === moduleId && module.models.some(value => value.name === model));
275
+ if (!collections.has(target) && !moduleReference)
276
+ diagnostics.push({ severity: 'error', path: `${collection.name}.${field.name}`, message: `Unknown referenced collection or module model ${target}` });
277
+ }
254
278
  }
255
279
  }
256
280
  for (const workflow of spec.workflows) {
@@ -258,6 +282,14 @@ export function validateFlowSpec(spec) {
258
282
  if (!workflow.steps.length)
259
283
  diagnostics.push({ severity: 'error', message: `Workflow ${workflow.name} has no steps` });
260
284
  }
285
+ for (const trigger of spec.triggers) {
286
+ const moduleId = trigger.event.split('.')[0];
287
+ const module = resolvedModules.find(value => value.id === moduleId);
288
+ if (moduleId && trigger.event.split('.').length >= 3 && !module)
289
+ diagnostics.push({ severity: 'error', path: `trigger ${trigger.event}`, message: `Module event ${trigger.event} requires module ${moduleId}` });
290
+ else if (module && !module.events.includes(trigger.event))
291
+ diagnostics.push({ severity: 'error', path: `trigger ${trigger.event}`, message: `Unknown ${moduleId} module event ${trigger.event}` });
292
+ }
261
293
  return diagnostics;
262
294
  }
263
295
  function block(kind, value, indent = ' ') { return `${indent}${kind} ${value.name} {\n${value.statements.map(line => `${indent} ${line}`).join('\n')}\n${indent}}`; }
@@ -277,13 +309,15 @@ export function formatFlowSpec(spec) {
277
309
  declarations.push(block('policy', value));
278
310
  for (const value of spec.schedules)
279
311
  declarations.push(block('schedule', value));
312
+ for (const value of spec.modules ?? [])
313
+ declarations.push(` module ${value.name} {\n provider ${value.provider}\n${value.version ? ` version ${value.version}\n` : ''} }`);
280
314
  return `flow_version ${spec.version}\n\napp ${spec.app} {\n${declarations.join('\n\n')}\n}\n`;
281
315
  }
282
316
  export function diffFlowSpec(before, after) {
283
317
  const flatten = (spec) => new Map([
284
318
  ...spec.collections.map(value => [`collection ${value.name}`, JSON.stringify(value)]), ...spec.capabilities.map(value => [`capability ${value.name}`, JSON.stringify(value)]),
285
319
  ...spec.agents.map(value => [`agent ${value.name}`, JSON.stringify(value)]), ...spec.workflows.map(value => [`workflow ${value.name}`, JSON.stringify(value)]),
286
- ...spec.triggers.map(value => [`trigger ${value.event}`, JSON.stringify(value)]), ...spec.policies.map(value => [`policy ${value.name}`, JSON.stringify(value)]), ...spec.schedules.map(value => [`schedule ${value.name}`, JSON.stringify(value)]),
320
+ ...spec.triggers.map(value => [`trigger ${value.event}`, JSON.stringify(value)]), ...spec.policies.map(value => [`policy ${value.name}`, JSON.stringify(value)]), ...spec.schedules.map(value => [`schedule ${value.name}`, JSON.stringify(value)]), ...(spec.modules ?? []).map(value => [`module ${value.name}`, JSON.stringify(value)]),
287
321
  ]);
288
322
  const left = flatten(before), right = flatten(after);
289
323
  return { added: [...right.keys()].filter(key => !left.has(key)), removed: [...left.keys()].filter(key => !right.has(key)), changed: [...right.keys()].filter(key => left.has(key) && left.get(key) !== right.get(key)) };
@@ -323,4 +357,4 @@ export function planFlowSpecMigration(before, after) {
323
357
  operations.push({ kind: 'remove', target, safety: 'destructive', detail: 'Application primitive is no longer declared' });
324
358
  return operations;
325
359
  }
326
- export function emptyFlowSpec(app = 'MyApp') { return { version: 1, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [] }; }
360
+ export function emptyFlowSpec(app = 'MyApp') { return { version: 1, app, collections: [], capabilities: [], agents: [], workflows: [], triggers: [], policies: [], schedules: [], modules: [] }; }
@@ -4,31 +4,152 @@
4
4
  * Provides a simple interface for connecting to and querying remote FeltDB instances.
5
5
  * For applications that only need HTTP access without the full core framework.
6
6
  */
7
+ import type { FeltDBProposalEvent, FeltDBProposalPreviewArtifact, FeltDBProposalRecord, ProposalReadiness, ProposalStatus } from './proposal.js';
8
+ import type { CanonicalQuery, QueryResult, TransactionResult } from './state-contract.js';
7
9
  export interface ClientOptions {
8
10
  url: string;
9
11
  token: string;
12
+ applicationId?: string;
13
+ environment?: string;
14
+ revisionId?: string;
15
+ fetch?: typeof globalThis.fetch;
10
16
  }
11
- export interface RemoteRecord<T> {
12
- id: string;
13
- key: string;
14
- value: T;
15
- version_ms: number;
17
+ export interface ServiceApiErrorBody {
18
+ code?: string;
19
+ error?: string;
20
+ message?: string;
21
+ request_id?: string;
22
+ }
23
+ export declare class FeltDBServiceApiError extends Error {
24
+ readonly code: string;
25
+ readonly status: number;
26
+ readonly requestId?: string | undefined;
27
+ constructor(code: string, message: string, status: number, requestId?: string | undefined);
28
+ }
29
+ export interface ApplicationInspection {
30
+ application_id: string;
31
+ application_name?: string;
32
+ environment: string;
33
+ version: number;
34
+ revision_id: string;
35
+ contract_hash: string;
36
+ flow_hash?: string;
37
+ snapshot: Record<string, unknown>;
38
+ }
39
+ export interface ApplicationSchemaInspection {
40
+ application: {
41
+ application_id: string;
42
+ environment: string;
43
+ };
44
+ contract_version: number;
45
+ collections: Array<{
46
+ collection: string;
47
+ fields: Array<{
48
+ name: string;
49
+ type: string;
50
+ required?: boolean;
51
+ reference?: string;
52
+ }>;
53
+ relationships: unknown[];
54
+ }>;
55
+ }
56
+ export interface InspectionPage<T> {
57
+ collection: string;
58
+ records: T[];
59
+ pagination: {
60
+ limit: number;
61
+ nextCursor: string | null;
62
+ };
63
+ state_version: number;
64
+ }
65
+ export interface InspectionRecord<T> {
66
+ collection: string;
67
+ record: T;
68
+ relationships: unknown[];
16
69
  }
17
70
  export declare class FeltDBClient {
18
71
  private readonly url;
19
72
  private readonly token;
73
+ private readonly applicationId?;
74
+ private readonly environment;
75
+ private readonly fetcher;
76
+ private revisionId?;
20
77
  constructor(options: ClientOptions);
21
78
  private headers;
22
79
  private request;
23
- runtime(): Promise<Record<string, unknown>>;
24
- list<T>(collection: string): Promise<Array<RemoteRecord<T>>>;
25
- get<T>(collection: string, id: string | number): Promise<RemoteRecord<T>>;
26
- insert<T extends object>(collection: string, value: T, id?: string | number): Promise<RemoteRecord<T>>;
27
- update<T extends object>(collection: string, id: string | number, changes: Partial<T>): Promise<RemoteRecord<T>>;
28
- delete(collection: string, id: string | number): Promise<void>;
29
- getInfo(): {
30
- runtime: 'remote';
31
- url: string;
80
+ private scoped;
81
+ private scopeBody;
82
+ private activeRevision;
83
+ /** Canonical application Contract Snapshot and schema inspection. */
84
+ readonly application: {
85
+ get: () => Promise<ApplicationInspection>;
86
+ schema: () => Promise<ApplicationSchemaInspection>;
87
+ };
88
+ /** Bounded, authorized application-state inspection. Cursors remain opaque. */
89
+ readonly data: {
90
+ list: <T = Record<string, unknown>>(collection: string, options?: {
91
+ limit?: number;
92
+ cursor?: string;
93
+ filters?: Record<string, string>;
94
+ }) => Promise<InspectionPage<T>>;
95
+ get: <T = Record<string, unknown>>(collection: string, id: string) => Promise<InspectionRecord<T>>;
96
+ };
97
+ /** Canonical application queries and atomic transactions against the active revision. */
98
+ readonly state: {
99
+ query: <T = Record<string, unknown>>(query: CanonicalQuery) => Promise<QueryResult<T>>;
100
+ transaction: (transaction: Record<string, unknown>) => Promise<TransactionResult>;
101
+ version: () => Promise<{
102
+ state_version: number;
103
+ }>;
104
+ };
105
+ /** Public contract-declared application capability invocation. */
106
+ readonly capabilities: {
107
+ run: (name: string, input: unknown) => Promise<unknown>;
108
+ };
109
+ /** Canonical Proposal lifecycle. Repository application is intentionally absent. */
110
+ readonly proposals: {
111
+ create: (proposal: {
112
+ base_contract_hash: string;
113
+ base_flow_hash: string;
114
+ proposed_flow: string;
115
+ summary: string;
116
+ warnings?: string[];
117
+ metadata?: Record<string, string>;
118
+ parent_proposal_id?: string;
119
+ expires_at?: string | number;
120
+ }) => Promise<FeltDBProposalRecord>;
121
+ list: (options?: {
122
+ status?: ProposalStatus;
123
+ limit?: number;
124
+ cursor?: string;
125
+ }) => Promise<{
126
+ proposals: FeltDBProposalRecord[];
127
+ pagination: {
128
+ limit: number;
129
+ nextCursor: string | null;
130
+ };
131
+ }>;
132
+ get: (id: string) => Promise<FeltDBProposalRecord>;
133
+ history: (id: string) => Promise<{
134
+ events: FeltDBProposalEvent[];
135
+ }>;
136
+ status: (id: string) => Promise<ProposalReadiness>;
137
+ validate: (id: string, proposedManifest?: unknown) => Promise<FeltDBProposalRecord>;
138
+ createPreview: (id: string) => Promise<FeltDBProposalPreviewArtifact>;
139
+ preview: (previewId: string) => Promise<FeltDBProposalPreviewArtifact>;
140
+ previewData: <T = Record<string, unknown>>(previewId: string, collection: string, options?: {
141
+ limit?: number;
142
+ cursor?: string;
143
+ }) => Promise<InspectionPage<T>>;
144
+ simulatePreview: (previewId: string, event: string) => Promise<{
145
+ preview_id: string;
146
+ event: string;
147
+ status: "simulated";
148
+ external_side_effects: "none";
149
+ trace: string[];
150
+ }>;
151
+ approve: (id: string, approveAuthorization?: boolean) => Promise<FeltDBProposalRecord>;
152
+ reject: (id: string, reason?: string) => Promise<FeltDBProposalRecord>;
32
153
  };
33
154
  }
34
155
  export declare function createClient(options: ClientOptions): FeltDBClient;
@@ -1 +1 @@
1
- {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,CAAC,CAAC;IACT,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,OAAO,EAAE,aAAa;IAMlC,OAAO,CAAC,OAAO;YAQD,OAAO;IAgBrB,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAI3C,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAI5D,GAAG,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAIzE,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAOtG,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAOhH,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9D,OAAO,IAAI;QAAE,OAAO,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;CAG9C;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,YAAY,CAEjE"}
1
+ {"version":3,"file":"http-client.d.ts","sourceRoot":"","sources":["../src/http-client.ts"],"names":[],"mappings":"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"}
@@ -4,14 +4,33 @@
4
4
  * Provides a simple interface for connecting to and querying remote FeltDB instances.
5
5
  * For applications that only need HTTP access without the full core framework.
6
6
  */
7
+ export class FeltDBServiceApiError extends Error {
8
+ code;
9
+ status;
10
+ requestId;
11
+ constructor(code, message, status, requestId) {
12
+ super(message);
13
+ this.code = code;
14
+ this.status = status;
15
+ this.requestId = requestId;
16
+ }
17
+ }
7
18
  export class FeltDBClient {
8
19
  url;
9
20
  token;
21
+ applicationId;
22
+ environment;
23
+ fetcher;
24
+ revisionId;
10
25
  constructor(options) {
11
26
  if (!options.url || !options.token)
12
27
  throw new Error('FeltDBClient requires url and token');
13
28
  this.url = options.url.replace(/\/$/, '');
14
29
  this.token = options.token;
30
+ this.applicationId = options.applicationId;
31
+ this.environment = options.environment || 'production';
32
+ this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
33
+ this.revisionId = options.revisionId;
15
34
  }
16
35
  headers(json = false) {
17
36
  return {
@@ -21,51 +40,80 @@ export class FeltDBClient {
21
40
  };
22
41
  }
23
42
  async request(path, init = {}) {
24
- const response = await fetch(`${this.url}${path}`, {
43
+ const response = await this.fetcher(`${this.url}${path}`, {
25
44
  ...init,
26
45
  headers: { ...this.headers(init.body !== undefined), ...init.headers },
27
46
  });
28
47
  if (!response.ok) {
29
48
  let message = `FeltDB request failed: HTTP ${response.status}`;
49
+ let body = {};
30
50
  try {
31
- const body = await response.json();
32
- if (body.error)
33
- message = body.error;
51
+ body = await response.json();
34
52
  }
35
53
  catch { /* non-JSON error */ }
36
- throw new Error(message);
54
+ if (body.message || body.error)
55
+ message = body.message || body.error || message;
56
+ throw new FeltDBServiceApiError(body.code || body.error || 'REQUEST_FAILED', message, response.status, body.request_id);
37
57
  }
38
58
  return (response.status === 204 ? undefined : response.json());
39
59
  }
40
- runtime() {
41
- return this.request('/runtime');
42
- }
43
- list(collection) {
44
- return this.request(`/collections/${encodeURIComponent(collection)}`);
60
+ scoped(path, query = {}) {
61
+ if (!this.applicationId)
62
+ throw new Error('This Service API operation requires createClient({ applicationId })');
63
+ const separator = path.includes('?') ? '&' : '?';
64
+ const parameters = new URLSearchParams({ application_id: this.applicationId, environment: this.environment });
65
+ for (const [name, value] of Object.entries(query))
66
+ if (value !== undefined && value !== '')
67
+ parameters.set(name, String(value));
68
+ return `${path}${separator}${parameters}`;
45
69
  }
46
- get(collection, id) {
47
- return this.request(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`);
48
- }
49
- insert(collection, value, id) {
50
- return this.request(`/collections/${encodeURIComponent(collection)}`, {
51
- method: 'POST',
52
- body: JSON.stringify(id === undefined ? value : { ...value, id }),
53
- });
54
- }
55
- update(collection, id, changes) {
56
- return this.request(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`, {
57
- method: 'PATCH',
58
- body: JSON.stringify(changes),
59
- });
60
- }
61
- delete(collection, id) {
62
- return this.request(`/collections/${encodeURIComponent(collection)}/${encodeURIComponent(String(id))}`, {
63
- method: 'DELETE',
64
- });
70
+ scopeBody(value = {}) {
71
+ if (!this.applicationId)
72
+ throw new Error('This Service API operation requires createClient({ applicationId })');
73
+ return { application_id: this.applicationId, environment: this.environment, ...value };
65
74
  }
66
- getInfo() {
67
- return { runtime: 'remote', url: this.url };
75
+ async activeRevision() {
76
+ if (this.revisionId)
77
+ return this.revisionId;
78
+ const application = await this.application.get();
79
+ this.revisionId = application.revision_id;
80
+ return application.revision_id;
68
81
  }
82
+ /** Canonical application Contract Snapshot and schema inspection. */
83
+ application = {
84
+ get: () => this.request(this.scoped('/v1/application')),
85
+ schema: () => this.request(this.scoped('/v1/application/schema')),
86
+ };
87
+ /** Bounded, authorized application-state inspection. Cursors remain opaque. */
88
+ data = {
89
+ list: (collection, options = {}) => this.request(this.scoped(`/v1/data/${encodeURIComponent(collection)}`, { limit: options.limit, cursor: options.cursor, ...options.filters })),
90
+ get: (collection, id) => this.request(this.scoped(`/v1/data/${encodeURIComponent(collection)}/${encodeURIComponent(id)}`)),
91
+ };
92
+ /** Canonical application queries and atomic transactions against the active revision. */
93
+ state = {
94
+ query: async (query) => this.request('/v1/query', { method: 'POST', body: JSON.stringify({ ...this.scopeBody(), revision_id: await this.activeRevision(), query }) }),
95
+ transaction: async (transaction) => this.request('/v1/transactions', { method: 'POST', body: JSON.stringify({ ...this.scopeBody(), revision_id: await this.activeRevision(), transaction }) }),
96
+ version: async () => this.request(this.scoped('/v1/state/version', { revision_id: await this.activeRevision() })),
97
+ };
98
+ /** Public contract-declared application capability invocation. */
99
+ capabilities = {
100
+ run: (name, input) => this.request(this.scoped(`/v1/capabilities/${encodeURIComponent(name)}/run`), { method: 'POST', body: JSON.stringify(input) }),
101
+ };
102
+ /** Canonical Proposal lifecycle. Repository application is intentionally absent. */
103
+ proposals = {
104
+ create: (proposal) => this.request('/v1/proposals', { method: 'POST', body: JSON.stringify(this.scopeBody(proposal)) }),
105
+ list: (options = {}) => this.request(this.scoped('/v1/proposals', options)),
106
+ get: (id) => this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}`)),
107
+ history: (id) => this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/history`)),
108
+ status: (id) => this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/status`)),
109
+ validate: (id, proposedManifest) => this.request(`/v1/proposals/${encodeURIComponent(id)}/validate`, { method: 'POST', body: JSON.stringify(this.scopeBody({ proposed_manifest: proposedManifest })) }),
110
+ createPreview: (id) => this.request(`/v1/proposals/${encodeURIComponent(id)}/preview`, { method: 'POST', body: JSON.stringify(this.scopeBody()) }),
111
+ preview: (previewId) => this.request(this.scoped(`/v1/previews/${encodeURIComponent(previewId)}`)),
112
+ previewData: (previewId, collection, options = {}) => this.request(this.scoped(`/v1/previews/${encodeURIComponent(previewId)}/data/${encodeURIComponent(collection)}`, options)),
113
+ simulatePreview: (previewId, event) => this.request(this.scoped(`/v1/previews/${encodeURIComponent(previewId)}/simulate`), { method: 'POST', body: JSON.stringify({ event }) }),
114
+ approve: (id, approveAuthorization = false) => this.request(`/v1/proposals/${encodeURIComponent(id)}/approve`, { method: 'POST', body: JSON.stringify(this.scopeBody({ approve_authorization: approveAuthorization })) }),
115
+ reject: (id, reason) => this.request(`/v1/proposals/${encodeURIComponent(id)}/reject`, { method: 'POST', body: JSON.stringify(this.scopeBody({ reason })) }),
116
+ };
69
117
  }
70
118
  export function createClient(options) {
71
119
  return new FeltDBClient(options);
@@ -55,6 +55,8 @@ export * from './application-contract.js';
55
55
  export * from './canonical-application.js';
56
56
  export * from './application-development.js';
57
57
  export * from './proposal.js';
58
+ export * from './module.js';
59
+ export * from './module-intent.js';
58
60
  export { ProtocolHandler, type Message, type Operation as ProtocolOperation, type Response as ProtocolResponse, } from './protocol.js';
59
61
  /**
60
62
  * Agent Runtime APIs
@@ -1 +1 @@
1
- {"version":3,"file":"index-core.d.ts","sourceRoot":"","sources":["../src/index-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC9E,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,eAAe,CAAC;AAC9B,OAAO,EACL,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,SAAS,IAAI,iBAAiB,EACnC,KAAK,QAAQ,IAAI,gBAAgB,GAClC,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AAEnC;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACnE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzE;;GAEG;AACH,YAAY,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index-core.d.ts","sourceRoot":"","sources":["../src/index-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AACnC,YAAY,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC9E,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,2BAA2B,CAAC;AAC1C,cAAc,qBAAqB,CAAC;AACpC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,iCAAiC,CAAC;AAChD,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,eAAe,EACf,KAAK,OAAO,EACZ,KAAK,SAAS,IAAI,iBAAiB,EACnC,KAAK,QAAQ,IAAI,gBAAgB,GAClC,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,cAAc,YAAY,CAAC;AAC3B,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,oBAAoB,CAAC;AAEnC;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACnE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzE;;GAEG;AACH,YAAY,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,cAAc,wBAAwB,CAAC;AACvC,cAAc,iCAAiC,CAAC"}
@@ -54,6 +54,8 @@ export * from './application-contract.js';
54
54
  export * from './canonical-application.js';
55
55
  export * from './application-development.js';
56
56
  export * from './proposal.js';
57
+ export * from './module.js';
58
+ export * from './module-intent.js';
57
59
  export { ProtocolHandler, } from './protocol.js';
58
60
  /**
59
61
  * Agent Runtime APIs
@@ -0,0 +1,37 @@
1
+ import type { FeltDBContractSnapshot, FeltDBFlowProposal } from './application-development.js';
2
+ import { type FeltDBModuleDefinition, type FeltDBModuleOwnership } from './module.js';
3
+ export interface ModuleIntent {
4
+ action: 'implement' | 'extend';
5
+ module: string;
6
+ provider: string;
7
+ version: string;
8
+ original: string;
9
+ }
10
+ export interface ModuleImplementationPlan {
11
+ intent: ModuleIntent;
12
+ module: FeltDBModuleDefinition;
13
+ alreadyInstalled: boolean;
14
+ owner: string;
15
+ applicationModels: Array<{
16
+ name: string;
17
+ ownership: FeltDBModuleOwnership;
18
+ purpose: string;
19
+ }>;
20
+ relationships: string[];
21
+ events: string[];
22
+ workflows: string[];
23
+ capabilities: string[];
24
+ authorization: FeltDBModuleDefinition['authorization'];
25
+ configuration: string[];
26
+ secrets: string[];
27
+ stateFlow: string[];
28
+ warnings: string[];
29
+ }
30
+ export declare function resolveModuleIntent(value: string): ModuleIntent | undefined;
31
+ export declare function planModuleImplementation(intent: ModuleIntent, snapshot: FeltDBContractSnapshot, currentFlow: string): ModuleImplementationPlan;
32
+ export declare function createModuleImplementationProposal(input: {
33
+ intent: string;
34
+ snapshot: FeltDBContractSnapshot;
35
+ currentFlow: string;
36
+ }): Promise<FeltDBFlowProposal>;
37
+ //# sourceMappingURL=module-intent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-intent.d.ts","sourceRoot":"","sources":["../src/module-intent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAE/F,OAAO,EAA6D,KAAK,sBAAsB,EAAE,KAAK,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEjJ,MAAM,WAAW,YAAY;IAAG,MAAM,EAAE,WAAW,GAAG,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE;AACrI,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAC/F,iBAAiB,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,qBAAqB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9F,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvF,aAAa,EAAE,sBAAsB,CAAC,eAAe,CAAC,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IACnG,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACzC;AAMD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAU3E;AAQD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,GAAG,wBAAwB,CAgB9I;AAGD,wBAAsB,kCAAkC,CAAC,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,sBAAsB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAuBtK"}