From cf7b963a8eaf302f47ddf6b4fea3f627fc76c437 Mon Sep 17 00:00:00 2001 From: Jarrod Overson Date: Wed, 13 Sep 2023 12:36:44 -0400 Subject: [PATCH] feat: added optional directory listing for the static server --- Cargo.lock | 2 + Cargo.toml | 1 + .../wick-config/definitions/v1/manifest.apex | 3 + crates/wick/wick-config/docs/v1.md | 1 + .../wick-config/json-schema/manifest.json | 3672 +++++++++-------- .../wick-config/json-schema/v1/manifest.json | 5 + .../app_config/triggers/http/static_router.rs | 2 + crates/wick/wick-config/src/v1.rs | 4 + crates/wick/wick-config/src/v1/conversions.rs | 2 + crates/wick/wick-runtime/Cargo.toml | 2 + .../src/triggers/http/routers/static_.rs | 37 +- .../http/routers/static_/index_list.rs | 120 + .../http/routers/static_/template.html | 75 + .../components/config-generator/src/lib.rs | 1 + 14 files changed, 2083 insertions(+), 1844 deletions(-) create mode 100644 crates/wick/wick-runtime/src/triggers/http/routers/static_/index_list.rs create mode 100644 crates/wick/wick-runtime/src/triggers/http/routers/static_/template.html diff --git a/Cargo.lock b/Cargo.lock index 9831b804..8542120d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6780,9 +6780,11 @@ dependencies = [ "hyper-staticfile", "itertools 0.11.0", "lazy_static", + "liquid", "once_cell", "openapiv3", "parking_lot", + "percent-encoding", "pretty_assertions", "reqwest", "rstest", diff --git a/Cargo.toml b/Cargo.toml index ee35aa70..771adf52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -220,6 +220,7 @@ option-utils = { version = "0.1", default-features = false } opentelemetry = { version = "0.19.0", default-features = false } opentelemetry-otlp = { version = "0.12.0", default-features = false } parking_lot = { version = "0.12", default-features = false } +percent-encoding = { version = "2.1" } paste = { version = "1.0", default-features = false } pin-project-lite = { version = "0.2", default-features = false } postgres-openssl = { version = "0.5", default-features = false } diff --git a/crates/wick/wick-config/definitions/v1/manifest.apex b/crates/wick/wick-config/definitions/v1/manifest.apex index 6468e385..44a49edd 100644 --- a/crates/wick/wick-config/definitions/v1/manifest.apex +++ b/crates/wick/wick-config/definitions/v1/manifest.apex @@ -306,6 +306,9 @@ type StaticRouter @tagged("wick/router/static@v1") { "Fallback path (relative to volume `resource`) for files to serve in case of a 404. Useful for SPA's. if volume resource is: /www and fallback: index.html, then a 404 will serve /www/index.html" fallback: string? + + "Whether or not to serve directory listings when a directory is requested." + indexes: bool } "A router that delegates all requests to the configured operation, optionally encoding/decoding based on the specified codec." diff --git a/crates/wick/wick-config/docs/v1.md b/crates/wick/wick-config/docs/v1.md index 5fdb4487..822916fc 100644 --- a/crates/wick/wick-config/docs/v1.md +++ b/crates/wick/wick-config/docs/v1.md @@ -566,6 +566,7 @@ Any one of the following types: | `middleware` | [`Middleware`](#middleware) |Middleware operations for this router.||| | `volume` | `string` |The volume to serve static files from.|Yes|| | `fallback` | `string` |Fallback path (relative to volume `resource`) for files to serve in case of a 404. Useful for SPA's. if volume resource is: /www and fallback: index.html, then a 404 will serve /www/index.html||| +| `indexes` | `bool` |Whether or not to serve directory listings when a directory is requested.||| diff --git a/crates/wick/wick-config/json-schema/manifest.json b/crates/wick/wick-config/json-schema/manifest.json index 0d5efd47..41797b5b 100644 --- a/crates/wick/wick-config/json-schema/manifest.json +++ b/crates/wick/wick-config/json-schema/manifest.json @@ -3,103 +3,36 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "type": "object", "$defs": { - "v0.CollectionDefinition": { - "$anchor": "v0.CollectionDefinition", + "v0.HostManifest": { + "$anchor": "v0.HostManifest", "additionalProperties": false, "type": "object", "properties": { - "namespace": { - "description": "The local namespace for the collection.", - "type": "string" - }, - "kind": { - "description": "The kind/type of the collection.", - "$ref": "#/$defs/v0.CollectionKind" - }, - "reference": { - "description": "The reference/location of the collection.", - "type": "string" + "format": { + "description": "The configuration manifest format.", + "enum": [ + 0 + ] }, - "data": { - "description": "Data or configuration used to initialize the collection.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": {} - } - } - }, - "required": [] - }, - "v0.CollectionKind": { - "$anchor": "v0.CollectionKind", - "enum": [ - "Native", - "GrpcUrl", - "WaPc", - "Network" - ] - }, - "v0.ComponentDefinition": { - "$anchor": "v0.ComponentDefinition", - "additionalProperties": false, - "type": "object", - "properties": { - "id": { - "description": "The ID of the component (i.e. the alias, key, or namespace).", + "version": { + "description": "The version of the configuration.", "type": "string" }, - "data": { - "description": "Data to associate with the reference.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": {} - } - } - }, - "required": [ - "id" - ] - }, - "v0.ConnectionDefinition": { - "$anchor": "v0.ConnectionDefinition", - "additionalProperties": false, - "type": "object", - "properties": { - "from": { - "description": "The originating component from upstream.", - "$ref": "#/$defs/v0.ConnectionTargetDefinition" + "host": { + "description": "Additional host configuration.", + "$ref": "#/$defs/v0.HostConfig" }, - "to": { - "description": "The destination component (downstream).", - "$ref": "#/$defs/v0.ConnectionTargetDefinition" - } - }, - "required": [] - }, - "v0.ConnectionTargetDefinition": { - "$anchor": "v0.ConnectionTargetDefinition", - "additionalProperties": false, - "type": "object", - "properties": { - "instance": { - "description": "The instance name of the referenced component.", - "type": "string" + "network": { + "description": "The configuration for a Wick network.", + "$ref": "#/$defs/v0.NetworkManifest" }, - "port": { - "description": "The component's port.", + "default_schematic": { + "description": "The default schematic to execute if none is provided.", "type": "string" - }, - "data": { - "description": "Data to associate with a connection.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": {} - } } }, "required": [ - "instance", - "port" + "format" ] }, "v0.HostConfig": { @@ -144,38 +77,6 @@ }, "required": [] }, - "v0.HostManifest": { - "$anchor": "v0.HostManifest", - "additionalProperties": false, - "type": "object", - "properties": { - "format": { - "description": "The configuration manifest format.", - "enum": [ - 0 - ] - }, - "version": { - "description": "The version of the configuration.", - "type": "string" - }, - "host": { - "description": "Additional host configuration.", - "$ref": "#/$defs/v0.HostConfig" - }, - "network": { - "description": "The configuration for a Wick network.", - "$ref": "#/$defs/v0.NetworkManifest" - }, - "default_schematic": { - "description": "The default schematic to execute if none is provided.", - "type": "string" - } - }, - "required": [ - "format" - ] - }, "v0.HttpConfig": { "$anchor": "v0.HttpConfig", "additionalProperties": false, @@ -265,6 +166,42 @@ }, "required": [] }, + "v0.CollectionDefinition": { + "$anchor": "v0.CollectionDefinition", + "additionalProperties": false, + "type": "object", + "properties": { + "namespace": { + "description": "The local namespace for the collection.", + "type": "string" + }, + "kind": { + "description": "The kind/type of the collection.", + "$ref": "#/$defs/v0.CollectionKind" + }, + "reference": { + "description": "The reference/location of the collection.", + "type": "string" + }, + "data": { + "description": "Data or configuration used to initialize the collection.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": {} + } + } + }, + "required": [] + }, + "v0.CollectionKind": { + "$anchor": "v0.CollectionKind", + "enum": [ + "Native", + "GrpcUrl", + "WaPc", + "Network" + ] + }, "v0.SchematicManifest": { "$anchor": "v0.SchematicManifest", "additionalProperties": false, @@ -304,135 +241,118 @@ "name" ] }, - "v1.AppConfiguration": { - "$anchor": "v1.AppConfiguration", + "v0.ComponentDefinition": { + "$anchor": "v0.ComponentDefinition", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/app@v1" - ] - }, - "name": { - "description": "The application's name.", + "id": { + "description": "The ID of the component (i.e. the alias, key, or namespace).", "type": "string" }, - "metadata": { - "description": "Associated metadata for this application.", - "$ref": "#/$defs/v1.Metadata" - }, - "package": { - "description": "Details about the package for this application.", - "$ref": "#/$defs/v1.PackageDefinition" - }, - "resources": { - "description": "Resources and configuration that the application and its components can access.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.ResourceBinding" - } - }, - "import": { - "description": "Components that to import and make available to the application.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.ImportBinding" - } - }, - "triggers": { - "description": "Triggers to load and instantiate to drive the application's behavior.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.TriggerDefinition" + "data": { + "description": "Data to associate with the reference.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": {} } } }, - "required": [] - }, - "v1.AssertionOperator": { - "$anchor": "v1.AssertionOperator", - "enum": [ - "Equals", - "LessThan", - "GreaterThan", - "Regex", - "Contains" + "required": [ + "id" ] }, - "v1.BlockExpression": { - "$anchor": "v1.BlockExpression", + "v0.ConnectionDefinition": { + "$anchor": "v0.ConnectionDefinition", "additionalProperties": false, "type": "object", "properties": { - "expressions": { - "type": "array", - "items": { - "$ref": "#/$defs/v1.FlowExpression" - } + "from": { + "description": "The originating component from upstream.", + "$ref": "#/$defs/v0.ConnectionTargetDefinition" + }, + "to": { + "description": "The destination component (downstream).", + "$ref": "#/$defs/v0.ConnectionTargetDefinition" } }, - "required": [ - "expressions" - ] + "required": [] }, - "v1.Bool": { - "$anchor": "v1.Bool", - "additionalProperties": false, - "type": "string", - "enum": [ - "i8" - ] - }, - "v1.Bytes": { - "$anchor": "v1.Bytes", - "additionalProperties": false, - "type": "string", - "enum": [ - "bytes" - ] - }, - "v1.CliTrigger": { - "$anchor": "v1.CliTrigger", + "v0.ConnectionTargetDefinition": { + "$anchor": "v0.ConnectionTargetDefinition", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/trigger/cli@v1" - ] + "instance": { + "description": "The instance name of the referenced component.", + "type": "string" }, - "operation": { - "description": "The operation that will act as the main entrypoint for this trigger.", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/$defs/v1.ComponentOperationExpression" - } - ] + "port": { + "description": "The component's port.", + "type": "string" + }, + "data": { + "description": "Data to associate with a connection.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": {} + } } }, "required": [ - "operation" + "instance", + "port" ] }, - "v1.Codec": { - "$anchor": "v1.Codec", - "enum": [ - "Json", - "Raw", - "FormData", - "Text" + "v1.WickConfig": { + "oneOf": [ + { + "$ref": "#/$defs/v1.AppConfiguration" + }, + { + "$ref": "#/$defs/v1.ComponentConfiguration" + }, + { + "$ref": "#/$defs/v1.TypesConfiguration" + }, + { + "$ref": "#/$defs/v1.TestConfiguration" + }, + { + "$ref": "#/$defs/v1.LockdownConfiguration" + } ] }, - "v1.ComponentConfiguration": { - "$anchor": "v1.ComponentConfiguration", + "v1.LocationReference": { + "type": "string" + }, + "v1.LiquidJsonValue": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object" + }, + { + "type": "array" + }, + { + "type": "string" + }, + { + "type": "number" + } + ] + }, + "v1.LiquidTemplate": { + "type": "string" + }, + "v1.Glob": { + "type": "string" + }, + "v1.AppConfiguration": { + "$anchor": "v1.AppConfiguration", "additionalProperties": false, "type": "object", "properties": { @@ -440,102 +360,390 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/component@v1" + "wick/app@v1" ] }, "name": { - "description": "The name of the component.", + "description": "The application's name.", "type": "string" }, "metadata": { - "description": "Associated metadata for this component.", + "description": "Associated metadata for this application.", "$ref": "#/$defs/v1.Metadata" }, "package": { - "description": "Details about the package for this component.", + "description": "Details about the package for this application.", "$ref": "#/$defs/v1.PackageDefinition" }, - "host": { - "description": "Configuration for when wick hosts this component as a service.", - "$ref": "#/$defs/v1.HostConfig" - }, "resources": { - "description": "Resources that the component can access.", + "description": "Resources and configuration that the application and its components can access.", "type": "array", "items": { "$ref": "#/$defs/v1.ResourceBinding" } }, "import": { - "description": "Components or types to import into this component's scope.", + "description": "Components that to import and make available to the application.", "type": "array", "items": { "$ref": "#/$defs/v1.ImportBinding" } }, - "types": { - "description": "Additional types to export and make available to the component.", + "triggers": { + "description": "Triggers to load and instantiate to drive the application's behavior.", "type": "array", "items": { - "$ref": "#/$defs/v1.TypeDefinition" + "$ref": "#/$defs/v1.TriggerDefinition" + } + } + }, + "required": [] + }, + "v1.Metadata": { + "$anchor": "v1.Metadata", + "additionalProperties": false, + "type": "object", + "properties": { + "version": { + "description": "The version of the artifact.", + "type": "string" + }, + "authors": { + "description": "A list of the authors.", + "type": "array", + "items": { + "type": "string" } }, - "requires": { - "description": "Interfaces the component requires to operate.", + "vendors": { + "description": "A list of any vendors associated with the artifact.", "type": "array", "items": { - "$ref": "#/$defs/v1.InterfaceBinding" + "type": "string" } }, - "component": { - "description": "Configuration specific to different kinds of components.", - "$ref": "#/$defs/v1.ComponentKind" + "description": { + "description": "A short description.", + "type": "string" }, - "tests": { - "description": "Assertions that can be run against the component to validate its behavior.", + "documentation": { + "description": "Where to find documentation.", + "type": "string" + }, + "licenses": { + "description": "The license(s) for the artifact.", "type": "array", "items": { - "$ref": "#/$defs/v1.TestConfiguration" + "type": "string" + } + }, + "icon": { + "description": "An icon to associate with the artifact.", + "type": "string" + } + }, + "required": [] + }, + "v1.PackageDefinition": { + "$anchor": "v1.PackageDefinition", + "additionalProperties": false, + "type": "object", + "properties": { + "files": { + "description": "The list of files and folders to be included with the package.", + "type": "array", + "items": { + "type": "string" } + }, + "registry": { + "description": "Configuration for publishing the package to a registry.", + "$ref": "#/$defs/v1.RegistryDefinition" + } + }, + "required": [] + }, + "v1.RegistryDefinition": { + "$anchor": "v1.RegistryDefinition", + "additionalProperties": false, + "type": "object", + "properties": { + "host": { + "description": "The registry to publish to, e.g. registry.candle.dev", + "type": "string" + }, + "namespace": { + "description": "The namespace on the registry. e.g.: [*your username*]", + "type": "string" + } + }, + "required": [] + }, + "v1.ResourceBinding": { + "$anchor": "v1.ResourceBinding", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the binding.", + "type": "string" + }, + "resource": { + "description": "The resource to bind to.", + "$ref": "#/$defs/v1.ResourceDefinition" + } + }, + "required": [ + "name", + "resource" + ] + }, + "v1.ImportBinding": { + "$anchor": "v1.ImportBinding", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the binding.", + "type": "string" + }, + "component": { + "description": "The import to bind to.", + "$ref": "#/$defs/v1.ImportDefinition" } }, "required": [ + "name", "component" ] }, - "v1.ComponentDefinition": { + "v1.ResourceDefinition": { "oneOf": [ { - "$ref": "#/$defs/v1.GrpcUrlComponent" - }, - { - "$ref": "#/$defs/v1.ManifestComponent" + "$ref": "#/$defs/v1.TcpPort" }, { - "$ref": "#/$defs/v1.ComponentReference" + "$ref": "#/$defs/v1.UdpPort" }, { - "$ref": "#/$defs/v1.SqlComponent" + "$ref": "#/$defs/v1.Url" }, { - "$ref": "#/$defs/v1.HttpClientComponent" + "$ref": "#/$defs/v1.Volume" } ] }, - "v1.ComponentKind": { - "oneOf": [ - { - "$ref": "#/$defs/v1.WasmComponentConfiguration" + "v1.TcpPort": { + "$anchor": "v1.TcpPort", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/tcpport@v1" + ] + }, + "port": { + "description": "The port to bind to.", + "$ref": "#/$defs/v1.LiquidTemplate" + }, + "address": { + "description": "The address to bind to.", + "$ref": "#/$defs/v1.LiquidTemplate" + } + }, + "required": [] + }, + "v1.UdpPort": { + "$anchor": "v1.UdpPort", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/udpport@v1" + ] + }, + "port": { + "description": "The port to bind to.", + "$ref": "#/$defs/v1.LiquidTemplate" + }, + "address": { + "description": "The address to bind to.", + "$ref": "#/$defs/v1.LiquidTemplate" + } + }, + "required": [] + }, + "v1.Volume": { + "$anchor": "v1.Volume", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/volume@v1" + ] + }, + "path": { + "description": "The path.", + "$ref": "#/$defs/v1.LiquidTemplate" + } + }, + "required": [ + "path" + ] + }, + "v1.Url": { + "$anchor": "v1.Url", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/url@v1" + ] }, + "url": { + "description": "The url string.", + "$ref": "#/$defs/v1.LiquidTemplate" + } + }, + "required": [ + "url" + ] + }, + "v1.TriggerDefinition": { + "oneOf": [ { - "$ref": "#/$defs/v1.CompositeComponentConfiguration" + "$ref": "#/$defs/v1.CliTrigger" }, { - "$ref": "#/$defs/v1.SqlComponent" + "$ref": "#/$defs/v1.HttpTrigger" }, { - "$ref": "#/$defs/v1.HttpClientComponent" + "$ref": "#/$defs/v1.TimeTrigger" + } + ] + }, + "v1.CliTrigger": { + "$anchor": "v1.CliTrigger", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/trigger/cli@v1" + ] + }, + "operation": { + "description": "The operation that will act as the main entrypoint for this trigger.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ComponentOperationExpression" + } + ] + } + }, + "required": [ + "operation" + ] + }, + "v1.TimeTrigger": { + "$anchor": "v1.TimeTrigger", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/trigger/time@v1" + ] + }, + "schedule": { + "description": "The schedule to run the trigger with.", + "$ref": "#/$defs/v1.Schedule" + }, + "operation": { + "description": "The operation to execute on the schedule.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ComponentOperationExpression" + } + ] + }, + "payload": { + "description": "Values passed to the operation as inputs", + "type": "array", + "items": { + "$ref": "#/$defs/v1.OperationInput" + } + } + }, + "required": [ + "schedule", + "operation", + "payload" + ] + }, + "v1.OperationInput": { + "$anchor": "v1.OperationInput", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the input.", + "type": "string" + }, + "value": { + "description": "The value to pass." + } + }, + "required": [ + "name", + "value" + ] + }, + "v1.Schedule": { + "$anchor": "v1.Schedule", + "additionalProperties": false, + "type": "object", + "properties": { + "cron": { + "description": "Schedule in cron format with second precision. See [cron.help](https://cron.help) for more information.", + "type": "string" + }, + "repeat": { + "description": "repeat `n` times. Use `0` to repeat indefinitely", + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] } + }, + "required": [ + "cron" ] }, "v1.ComponentOperationExpression": { @@ -584,8 +792,8 @@ "name" ] }, - "v1.ComponentReference": { - "$anchor": "v1.ComponentReference", + "v1.HttpTrigger": { + "$anchor": "v1.HttpTrigger", "additionalProperties": false, "type": "object", "properties": { @@ -593,20 +801,43 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/component/reference@v1" + "wick/trigger/http@v1" ] }, - "id": { - "description": "The id of the referenced component.", + "resource": { + "description": "The TcpPort resource to listen on for connections.", "type": "string" + }, + "routers": { + "description": "The router to handle incoming requests", + "type": "array", + "items": { + "$ref": "#/$defs/v1.HttpRouter" + } } }, "required": [ - "id" + "resource" ] }, - "v1.CompositeComponentConfiguration": { - "$anchor": "v1.CompositeComponentConfiguration", + "v1.HttpRouter": { + "oneOf": [ + { + "$ref": "#/$defs/v1.RawRouter" + }, + { + "$ref": "#/$defs/v1.RestRouter" + }, + { + "$ref": "#/$defs/v1.StaticRouter" + }, + { + "$ref": "#/$defs/v1.ProxyRouter" + } + ] + }, + "v1.ProxyRouter": { + "$anchor": "v1.ProxyRouter", "additionalProperties": false, "type": "object", "properties": { @@ -614,191 +845,220 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/component/composite@v1" + "wick/router/proxy@v1" ] }, - "operations": { - "description": "A list of operations exposed by the Composite component.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.CompositeOperationDefinition" - } - }, - "with": { - "description": "Configuration necessary to provide when instantiating the component.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } + "path": { + "description": "The path that this router will trigger for.", + "type": "string" }, - "extends": { - "description": "A component or components whose operations you want to inherit from.", - "type": "array", - "items": { - "type": "string" - } + "middleware": { + "description": "Middleware operations for this router.", + "$ref": "#/$defs/v1.Middleware" + }, + "url": { + "description": "The URL resource to proxy to.", + "type": "string" + }, + "strip_path": { + "description": "Whether or not to strip the router's path from the proxied request.", + "type": "boolean" } }, - "required": [] + "required": [ + "path", + "url" + ] }, - "v1.CompositeOperationDefinition": { - "$anchor": "v1.CompositeOperationDefinition", + "v1.RestRouter": { + "$anchor": "v1.RestRouter", "additionalProperties": false, "type": "object", "properties": { - "name": { - "description": "The name of the operation.", + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/router/rest@v1" + ] + }, + "path": { + "description": "The path that this router will trigger for.", "type": "string" }, - "with": { - "description": "Any configuration required by the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } + "tools": { + "description": "Additional tools and services to enable.", + "$ref": "#/$defs/v1.Tools" }, - "inputs": { - "description": "Types of the inputs to the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } + "middleware": { + "description": "Middleware operations for this router.", + "$ref": "#/$defs/v1.Middleware" }, - "outputs": { - "description": "Types of the outputs to the operation.", + "routes": { + "description": "The routes to serve and operations that handle them.", "type": "array", "items": { - "$ref": "#/$defs/v1.Field" + "$ref": "#/$defs/v1.Route" } }, - "uses": { - "description": "A map of IDs to specific operations.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.OperationInstance" - } + "info": { + "description": "Information about the router to use when generating documentation and other tools.", + "$ref": "#/$defs/v1.Info" + } + }, + "required": [ + "path" + ] + }, + "v1.Route": { + "$anchor": "v1.Route", + "additionalProperties": false, + "type": "object", + "properties": { + "sub_path": { + "description": "The path to serve this route from. See [URI documentation](/docs/configuration/uri) for more information on specifying query and path parameters.", + "type": "string" }, - "flow": { - "description": "A list of connections from operation to operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.FlowExpression" - } + "operation": { + "description": "The operation that will act as the main entrypoint for this route.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ComponentOperationExpression" + } + ] }, - "operations": { - "description": "Additional `CompositeOperationDefinition`s to define as children.", + "methods": { + "description": "The HTTP methods to serve this route for.", "type": "array", "items": { - "$ref": "#/$defs/v1.CompositeOperationDefinition" + "$ref": "#/$defs/v1.HttpMethod" } + }, + "id": { + "description": "The unique ID of the route, used for documentation and tooling.", + "type": "string" + }, + "description": { + "description": "A short description of the route.", + "type": "string" + }, + "summary": { + "description": "A longer description of the route.", + "type": "string" } }, - "required": [] + "required": [ + "sub_path", + "operation" + ] }, - "v1.ConnectionDefinition": { - "$anchor": "v1.ConnectionDefinition", + "v1.Tools": { + "$anchor": "v1.Tools", "additionalProperties": false, "type": "object", "properties": { - "from": { - "description": "An upstream operation's output.", - "$ref": "#/$defs/v1.ConnectionTargetDefinition" - }, - "to": { - "description": "A downstream operation's input.", - "$ref": "#/$defs/v1.ConnectionTargetDefinition" + "openapi": { + "description": "Set to true to generate an OpenAPI specification and serve it at *router_path*/openapi.json", + "type": "boolean" } }, - "required": [ - "from", - "to" - ] + "required": [] }, - "v1.ConnectionTargetDefinition": { - "$anchor": "v1.ConnectionTargetDefinition", + "v1.Info": { + "$anchor": "v1.Info", "additionalProperties": false, "type": "object", "properties": { - "instance": { - "description": "The instance ID of the component operation.", + "title": { + "description": "The title of the API.", "type": "string" }, - "port": { - "description": "The operation's input or output (depending on to/from).", + "description": { + "description": "A short description of the API.", "type": "string" }, - "data": { - "description": "The default value to provide on this connection in the event of an error.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": { - "$ref": "#/$defs/v1.LiquidJsonValue" - } - } + "tos": { + "description": "The terms of service for the API.", + "type": "string" + }, + "contact": { + "description": "The contact information for the API.", + "$ref": "#/$defs/v1.Contact" + }, + "license": { + "description": "The license information for the API.", + "$ref": "#/$defs/v1.License" + }, + "version": { + "description": "The version of the API.", + "type": "string" + }, + "documentation": { + "description": "The URL to the API's terms of service.", + "$ref": "#/$defs/v1.Documentation" } }, - "required": [ - "instance" - ] + "required": [] }, - "v1.Contact": { - "$anchor": "v1.Contact", + "v1.Documentation": { + "$anchor": "v1.Documentation", "additionalProperties": false, "type": "object", "properties": { - "name": { - "description": "The name of the contact.", - "type": "string" - }, "url": { - "description": "The URL to the contact.", + "description": "The URL to the API's documentation.", "type": "string" }, - "email": { - "description": "The email address of the contact.", + "description": { + "description": "A short description of the documentation.", "type": "string" } }, "required": [] }, - "v1.Custom": { - "$anchor": "v1.Custom", + "v1.License": { + "$anchor": "v1.License", "additionalProperties": false, "type": "object", "properties": { "name": { - "description": "The name of the custom type.", + "description": "The name of the license.", + "type": "string" + }, + "url": { + "description": "The URL to the license.", "type": "string" } }, - "required": [] - }, - "v1.Datetime": { - "$anchor": "v1.Datetime", - "additionalProperties": false, - "type": "string", - "enum": [ - "datetime" + "required": [ + "name" ] }, - "v1.Documentation": { - "$anchor": "v1.Documentation", + "v1.Contact": { + "$anchor": "v1.Contact", "additionalProperties": false, "type": "object", "properties": { + "name": { + "description": "The name of the contact.", + "type": "string" + }, "url": { - "description": "The URL to the API's documentation.", + "description": "The URL to the contact.", "type": "string" }, - "description": { - "description": "A short description of the documentation.", + "email": { + "description": "The email address of the contact.", "type": "string" } }, "required": [] }, - "v1.EnumSignature": { - "$anchor": "v1.EnumSignature", + "v1.StaticRouter": { + "$anchor": "v1.StaticRouter", "additionalProperties": false, "type": "object", "properties": { @@ -806,169 +1066,155 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/type/enum@v1" + "wick/router/static@v1" ] }, - "name": { - "description": "The name of the enum.", + "path": { + "description": "The path that this router will trigger for.", "type": "string" }, - "variants": { - "description": "The variants in the enum.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.EnumVariant" - } + "middleware": { + "description": "Middleware operations for this router.", + "$ref": "#/$defs/v1.Middleware" }, - "description": { - "description": "The description of the enum.", - "type": "string" - } - }, - "required": [] - }, - "v1.EnumVariant": { - "$anchor": "v1.EnumVariant", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the variant.", + "volume": { + "description": "The volume to serve static files from.", "type": "string" }, - "index": { - "description": "The index of the variant.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - }, - "value": { - "description": "The optional value of the variant.", + "fallback": { + "description": "Fallback path (relative to volume `resource`) for files to serve in case of a 404. Useful for SPA's. if volume resource is: /www and fallback: index.html, then a 404 will serve /www/index.html", "type": "string" }, - "description": { - "description": "A description of the variant.", - "type": "string" + "indexes": { + "description": "Whether or not to serve directory listings when a directory is requested.", + "type": "boolean" } }, - "required": [] - }, - "v1.ErrorBehavior": { - "$anchor": "v1.ErrorBehavior", - "enum": [ - "Ignore", - "Commit", - "Rollback" + "required": [ + "path", + "volume" ] }, - "v1.ErrorPacket": { - "$anchor": "v1.ErrorPacket", + "v1.RawRouter": { + "$anchor": "v1.RawRouter", "additionalProperties": false, "type": "object", "properties": { - "name": { - "description": "The name of the input or output this packet is going to or coming from.", + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/router/raw@v1" + ] + }, + "path": { + "description": "The path that this router will trigger for.", "type": "string" }, - "flags": { - "description": "Any flags set on the packet. Deprecated, use 'flag:' instead", - "$ref": "#/$defs/v1.PacketFlags" + "middleware": { + "description": "Middleware operations for this router.", + "$ref": "#/$defs/v1.Middleware" }, - "flag": { - "description": "The flag set on the packet.", - "$ref": "#/$defs/v1.PacketFlag" + "codec": { + "description": "The codec to use when encoding/decoding data.", + "$ref": "#/$defs/v1.Codec" }, - "error": { - "description": "The error message.", - "$ref": "#/$defs/v1.LiquidTemplate" + "operation": { + "description": "The operation that handles HTTP requests.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ComponentOperationExpression" + } + ] } }, "required": [ - "name", - "error" + "path", + "operation" ] }, - "v1.ExposedVolume": { - "$anchor": "v1.ExposedVolume", + "v1.Middleware": { + "$anchor": "v1.Middleware", "additionalProperties": false, "type": "object", "properties": { - "resource": { - "description": "The resource ID of the volume.", - "type": "string" + "request": { + "description": "The middleware to apply to requests.", + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ComponentOperationExpression" + } + ] + } }, - "path": { - "description": "The path to map it to in the component.", - "type": "string" + "response": { + "description": "The middleware to apply to responses.", + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ComponentOperationExpression" + } + ] + } } }, - "required": [ - "resource", - "path" - ] - }, - "v1.F32": { - "$anchor": "v1.F32", - "additionalProperties": false, - "type": "string", - "enum": [ - "f32" - ] - }, - "v1.F64": { - "$anchor": "v1.F64", - "additionalProperties": false, - "type": "string", - "enum": [ - "f64" - ] + "required": [] }, - "v1.Field": { - "$anchor": "v1.Field", + "v1.TypesConfiguration": { + "$anchor": "v1.TypesConfiguration", "additionalProperties": false, "type": "object", "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/types@v1" + ] + }, "name": { - "description": "The name of the field.", + "description": "The name of this type.", "type": "string" }, - "type": { - "description": "The type signature of the field.", - "$ref": "#/$defs/v1.TypeSignature" + "metadata": { + "description": "Associated metadata for this type.", + "$ref": "#/$defs/v1.Metadata" }, - "description": { - "description": "The description of the field.", - "type": "string" - } - }, - "required": [ - "name", - "type" - ] - }, - "v1.FlowExpression": { - "oneOf": [ - { - "type": "string" + "types": { + "description": "Additional types to export and make available to the type.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.TypeDefinition" + } }, - { - "$ref": "#/$defs/v1.ConnectionDefinition" + "operations": { + "description": "A list of operation signatures.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.OperationDefinition" + } }, - { - "$ref": "#/$defs/v1.BlockExpression" + "package": { + "description": "Details about the package for this types.", + "$ref": "#/$defs/v1.PackageDefinition" } - ] - }, - "v1.Glob": { - "type": "string" + }, + "required": [] }, - "v1.GrpcUrlComponent": { - "$anchor": "v1.GrpcUrlComponent", + "v1.TestConfiguration": { + "$anchor": "v1.TestConfiguration", "additionalProperties": false, "type": "object", "properties": { @@ -976,52 +1222,34 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/component/grpc@v1" + "wick/tests@v1" ] }, - "url": { - "description": "The GRPC URL to connect to.", + "name": { + "description": "The name of this component.", "type": "string" }, "with": { - "description": "Any configuration necessary for the component.", + "description": "Configuration used to instantiate this component.", "type": "object", "patternProperties": { "[a-zA-Z0-9][a-zA-Z0-9_]*": { "$ref": "#/$defs/v1.LiquidJsonValue" } } - } - }, - "required": [ - "url" - ] - }, - "v1.HostConfig": { - "$anchor": "v1.HostConfig", - "additionalProperties": false, - "type": "object", - "properties": { - "allow_latest": { - "description": "Whether or not to allow the `:latest` tag on remote artifacts.", - "type": "boolean" }, - "insecure_registries": { - "description": "A list of registries to connect to insecurely (over HTTP vs HTTPS).", + "cases": { + "description": "Unit tests to run against components and operations.", "type": "array", "items": { - "type": "string" + "$ref": "#/$defs/v1.TestDefinition" } - }, - "rpc": { - "description": "Configuration for the GRPC server.", - "$ref": "#/$defs/v1.HttpConfig" } }, "required": [] }, - "v1.HttpClientComponent": { - "$anchor": "v1.HttpClientComponent", + "v1.LockdownConfiguration": { + "$anchor": "v1.LockdownConfiguration", "additionalProperties": false, "type": "object", "properties": { @@ -1029,805 +1257,276 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/component/http@v1" + "wick/lockdown@v1" ] }, - "resource": { - "description": "The URL base to use.", - "type": "string" - }, - "codec": { - "description": "The codec to use when encoding/decoding data. Can be overridden by individual operations.", - "$ref": "#/$defs/v1.Codec" - }, - "with": { - "description": "Configuration necessary to provide when instantiating the component.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } - }, - "operations": { - "description": "A list of operations to expose on this component.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.HttpClientOperationDefinition" - } - } - }, - "required": [] - }, - "v1.HttpClientOperationDefinition": { - "$anchor": "v1.HttpClientOperationDefinition", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the operation.", - "type": "string" - }, - "with": { - "description": "Any configuration required by the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } - }, - "inputs": { - "description": "Types of the inputs to the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } - }, - "method": { - "description": "The HTTP method to use.", - "$ref": "#/$defs/v1.HttpMethod" - }, - "codec": { - "description": "The codec to use when encoding/decoding data.", - "$ref": "#/$defs/v1.Codec" - }, - "headers": { - "description": "Any headers to add to the request.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "body": { - "description": "The body to send, processed as a structured JSON liquid template.", - "$ref": "#/$defs/v1.LiquidJsonValue" - }, - "path": { - "description": "The path to append to our base URL, processed as a liquid template with each input as part of the template data.", - "type": "string" - } - }, - "required": [] - }, - "v1.HttpConfig": { - "$anchor": "v1.HttpConfig", - "additionalProperties": false, - "type": "object", - "properties": { - "enabled": { - "description": "Enable/disable the server.", - "type": "boolean" - }, - "port": { - "description": "The port to bind to.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - }, - "address": { - "description": "The address to bind to.", - "type": "string" - }, - "pem": { - "description": "Path to pem file for TLS.", - "type": "string" - }, - "key": { - "description": "Path to key file for TLS.", - "type": "string" - }, - "ca": { - "description": "Path to CA file.", - "type": "string" - } - }, - "required": [] - }, - "v1.HttpMethod": { - "$anchor": "v1.HttpMethod", - "enum": [ - "Get", - "Post", - "Put", - "Delete" - ] - }, - "v1.HttpRouter": { - "oneOf": [ - { - "$ref": "#/$defs/v1.RawRouter" - }, - { - "$ref": "#/$defs/v1.RestRouter" - }, - { - "$ref": "#/$defs/v1.StaticRouter" - }, - { - "$ref": "#/$defs/v1.ProxyRouter" - } - ] - }, - "v1.HttpTrigger": { - "$anchor": "v1.HttpTrigger", - "additionalProperties": false, - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/trigger/http@v1" - ] - }, - "resource": { - "description": "The TcpPort resource to listen on for connections.", - "type": "string" - }, - "routers": { - "description": "The router to handle incoming requests", - "type": "array", - "items": { - "$ref": "#/$defs/v1.HttpRouter" - } - } - }, - "required": [ - "resource" - ] - }, - "v1.I16": { - "$anchor": "v1.I16", - "additionalProperties": false, - "type": "string", - "enum": [ - "i16" - ] - }, - "v1.I32": { - "$anchor": "v1.I32", - "additionalProperties": false, - "type": "string", - "enum": [ - "i32" - ] - }, - "v1.I64": { - "$anchor": "v1.I64", - "additionalProperties": false, - "type": "string", - "enum": [ - "i64" - ] - }, - "v1.I8": { - "$anchor": "v1.I8", - "additionalProperties": false, - "type": "string", - "enum": [ - "i8" - ] - }, - "v1.ImportBinding": { - "$anchor": "v1.ImportBinding", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the binding.", - "type": "string" - }, - "component": { - "description": "The import to bind to.", - "$ref": "#/$defs/v1.ImportDefinition" - } - }, - "required": [ - "name", - "component" - ] - }, - "v1.ImportDefinition": { - "oneOf": [ - { - "$ref": "#/$defs/v1.TypesComponent" - }, - { - "$ref": "#/$defs/v1.ManifestComponent" - }, - { - "$ref": "#/$defs/v1.SqlComponent" - }, - { - "$ref": "#/$defs/v1.HttpClientComponent" - } - ] - }, - "v1.Info": { - "$anchor": "v1.Info", - "additionalProperties": false, - "type": "object", - "properties": { - "title": { - "description": "The title of the API.", - "type": "string" - }, - "description": { - "description": "A short description of the API.", - "type": "string" - }, - "tos": { - "description": "The terms of service for the API.", - "type": "string" - }, - "contact": { - "description": "The contact information for the API.", - "$ref": "#/$defs/v1.Contact" - }, - "license": { - "description": "The license information for the API.", - "$ref": "#/$defs/v1.License" - }, - "version": { - "description": "The version of the API.", - "type": "string" - }, - "documentation": { - "description": "The URL to the API's terms of service.", - "$ref": "#/$defs/v1.Documentation" - } - }, - "required": [] - }, - "v1.InherentData": { - "$anchor": "v1.InherentData", - "additionalProperties": false, - "type": "object", - "properties": { - "seed": { - "description": "A random seed, i.e. to initialize a random number generator.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - }, - "timestamp": { - "description": "A timestamp.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - } - }, - "required": [] - }, - "v1.InterfaceBinding": { - "$anchor": "v1.InterfaceBinding", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the interface.", - "type": "string" - }, - "interface": { - "description": "The interface to bind to.", - "$ref": "#/$defs/v1.InterfaceDefinition" - } - }, - "required": [ - "name", - "interface" - ] - }, - "v1.InterfaceDefinition": { - "$anchor": "v1.InterfaceDefinition", - "additionalProperties": false, - "type": "object", - "properties": { - "types": { - "description": "Types used by the interface's operations", - "type": "array", - "items": { - "$ref": "#/$defs/v1.TypeDefinition" - } - }, - "operations": { - "description": "A list of operations defined by this interface.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.OperationDefinition" - } - } - }, - "required": [] - }, - "v1.License": { - "$anchor": "v1.License", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the license.", - "type": "string" - }, - "url": { - "description": "The URL to the license.", - "type": "string" - } - }, - "required": [ - "name" - ] - }, - "v1.LiquidJsonValue": { - "oneOf": [ - { - "type": "boolean" - }, - { - "type": "object" - }, - { - "type": "array" - }, - { - "type": "string" - }, - { - "type": "number" - } - ] - }, - "v1.LiquidTemplate": { - "type": "string" - }, - "v1.List": { - "$anchor": "v1.List", - "additionalProperties": false, - "type": "object", - "properties": { - "type": { - "$ref": "#/$defs/v1.TypeSignature" - } - }, - "required": [ - "type" - ] - }, - "v1.LocationReference": { - "type": "string" - }, - "v1.LockdownConfiguration": { - "$anchor": "v1.LockdownConfiguration", - "additionalProperties": false, - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/lockdown@v1" - ] - }, - "metadata": { - "description": "Associated metadata for this configuration.", - "$ref": "#/$defs/v1.Metadata" - }, - "resources": { - "description": "Restrictions to apply to resources before an application or component can be run.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.ResourceRestriction" - } - } - }, - "required": [] - }, - "v1.ManifestComponent": { - "$anchor": "v1.ManifestComponent", - "additionalProperties": false, - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/component/manifest@v1" - ] - }, - "ref": { - "description": "The URL (and optional tag) or local file path to find the manifest.", - "type": "string" - }, - "with": { - "description": "Any configuration necessary for the component.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": { - "$ref": "#/$defs/v1.LiquidJsonValue" - } - } - }, - "provide": { - "description": "External components to provide to the referenced component.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": { - "type": "string" - } - } - }, - "max_packet_size": { - "description": "If applicable, the default size to allocate to the component's send/receive buffer.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - } - }, - "required": [ - "ref" - ] - }, - "v1.Map": { - "$anchor": "v1.Map", - "additionalProperties": false, - "type": "object", - "properties": { - "key": { - "$ref": "#/$defs/v1.TypeSignature" - }, - "value": { - "$ref": "#/$defs/v1.TypeSignature" - } - }, - "required": [ - "key", - "value" - ] - }, - "v1.Metadata": { - "$anchor": "v1.Metadata", - "additionalProperties": false, - "type": "object", - "properties": { - "version": { - "description": "The version of the artifact.", - "type": "string" - }, - "authors": { - "description": "A list of the authors.", - "type": "array", - "items": { - "type": "string" - } - }, - "vendors": { - "description": "A list of any vendors associated with the artifact.", - "type": "array", - "items": { - "type": "string" - } - }, - "description": { - "description": "A short description.", - "type": "string" - }, - "documentation": { - "description": "Where to find documentation.", - "type": "string" - }, - "licenses": { - "description": "The license(s) for the artifact.", - "type": "array", - "items": { - "type": "string" - } - }, - "icon": { - "description": "An icon to associate with the artifact.", - "type": "string" - } - }, - "required": [] - }, - "v1.Middleware": { - "$anchor": "v1.Middleware", - "additionalProperties": false, - "type": "object", - "properties": { - "request": { - "description": "The middleware to apply to requests.", - "type": "array", - "items": { - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/$defs/v1.ComponentOperationExpression" - } - ] - } - }, - "response": { - "description": "The middleware to apply to responses.", - "type": "array", - "items": { - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/$defs/v1.ComponentOperationExpression" - } - ] - } - } - }, - "required": [] - }, - "v1.Object": { - "$anchor": "v1.Object", - "additionalProperties": false, - "type": "string", - "enum": [ - "object" - ] - }, - "v1.OperationDefinition": { - "$anchor": "v1.OperationDefinition", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the operation.", - "type": "string" - }, - "with": { - "description": "Any configuration required by the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } - }, - "inputs": { - "description": "Types of the inputs to the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } + "metadata": { + "description": "Associated metadata for this configuration.", + "$ref": "#/$defs/v1.Metadata" }, - "outputs": { - "description": "Types of the outputs to the operation.", + "resources": { + "description": "Restrictions to apply to resources before an application or component can be run.", "type": "array", "items": { - "$ref": "#/$defs/v1.Field" + "$ref": "#/$defs/v1.ResourceRestriction" } } }, "required": [] }, - "v1.OperationInput": { - "$anchor": "v1.OperationInput", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the input.", - "type": "string" + "v1.ResourceRestriction": { + "oneOf": [ + { + "$ref": "#/$defs/v1.VolumeRestriction" }, - "value": { - "description": "The value to pass." + { + "$ref": "#/$defs/v1.UrlRestriction" + }, + { + "$ref": "#/$defs/v1.TcpPortRestriction" + }, + { + "$ref": "#/$defs/v1.UdpPortRestriction" } - }, - "required": [ - "name", - "value" ] }, - "v1.OperationInstance": { - "$anchor": "v1.OperationInstance", + "v1.VolumeRestriction": { + "$anchor": "v1.VolumeRestriction", "additionalProperties": false, "type": "object", "properties": { - "name": { - "description": "The name of the binding.", - "type": "string" - }, - "operation": { - "description": "The operation to bind to.", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/$defs/v1.ComponentOperationExpression" - } + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/volume@v1" ] }, - "with": { - "description": "Data to associate with the reference, if any.", - "type": "object", - "patternProperties": { - "[a-zA-Z0-9][a-zA-Z0-9_]*": { - "$ref": "#/$defs/v1.LiquidJsonValue" - } + "components": { + "description": "The components this restriction applies to", + "type": "array", + "items": { + "type": "string" } }, - "timeout": { - "description": "Timeout (in milliseconds) to wait for the operation to complete. Use 0 to wait indefinitely.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] + "allow": { + "description": "The volumes to allow", + "$ref": "#/$defs/v1.LiquidTemplate" } }, "required": [ - "name", - "operation" + "allow" ] }, - "v1.Optional": { - "$anchor": "v1.Optional", + "v1.UrlRestriction": { + "$anchor": "v1.UrlRestriction", "additionalProperties": false, "type": "object", "properties": { - "type": { - "$ref": "#/$defs/v1.TypeSignature" + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/url@v1" + ] + }, + "components": { + "description": "The components this restriction applies to", + "type": "array", + "items": { + "type": "string" + } + }, + "allow": { + "description": "The URLs to allow", + "$ref": "#/$defs/v1.LiquidTemplate" } }, "required": [ - "type" + "allow" ] }, - "v1.PackageDefinition": { - "$anchor": "v1.PackageDefinition", + "v1.TcpPortRestriction": { + "$anchor": "v1.TcpPortRestriction", "additionalProperties": false, "type": "object", "properties": { - "files": { - "description": "The list of files and folders to be included with the package.", + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/tcpport@v1" + ] + }, + "components": { + "description": "The components this restriction applies to", "type": "array", "items": { "type": "string" } }, - "registry": { - "description": "Configuration for publishing the package to a registry.", - "$ref": "#/$defs/v1.RegistryDefinition" + "address": { + "description": "The address to allow", + "$ref": "#/$defs/v1.LiquidTemplate" + }, + "port": { + "description": "The port to allow", + "$ref": "#/$defs/v1.LiquidTemplate" } }, - "required": [] + "required": [ + "address", + "port" + ] }, - "v1.PacketAssertion": { - "$anchor": "v1.PacketAssertion", + "v1.UdpPortRestriction": { + "$anchor": "v1.UdpPortRestriction", "additionalProperties": false, "type": "object", "properties": { - "path": { - "description": "The optional path to a value in the packet to assert against.", - "type": "string" + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/resource/udpport@v1" + ] }, - "operator": { - "description": "The operation to use when asserting against a packet.", - "$ref": "#/$defs/v1.AssertionOperator" + "components": { + "description": "The components this restriction applies to", + "type": "array", + "items": { + "type": "string" + } }, - "value": { - "description": "A value or object combine with the operator to assert against a packet value.", - "$ref": "#/$defs/v1.LiquidJsonValue" + "address": { + "description": "The address to allow", + "$ref": "#/$defs/v1.LiquidTemplate" + }, + "port": { + "description": "The port to allow", + "$ref": "#/$defs/v1.LiquidTemplate" } }, "required": [ - "operator", - "value" + "address", + "port" ] }, - "v1.PacketAssertionDef": { - "$anchor": "v1.PacketAssertionDef", + "v1.ComponentConfiguration": { + "$anchor": "v1.ComponentConfiguration", "additionalProperties": false, "type": "object", "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/component@v1" + ] + }, "name": { - "description": "The name of the input or output this packet is going to or coming from.", + "description": "The name of the component.", "type": "string" }, - "assertions": { - "description": "An assertion to test against the packet.", + "metadata": { + "description": "Associated metadata for this component.", + "$ref": "#/$defs/v1.Metadata" + }, + "package": { + "description": "Details about the package for this component.", + "$ref": "#/$defs/v1.PackageDefinition" + }, + "host": { + "description": "Configuration for when wick hosts this component as a service.", + "$ref": "#/$defs/v1.HostConfig" + }, + "resources": { + "description": "Resources that the component can access.", "type": "array", "items": { - "$ref": "#/$defs/v1.PacketAssertion" + "$ref": "#/$defs/v1.ResourceBinding" + } + }, + "import": { + "description": "Components or types to import into this component's scope.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.ImportBinding" + } + }, + "types": { + "description": "Additional types to export and make available to the component.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.TypeDefinition" + } + }, + "requires": { + "description": "Interfaces the component requires to operate.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.InterfaceBinding" + } + }, + "component": { + "description": "Configuration specific to different kinds of components.", + "$ref": "#/$defs/v1.ComponentKind" + }, + "tests": { + "description": "Assertions that can be run against the component to validate its behavior.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.TestConfiguration" } } }, "required": [ - "name" + "component" ] }, - "v1.PacketData": { - "oneOf": [ - { - "$ref": "#/$defs/v1.SuccessPacket" + "v1.InterfaceBinding": { + "$anchor": "v1.InterfaceBinding", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the interface.", + "type": "string" }, - { - "$ref": "#/$defs/v1.ErrorPacket" + "interface": { + "description": "The interface to bind to.", + "$ref": "#/$defs/v1.InterfaceDefinition" } + }, + "required": [ + "name", + "interface" ] }, - "v1.PacketFlag": { - "$anchor": "v1.PacketFlag", - "enum": [ - "Done", - "Open", - "Close" - ] - }, - "v1.PacketFlags": { - "$anchor": "v1.PacketFlags", + "v1.InterfaceDefinition": { + "$anchor": "v1.InterfaceDefinition", "additionalProperties": false, "type": "object", "properties": { - "done": { - "description": "Indicates the port should be considered closed.", - "type": "boolean" - }, - "open": { - "description": "Indicates the opening of a new substream context within the parent stream.", - "type": "boolean" + "types": { + "description": "Types used by the interface's operations", + "type": "array", + "items": { + "$ref": "#/$defs/v1.TypeDefinition" + } }, - "close": { - "description": "Indicates the closing of a substream context within the parent stream.", - "type": "boolean" + "operations": { + "description": "A list of operations defined by this interface.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.OperationDefinition" + } } }, "required": [] }, - "v1.ProxyRouter": { - "$anchor": "v1.ProxyRouter", + "v1.CompositeComponentConfiguration": { + "$anchor": "v1.CompositeComponentConfiguration", "additionalProperties": false, "type": "object", "properties": { @@ -1835,33 +1534,35 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/router/proxy@v1" + "wick/component/composite@v1" ] }, - "path": { - "description": "The path that this router will trigger for.", - "type": "string" - }, - "middleware": { - "description": "Middleware operations for this router.", - "$ref": "#/$defs/v1.Middleware" + "operations": { + "description": "A list of operations exposed by the Composite component.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.CompositeOperationDefinition" + } }, - "url": { - "description": "The URL resource to proxy to.", - "type": "string" + "with": { + "description": "Configuration necessary to provide when instantiating the component.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } }, - "strip_path": { - "description": "Whether or not to strip the router's path from the proxied request.", - "type": "boolean" + "extends": { + "description": "A component or components whose operations you want to inherit from.", + "type": "array", + "items": { + "type": "string" + } } }, - "required": [ - "path", - "url" - ] + "required": [] }, - "v1.RawRouter": { - "$anchor": "v1.RawRouter", + "v1.WasmComponentConfiguration": { + "$anchor": "v1.WasmComponentConfiguration", "additionalProperties": false, "type": "object", "properties": { @@ -1869,107 +1570,122 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/router/raw@v1" + "wick/component/wasmrs@v1" ] }, - "path": { - "description": "The path that this router will trigger for.", + "ref": { + "description": "The path or OCI reference to the WebAssembly module", "type": "string" }, - "middleware": { - "description": "Middleware operations for this router.", - "$ref": "#/$defs/v1.Middleware" - }, - "codec": { - "description": "The codec to use when encoding/decoding data.", - "$ref": "#/$defs/v1.Codec" + "volumes": { + "description": "Volumes to expose to the component.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.ExposedVolume" + } }, - "operation": { - "description": "The operation that handles HTTP requests.", + "max_packet_size": { + "description": "The default size to allocate to the component's send/receive buffer.", "oneOf": [ { - "type": "string" + "type": "number" }, { - "$ref": "#/$defs/v1.ComponentOperationExpression" + "type": "string" } ] + }, + "with": { + "description": "Configuration necessary to provide when instantiating the component.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } + }, + "operations": { + "description": "A list of operations implemented by the WebAssembly module.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.OperationDefinition" + } } }, "required": [ - "path", - "operation" + "ref" ] }, - "v1.RegistryDefinition": { - "$anchor": "v1.RegistryDefinition", + "v1.ExposedVolume": { + "$anchor": "v1.ExposedVolume", "additionalProperties": false, "type": "object", "properties": { - "host": { - "description": "The registry to publish to, e.g. registry.candle.dev", + "resource": { + "description": "The resource ID of the volume.", "type": "string" }, - "namespace": { - "description": "The namespace on the registry. e.g.: [*your username*]", + "path": { + "description": "The path to map it to in the component.", "type": "string" } }, - "required": [] + "required": [ + "resource", + "path" + ] }, - "v1.ResourceBinding": { - "$anchor": "v1.ResourceBinding", - "additionalProperties": false, - "type": "object", - "properties": { - "name": { - "description": "The name of the binding.", - "type": "string" + "v1.ComponentKind": { + "oneOf": [ + { + "$ref": "#/$defs/v1.WasmComponentConfiguration" }, - "resource": { - "description": "The resource to bind to.", - "$ref": "#/$defs/v1.ResourceDefinition" + { + "$ref": "#/$defs/v1.CompositeComponentConfiguration" + }, + { + "$ref": "#/$defs/v1.SqlComponent" + }, + { + "$ref": "#/$defs/v1.HttpClientComponent" } - }, - "required": [ - "name", - "resource" ] }, - "v1.ResourceDefinition": { + "v1.ImportDefinition": { "oneOf": [ { - "$ref": "#/$defs/v1.TcpPort" + "$ref": "#/$defs/v1.TypesComponent" }, { - "$ref": "#/$defs/v1.UdpPort" + "$ref": "#/$defs/v1.ManifestComponent" }, { - "$ref": "#/$defs/v1.Url" + "$ref": "#/$defs/v1.SqlComponent" }, { - "$ref": "#/$defs/v1.Volume" + "$ref": "#/$defs/v1.HttpClientComponent" } ] }, - "v1.ResourceRestriction": { + "v1.ComponentDefinition": { "oneOf": [ { - "$ref": "#/$defs/v1.VolumeRestriction" + "$ref": "#/$defs/v1.GrpcUrlComponent" }, { - "$ref": "#/$defs/v1.UrlRestriction" + "$ref": "#/$defs/v1.ManifestComponent" }, { - "$ref": "#/$defs/v1.TcpPortRestriction" + "$ref": "#/$defs/v1.ComponentReference" }, { - "$ref": "#/$defs/v1.UdpPortRestriction" + "$ref": "#/$defs/v1.SqlComponent" + }, + { + "$ref": "#/$defs/v1.HttpClientComponent" } ] }, - "v1.RestRouter": { - "$anchor": "v1.RestRouter", + "v1.TypesComponent": { + "$anchor": "v1.TypesComponent", "additionalProperties": false, "type": "object", "properties": { @@ -1977,93 +1693,174 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/router/rest@v1" + "wick/component/types@v1" ] }, - "path": { - "description": "The path that this router will trigger for.", + "ref": { + "description": "The URL (and optional tag) or local file path to find the types manifest.", "type": "string" }, - "tools": { - "description": "Additional tools and services to enable.", - "$ref": "#/$defs/v1.Tools" + "types": { + "description": "The types to import from the manifest.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "ref" + ] + }, + "v1.ComponentReference": { + "$anchor": "v1.ComponentReference", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/component/reference@v1" + ] }, - "middleware": { - "description": "Middleware operations for this router.", - "$ref": "#/$defs/v1.Middleware" + "id": { + "description": "The id of the referenced component.", + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "v1.HostConfig": { + "$anchor": "v1.HostConfig", + "additionalProperties": false, + "type": "object", + "properties": { + "allow_latest": { + "description": "Whether or not to allow the `:latest` tag on remote artifacts.", + "type": "boolean" }, - "routes": { - "description": "The routes to serve and operations that handle them.", + "insecure_registries": { + "description": "A list of registries to connect to insecurely (over HTTP vs HTTPS).", "type": "array", "items": { - "$ref": "#/$defs/v1.Route" + "type": "string" } }, - "info": { - "description": "Information about the router to use when generating documentation and other tools.", - "$ref": "#/$defs/v1.Info" + "rpc": { + "description": "Configuration for the GRPC server.", + "$ref": "#/$defs/v1.HttpConfig" } }, - "required": [ - "path" - ] + "required": [] }, - "v1.Route": { - "$anchor": "v1.Route", + "v1.HttpConfig": { + "$anchor": "v1.HttpConfig", "additionalProperties": false, "type": "object", "properties": { - "sub_path": { - "description": "The path to serve this route from. See [URI documentation](/docs/configuration/uri) for more information on specifying query and path parameters.", - "type": "string" + "enabled": { + "description": "Enable/disable the server.", + "type": "boolean" }, - "operation": { - "description": "The operation that will act as the main entrypoint for this route.", + "port": { + "description": "The port to bind to.", "oneOf": [ { - "type": "string" + "type": "number" }, { - "$ref": "#/$defs/v1.ComponentOperationExpression" + "type": "string" } ] }, - "methods": { - "description": "The HTTP methods to serve this route for.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.HttpMethod" - } + "address": { + "description": "The address to bind to.", + "type": "string" }, - "id": { - "description": "The unique ID of the route, used for documentation and tooling.", + "pem": { + "description": "Path to pem file for TLS.", "type": "string" }, - "description": { - "description": "A short description of the route.", + "key": { + "description": "Path to key file for TLS.", "type": "string" }, - "summary": { - "description": "A longer description of the route.", + "ca": { + "description": "Path to CA file.", + "type": "string" + } + }, + "required": [] + }, + "v1.GrpcUrlComponent": { + "$anchor": "v1.GrpcUrlComponent", + "additionalProperties": false, + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/component/grpc@v1" + ] + }, + "url": { + "description": "The GRPC URL to connect to.", "type": "string" + }, + "with": { + "description": "Any configuration necessary for the component.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": { + "$ref": "#/$defs/v1.LiquidJsonValue" + } + } } }, "required": [ - "sub_path", - "operation" + "url" ] }, - "v1.Schedule": { - "$anchor": "v1.Schedule", + "v1.ManifestComponent": { + "$anchor": "v1.ManifestComponent", "additionalProperties": false, "type": "object", "properties": { - "cron": { - "description": "Schedule in cron format with second precision. See [cron.help](https://cron.help) for more information.", + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/component/manifest@v1" + ] + }, + "ref": { + "description": "The URL (and optional tag) or local file path to find the manifest.", "type": "string" }, - "repeat": { - "description": "repeat `n` times. Use `0` to repeat indefinitely", + "with": { + "description": "Any configuration necessary for the component.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": { + "$ref": "#/$defs/v1.LiquidJsonValue" + } + } + }, + "provide": { + "description": "External components to provide to the referenced component.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": { + "type": "string" + } + } + }, + "max_packet_size": { + "description": "If applicable, the default size to allocate to the component's send/receive buffer.", "oneOf": [ { "type": "number" @@ -2075,48 +1872,140 @@ } }, "required": [ - "cron" + "ref" ] }, - "v1.SqlComponent": { - "$anchor": "v1.SqlComponent", + "v1.CompositeOperationDefinition": { + "$anchor": "v1.CompositeOperationDefinition", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/component/sql@v1" - ] - }, - "resource": { - "description": "The connect string URL resource for the database.", + "name": { + "description": "The name of the operation.", "type": "string" }, - "tls": { - "description": "Whether or not to use TLS.", - "type": "boolean" - }, "with": { - "description": "Configuration necessary to provide when instantiating the component.", + "description": "Any configuration required by the operation.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } + }, + "inputs": { + "description": "Types of the inputs to the operation.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } + }, + "outputs": { + "description": "Types of the outputs to the operation.", "type": "array", "items": { "$ref": "#/$defs/v1.Field" } }, + "uses": { + "description": "A map of IDs to specific operations.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.OperationInstance" + } + }, + "flow": { + "description": "A list of connections from operation to operation.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.FlowExpression" + } + }, "operations": { - "description": "A list of operations to expose on this component.", + "description": "Additional `CompositeOperationDefinition`s to define as children.", "type": "array", "items": { - "$ref": "#/$defs/v1.SqlQueryKind" + "$ref": "#/$defs/v1.CompositeOperationDefinition" } } }, "required": [] }, - "v1.SqlExecOperationDefinition": { - "$anchor": "v1.SqlExecOperationDefinition", + "v1.FlowExpression": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ConnectionDefinition" + }, + { + "$ref": "#/$defs/v1.BlockExpression" + } + ] + }, + "v1.BlockExpression": { + "$anchor": "v1.BlockExpression", + "additionalProperties": false, + "type": "object", + "properties": { + "expressions": { + "type": "array", + "items": { + "$ref": "#/$defs/v1.FlowExpression" + } + } + }, + "required": [ + "expressions" + ] + }, + "v1.ConnectionDefinition": { + "$anchor": "v1.ConnectionDefinition", + "additionalProperties": false, + "type": "object", + "properties": { + "from": { + "description": "An upstream operation's output.", + "$ref": "#/$defs/v1.ConnectionTargetDefinition" + }, + "to": { + "description": "A downstream operation's input.", + "$ref": "#/$defs/v1.ConnectionTargetDefinition" + } + }, + "required": [ + "from", + "to" + ] + }, + "v1.ConnectionTargetDefinition": { + "$anchor": "v1.ConnectionTargetDefinition", + "additionalProperties": false, + "type": "object", + "properties": { + "instance": { + "description": "The instance ID of the component operation.", + "type": "string" + }, + "port": { + "description": "The operation's input or output (depending on to/from).", + "type": "string" + }, + "data": { + "description": "The default value to provide on this connection in the event of an error.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": { + "$ref": "#/$defs/v1.LiquidJsonValue" + } + } + } + }, + "required": [ + "instance" + ] + }, + "v1.OperationDefinition": { + "$anchor": "v1.OperationDefinition", "additionalProperties": false, "type": "object", "properties": { @@ -2144,127 +2033,226 @@ "items": { "$ref": "#/$defs/v1.Field" } - }, - "exec": { - "description": "The query to execute.", + } + }, + "required": [] + }, + "v1.Field": { + "$anchor": "v1.Field", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the field.", "type": "string" }, - "arguments": { - "description": "The positional arguments to the query, defined as a list of input names.", - "type": "array", - "items": { - "type": "string" - } + "type": { + "description": "The type signature of the field.", + "$ref": "#/$defs/v1.TypeSignature" }, - "on_error": { - "description": "What to do when an error occurs.", - "$ref": "#/$defs/v1.ErrorBehavior" + "description": { + "description": "The description of the field.", + "type": "string" } }, "required": [ - "exec" + "name", + "type" ] }, - "v1.SqlQueryKind": { + "v1.TypeSignature": { "oneOf": [ { - "$ref": "#/$defs/v1.SqlQueryOperationDefinition" - }, - { - "$ref": "#/$defs/v1.SqlExecOperationDefinition" + "type": "string" } ] }, - "v1.SqlQueryOperationDefinition": { - "$anchor": "v1.SqlQueryOperationDefinition", + "v1.I8": { + "$anchor": "v1.I8", + "additionalProperties": false, + "type": "string", + "enum": [ + "i8" + ] + }, + "v1.I16": { + "$anchor": "v1.I16", + "additionalProperties": false, + "type": "string", + "enum": [ + "i16" + ] + }, + "v1.I32": { + "$anchor": "v1.I32", + "additionalProperties": false, + "type": "string", + "enum": [ + "i32" + ] + }, + "v1.I64": { + "$anchor": "v1.I64", + "additionalProperties": false, + "type": "string", + "enum": [ + "i64" + ] + }, + "v1.U8": { + "$anchor": "v1.U8", + "additionalProperties": false, + "type": "string", + "enum": [ + "u8" + ] + }, + "v1.U16": { + "$anchor": "v1.U16", + "additionalProperties": false, + "type": "string", + "enum": [ + "u16" + ] + }, + "v1.U32": { + "$anchor": "v1.U32", + "additionalProperties": false, + "type": "string", + "enum": [ + "u32" + ] + }, + "v1.U64": { + "$anchor": "v1.U64", + "additionalProperties": false, + "type": "string", + "enum": [ + "u64" + ] + }, + "v1.F32": { + "$anchor": "v1.F32", + "additionalProperties": false, + "type": "string", + "enum": [ + "f32" + ] + }, + "v1.F64": { + "$anchor": "v1.F64", + "additionalProperties": false, + "type": "string", + "enum": [ + "f64" + ] + }, + "v1.Bool": { + "$anchor": "v1.Bool", + "additionalProperties": false, + "type": "string", + "enum": [ + "i8" + ] + }, + "v1.StringType": { + "$anchor": "v1.StringType", + "additionalProperties": false, + "type": "string", + "enum": [ + "string" + ] + }, + "v1.Datetime": { + "$anchor": "v1.Datetime", + "additionalProperties": false, + "type": "string", + "enum": [ + "datetime" + ] + }, + "v1.Bytes": { + "$anchor": "v1.Bytes", + "additionalProperties": false, + "type": "string", + "enum": [ + "bytes" + ] + }, + "v1.Custom": { + "$anchor": "v1.Custom", "additionalProperties": false, "type": "object", "properties": { "name": { - "description": "The name of the operation.", - "type": "string" - }, - "with": { - "description": "Any configuration required by the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } - }, - "inputs": { - "description": "Types of the inputs to the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } - }, - "outputs": { - "description": "Types of the outputs to the operation.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.Field" - } - }, - "query": { - "description": "The query to execute.", + "description": "The name of the custom type.", "type": "string" - }, - "arguments": { - "description": "The positional arguments to the query, defined as a list of input names.", - "type": "array", - "items": { - "type": "string" - } - }, - "on_error": { - "description": "What to do when an error occurs.", - "$ref": "#/$defs/v1.ErrorBehavior" + } + }, + "required": [] + }, + "v1.Optional": { + "$anchor": "v1.Optional", + "additionalProperties": false, + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/v1.TypeSignature" } }, "required": [ - "query" + "type" ] }, - "v1.StaticRouter": { - "$anchor": "v1.StaticRouter", + "v1.List": { + "$anchor": "v1.List", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/router/static@v1" - ] - }, - "path": { - "description": "The path that this router will trigger for.", - "type": "string" - }, - "middleware": { - "description": "Middleware operations for this router.", - "$ref": "#/$defs/v1.Middleware" - }, - "volume": { - "description": "The volume to serve static files from.", - "type": "string" + "type": { + "$ref": "#/$defs/v1.TypeSignature" + } + }, + "required": [ + "type" + ] + }, + "v1.Map": { + "$anchor": "v1.Map", + "additionalProperties": false, + "type": "object", + "properties": { + "key": { + "$ref": "#/$defs/v1.TypeSignature" }, - "fallback": { - "description": "Fallback path (relative to volume `resource`) for files to serve in case of a 404. Useful for SPA's. if volume resource is: /www and fallback: index.html, then a 404 will serve /www/index.html", - "type": "string" + "value": { + "$ref": "#/$defs/v1.TypeSignature" } }, "required": [ - "path", - "volume" + "key", + "value" ] }, - "v1.StringType": { - "$anchor": "v1.StringType", + "v1.Object": { + "$anchor": "v1.Object", "additionalProperties": false, "type": "string", "enum": [ - "string" + "object" + ] + }, + "v1.TypeDefinition": { + "oneOf": [ + { + "$ref": "#/$defs/v1.StructSignature" + }, + { + "$ref": "#/$defs/v1.EnumSignature" + }, + { + "$ref": "#/$defs/v1.UnionSignature" + } ] }, "v1.StructSignature": { @@ -2297,34 +2285,38 @@ }, "required": [] }, - "v1.SuccessPacket": { - "$anchor": "v1.SuccessPacket", + "v1.UnionSignature": { + "$anchor": "v1.UnionSignature", "additionalProperties": false, "type": "object", "properties": { + "kind": { + "type": "string", + "description": "The kind of the collection", + "enum": [ + "wick/type/union@v1" + ] + }, "name": { - "description": "The name of the input or output this packet is going to or coming from.", + "description": "The name of the union.", "type": "string" }, - "flags": { - "description": "Any flags set on the packet. Deprecated, use 'flag:' instead", - "$ref": "#/$defs/v1.PacketFlags" - }, - "flag": { - "description": "The flag set on the packet.", - "$ref": "#/$defs/v1.PacketFlag" + "types": { + "description": "The types in the union.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.TypeSignature" + } }, - "value": { - "description": "The packet payload.", - "$ref": "#/$defs/v1.LiquidJsonValue" + "description": { + "description": "The description of the union.", + "type": "string" } }, - "required": [ - "name" - ] + "required": [] }, - "v1.TcpPort": { - "$anchor": "v1.TcpPort", + "v1.EnumSignature": { + "$anchor": "v1.EnumSignature", "additionalProperties": false, "type": "object", "properties": { @@ -2332,71 +2324,80 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/resource/tcpport@v1" + "wick/type/enum@v1" ] }, - "port": { - "description": "The port to bind to.", - "$ref": "#/$defs/v1.LiquidTemplate" + "name": { + "description": "The name of the enum.", + "type": "string" }, - "address": { - "description": "The address to bind to.", - "$ref": "#/$defs/v1.LiquidTemplate" + "variants": { + "description": "The variants in the enum.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.EnumVariant" + } + }, + "description": { + "description": "The description of the enum.", + "type": "string" } }, "required": [] }, - "v1.TcpPortRestriction": { - "$anchor": "v1.TcpPortRestriction", + "v1.EnumVariant": { + "$anchor": "v1.EnumVariant", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/resource/tcpport@v1" - ] + "name": { + "description": "The name of the variant.", + "type": "string" }, - "components": { - "description": "The components this restriction applies to", - "type": "array", - "items": { - "type": "string" - } + "index": { + "description": "The index of the variant.", + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] }, - "address": { - "description": "The address to allow", - "$ref": "#/$defs/v1.LiquidTemplate" + "value": { + "description": "The optional value of the variant.", + "type": "string" }, - "port": { - "description": "The port to allow", - "$ref": "#/$defs/v1.LiquidTemplate" + "description": { + "description": "A description of the variant.", + "type": "string" } }, - "required": [ - "address", - "port" - ] + "required": [] }, - "v1.TestConfiguration": { - "$anchor": "v1.TestConfiguration", + "v1.OperationInstance": { + "$anchor": "v1.OperationInstance", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/tests@v1" - ] - }, "name": { - "description": "The name of this component.", + "description": "The name of the binding.", "type": "string" }, + "operation": { + "description": "The operation to bind to.", + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/$defs/v1.ComponentOperationExpression" + } + ] + }, "with": { - "description": "Configuration used to instantiate this component.", + "description": "Data to associate with the reference, if any.", "type": "object", "patternProperties": { "[a-zA-Z0-9][a-zA-Z0-9_]*": { @@ -2404,15 +2405,22 @@ } } }, - "cases": { - "description": "Unit tests to run against components and operations.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.TestDefinition" - } + "timeout": { + "description": "Timeout (in milliseconds) to wait for the operation to complete. Use 0 to wait indefinitely.", + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] } }, - "required": [] + "required": [ + "name", + "operation" + ] }, "v1.TestDefinition": { "$anchor": "v1.TestDefinition", @@ -2459,6 +2467,46 @@ "operation" ] }, + "v1.InherentData": { + "$anchor": "v1.InherentData", + "additionalProperties": false, + "type": "object", + "properties": { + "seed": { + "description": "A random seed, i.e. to initialize a random number generator.", + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "timestamp": { + "description": "A timestamp.", + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + } + }, + "required": [] + }, + "v1.PacketData": { + "oneOf": [ + { + "$ref": "#/$defs/v1.SuccessPacket" + }, + { + "$ref": "#/$defs/v1.ErrorPacket" + } + ] + }, "v1.TestPacketData": { "oneOf": [ { @@ -2472,115 +2520,143 @@ } ] }, - "v1.TimeTrigger": { - "$anchor": "v1.TimeTrigger", + "v1.SuccessPacket": { + "$anchor": "v1.SuccessPacket", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/trigger/time@v1" - ] + "name": { + "description": "The name of the input or output this packet is going to or coming from.", + "type": "string" }, - "schedule": { - "description": "The schedule to run the trigger with.", - "$ref": "#/$defs/v1.Schedule" + "flags": { + "description": "Any flags set on the packet. Deprecated, use 'flag:' instead", + "$ref": "#/$defs/v1.PacketFlags" }, - "operation": { - "description": "The operation to execute on the schedule.", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "#/$defs/v1.ComponentOperationExpression" - } - ] + "flag": { + "description": "The flag set on the packet.", + "$ref": "#/$defs/v1.PacketFlag" + }, + "value": { + "description": "The packet payload.", + "$ref": "#/$defs/v1.LiquidJsonValue" + } + }, + "required": [ + "name" + ] + }, + "v1.PacketAssertionDef": { + "$anchor": "v1.PacketAssertionDef", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the input or output this packet is going to or coming from.", + "type": "string" }, - "payload": { - "description": "Values passed to the operation as inputs", + "assertions": { + "description": "An assertion to test against the packet.", "type": "array", "items": { - "$ref": "#/$defs/v1.OperationInput" + "$ref": "#/$defs/v1.PacketAssertion" } } }, "required": [ - "schedule", - "operation", - "payload" + "name" ] }, - "v1.Tools": { - "$anchor": "v1.Tools", + "v1.PacketAssertion": { + "$anchor": "v1.PacketAssertion", "additionalProperties": false, "type": "object", "properties": { - "openapi": { - "description": "Set to true to generate an OpenAPI specification and serve it at *router_path*/openapi.json", - "type": "boolean" - } - }, - "required": [] - }, - "v1.TriggerDefinition": { - "oneOf": [ - { - "$ref": "#/$defs/v1.CliTrigger" + "path": { + "description": "The optional path to a value in the packet to assert against.", + "type": "string" }, - { - "$ref": "#/$defs/v1.HttpTrigger" + "operator": { + "description": "The operation to use when asserting against a packet.", + "$ref": "#/$defs/v1.AssertionOperator" }, - { - "$ref": "#/$defs/v1.TimeTrigger" + "value": { + "description": "A value or object combine with the operator to assert against a packet value.", + "$ref": "#/$defs/v1.LiquidJsonValue" } + }, + "required": [ + "operator", + "value" ] }, - "v1.TypeDefinition": { - "oneOf": [ - { - "$ref": "#/$defs/v1.StructSignature" + "v1.AssertionOperator": { + "$anchor": "v1.AssertionOperator", + "enum": [ + "Equals", + "LessThan", + "GreaterThan", + "Regex", + "Contains" + ] + }, + "v1.ErrorPacket": { + "$anchor": "v1.ErrorPacket", + "additionalProperties": false, + "type": "object", + "properties": { + "name": { + "description": "The name of the input or output this packet is going to or coming from.", + "type": "string" }, - { - "$ref": "#/$defs/v1.EnumSignature" + "flags": { + "description": "Any flags set on the packet. Deprecated, use 'flag:' instead", + "$ref": "#/$defs/v1.PacketFlags" }, - { - "$ref": "#/$defs/v1.UnionSignature" + "flag": { + "description": "The flag set on the packet.", + "$ref": "#/$defs/v1.PacketFlag" + }, + "error": { + "description": "The error message.", + "$ref": "#/$defs/v1.LiquidTemplate" } + }, + "required": [ + "name", + "error" ] }, - "v1.TypesComponent": { - "$anchor": "v1.TypesComponent", + "v1.PacketFlags": { + "$anchor": "v1.PacketFlags", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/component/types@v1" - ] + "done": { + "description": "Indicates the port should be considered closed.", + "type": "boolean" }, - "ref": { - "description": "The URL (and optional tag) or local file path to find the types manifest.", - "type": "string" + "open": { + "description": "Indicates the opening of a new substream context within the parent stream.", + "type": "boolean" }, - "types": { - "description": "The types to import from the manifest.", - "type": "array", - "items": { - "type": "string" - } + "close": { + "description": "Indicates the closing of a substream context within the parent stream.", + "type": "boolean" } }, - "required": [ - "ref" + "required": [] + }, + "v1.PacketFlag": { + "$anchor": "v1.PacketFlag", + "enum": [ + "Done", + "Open", + "Close" ] }, - "v1.TypesConfiguration": { - "$anchor": "v1.TypesConfiguration", + "v1.SqlComponent": { + "$anchor": "v1.SqlComponent", "additionalProperties": false, "type": "object", "properties": { @@ -2588,235 +2664,154 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/types@v1" + "wick/component/sql@v1" ] }, - "name": { - "description": "The name of this type.", + "resource": { + "description": "The connect string URL resource for the database.", "type": "string" }, - "metadata": { - "description": "Associated metadata for this type.", - "$ref": "#/$defs/v1.Metadata" + "tls": { + "description": "Whether or not to use TLS.", + "type": "boolean" }, - "types": { - "description": "Additional types to export and make available to the type.", + "with": { + "description": "Configuration necessary to provide when instantiating the component.", "type": "array", "items": { - "$ref": "#/$defs/v1.TypeDefinition" + "$ref": "#/$defs/v1.Field" } }, "operations": { - "description": "A list of operation signatures.", + "description": "A list of operations to expose on this component.", "type": "array", "items": { - "$ref": "#/$defs/v1.OperationDefinition" + "$ref": "#/$defs/v1.SqlQueryKind" } - }, - "package": { - "description": "Details about the package for this types.", - "$ref": "#/$defs/v1.PackageDefinition" } }, "required": [] }, - "v1.TypeSignature": { + "v1.SqlQueryKind": { "oneOf": [ { - "type": "string" + "$ref": "#/$defs/v1.SqlQueryOperationDefinition" + }, + { + "$ref": "#/$defs/v1.SqlExecOperationDefinition" } ] }, - "v1.U16": { - "$anchor": "v1.U16", - "additionalProperties": false, - "type": "string", - "enum": [ - "u16" - ] - }, - "v1.U32": { - "$anchor": "v1.U32", - "additionalProperties": false, - "type": "string", - "enum": [ - "u32" - ] - }, - "v1.U64": { - "$anchor": "v1.U64", - "additionalProperties": false, - "type": "string", - "enum": [ - "u64" - ] - }, - "v1.U8": { - "$anchor": "v1.U8", - "additionalProperties": false, - "type": "string", - "enum": [ - "u8" - ] - }, - "v1.UdpPort": { - "$anchor": "v1.UdpPort", + "v1.SqlQueryOperationDefinition": { + "$anchor": "v1.SqlQueryOperationDefinition", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/resource/udpport@v1" - ] + "name": { + "description": "The name of the operation.", + "type": "string" }, - "port": { - "description": "The port to bind to.", - "$ref": "#/$defs/v1.LiquidTemplate" + "with": { + "description": "Any configuration required by the operation.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } }, - "address": { - "description": "The address to bind to.", - "$ref": "#/$defs/v1.LiquidTemplate" - } - }, - "required": [] - }, - "v1.UdpPortRestriction": { - "$anchor": "v1.UdpPortRestriction", - "additionalProperties": false, - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/resource/udpport@v1" - ] + "inputs": { + "description": "Types of the inputs to the operation.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } }, - "components": { - "description": "The components this restriction applies to", + "outputs": { + "description": "Types of the outputs to the operation.", "type": "array", "items": { - "type": "string" + "$ref": "#/$defs/v1.Field" } }, - "address": { - "description": "The address to allow", - "$ref": "#/$defs/v1.LiquidTemplate" + "query": { + "description": "The query to execute.", + "type": "string" + }, + "arguments": { + "description": "The positional arguments to the query, defined as a list of input names.", + "type": "array", + "items": { + "type": "string" + } }, - "port": { - "description": "The port to allow", - "$ref": "#/$defs/v1.LiquidTemplate" + "on_error": { + "description": "What to do when an error occurs.", + "$ref": "#/$defs/v1.ErrorBehavior" } }, "required": [ - "address", - "port" + "query" ] }, - "v1.UnionSignature": { - "$anchor": "v1.UnionSignature", + "v1.SqlExecOperationDefinition": { + "$anchor": "v1.SqlExecOperationDefinition", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/type/union@v1" - ] - }, "name": { - "description": "The name of the union.", + "description": "The name of the operation.", "type": "string" }, - "types": { - "description": "The types in the union.", + "with": { + "description": "Any configuration required by the operation.", "type": "array", "items": { - "$ref": "#/$defs/v1.TypeSignature" + "$ref": "#/$defs/v1.Field" } }, - "description": { - "description": "The description of the union.", - "type": "string" - } - }, - "required": [] - }, - "v1.Url": { - "$anchor": "v1.Url", - "additionalProperties": false, - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/resource/url@v1" - ] + "inputs": { + "description": "Types of the inputs to the operation.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } }, - "url": { - "description": "The url string.", - "$ref": "#/$defs/v1.LiquidTemplate" - } - }, - "required": [ - "url" - ] - }, - "v1.UrlRestriction": { - "$anchor": "v1.UrlRestriction", - "additionalProperties": false, - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/resource/url@v1" - ] + "outputs": { + "description": "Types of the outputs to the operation.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.Field" + } }, - "components": { - "description": "The components this restriction applies to", + "exec": { + "description": "The query to execute.", + "type": "string" + }, + "arguments": { + "description": "The positional arguments to the query, defined as a list of input names.", "type": "array", "items": { "type": "string" } }, - "allow": { - "description": "The URLs to allow", - "$ref": "#/$defs/v1.LiquidTemplate" + "on_error": { + "description": "What to do when an error occurs.", + "$ref": "#/$defs/v1.ErrorBehavior" } }, "required": [ - "allow" + "exec" ] }, - "v1.Volume": { - "$anchor": "v1.Volume", - "additionalProperties": false, - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/resource/volume@v1" - ] - }, - "path": { - "description": "The path.", - "$ref": "#/$defs/v1.LiquidTemplate" - } - }, - "required": [ - "path" + "v1.ErrorBehavior": { + "$anchor": "v1.ErrorBehavior", + "enum": [ + "Ignore", + "Commit", + "Rollback" ] }, - "v1.VolumeRestriction": { - "$anchor": "v1.VolumeRestriction", + "v1.HttpClientComponent": { + "$anchor": "v1.HttpClientComponent", "additionalProperties": false, "type": "object", "properties": { @@ -2824,95 +2819,104 @@ "type": "string", "description": "The kind of the collection", "enum": [ - "wick/resource/volume@v1" + "wick/component/http@v1" ] }, - "components": { - "description": "The components this restriction applies to", + "resource": { + "description": "The URL base to use.", + "type": "string" + }, + "codec": { + "description": "The codec to use when encoding/decoding data. Can be overridden by individual operations.", + "$ref": "#/$defs/v1.Codec" + }, + "with": { + "description": "Configuration necessary to provide when instantiating the component.", "type": "array", "items": { - "type": "string" + "$ref": "#/$defs/v1.Field" } }, - "allow": { - "description": "The volumes to allow", - "$ref": "#/$defs/v1.LiquidTemplate" + "operations": { + "description": "A list of operations to expose on this component.", + "type": "array", + "items": { + "$ref": "#/$defs/v1.HttpClientOperationDefinition" + } } }, - "required": [ - "allow" - ] + "required": [] }, - "v1.WasmComponentConfiguration": { - "$anchor": "v1.WasmComponentConfiguration", + "v1.HttpClientOperationDefinition": { + "$anchor": "v1.HttpClientOperationDefinition", "additionalProperties": false, "type": "object", "properties": { - "kind": { - "type": "string", - "description": "The kind of the collection", - "enum": [ - "wick/component/wasmrs@v1" - ] - }, - "ref": { - "description": "The path or OCI reference to the WebAssembly module", + "name": { + "description": "The name of the operation.", "type": "string" }, - "volumes": { - "description": "Volumes to expose to the component.", - "type": "array", - "items": { - "$ref": "#/$defs/v1.ExposedVolume" - } - }, - "max_packet_size": { - "description": "The default size to allocate to the component's send/receive buffer.", - "oneOf": [ - { - "type": "number" - }, - { - "type": "string" - } - ] - }, "with": { - "description": "Configuration necessary to provide when instantiating the component.", + "description": "Any configuration required by the operation.", "type": "array", "items": { "$ref": "#/$defs/v1.Field" } }, - "operations": { - "description": "A list of operations implemented by the WebAssembly module.", + "inputs": { + "description": "Types of the inputs to the operation.", "type": "array", "items": { - "$ref": "#/$defs/v1.OperationDefinition" + "$ref": "#/$defs/v1.Field" } - } - }, - "required": [ - "ref" - ] - }, - "v1.WickConfig": { - "oneOf": [ - { - "$ref": "#/$defs/v1.AppConfiguration" }, - { - "$ref": "#/$defs/v1.ComponentConfiguration" + "method": { + "description": "The HTTP method to use.", + "$ref": "#/$defs/v1.HttpMethod" }, - { - "$ref": "#/$defs/v1.TypesConfiguration" + "codec": { + "description": "The codec to use when encoding/decoding data.", + "$ref": "#/$defs/v1.Codec" }, - { - "$ref": "#/$defs/v1.TestConfiguration" + "headers": { + "description": "Any headers to add to the request.", + "type": "object", + "patternProperties": { + "[a-zA-Z0-9][a-zA-Z0-9_]*": { + "type": "array", + "items": { + "type": "string" + } + } + } }, - { - "$ref": "#/$defs/v1.LockdownConfiguration" + "body": { + "description": "The body to send, processed as a structured JSON liquid template.", + "$ref": "#/$defs/v1.LiquidJsonValue" + }, + "path": { + "description": "The path to append to our base URL, processed as a liquid template with each input as part of the template data.", + "type": "string" } + }, + "required": [] + }, + "v1.Codec": { + "$anchor": "v1.Codec", + "enum": [ + "Json", + "Raw", + "FormData", + "Text" + ] + }, + "v1.HttpMethod": { + "$anchor": "v1.HttpMethod", + "enum": [ + "Get", + "Post", + "Put", + "Delete" ] } }, diff --git a/crates/wick/wick-config/json-schema/v1/manifest.json b/crates/wick/wick-config/json-schema/v1/manifest.json index 8ee59db9..d590a05e 100644 --- a/crates/wick/wick-config/json-schema/v1/manifest.json +++ b/crates/wick/wick-config/json-schema/v1/manifest.json @@ -759,6 +759,11 @@ "description": "Fallback path (relative to volume `resource`) for files to serve in case of a 404. Useful for SPA's. if volume resource is: /www and fallback: index.html, then a 404 will serve /www/index.html", "type": "string" + }, + "indexes": { + "description": "Whether or not to serve directory listings when a directory is requested.", + + "type": "boolean" } }, "required": ["path", "volume"] diff --git a/crates/wick/wick-config/src/config/app_config/triggers/http/static_router.rs b/crates/wick/wick-config/src/config/app_config/triggers/http/static_router.rs index 34e65acb..49ea2169 100644 --- a/crates/wick/wick-config/src/config/app_config/triggers/http/static_router.rs +++ b/crates/wick/wick-config/src/config/app_config/triggers/http/static_router.rs @@ -29,6 +29,8 @@ pub struct StaticRouterConfig { #[asset(skip)] #[serde(skip_serializing_if = "Option::is_none")] pub(crate) fallback: Option, + #[asset(skip)] + pub(crate) indexes: bool, } impl super::WickRouter for StaticRouterConfig { diff --git a/crates/wick/wick-config/src/v1.rs b/crates/wick/wick-config/src/v1.rs index 3940b927..7fd89d10 100644 --- a/crates/wick/wick-config/src/v1.rs +++ b/crates/wick/wick-config/src/v1.rs @@ -584,6 +584,10 @@ pub struct StaticRouter { #[serde(default)] #[serde(skip_serializing_if = "Option::is_none")] pub fallback: Option, + /// Whether or not to serve directory listings when a directory is requested. + + #[serde(default)] + pub indexes: bool, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/crates/wick/wick-config/src/v1/conversions.rs b/crates/wick/wick-config/src/v1/conversions.rs index 8140d863..9615dbb1 100644 --- a/crates/wick/wick-config/src/v1/conversions.rs +++ b/crates/wick/wick-config/src/v1/conversions.rs @@ -392,6 +392,7 @@ impl TryFrom for v1::StaticRouter { volume: value.volume, fallback: value.fallback, middleware: value.middleware.try_map_into()?, + indexes: value.indexes, }) } } @@ -1077,6 +1078,7 @@ impl TryFrom for HttpRouterConfig { volume: v.volume, fallback: v.fallback, middleware: v.middleware.try_map_into()?, + indexes: v.indexes, }), v1::HttpRouter::ProxyRouter(v) => Self::ProxyRouter(ProxyRouterConfig { path: v.path, diff --git a/crates/wick/wick-runtime/Cargo.toml b/crates/wick/wick-runtime/Cargo.toml index 1084090d..ae5c8481 100644 --- a/crates/wick/wick-runtime/Cargo.toml +++ b/crates/wick/wick-runtime/Cargo.toml @@ -47,6 +47,8 @@ hyper-reverse-proxy = { workspace = true } url = { workspace = true } bytes = { workspace = true } openapiv3 = { workspace = true } +percent-encoding = { workspace = true } +liquid = { workspace = true } # # For CLI Trigger # diff --git a/crates/wick/wick-runtime/src/triggers/http/routers/static_.rs b/crates/wick/wick-runtime/src/triggers/http/routers/static_.rs index 87007d4a..d1a1f660 100644 --- a/crates/wick/wick-runtime/src/triggers/http/routers/static_.rs +++ b/crates/wick/wick-runtime/src/triggers/http/routers/static_.rs @@ -3,6 +3,7 @@ use std::net::SocketAddr; use std::path::PathBuf; use std::sync::Arc; use std::task::{Context, Poll}; +mod index_list; use futures::future::BoxFuture; use hyper::service::Service; @@ -12,6 +13,7 @@ use tracing::{Instrument, Span}; use uuid::Uuid; use wick_config::config::{StaticRouterConfig, TriggerKind, WickRouter}; +use self::index_list::StaticError; use crate::dev::prelude::RuntimeError; use crate::resources::{Resource, ResourceKind}; use crate::triggers::http::middleware::resolve_middleware_components; @@ -25,9 +27,9 @@ pub(super) struct StaticRouter { } impl StaticRouter { - pub(super) fn new(root: PathBuf, strip: Option, fallback: Option) -> Self { + pub(super) fn new(root: PathBuf, strip: Option, indexes: bool, fallback: Option) -> Self { debug!(directory = %root.display(), "http:static:serving"); - let handler = Static::new(root, strip, fallback); + let handler = Static::new(root, strip, indexes, fallback); Self { handler } } } @@ -55,7 +57,7 @@ impl RawRouter for StaticRouter { } } -fn create_response(request: &Request, result: ResolveResult) -> Result, std::io::Error> +fn create_response(request: &Request, result: ResolveResult) -> Result, StaticError> where B: Send + Sync + 'static, { @@ -67,26 +69,36 @@ where .expect("unable to build response"), ) } - #[derive(Clone)] struct Static { root: PathBuf, strip: Option, fallback: Option, + indexes: bool, } impl Static { - fn new(root: impl Into, strip: Option, fallback: Option) -> Self { + fn new(root: impl Into, strip: Option, indexes: bool, fallback: Option) -> Self { let root = root.into(); - Static { root, strip, fallback } + Static { + root, + strip, + indexes, + fallback, + } } /// Serve a request. - async fn serve(self, request: Request) -> Result, std::io::Error> + async fn serve(self, request: Request) -> Result, StaticError> where B: Send + Sync + 'static, { - let Self { root, strip, fallback } = self; + let Self { + root, + strip, + indexes, + fallback, + } = self; // Handle only `GET`/`HEAD` and absolute paths. match *request.method() { Method::HEAD | Method::GET => {} @@ -110,6 +122,10 @@ impl Static { match result { Ok(ResolveResult::Found(_, _, _)) => create_response(&request, result?), + Ok(ResolveResult::IsDirectory) if indexes => index_list::create(&root, &request).await, + Ok(ResolveResult::NotFound) if (indexes && request.uri().path() == "/") => { + index_list::create(&root, &request).await + } _ => { if let Some(fb) = &fallback { let fallback_result = resolve_path(root.clone(), fb).await; @@ -124,7 +140,7 @@ impl Static { impl Service> for Static { type Response = Response; - type Error = std::io::Error; + type Error = StaticError; type Future = BoxFuture<'static, Result>; fn poll_ready(&mut self, _cx: &mut Context) -> Poll> { @@ -161,8 +177,9 @@ pub(crate) fn register_static_router( }; let fallback = router_config.fallback().cloned(); + let indexes = router_config.indexes(); - let router = StaticRouter::new(volume, Some(router_config.path().to_owned()), fallback); + let router = StaticRouter::new(volume, Some(router_config.path().to_owned()), indexes, fallback); Ok(HttpRouter::Raw(RawRouterHandler { path: router_config.path().to_owned(), diff --git a/crates/wick/wick-runtime/src/triggers/http/routers/static_/index_list.rs b/crates/wick/wick-runtime/src/triggers/http/routers/static_/index_list.rs new file mode 100644 index 00000000..df11bc90 --- /dev/null +++ b/crates/wick/wick-runtime/src/triggers/http/routers/static_/index_list.rs @@ -0,0 +1,120 @@ +use std::cmp::Ordering; +use std::path::{Component, Path, PathBuf}; + +use hyper::{Body, Request, Response, StatusCode}; +use once_cell::sync::Lazy; + +static INDEX_TEMPLATE: Lazy = Lazy::new(|| { + let template = include_str!("template.html"); + liquid::ParserBuilder::with_stdlib() + .build() + .unwrap() + .parse(template) + .unwrap() +}); + +#[derive(serde::Serialize, Debug)] +struct Entry { + name: String, + is_dir: bool, +} + +pub(super) enum StaticError { + Template, + Io(std::io::Error), +} + +impl From for StaticError { + fn from(value: std::io::Error) -> Self { + Self::Io(value) + } +} + +impl std::fmt::Display for StaticError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Template => write!(f, "Template error"), + Self::Io(e) => e.fmt(f), + } + } +} + +pub(super) async fn create(root: &Path, request: &Request) -> Result, StaticError> +where + B: Send + Sync + 'static, +{ + let resolved = RequestedPath::resolve(request.uri().path()); + let mut base = root.to_path_buf(); + base.push(&resolved.sanitized); + + let mut entries = Vec::new(); + let mut list = tokio::fs::read_dir(base).await?; + + while let Ok(Some(entry)) = list.next_entry().await { + let name = entry.file_name().to_string_lossy().to_string(); + let is_dir = entry.file_type().await?.is_dir(); + entries.push(Entry { name, is_dir }); + } + + entries.sort_by(|a, b| { + if a.is_dir && b.is_dir { + a.name.cmp(&b.name) + } else if a.is_dir { + Ordering::Less + } else if b.is_dir { + Ordering::Greater + } else { + a.name.cmp(&b.name) + } + }); + + let globals = liquid::object!({ + "path": resolved.sanitized.to_string_lossy(), + "entries":entries + }); + + let page = INDEX_TEMPLATE.render(&globals).map_err(|_| StaticError::Template)?; + + Ok(Response::builder().status(StatusCode::OK).body(page.into()).unwrap()) +} + +#[inline] +fn decode_percents(string: &str) -> String { + percent_encoding::percent_decode_str(string) + .decode_utf8_lossy() + .into_owned() +} + +fn normalize_path(path: &Path) -> PathBuf { + path.components().fold(PathBuf::new(), |mut result, p| match p { + Component::Normal(x) => { + // Parse again to prevent a malicious component containing + // a Windows drive letter, e.g.: `/anypath/c:/windows/win.ini` + if Path::new(&x).components().all(|c| matches!(c, Component::Normal(_))) { + result.push(x); + } + result + } + Component::ParentDir => { + result.pop(); + result + } + _ => result, + }) +} + +/// Resolved request path. +pub(crate) struct RequestedPath { + /// Sanitized path of the request. + pub(crate) sanitized: PathBuf, +} + +impl RequestedPath { + /// Resolve the requested path to a full filesystem path, limited to the root. + pub(crate) fn resolve(request_path: &str) -> Self { + let request_path = PathBuf::from(decode_percents(request_path)); + RequestedPath { + sanitized: normalize_path(&request_path), + } + } +} diff --git a/crates/wick/wick-runtime/src/triggers/http/routers/static_/template.html b/crates/wick/wick-runtime/src/triggers/http/routers/static_/template.html new file mode 100644 index 00000000..9a289d9a --- /dev/null +++ b/crates/wick/wick-runtime/src/triggers/http/routers/static_/template.html @@ -0,0 +1,75 @@ + + + + + + + Index of / + + + + + +

Index of {{ path }}/

+ +
+ {% for entry in entries %} +
+ + {%if entry.is_dir%}📁{%else%}📄{% endif %} + {{entry.name}}{%if entry.is_dir%}/{% endif %} + +
+ + {% endfor %} +
+
Wick static server
+ \ No newline at end of file diff --git a/examples/components/config-generator/src/lib.rs b/examples/components/config-generator/src/lib.rs index 9f93fadd..6906043d 100644 --- a/examples/components/config-generator/src/lib.rs +++ b/examples/components/config-generator/src/lib.rs @@ -39,6 +39,7 @@ fn build_static_config(app_name: &str, dir: String) -> WickConfig { volume: volume_resource_id.clone(), middleware: None, fallback: None, + indexes: true, })], })], ..Default::default()