app_bridge 1.0.0-x86_64-darwin → 2.1.0-x86_64-darwin

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65ea1d5385128308bac7eab284a4f9d3389e2a51f4e575b550d8107a053dff08
4
- data.tar.gz: ba3af926e040bee469c771ac3e8ad0b6160fa7f4736c021b5b4590e32ee47ac5
3
+ metadata.gz: 532c2aa5286173c345b2d3e09802f58268a02dda0bb1a2922ae4906107a5b9e2
4
+ data.tar.gz: ba6e0025d397e6fac6f2feb4e4953a3de6446747d0bfb9c3d2f56604b347a833
5
5
  SHA512:
6
- metadata.gz: 0d87dd1f0b10cf1fcf43672a4838a26fcf98b0c043ce4dcb530246dd40f227b8fd351ba832b74d03b4d719064dbb992c566ffb3a98d78d28d77f3bbadd179961
7
- data.tar.gz: 8efd8b6b06e68d5130a8052e267ada3212ca53b1d672c39483975414e19e412fb83b464b17ee9e53a3a3d2b9174d77cd1bbc123dba89f0187ef4e271b52090e5
6
+ metadata.gz: 33e5273b58ba665b0d578cc114bd10342e52d856e0d0c582ddc49249ad9a5767cc5267e023422628c9bbca6302f8a8e8cee4184b747b8f032e8ea6bc95d3e410
7
+ data.tar.gz: a85d038d6f286a1fd5b7cad816b6e02773ee78b4fb135be0c88a5f26828b2e5b8708853ef4affdc0784c9fbf85725be9e8c4170ab998bd25178dcc28ea79bef4
data/.editorconfig ADDED
@@ -0,0 +1,18 @@
1
+ # EditorConfig helps developers define and maintain consistent
2
+ # coding styles between different editors and IDEs
3
+ # http://editorconfig.org/
4
+
5
+ root = true
6
+
7
+ [*]
8
+ end_of_line = lf
9
+ trim_trailing_whitespace = true
10
+ insert_final_newline = true
11
+ charset = utf-8
12
+ indent_style = space
13
+ indent_size = 2
14
+ curly_bracket_next_line = false
15
+ indent_brace_style = K&R
16
+
17
+ [*.md]
18
+ trim_trailing_whitespace = false
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.4.2
data/Rakefile CHANGED
@@ -3,8 +3,6 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
5
 
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
6
  require "rubocop/rake_task"
9
7
 
10
8
  RuboCop::RakeTask.new
@@ -22,4 +20,9 @@ end
22
20
  # Load all project specific rake tasks
23
21
  Dir.glob(File.expand_path("tasks/**/*.rake", __dir__)).each { |file| load file }
24
22
 
25
- task default: %i[fixtures compile spec rubocop]
23
+ # Set test mode environment variable for specs
24
+ RSpec::Core::RakeTask.new(:spec) do |_t|
25
+ ENV["APP_BRIDGE_TEST_MODE"] = "1"
26
+ end
27
+
28
+ task default: %i[fixtures compile rust:test spec rubocop]
@@ -1,4 +1,4 @@
1
- package standout:app@0.3.0;
1
+ package standout:app@2.1.0;
2
2
 
3
3
  interface types {
4
4
  // The trigger-store is a string that is used to store data between trigger
@@ -10,10 +10,10 @@ interface types {
10
10
  // need to add more data to the store.
11
11
  type trigger-store = string;
12
12
 
13
- record account {
13
+ record connection {
14
14
  id: string,
15
15
  name: string,
16
- // The account data is a JSON object serialized into a string. The JSON root
16
+ // The connection data is a JSON object serialized into a string. The JSON root
17
17
  // will always be an object.
18
18
  serialized-data: string,
19
19
  }
@@ -23,12 +23,31 @@ interface types {
23
23
  // invoked.
24
24
  trigger-id: string,
25
25
 
26
- // The account that the trigger is invoked for.
27
- account: account,
26
+ // The connection that the trigger is invoked for.
27
+ // Connection is required for all trigger operations.
28
+ connection: connection,
28
29
 
29
30
  // The store will contain the data that was stored in the trigger store the
30
31
  // last time the trigger was invoked.
31
32
  store: trigger-store,
33
+
34
+ // The input data for the trigger, serialized as a JSON object string.
35
+ // This contains the input data from the trigger configuration form.
36
+ serialized-input: string,
37
+ }
38
+
39
+ record action-context {
40
+ // Action ID is a unique identifier for the action that is requested to be
41
+ // invoked.
42
+ action-id: string,
43
+
44
+ // The connection that the action is invoked for.
45
+ // Connection is required for all action operations.
46
+ connection: connection,
47
+
48
+ // The input data for the action, serialized as a JSON object string.
49
+ // This contains the data passed from the previous step in the workflow.
50
+ serialized-input: string,
32
51
  }
33
52
 
34
53
  record trigger-response {
@@ -41,10 +60,17 @@ interface types {
41
60
  store: trigger-store,
42
61
  }
43
62
 
63
+ record action-response {
64
+ // The output data from the action, serialized as a JSON object string.
65
+ // This contains the data that will be passed to the next step in the workflow.
66
+ // The data must be a valid JSON object (not an array or primitive).
67
+ serialized-output: string
68
+ }
69
+
44
70
  record trigger-event {
45
71
  // The ID of the trigger event
46
72
  //
47
- // If the account used for the given instance of the trigger is the same,
73
+ // If the connection used for the given instance of the trigger is the same,
48
74
  // as seen before. Then the event will be ignored.
49
75
  //
50
76
  // A scheduler could therefore use an timestamp as the ID, to ensure that
@@ -59,24 +85,74 @@ interface types {
59
85
  // ensure that the event is only triggered once per order update.
60
86
  id: string,
61
87
 
62
- // The timestamp of the event.
63
- // Must be a unix timestamp in milliseconds since epoch (UTC).
64
- // In JavaScript `Date.now()` can be used to get the current timestamp in
65
- // milliseconds.
66
- timestamp: u64,
67
-
68
88
  // Serialized data must be a JSON object serialized into a string
69
89
  // Note that it is important that the root is a object, not an array,
70
90
  // or another primitive type.
71
91
  serialized-data: string,
72
92
  }
93
+
94
+ /// A structured error that can be returned by for example a call to a trigger or action.
95
+ /// Contains a machine-readable code and a human-readable message.
96
+ record app-error {
97
+ /// The error code identifying the type of failure.
98
+ code: error-code,
99
+
100
+ /// A human-readable message describing the error in more detail.
101
+ message: string,
102
+ }
103
+
104
+ /// An enumeration of error codes that can be returned by a trigger implementation.
105
+ /// These codes help the platform and plugin developers distinguish between different types of failures.
106
+ variant error-code {
107
+ /// Authentication failed. Typically due to an invalid or expired API key or token.
108
+ unauthenticated,
109
+
110
+ /// Authorization failed. The connection is valid but does not have the necessary permissions.
111
+ forbidden,
112
+
113
+ /// The trigger is misconfigured. For example, a required setting is missing or invalid.
114
+ misconfigured,
115
+
116
+ /// The target system does not support a required feature or endpoint.
117
+ unsupported,
118
+
119
+ /// The target system is rate-limiting requests. Try again later.
120
+ rate-limit,
121
+
122
+ /// The request timed out. The target system did not respond in time.
123
+ timeout,
124
+
125
+ /// The target system is currently unavailable or unreachable.
126
+ unavailable,
127
+
128
+ /// An unexpected internal error occurred in the plugin.
129
+ internal-error,
130
+
131
+ /// The response from the external system could not be parsed or was in an invalid format.
132
+ malformed-response,
133
+
134
+ /// A catch-all for all other types of errors. Should include a descriptive message.
135
+ other,
136
+ }
73
137
  }
74
138
 
75
139
 
76
140
  interface triggers {
77
- use types.{trigger-context, trigger-event, trigger-response};
141
+ use types.{trigger-context, trigger-event, trigger-response, app-error};
142
+
143
+ trigger-ids: func() -> result<list<string>, app-error>;
78
144
 
79
- get-triggers: func() -> list<string>;
145
+ // Get the input schema for a specific trigger
146
+ // Returns a JSON Schema Draft 2020-12 schema as a string
147
+ // The schema may vary based on the connection in the context
148
+ // The trigger-id is extracted from the context
149
+ input-schema: func(context: trigger-context) -> result<string, app-error>;
150
+
151
+ // Get the output schema for a specific trigger
152
+ // Returns a JSON Schema Draft 2020-12 schema as a string
153
+ // The schema may vary based on the connection in the context
154
+ // The trigger-id is extracted from the context
155
+ output-schema: func(context: trigger-context) -> result<string, app-error>;
80
156
 
81
157
  // Fetch events
82
158
  //
@@ -99,7 +175,48 @@ interface triggers {
99
175
  // the same events. That will ensure that the user that is building an
100
176
  // integration with your trigger will not miss any events if your system is
101
177
  // down for a short period of time.
102
- fetch-events: func(context: trigger-context) -> trigger-response;
178
+ fetch-events: func(context: trigger-context) -> result<trigger-response, app-error>;
179
+ }
180
+
181
+ interface actions {
182
+ use types.{action-context, action-response, app-error};
183
+
184
+ action-ids: func() -> result<list<string>, app-error>;
185
+
186
+ // Get the input schema for a specific action
187
+ // Returns a JSON Schema Draft 2020-12 schema as a string
188
+ // The schema may vary based on the connection in the context
189
+ // The action-id is extracted from the context
190
+ input-schema: func(context: action-context) -> result<string, app-error>;
191
+
192
+ // Get the output schema for a specific action
193
+ // Returns a JSON Schema Draft 2020-12 schema as a string
194
+ // The schema may vary based on the connection in the context
195
+ // The action-id is extracted from the context
196
+ output-schema: func(context: action-context) -> result<string, app-error>;
197
+
198
+ // Execute an action
199
+ //
200
+ // There are some limitations to the function:
201
+ // - It must return an `action-response` within 30 seconds
202
+ // - The serialized-output must be a valid JSON object serialized as a string
203
+ //
204
+ // Actions can perform various operations such as:
205
+ // - Making HTTP requests to external APIs
206
+ // - Processing and transforming data
207
+ // - Storing data for future use
208
+ // - Triggering other systems or workflows
209
+ //
210
+ // The action receives input data from the previous step and can return
211
+ // serialized output data to be passed to the next step in the workflow.
212
+ execute: func(context: action-context) -> result<action-response, app-error>;
213
+ }
214
+
215
+ interface environment {
216
+ // Get all environment variables
217
+ env-vars: func() -> list<tuple<string, string>>;
218
+ // Get a specific environment variable by name
219
+ env-var: func(name: string) -> option<string>;
103
220
  }
104
221
 
105
222
  interface http {
@@ -154,5 +271,7 @@ interface http {
154
271
 
155
272
  world bridge {
156
273
  import http;
274
+ import environment;
157
275
  export triggers;
276
+ export actions;
158
277
  }
Binary file
Binary file
@@ -3,8 +3,16 @@
3
3
  require "timeout"
4
4
 
5
5
  module AppBridge
6
- # An app that can be used to fetch events.
6
+ # An app that can be used to fetch events and execute actions.
7
7
  class App
8
+ def initialize(component_path, environment_variables: {})
9
+ @component_path = component_path
10
+ @environment_variables = environment_variables
11
+ _rust_initialize(component_path, environment_variables)
12
+ rescue StandardError
13
+ raise InternalError, "Incompatible WASM file version"
14
+ end
15
+
8
16
  def fetch_events(context)
9
17
  response = request_events_with_timeout(context)
10
18
 
@@ -14,7 +22,15 @@ module AppBridge
14
22
  response
15
23
  end
16
24
 
17
- def polling_timeout
25
+ def execute_action(context)
26
+ response = request_action_with_timeout(context)
27
+
28
+ validate_action_response_size!(response.serialized_output)
29
+
30
+ response
31
+ end
32
+
33
+ def timeout_seconds
18
34
  30 # seconds
19
35
  end
20
36
 
@@ -32,8 +48,20 @@ module AppBridge
32
48
  raise StoreTooLargeError, "Store size exceeds 64 kB limit"
33
49
  end
34
50
 
51
+ def validate_action_response_size!(serialized_output)
52
+ return if serialized_output.size <= 64 * 1024
53
+
54
+ raise ActionResponseTooLargeError, "Action response size exceeds 64 kB limit"
55
+ end
56
+
57
+ def request_action_with_timeout(context)
58
+ Timeout.timeout(timeout_seconds, TimeoutError, "Action exceeded #{timeout_seconds} seconds") do
59
+ _rust_execute_action(context)
60
+ end
61
+ end
62
+
35
63
  def request_events_with_timeout(context)
36
- Timeout.timeout(polling_timeout, TimeoutError, "Polling exceeded #{polling_timeout} seconds") do
64
+ Timeout.timeout(timeout_seconds, TimeoutError, "Polling exceeded #{timeout_seconds} seconds") do
37
65
  _rust_fetch_events(context)
38
66
  end
39
67
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # When updating the version, please also update the version in the
4
+ # ext/app_bridge/Cargo.toml file to keep them in sync.
5
+
3
6
  module AppBridge
4
- VERSION = "1.0.0"
7
+ VERSION = "2.1.0"
5
8
  end
data/lib/app_bridge.rb CHANGED
@@ -3,17 +3,12 @@
3
3
  require_relative "app_bridge/version"
4
4
  require_relative "app_bridge/app"
5
5
 
6
- begin
7
- require "app_bridge/#{RUBY_VERSION.split(".").first(2).join(".")}/app_bridge"
8
- rescue LoadError
9
- require "app_bridge/app_bridge"
10
- end
11
-
12
6
  module AppBridge
13
7
  class Error < StandardError; end
14
8
  class TimeoutError < Error; end
15
9
  class TooManyEventsError < Error; end
16
10
  class StoreTooLargeError < Error; end
11
+ class ActionResponseTooLargeError < Error; end
17
12
 
18
13
  # Represents a trigger event that is recieved from the app.
19
14
  class TriggerEvent
@@ -23,3 +18,6 @@ module AppBridge
23
18
  end
24
19
  end
25
20
  end
21
+
22
+ # Load the extension after the module is defined
23
+ require_relative "app_bridge/app_bridge"
data/sig/app_bridge.rbs CHANGED
@@ -7,11 +7,15 @@ module AppBridge
7
7
 
8
8
  def triggers: () -> Array[String]
9
9
 
10
+ def actions: () -> Array[String]
11
+
10
12
  def fetch_events: (TriggerContext) -> TriggerResponse
13
+
14
+ def execute_action: (ActionContext) -> ActionResponse
11
15
  end
12
16
 
13
- class Account
14
- def self.new: (String, String, String) -> Account
17
+ class Connection
18
+ def self.new: (String, String, String) -> Connection
15
19
 
16
20
  def id: () -> String
17
21
 
@@ -39,12 +43,30 @@ module AppBridge
39
43
  end
40
44
 
41
45
  class TriggerContext
42
- def self.new: (String, Account, String) -> TriggerContext
46
+ def self.new: (String, Connection, String, String) -> TriggerContext
43
47
 
44
48
  def trigger_id: () -> String
45
49
 
46
- def account: () -> Account
50
+ def connection: () -> Connection
47
51
 
48
52
  def store: () -> String
53
+
54
+ def serialized_input: () -> String
55
+ end
56
+
57
+ class ActionContext
58
+ def self.new: (String, Connection, String) -> ActionContext
59
+
60
+ def action_id: () -> String
61
+
62
+ def connection: () -> Connection
63
+
64
+ def serialized_input: () -> String
65
+ end
66
+
67
+ class ActionResponse
68
+ def self.new: (String) -> ActionResponse
69
+
70
+ def serialized_output: () -> String
49
71
  end
50
72
  end
data/tasks/fixtures.rake CHANGED
@@ -2,36 +2,43 @@
2
2
 
3
3
  require "English"
4
4
 
5
- namespace :fixtures do
5
+ namespace :fixtures do # rubocop:disable Metrics/BlockLength
6
6
  namespace :apps do
7
7
  desc "Clean up build artifacts"
8
8
  task :clean do
9
- # In context of the path spec/fixtures/components/example.
9
+ # In context of the path spec/fixtures/components/rust_app.
10
10
  # Execute cargo clean.
11
11
  #
12
- pwd = "spec/fixtures/components/example"
12
+ pwd = "spec/fixtures/components/rust_app"
13
13
  pid = Process.spawn("cargo clean", chdir: pwd)
14
14
  Process.wait(pid)
15
15
  raise "Failed to clean build artifacts" unless $CHILD_STATUS.success?
16
16
 
17
17
  # Remove the built wasm artifact.
18
- pid = Process.spawn("rm example.wasm", chdir: "spec/fixtures/components")
18
+ pid = Process.spawn("rm rust_app.wasm", chdir: "spec/fixtures/components")
19
19
  Process.wait(pid)
20
20
  end
21
21
 
22
22
  desc "Compile the fixture apps"
23
- task :compile do
24
- pwd = "spec/fixtures/components/example"
23
+ task :compile_rust do
24
+ pwd = "spec/fixtures/components/rust_app"
25
25
  compile_pid = Process.spawn("cargo clean && cargo build --release --target wasm32-wasip2",
26
26
  chdir: pwd)
27
27
  Process.wait(compile_pid)
28
28
  raise "Failed to build artifacts" unless $CHILD_STATUS.success?
29
29
 
30
- move_pid = Process.spawn("mv #{pwd}/target/wasm32-wasip2/release/example.wasm #{pwd}/../example.wasm")
30
+ move_pid = Process.spawn("mv #{pwd}/target/wasm32-wasip2/release/rust_app.wasm #{pwd}/../rust_app.wasm")
31
31
  Process.wait(move_pid)
32
32
  end
33
+
34
+ task :compile_js do
35
+ pwd = "spec/fixtures/components/js_app"
36
+ pid = Process.spawn("npm run build", chdir: pwd)
37
+ Process.wait(pid)
38
+ raise "Failed to build artifacts" unless $CHILD_STATUS.success?
39
+ end
33
40
  end
34
41
  end
35
42
 
36
43
  desc "Build all fixtures"
37
- task fixtures: %i[fixtures:apps:clean fixtures:apps:compile]
44
+ task fixtures: %i[fixtures:apps:clean fixtures:apps:compile_rust fixtures:apps:compile_js]
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rake task to run Rust tests
4
+ namespace :rust do
5
+ desc "Run Rust tests"
6
+ task :test do
7
+ puts "Running Rust tests..."
8
+ system("cargo test")
9
+ puts "Rust tests completed."
10
+ end
11
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_bridge
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.1.0
5
5
  platform: x86_64-darwin
6
6
  authors:
7
7
  - Alexander Ross
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-03 00:00:00.000000000 Z
11
+ date: 2025-10-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: The app_bridge gem is designed to enable seamless interaction with WebAssembly
14
14
  components that adhere to the WIT specification `standout:app`. It is developed
@@ -19,8 +19,10 @@ executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
+ - ".editorconfig"
22
23
  - ".rspec"
23
24
  - ".rubocop.yml"
25
+ - ".tool-versions"
24
26
  - CHANGELOG.md
25
27
  - README.md
26
28
  - Rakefile
@@ -32,6 +34,7 @@ files:
32
34
  - lib/app_bridge/version.rb
33
35
  - sig/app_bridge.rbs
34
36
  - tasks/fixtures.rake
37
+ - tasks/rust_test.rake
35
38
  homepage: https://github.com/standout/app_bridge
36
39
  licenses: []
37
40
  metadata: