///|
/// One JavaScript-facing API descriptor exposed by a user app command extension.
pub struct AppCommandApiDescriptor {
api_name : String
}
///|
/// JavaScript bridge metadata exposed by a user app command extension.
pub struct AppCommandExtensionDescriptor {
id : String
js_namespace : String
apis : Array[AppCommandApiDescriptor]
scripts : Array[String]
}
///|
pub type AppCommandRequestContext = @core.AppCommandRequestContext
///|
/// Extension definition registered once during application startup.
pub struct AppCommandExtensionSpec {
id : String
js_namespace : String
scripts : Array[String]
dependencies : Array[String]
register : (CommandRegistrar) -> Unit raise
resolve_permission : (Json, String) -> Json raise PermissionScopeValidationError
on_start : (@async.TaskGroup[Unit]) -> Unit raise
on_destroy : () -> Unit raise
}
///|
pub fn AppCommandApiDescriptor::AppCommandApiDescriptor(
api_name : String,
) -> AppCommandApiDescriptor {
AppCommandApiDescriptor::{ api_name, }
}
///|
pub fn AppCommandExtensionDescriptor::AppCommandExtensionDescriptor(
id : String,
js_namespace : String,
apis : Array[AppCommandApiDescriptor],
scripts? : Array[String] = [],
) -> AppCommandExtensionDescriptor {
AppCommandExtensionDescriptor::{
id,
js_namespace,
apis: copy_command_api_descriptors(apis),
scripts: copy_strings(scripts),
}
}
///|
/// Creates an extension spec whose JavaScript APIs and native operation names
/// are derived from the descriptors actually bound during registration.
pub fn AppCommandExtensionSpec::typed(
contract : @proton_contract.ExtensionContract,
register : (CommandRegistrar) -> Unit raise,
scripts? : Array[String] = [],
dependencies? : Array[String] = [],
resolve_permission? : (Json, String) -> Json raise PermissionScopeValidationError,
on_start? : (@async.TaskGroup[Unit]) -> Unit raise = fn(_tasks) { },
on_destroy? : () -> Unit raise = fn() { },
) -> AppCommandExtensionSpec raise ExtensionSpecError {
contract.validate() catch {
error => raise InvalidContract(detail=error.message())
}
let id = contract.id()
let js_namespace = contract.js_namespace()
let resolve_permission : (Json, String) -> Json raise PermissionScopeValidationError = match
resolve_permission {
Some(resolve) => resolve
None =>
fn(scope, _base_path) -> Json raise PermissionScopeValidationError {
guard scope is Object(fields) else {
raise PermissionScopeValidationError::Invalid(
detail="extension " + id + " permission scope must be an object",
)
}
for _, _ in fields {
raise PermissionScopeValidationError::Invalid(
detail="extension " +
id +
" does not accept a non-empty permission scope",
)
}
scope
}
}
AppCommandExtensionSpec::{
id,
js_namespace,
scripts: copy_strings(scripts),
dependencies: copy_strings(dependencies),
register,
resolve_permission,
on_start,
on_destroy,
}
}
///|
pub fn AppCommandExtensionSpec::id(self : AppCommandExtensionSpec) -> String {
self.id
}
///|
pub fn AppCommandExtensionSpec::js_namespace(
self : AppCommandExtensionSpec,
) -> String {
self.js_namespace
}
///|
pub fn AppCommandExtensionSpec::dependencies(
self : AppCommandExtensionSpec,
) -> Array[String] {
copy_strings(self.dependencies)
}
///|
/// Returns the operation names derived from the registered commands.
pub fn AppCommandExtensionDescriptor::op_names(
self : AppCommandExtensionDescriptor,
) -> Array[String] {
self.apis.map(fn(api) {
app_command_extension_op_name(self.js_namespace, api.api_name)
})
}
///|
/// Registers handlers and returns their frontend metadata.
/// The callback's registrar expires when registration returns or raises.
pub fn AppCommandExtensionSpec::register(
self : AppCommandExtensionSpec,
host : @core.AppCommandHost,
) -> AppCommandExtensionDescriptor raise {
let scoped = CommandRegistrar::{
host,
owner: Some((self.id, self.js_namespace)),
routes: [],
registration_closed: false,
}
defer {
scoped.registration_closed = true
}
(self.register)(scoped)
AppCommandExtensionDescriptor(
self.id,
self.js_namespace,
scoped.routes.map(route => AppCommandApiDescriptor(route.member_name())),
scripts=self.scripts,
)
}
///|
pub fn AppCommandExtensionSpec::resolve_permission(
self : AppCommandExtensionSpec,
scope : Json,
base_path : String,
) -> Json raise PermissionScopeValidationError {
(self.resolve_permission)(scope, base_path)
}
///|
pub fn AppCommandExtensionSpec::destroy(
self : AppCommandExtensionSpec,
) -> Unit raise {
(self.on_destroy)()
}
///|
/// Returns the registered frontend command names.
pub fn AppCommandExtensionDescriptor::apis(
self : AppCommandExtensionDescriptor,
) -> Array[AppCommandApiDescriptor] {
copy_command_api_descriptors(self.apis)
}
///|
pub fn AppCommandExtensionSpec::scripts(
self : AppCommandExtensionSpec,
) -> Array[String] {
copy_strings(self.scripts)
}
///|
pub fn AppCommandApiDescriptor::api_name(
self : AppCommandApiDescriptor,
) -> String {
self.api_name
}
///|
pub fn app_command_js_property_is_reserved(name : String) -> Bool {
name.has_prefix("@@") ||
name == "__proto__" ||
name == "prototype" ||
name == "constructor"
}
///|
pub fn app_command_extension_op_name(
js_namespace : String,
api_name : String,
) -> String {
"ext:" + js_namespace + "/" + api_name
}
///|
fn copy_command_api_descriptors(
values : Array[AppCommandApiDescriptor],
) -> Array[AppCommandApiDescriptor] {
values.map(fn(value) { value })
}
///|
fn copy_strings(values : Array[String]) -> Array[String] {
values.map(fn(value) { value })
}
///|
/// Starts extension-owned work in the application's task lifetime.
/// Called after registration and before application startup hooks.
pub fn AppCommandExtensionSpec::start(
self : AppCommandExtensionSpec,
tasks : @async.TaskGroup[Unit],
) -> Unit raise {
(self.on_start)(tasks)
}
///|
/// Returns the registered extension identity.
pub fn AppCommandExtensionDescriptor::id(
self : AppCommandExtensionDescriptor,
) -> String {
self.id
}
///|
/// Returns the registered JavaScript namespace.
pub fn AppCommandExtensionDescriptor::js_namespace(
self : AppCommandExtensionDescriptor,
) -> String {
self.js_namespace
}
///|
/// Returns the extension initialization scripts.
pub fn AppCommandExtensionDescriptor::scripts(
self : AppCommandExtensionDescriptor,
) -> Array[String] {
copy_strings(self.scripts)
}