diff --git a/crates/stackless-integrations/provisional-allowlist.txt b/crates/stackless-integrations/provisional-allowlist.txt index bb67d09..37e0d3c 100644 --- a/crates/stackless-integrations/provisional-allowlist.txt +++ b/crates/stackless-integrations/provisional-allowlist.txt @@ -7,3 +7,4 @@ # Externally blocked integrations keep source on disk but omit `pub mod` # + registry row and live in EXCL — see docs/ADDING-A-PROVIDER.md # § External pin blockers. Those files are not scanned here. +shopify/store diff --git a/crates/stackless-integrations/src/providers/mod.rs b/crates/stackless-integrations/src/providers/mod.rs index a68d3a5..59895df 100644 --- a/crates/stackless-integrations/src/providers/mod.rs +++ b/crates/stackless-integrations/src/providers/mod.rs @@ -39,6 +39,7 @@ pub mod revenuecat; pub mod runloop; pub mod schematic; pub mod sentry; +pub mod shopify; pub mod steel; pub mod supabase; pub mod supermemory; @@ -59,8 +60,8 @@ mod tests { clickhouse, cloudflare, composio, customerio, datadog, depot, e2b, elevenlabs, exa, firecrawl, flyio, gitlab, huggingface, inngest, kernel, laravel_cloud, metronome, mixpanel, neon, openrouter, parallel, planetscale, postalform, posthog, prisma, pydantic, railway, - render_db, revenuecat, runloop, schematic, sentry, steel, supabase, supermemory, tabstack, - turso, upstash, wix, wordpress_com, workos, + render_db, revenuecat, runloop, schematic, sentry, shopify, steel, supabase, supermemory, + tabstack, turso, upstash, wix, wordpress_com, workos, }; fn assert_outputs_match() { @@ -134,6 +135,7 @@ mod tests { assert_outputs_match::(); assert_outputs_match::(); assert_outputs_match::(); + assert_outputs_match::(); assert_outputs_match::(); assert_outputs_match::(); assert_outputs_match::(); diff --git a/crates/stackless-integrations/src/providers/shopify/mod.rs b/crates/stackless-integrations/src/providers/shopify/mod.rs new file mode 100644 index 0000000..803e66e --- /dev/null +++ b/crates/stackless-integrations/src/providers/shopify/mod.rs @@ -0,0 +1,11 @@ +//! Shopify catalog resources via Stripe Projects. +//! +//! Output envelopes are provisional until pinned by `xtask discover`. + +pub mod store; + +#[allow(unused_imports)] +pub(crate) use crate::resource::{ + CatalogResource as FamilyResource, bool_optional, bool_required, int_optional, int_required, + integration_config, interp_optional, interp_required, +}; diff --git a/crates/stackless-integrations/src/providers/shopify/store.rs b/crates/stackless-integrations/src/providers/shopify/store.rs new file mode 100644 index 0000000..21859d7 --- /dev/null +++ b/crates/stackless-integrations/src/providers/shopify/store.rs @@ -0,0 +1,154 @@ +//! `shopify/store` integration. + +use std::collections::BTreeMap; + +use serde::Serialize; +use stackless_stripe_projects::catalog::verify::CatalogService; +use stackless_stripe_projects::provision::ProvisionContext; + +use super::FamilyResource; +use crate::error::IntegrationError; +use crate::hostable::{ConfigScope, Hostable, IntegrationHosting}; + +pub const RESOURCE_KIND: &str = "integration-shopify"; + +#[derive(Debug, Serialize)] +pub struct ShopifyStoreConfig { + #[serde(skip_serializing_if = "Option::is_none")] + pub plan: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub store_name: Option, +} + +impl CatalogService for ShopifyStoreConfig { + const REFERENCE: &'static str = "shopify/store"; +} + +#[derive(Debug)] +pub struct ShopifyStore; + +impl Hostable for ShopifyStore { + const PROVIDER: &'static str = "shopify"; + const HOSTING: IntegrationHosting = IntegrationHosting::Managed; + const CONFIG_SCOPE: ConfigScope = ConfigScope::GlobalOnly; + const RESOURCE_KIND: &'static str = RESOURCE_KIND; + const OUTPUTS: &'static [&'static str] = &["shop_domain", "shop_login_url", "signup_token"]; +} + +impl FamilyResource for ShopifyStore { + type Config = ShopifyStoreConfig; + const PROVIDER_PREFIX: &'static str = "SHOPIFY"; + // Provisional until pinned by `mise run discover shopify/store`. + const OUTPUT_FIELDS: &'static [(&'static str, &'static str, bool)] = &[ + ("SHOP_DOMAIN", "shop_domain", true), + ("SHOP_LOGIN_URL", "shop_login_url", true), + ("SIGNUP_TOKEN", "signup_token", true), + ]; + + fn build_config(ctx: &ProvisionContext<'_>) -> Result { + let config = super::integration_config(ctx)?; + Ok(ShopifyStoreConfig { + plan: super::interp_optional(ctx, &config, "plan")?, + store_name: super::interp_optional(ctx, &config, "store_name")?, + }) + } +} + +pub fn validate_config( + name: &str, + config: &BTreeMap, +) -> Result<(), IntegrationError> { + let _ = (name, config); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ProviderOps; + use crate::resource::ResourcePayload; + use stackless_core::def::StackDef; + use stackless_stripe_projects::stripe::StripeProjects; + use stackless_stripe_projects::test_support; + + #[test] + fn config_matches_catalog() { + const FIXTURE: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../stackless-stripe-projects/tests/fixtures/catalog.json" + )); + let catalog = stackless_stripe_projects::Catalog::from_json_envelope(FIXTURE).unwrap(); + let failures = stackless_stripe_projects::verify_service( + &catalog, + &ShopifyStoreConfig { + plan: None, + store_name: None, + }, + ); + assert!( + failures.is_empty(), + "shopify/store catalog gaps:\n{}", + failures.join("\n") + ); + } + + const CATALOG_ENVELOPE: &str = r##"{"ok":true,"command":"projects catalog","data":{"last_updated":"2026-07-11T00:00:00Z","services":[{"id":"prvsvc_store","object":"v2.provisioning.provider_service_detail","provider_id":"prvdr_shopify","provider_name":"Shopify","service_id":"store","categories":["ecommerce"],"kind":"deployable","scope":"project","availability":"available","development":false,"livemode":true,"pricing":{"type":"paid"},"configuration_schema":{"additionalProperties":false,"properties":{"plan":{"enum":["trial","basic","grow","advanced"],"type":"string"},"store_name":{"maxLength":50,"minLength":1,"type":"string"}},"optional":["store_name","plan"],"type":"object"}}]}}"##; + + fn test_def() -> StackDef { + StackDef::parse( + r#" +[stack] +name = "atto" +[stack.projects.stripe] +project = "project_1" +[integrations.res] +provider = "shopify" +[services.api] +source = { repo = "r", ref = "main" } +env = { OUT = "${integrations.res.shop_domain}" } +health = { path = "/health" } +[services.api.local] +run = "true" +"#, + ) + .unwrap() + } + + #[tokio::test] + async fn provision_records_outputs() { + let runner = test_support::provision_script( + CATALOG_ENVELOPE, + serde_json::json!({ + "SHOPIFY_SHOP_DOMAIN": "val_shop_domain", + "SHOPIFY_SHOP_LOGIN_URL": "val_shop_login_url", + "SHOPIFY_SIGNUP_TOKEN": "val_signup_token" + }), + 0, + ); + let dir = tempfile::tempdir().unwrap(); + std::fs::write( + dir.path().join("stackless.toml"), + "[stack]\nname=\"atto\"\n", + ) + .unwrap(); + let stripe = StripeProjects::new(&runner, dir.path()); + + let resource = ShopifyStore + .provision( + &stripe.as_dyn(), + &test_def(), + dir.path(), + "demo", + "res", + "local", + false, + ) + .await + .unwrap(); + assert_eq!(resource.resource_kind, "integration-shopify"); + let payload: ResourcePayload = serde_json::from_str(&resource.payload).unwrap(); + assert_eq!(payload.outputs["shop_domain"], "val_shop_domain"); + assert_eq!(payload.outputs["shop_login_url"], "val_shop_login_url"); + assert_eq!(payload.outputs["signup_token"], "val_signup_token"); + } +} diff --git a/crates/stackless-integrations/src/registry.rs b/crates/stackless-integrations/src/registry.rs index 1bac298..9e52738 100644 --- a/crates/stackless-integrations/src/registry.rs +++ b/crates/stackless-integrations/src/registry.rs @@ -126,6 +126,7 @@ register_providers! { (schematic::schematic_environment, SchematicEnvironment), (sentry::project, SentryProject), (sentry::seer, SentrySeer), + (shopify::store, ShopifyStore), (steel::browser, SteelBrowser), (supabase::project, SupabaseProject), (supermemory::memory, SupermemoryMemory), diff --git a/crates/stackless-stripe-projects/src/catalog.rs b/crates/stackless-stripe-projects/src/catalog.rs index e7ea19d..bdb2e01 100644 --- a/crates/stackless-stripe-projects/src/catalog.rs +++ b/crates/stackless-stripe-projects/src/catalog.rs @@ -612,6 +612,8 @@ pub struct ConfigSchema { #[serde(default)] pub required: Vec, #[serde(default)] + pub optional: Vec, + #[serde(default)] pub properties: BTreeMap, #[serde(flatten)] pub extra: BTreeMap, @@ -756,244 +758,4 @@ fn value_label(value: &Value) -> String { } #[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - fn service(reference: &str, schema: Value, pricing: Value) -> ServiceDetail { - let (provider, service_id) = reference.split_once('/').unwrap(); - serde_json::from_value(json!({ - "id": "prvsvc_test", - "object": "v2.provisioning.provider_service_detail", - "provider_id": "prvdr_test", - "provider_name": provider, - "service_id": service_id, - "categories": [], - "kind": "deployable", - "scope": "project", - "availability": "available", - "development": false, - "livemode": true, - "pricing": pricing, - "configuration_schema": schema, - })) - .unwrap() - } - - #[test] - fn reference_is_provider_lowercased_slash_service_id() { - let svc = service("Render/postgres", json!({}), json!({"type": "free"})); - assert_eq!(svc.reference(), "render/postgres"); - } - - #[test] - fn validate_accepts_pricing_selector_keys_outside_schema() { - // render/postgres: instance_type is a tier selector, not a schema prop. - let svc = service( - "Render/postgres", - json!({ - "type": "object", - "required": ["name"], - "additionalProperties": false, - "properties": { "name": {"type": "string"}, "version": {"type": "string"} } - }), - json!({ - "type": "paid", - "paid_pricing": [ - {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"}, - {"configuration": {"instance_type": "basic-256mb"}, "type": "freeform"} - ] - }), - ); - svc.validate_config( - &json!({"name": "db", "version": "17", "instance_type": "basic-256mb"}), - ) - .unwrap(); - } - - #[test] - fn validate_rejects_unknown_field_and_bad_tier_and_missing_required() { - let svc = service( - "Render/postgres", - json!({ - "type": "object", - "required": ["name"], - "additionalProperties": false, - "properties": { "name": {"type": "string"} } - }), - json!({ - "type": "paid", - "paid_pricing": [ - {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"}, - {"configuration": {"instance_type": "basic-256mb"}, "type": "freeform"} - ] - }), - ); - let err = svc - .validate_config(&json!({"bogus": 1, "instance_type": "nope"})) - .unwrap_err(); - assert!( - err.iter() - .any(|v| v.contains("missing required field `name`")), - "{err:?}" - ); - assert!( - err.iter().any(|v| v.contains("unknown field `bogus`")), - "{err:?}" - ); - assert!( - err.iter().any(|v| v.contains("not an allowed tier value")), - "{err:?}" - ); - } - - #[test] - fn validate_enforces_enum_and_type() { - let svc = service( - "Render/web-service", - json!({ - "type": "object", - "required": ["name", "runtime"], - "additionalProperties": false, - "properties": { - "name": {"type": "string"}, - "runtime": {"type": "string", "enum": ["rust", "node"]}, - "auto_deploy": {"type": "string", "enum": ["yes", "no"]} - } - }), - json!({"type": "paid", "paid_pricing": [ - {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"} - ]}), - ); - svc.validate_config(&json!({"name": "api", "runtime": "rust", "auto_deploy": "no"})) - .unwrap(); - let err = svc - .validate_config(&json!({"name": "api", "runtime": "elixir"})) - .unwrap_err(); - assert!(err.iter().any(|v| v.contains("not in enum")), "{err:?}"); - } - - #[test] - fn requires_confirmation_is_per_tier() { - let svc = service( - "Render/postgres", - json!({"type": "object", "required": ["name"], "properties": {"name": {"type": "string"}}}), - json!({ - "type": "paid", - "paid_pricing": [ - {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"}, - {"configuration": {"instance_type": "basic-256mb"}, "type": "freeform"} - ] - }), - ); - assert!(svc.requires_confirmation(&json!({"name": "db", "instance_type": "basic-256mb"}))); - // No selector → default (free) tier → no confirmation. - assert!(!svc.requires_confirmation(&json!({"name": "db"}))); - } - - #[test] - fn requires_confirmation_falls_back_to_pricing_kind() { - let free = service("Render/static-site", json!({}), json!({"type": "free"})); - assert!(!free.requires_confirmation(&json!({}))); - let paid = service( - "Vercel/pro", - json!({}), - json!({"type": "paid", "paid_pricing": [{"type": "freeform"}]}), - ); - assert!(paid.requires_confirmation(&json!({}))); - let component = service("Clerk/auth", json!({}), json!({"type": "component"})); - assert!(!component.requires_confirmation(&json!({}))); - } - - #[test] - fn required_parent_services_returns_single_parent_only() { - let kv = service( - "cloudflare/kv", - json!({"type": "object", "properties": {"title": {"type": "string"}}, "required": ["title"]}), - json!({ - "type": "component", - "component": { - "options": [ - {"type": "free", "parent_services": ["workers:free"]}, - {"type": "paid", "parent_services": ["workers:paid"]} - ] - } - }), - ); - assert_eq!( - kv.required_parent_services(&json!({"title": "cache"}), false), - vec!["workers:free"] - ); - assert_eq!( - kv.required_parent_services(&json!({"title": "cache"}), true), - vec!["workers:paid"] - ); - assert!(kv.requires_confirmation_with_paid(&json!({"title": "cache"}), true)); - - let vercel = service( - "vercel/project", - json!({"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}), - json!({ - "type": "component", - "component": { - "options": [ - {"type": "free", "parent_services": ["pro", "hobby"]} - ] - } - }), - ); - assert!( - vercel - .required_parent_services(&json!({"name": "demo"}), false) - .is_empty() - ); - } - - #[test] - fn match_option_without_paid_consent_ignores_paid_default() { - let svc = service( - "cloudflare/kv", - json!({}), - json!({ - "type": "component", - "component": { - "options": [ - {"type": "paid", "is_default": true, "parent_services": ["workers:paid"]}, - {"type": "free", "parent_services": ["workers:free"]} - ] - } - }), - ); - assert!(!svc.requires_confirmation_with_paid(&json!({}), false)); - assert_eq!( - svc.required_parent_services(&json!({}), false), - vec!["workers:free"] - ); - assert!(svc.requires_confirmation_with_paid(&json!({}), true)); - assert_eq!( - svc.required_parent_services(&json!({}), true), - vec!["workers:paid"] - ); - } - - #[test] - fn match_option_with_paid_consent_ignores_free_default() { - let svc = service( - "cloudflare/kv", - json!({}), - json!({ - "type": "component", - "component": { - "options": [ - {"type": "free", "is_default": true, "parent_services": ["workers:free"]}, - {"type": "paid", "parent_services": ["workers:paid"]} - ] - } - }), - ); - assert_eq!( - svc.required_parent_services(&json!({}), true), - vec!["workers:paid"] - ); - } -} +mod tests; diff --git a/crates/stackless-stripe-projects/src/catalog/tests.rs b/crates/stackless-stripe-projects/src/catalog/tests.rs new file mode 100644 index 0000000..46df8ac --- /dev/null +++ b/crates/stackless-stripe-projects/src/catalog/tests.rs @@ -0,0 +1,272 @@ +use super::*; +use serde_json::json; + +fn service(reference: &str, schema: Value, pricing: Value) -> ServiceDetail { + let (provider, service_id) = reference.split_once('/').unwrap(); + serde_json::from_value(json!({ + "id": "prvsvc_test", + "object": "v2.provisioning.provider_service_detail", + "provider_id": "prvdr_test", + "provider_name": provider, + "service_id": service_id, + "categories": [], + "kind": "deployable", + "scope": "project", + "availability": "available", + "development": false, + "livemode": true, + "pricing": pricing, + "configuration_schema": schema, + })) + .unwrap() +} + +#[test] +fn reference_is_provider_lowercased_slash_service_id() { + let svc = service("Render/postgres", json!({}), json!({"type": "free"})); + assert_eq!(svc.reference(), "render/postgres"); +} + +#[test] +fn validate_accepts_pricing_selector_keys_outside_schema() { + // render/postgres: instance_type is a tier selector, not a schema prop. + let svc = service( + "Render/postgres", + json!({ + "type": "object", + "required": ["name"], + "additionalProperties": false, + "properties": { "name": {"type": "string"}, "version": {"type": "string"} } + }), + json!({ + "type": "paid", + "paid_pricing": [ + {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"}, + {"configuration": {"instance_type": "basic-256mb"}, "type": "freeform"} + ] + }), + ); + svc.validate_config(&json!({"name": "db", "version": "17", "instance_type": "basic-256mb"})) + .unwrap(); +} + +#[test] +fn validate_rejects_unknown_field_and_bad_tier_and_missing_required() { + let svc = service( + "Render/postgres", + json!({ + "type": "object", + "required": ["name"], + "additionalProperties": false, + "properties": { "name": {"type": "string"} } + }), + json!({ + "type": "paid", + "paid_pricing": [ + {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"}, + {"configuration": {"instance_type": "basic-256mb"}, "type": "freeform"} + ] + }), + ); + let err = svc + .validate_config(&json!({"bogus": 1, "instance_type": "nope"})) + .unwrap_err(); + assert!( + err.iter() + .any(|v| v.contains("missing required field `name`")), + "{err:?}" + ); + assert!( + err.iter().any(|v| v.contains("unknown field `bogus`")), + "{err:?}" + ); + assert!( + err.iter().any(|v| v.contains("not an allowed tier value")), + "{err:?}" + ); +} + +#[test] +fn validate_enforces_enum_and_type() { + let svc = service( + "Render/web-service", + json!({ + "type": "object", + "required": ["name", "runtime"], + "additionalProperties": false, + "properties": { + "name": {"type": "string"}, + "runtime": {"type": "string", "enum": ["rust", "node"]}, + "auto_deploy": {"type": "string", "enum": ["yes", "no"]} + } + }), + json!({"type": "paid", "paid_pricing": [ + {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"} + ]}), + ); + svc.validate_config(&json!({"name": "api", "runtime": "rust", "auto_deploy": "no"})) + .unwrap(); + let err = svc + .validate_config(&json!({"name": "api", "runtime": "elixir"})) + .unwrap_err(); + assert!(err.iter().any(|v| v.contains("not in enum")), "{err:?}"); +} + +#[test] +fn requires_confirmation_is_per_tier() { + let svc = service( + "Render/postgres", + json!({"type": "object", "required": ["name"], "properties": {"name": {"type": "string"}}}), + json!({ + "type": "paid", + "paid_pricing": [ + {"configuration": {"instance_type": "free"}, "is_default": true, "type": "free"}, + {"configuration": {"instance_type": "basic-256mb"}, "type": "freeform"} + ] + }), + ); + assert!(svc.requires_confirmation(&json!({"name": "db", "instance_type": "basic-256mb"}))); + // No selector → default (free) tier → no confirmation. + assert!(!svc.requires_confirmation(&json!({"name": "db"}))); +} + +#[test] +fn requires_confirmation_falls_back_to_pricing_kind() { + let free = service("Render/static-site", json!({}), json!({"type": "free"})); + assert!(!free.requires_confirmation(&json!({}))); + let paid = service( + "Vercel/pro", + json!({}), + json!({"type": "paid", "paid_pricing": [{"type": "freeform"}]}), + ); + assert!(paid.requires_confirmation(&json!({}))); + let component = service("Clerk/auth", json!({}), json!({"type": "component"})); + assert!(!component.requires_confirmation(&json!({}))); +} + +#[test] +fn required_parent_services_returns_single_parent_only() { + let kv = service( + "cloudflare/kv", + json!({"type": "object", "properties": {"title": {"type": "string"}}, "required": ["title"]}), + json!({ + "type": "component", + "component": { + "options": [ + {"type": "free", "parent_services": ["workers:free"]}, + {"type": "paid", "parent_services": ["workers:paid"]} + ] + } + }), + ); + assert_eq!( + kv.required_parent_services(&json!({"title": "cache"}), false), + vec!["workers:free"] + ); + assert_eq!( + kv.required_parent_services(&json!({"title": "cache"}), true), + vec!["workers:paid"] + ); + assert!(kv.requires_confirmation_with_paid(&json!({"title": "cache"}), true)); + + let vercel = service( + "vercel/project", + json!({"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}), + json!({ + "type": "component", + "component": { + "options": [ + {"type": "free", "parent_services": ["pro", "hobby"]} + ] + } + }), + ); + assert!( + vercel + .required_parent_services(&json!({"name": "demo"}), false) + .is_empty() + ); +} + +#[test] +fn match_option_without_paid_consent_ignores_paid_default() { + let svc = service( + "cloudflare/kv", + json!({}), + json!({ + "type": "component", + "component": { + "options": [ + {"type": "paid", "is_default": true, "parent_services": ["workers:paid"]}, + {"type": "free", "parent_services": ["workers:free"]} + ] + } + }), + ); + assert!(!svc.requires_confirmation_with_paid(&json!({}), false)); + assert_eq!( + svc.required_parent_services(&json!({}), false), + vec!["workers:free"] + ); + assert!(svc.requires_confirmation_with_paid(&json!({}), true)); + assert_eq!( + svc.required_parent_services(&json!({}), true), + vec!["workers:paid"] + ); +} + +#[test] +fn config_schema_deserializes_optional_without_drift() { + let schema_json = json!({ + "type": "object", + "required": ["region"], + "optional": ["store_name", "plan"], + "properties": { + "region": {"type": "string"}, + "store_name": {"type": "string"}, + "plan": {"type": "string"} + } + }); + let schema: ConfigSchema = serde_json::from_value(schema_json.clone()).unwrap(); + assert_eq!( + schema.optional, + vec!["store_name".to_owned(), "plan".to_owned()] + ); + + let svc = service("shopify/store", schema_json, json!({"type": "free"})); + let catalog = Catalog { + last_updated: "test".to_owned(), + provider: None, + category_filter: None, + provider_filter: None, + source: None, + services: vec![svc], + extra: BTreeMap::new(), + }; + let drift = catalog.drift_report(); + assert!( + !drift.iter().any(|line| line.contains("optional")), + "optional must be modeled; drift={drift:?}" + ); +} + +#[test] +fn match_option_with_paid_consent_ignores_free_default() { + let svc = service( + "cloudflare/kv", + json!({}), + json!({ + "type": "component", + "component": { + "options": [ + {"type": "free", "is_default": true, "parent_services": ["workers:free"]}, + {"type": "paid", "parent_services": ["workers:paid"]} + ] + } + }), + ); + assert_eq!( + svc.required_parent_services(&json!({}), true), + vec!["workers:paid"] + ); +} diff --git a/crates/stackless-stripe-projects/src/surface.rs b/crates/stackless-stripe-projects/src/surface.rs index e7b8329..32ee210 100644 --- a/crates/stackless-stripe-projects/src/surface.rs +++ b/crates/stackless-stripe-projects/src/surface.rs @@ -13,7 +13,7 @@ use crate::stripe::{CommandRunner, StripeProjects}; /// never self-reorders — a newly shipped command surfaces as a diff in the /// top-level `--help` block and is then added here in the same change. Groups /// (`services`/`env`/`billing`) are listed immediately before their children. -/// Verified against plugin 0.30.0. +/// Verified against plugin 0.33.0. const TRAVERSAL: &[&[&str]] = &[ &[], // GET STARTED diff --git a/crates/stackless-stripe-projects/tests/catalog_drift.rs b/crates/stackless-stripe-projects/tests/catalog_drift.rs index f3b2c67..76bb0b8 100644 --- a/crates/stackless-stripe-projects/tests/catalog_drift.rs +++ b/crates/stackless-stripe-projects/tests/catalog_drift.rs @@ -46,6 +46,21 @@ fn fixture_has_no_unmodeled_drift() { ); } +#[test] +fn shopify_store_optional_is_modeled() { + let catalog = + Catalog::from_json_envelope(FIXTURE).expect("fixture is a valid catalog envelope"); + let schema = catalog + .lookup("shopify/store") + .and_then(|svc| svc.configuration_schema.as_ref()) + .expect("shopify/store has a configuration_schema"); + assert_eq!( + schema.optional, + ["store_name", "plan"], + "shopify/store optional keys must stay modeled, not extra" + ); +} + /// Offline coherence of the plugin-pinned snapshots — runs in the every-PR /// hermetic gate (no `stripe` needed). The live bless (`STRIPE_PROJECTS_REFRESH=1`) /// produces these; this guards against hand-edits, a stale surface after a diff --git a/crates/stackless-stripe-projects/tests/fixtures/catalog.json b/crates/stackless-stripe-projects/tests/fixtures/catalog.json index 83fc8f1..0ef0e18 100644 --- a/crates/stackless-stripe-projects/tests/fixtures/catalog.json +++ b/crates/stackless-stripe-projects/tests/fixtures/catalog.json @@ -10,19 +10,19 @@ "allowed_updates": [ { "direction": "any", - "service": "scaler_overages" + "service": "scaler" }, { "direction": "any", - "service": "pro" + "service": "developer_overages" }, { "direction": "any", - "service": "developer_overages" + "service": "pro" }, { "direction": "any", - "service": "scaler" + "service": "scaler_overages" }, { "direction": "any", @@ -39,15 +39,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -71,10 +71,10 @@ "scope": "account", "service_id": "starter", "updateable_to": [ - "scaler_overages", - "pro", - "developer_overages", "scaler", + "developer_overages", + "pro", + "scaler_overages", "developer", "pro_overages", "starter" @@ -88,15 +88,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -143,7 +143,7 @@ }, { "direction": "any", - "service": "pro" + "service": "scaler" }, { "direction": "any", @@ -151,7 +151,7 @@ }, { "direction": "any", - "service": "scaler" + "service": "pro" } ], "availability": "available", @@ -160,15 +160,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -205,9 +205,9 @@ "service_id": "developer_overages", "updateable_to": [ "scaler_overages", - "pro", - "pro_overages", "scaler", + "pro_overages", + "pro", "developer_overages" ] }, @@ -259,13 +259,13 @@ "is_default": null, "paid": null, "parent_services": [ - "developer_overages", - "developer", - "pro", - "scaler_overages", - "pro_overages", + "starter", "scaler", - "starter" + "pro_overages", + "scaler_overages", + "pro", + "developer", + "developer_overages" ], "type": "free" } @@ -292,11 +292,11 @@ }, { "direction": "any", - "service": "pro" + "service": "pro_overages" }, { "direction": "any", - "service": "pro_overages" + "service": "pro" } ], "availability": "available", @@ -305,15 +305,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -350,8 +350,8 @@ "service_id": "scaler", "updateable_to": [ "scaler_overages", - "pro", "pro_overages", + "pro", "scaler" ] }, @@ -368,15 +368,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -433,15 +433,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -486,23 +486,23 @@ "allowed_updates": [ { "direction": "any", - "service": "scaler" + "service": "developer_overages" }, { "direction": "any", - "service": "scaler_overages" + "service": "pro_overages" }, { "direction": "any", - "service": "developer_overages" + "service": "pro" }, { "direction": "any", - "service": "pro" + "service": "scaler_overages" }, { "direction": "any", - "service": "pro_overages" + "service": "scaler" } ], "availability": "available", @@ -511,15 +511,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -555,11 +555,11 @@ "scope": "account", "service_id": "developer", "updateable_to": [ - "scaler", - "scaler_overages", "developer_overages", - "pro", "pro_overages", + "pro", + "scaler_overages", + "scaler", "developer" ] }, @@ -766,8 +766,8 @@ "type": "freeform" }, "parent_services": [ - "hobby", - "pro" + "pro", + "hobby" ], "type": "paid" } @@ -825,8 +825,8 @@ "type": "freeform" }, "parent_services": [ - "hobby", - "pro" + "pro", + "hobby" ], "type": "paid" } @@ -889,8 +889,8 @@ "type": "freeform" }, "parent_services": [ - "hobby", - "pro" + "pro", + "hobby" ], "type": "paid" } @@ -913,11 +913,11 @@ "allowed_updates": [ { "direction": "any", - "service": "hobby" + "service": "pro" }, { "direction": "any", - "service": "pro" + "service": "hobby" } ], "availability": "available", @@ -950,8 +950,8 @@ "scope": "project", "service_id": "free", "updateable_to": [ - "hobby", "pro", + "hobby", "free" ] }, @@ -1000,8 +1000,8 @@ "type": "freeform" }, "parent_services": [ - "hobby", - "pro" + "pro", + "hobby" ], "type": "paid" } @@ -1068,8 +1068,8 @@ "type": "freeform" }, "parent_services": [ - "hobby", - "pro" + "pro", + "hobby" ], "type": "paid" } @@ -3876,7 +3876,7 @@ "configuration_schema": {}, "constraints": [], "created": null, - "description": "PostHog — product analytics, session replay, realtime destinations, feature flags & experiments, surveys, data warehouse, error tracking, ai observability, logs, posthog ai, inbox, posthog code (usage-based), emails, and more.", + "description": "PostHog — product analytics, session replay, realtime destinations, feature flags & experiments, surveys, data warehouse, error tracking, ai observability, logs, posthog ai, replay vision, inbox, posthog desktop (usage-based), emails, and more.", "development": false, "group": null, "id": "prvsvc_61ULYJXN0ZO3MN4Oh5Dho", @@ -4043,8 +4043,8 @@ "is_default": null, "paid": null, "parent_services": [ - "hobby", - "pro" + "pro", + "hobby" ], "type": "free" } @@ -4198,11 +4198,11 @@ "allowed_updates": [ { "direction": "any", - "service": "hobby" + "service": "pro" }, { "direction": "any", - "service": "pro" + "service": "hobby" } ], "availability": "available", @@ -4244,8 +4244,8 @@ "scope": "project", "service_id": "pro", "updateable_to": [ - "hobby", - "pro" + "pro", + "hobby" ] }, { @@ -4339,9 +4339,9 @@ }, "parent_services": [ "business", - "hobbyist", + "62.5K_emails+auto-replenish", "25K_emails+auto-replenish", - "62.5K_emails+auto-replenish" + "hobbyist" ], "type": "paid" } @@ -5077,11 +5077,11 @@ }, { "direction": "any", - "service": "plus-v3-10k-mtu-monthly" + "service": "plus-v3-50k-mtu-monthly" }, { "direction": "any", - "service": "plus-v3-50k-mtu-monthly" + "service": "plus-v3-10k-mtu-monthly" } ], "availability": "available", @@ -5134,8 +5134,8 @@ "service_id": "free", "updateable_to": [ "plus-v3-25k-mtu-monthly", - "plus-v3-10k-mtu-monthly", "plus-v3-50k-mtu-monthly", + "plus-v3-10k-mtu-monthly", "free" ] }, @@ -5143,19 +5143,19 @@ "allowed_updates": [ { "direction": "any", - "service": "plus-v3-50k-mtu-monthly" + "service": "plus-v3-10k-mtu-monthly" }, { "direction": "any", - "service": "plus-v3-25k-mtu-monthly" + "service": "plus-v3-50k-mtu-monthly" }, { "direction": "any", - "service": "free" + "service": "plus-v3-25k-mtu-monthly" }, { "direction": "any", - "service": "plus-v3-10k-mtu-monthly" + "service": "free" } ], "availability": "available", @@ -5254,10 +5254,10 @@ "scope": "project", "service_id": "analytics", "updateable_to": [ + "plus-v3-10k-mtu-monthly", "plus-v3-50k-mtu-monthly", "plus-v3-25k-mtu-monthly", "free", - "plus-v3-10k-mtu-monthly", "analytics" ] }, @@ -5320,11 +5320,11 @@ "allowed_updates": [ { "direction": "any", - "service": "hobby" + "service": "pro" }, { "direction": "any", - "service": "pro" + "service": "hobby" } ], "availability": "available", @@ -5366,19 +5366,19 @@ "scope": "project", "service_id": "pro", "updateable_to": [ - "hobby", - "pro" + "pro", + "hobby" ] }, { "allowed_updates": [ { "direction": "any", - "service": "hobby" + "service": "pro" }, { "direction": "any", - "service": "pro" + "service": "hobby" } ], "availability": "available", @@ -5408,8 +5408,8 @@ "scope": "project", "service_id": "hobby", "updateable_to": [ - "hobby", - "pro" + "pro", + "hobby" ] }, { @@ -5520,7 +5520,7 @@ }, { "direction": "any", - "service": "standard" + "service": "hobby" }, { "direction": "any", @@ -5528,7 +5528,7 @@ }, { "direction": "any", - "service": "hobby" + "service": "standard" } ], "availability": "available", @@ -5563,9 +5563,9 @@ "service_id": "free", "updateable_to": [ "growth", - "standard", - "api", "hobby", + "api", + "standard", "free" ] }, @@ -5577,7 +5577,7 @@ }, { "direction": "any", - "service": "standard" + "service": "hobby" }, { "direction": "any", @@ -5585,7 +5585,7 @@ }, { "direction": "any", - "service": "hobby" + "service": "standard" } ], "availability": "available", @@ -5626,9 +5626,9 @@ "type": "freeform" }, "parent_services": [ - "standard", + "hobby", "growth", - "hobby" + "standard" ], "type": "paid" } @@ -5645,9 +5645,9 @@ "service_id": "api", "updateable_to": [ "growth", - "standard", - "free", "hobby", + "free", + "standard", "api" ] }, @@ -5697,7 +5697,7 @@ }, { "direction": "any", - "service": "free" + "service": "hobby" }, { "direction": "any", @@ -5705,7 +5705,7 @@ }, { "direction": "any", - "service": "hobby" + "service": "free" } ], "availability": "available", @@ -5775,9 +5775,9 @@ "service_id": "standard", "updateable_to": [ "growth", - "free", - "api", "hobby", + "api", + "free", "standard" ] }, @@ -5789,7 +5789,7 @@ }, { "direction": "any", - "service": "standard" + "service": "api" }, { "direction": "any", @@ -5797,7 +5797,7 @@ }, { "direction": "any", - "service": "api" + "service": "standard" } ], "availability": "available", @@ -5867,9 +5867,9 @@ "service_id": "hobby", "updateable_to": [ "growth", - "standard", - "free", "api", + "free", + "standard", "hobby" ] }, @@ -5877,19 +5877,19 @@ "allowed_updates": [ { "direction": "any", - "service": "standard" + "service": "hobby" }, { "direction": "any", - "service": "free" + "service": "api" }, { "direction": "any", - "service": "api" + "service": "free" }, { "direction": "any", - "service": "hobby" + "service": "standard" } ], "availability": "available", @@ -5958,10 +5958,10 @@ "scope": "project", "service_id": "growth", "updateable_to": [ - "standard", - "free", - "api", "hobby", + "api", + "free", + "standard", "growth" ] }, @@ -6039,7 +6039,7 @@ "configuration_schema": {}, "constraints": [], "created": null, - "description": "Pay-as-you-go — per-token usage-based pricing across 400+ models with no minimum commitment.", + "description": "Pay-as-you-go — per-token usage-based pricing across 500+ models with no minimum commitment.", "development": false, "group": null, "id": "prvsvc_61UVxnfMkcvekEo625Koq", @@ -6083,7 +6083,7 @@ "configuration_schema": {}, "constraints": [], "created": null, - "description": "OpenRouter — browse, compare, and use 400+ AI models from leading providers through a single API. Text, image, video, and audio generation.", + "description": "OpenRouter — browse, compare, and use 500+ AI models from leading providers through a single API. Text, image, video, and audio generation.", "development": false, "group": null, "id": "prvsvc_61UVxnfns4DFOZPiC56hE", @@ -6161,10 +6161,10 @@ "paid": null, "parent_services": [ "b2b-essentials", - "b2b-professional", - "b2c-professional", + "b2c-essentials", "free", - "b2c-essentials" + "b2c-professional", + "b2b-professional" ], "type": "free" } @@ -6870,8 +6870,8 @@ "type": "freeform" }, "parent_services": [ - "pro", - "team" + "team", + "pro" ], "type": "paid" } @@ -6894,11 +6894,11 @@ "allowed_updates": [ { "direction": "any", - "service": "pro" + "service": "team" }, { "direction": "any", - "service": "team" + "service": "pro" } ], "availability": "available", @@ -6930,8 +6930,8 @@ "scope": "project", "service_id": "free", "updateable_to": [ - "pro", "team", + "pro", "free" ] }, @@ -6939,11 +6939,11 @@ "allowed_updates": [ { "direction": "any", - "service": "hobby" + "service": "pro" }, { "direction": "any", - "service": "pro" + "service": "hobby" } ], "availability": "available", @@ -6973,8 +6973,8 @@ "scope": "project", "service_id": "hobby", "updateable_to": [ - "hobby", - "pro" + "pro", + "hobby" ] }, { @@ -7018,8 +7018,8 @@ "is_default": null, "paid": null, "parent_services": [ - "hobby", - "pro" + "pro", + "hobby" ], "type": "free" } @@ -7060,16 +7060,16 @@ "type": "object" }, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" - } + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" + } ], "created": null, "description": "Free — access the Hugging Face platform at zero cost, no credit card required. Includes a free usage quota. Optional pay-as-you-go auto top-up can be enabled to bill usage above quota via a shared payment token. See https://huggingface.co/pricing", @@ -7100,11 +7100,11 @@ "allowed_updates": [ { "direction": "any", - "service": "launch" + "service": "free" }, { "direction": "any", - "service": "free" + "service": "launch" } ], "availability": "available", @@ -7134,19 +7134,19 @@ "scope": "project", "service_id": "free", "updateable_to": [ - "launch", - "free" + "free", + "launch" ] }, { "allowed_updates": [ { "direction": "any", - "service": "launch" + "service": "free" }, { "direction": "any", - "service": "free" + "service": "launch" } ], "availability": "available", @@ -7188,8 +7188,8 @@ "scope": "project", "service_id": "launch", "updateable_to": [ - "launch", - "free" + "free", + "launch" ] }, { @@ -7587,9 +7587,9 @@ "paid": null, "parent_services": [ "core", - "enterprise", + "free", "scale", - "free" + "enterprise" ], "type": "free" } @@ -7599,7 +7599,19 @@ "paid_pricing": [], "type": "component" }, - "provider_configuration_schema": null, + "provider_configuration_schema": { + "additionalProperties": false, + "properties": { + "account_name": { + "description": "Name of your Privy organization account", + "type": "string" + } + }, + "required": [ + "account_name" + ], + "type": "object" + }, "provider_id": "prvdr_61UWLVjDZyzuP1BCY5EAi", "provider_name": "Privy", "scope": "project", @@ -7833,7 +7845,7 @@ "allowed_updates": [ { "direction": "any", - "service": "free" + "service": "plus-v3-50k-mtu-monthly" }, { "direction": "any", @@ -7841,7 +7853,7 @@ }, { "direction": "any", - "service": "plus-v3-50k-mtu-monthly" + "service": "free" } ], "availability": "available", @@ -7905,9 +7917,9 @@ "scope": "project", "service_id": "plus-v3-25k-mtu-monthly", "updateable_to": [ - "free", - "plus-v3-10k-mtu-monthly", "plus-v3-50k-mtu-monthly", + "plus-v3-10k-mtu-monthly", + "free", "plus-v3-25k-mtu-monthly" ] }, @@ -7915,15 +7927,15 @@ "allowed_updates": [ { "direction": "any", - "service": "free" + "service": "plus-v3-25k-mtu-monthly" }, { "direction": "any", - "service": "plus-v3-25k-mtu-monthly" + "service": "plus-v3-50k-mtu-monthly" }, { "direction": "any", - "service": "plus-v3-50k-mtu-monthly" + "service": "free" } ], "availability": "available", @@ -7987,9 +7999,9 @@ "scope": "project", "service_id": "plus-v3-10k-mtu-monthly", "updateable_to": [ - "free", "plus-v3-25k-mtu-monthly", "plus-v3-50k-mtu-monthly", + "free", "plus-v3-10k-mtu-monthly" ] }, @@ -8263,15 +8275,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -8329,15 +8341,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -8382,11 +8394,11 @@ "allowed_updates": [ { "direction": "any", - "service": "business" + "service": "team" }, { "direction": "any", - "service": "team" + "service": "business" } ], "availability": "available", @@ -8395,15 +8407,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -8427,8 +8439,8 @@ "scope": "account", "service_id": "developer", "updateable_to": [ - "business", "team", + "business", "developer" ] }, @@ -8665,9 +8677,9 @@ }, "parent_services": [ "top-up-2000", - "top-up-1000", + "top-up-0500", "top-up-0025", - "top-up-0500" + "top-up-1000" ], "type": "paid" } @@ -8690,11 +8702,11 @@ "allowed_updates": [ { "direction": "any", - "service": "top-up-1000" + "service": "top-up-0025" }, { "direction": "any", - "service": "top-up-0025" + "service": "top-up-1000" }, { "direction": "any", @@ -8742,8 +8754,8 @@ "scope": "project", "service_id": "top-up-2000", "updateable_to": [ - "top-up-1000", "top-up-0025", + "top-up-1000", "top-up-0500", "top-up-2000" ] @@ -8752,11 +8764,11 @@ "allowed_updates": [ { "direction": "any", - "service": "top-up-1000" + "service": "top-up-2000" }, { "direction": "any", - "service": "top-up-2000" + "service": "top-up-1000" }, { "direction": "any", @@ -8804,8 +8816,8 @@ "scope": "project", "service_id": "top-up-0025", "updateable_to": [ - "top-up-1000", "top-up-2000", + "top-up-1000", "top-up-0500", "top-up-0025" ] @@ -8814,11 +8826,11 @@ "allowed_updates": [ { "direction": "any", - "service": "top-up-0025" + "service": "top-up-2000" }, { "direction": "any", - "service": "top-up-2000" + "service": "top-up-0025" }, { "direction": "any", @@ -8866,8 +8878,8 @@ "scope": "project", "service_id": "top-up-1000", "updateable_to": [ - "top-up-0025", "top-up-2000", + "top-up-0025", "top-up-0500", "top-up-1000" ] @@ -8876,11 +8888,11 @@ "allowed_updates": [ { "direction": "any", - "service": "top-up-1000" + "service": "top-up-2000" }, { "direction": "any", - "service": "top-up-2000" + "service": "top-up-1000" }, { "direction": "any", @@ -8928,8 +8940,8 @@ "scope": "project", "service_id": "top-up-0500", "updateable_to": [ - "top-up-1000", "top-up-2000", + "top-up-1000", "top-up-0025", "top-up-0500" ] @@ -9374,7 +9386,19 @@ ], "type": "paid" }, - "provider_configuration_schema": null, + "provider_configuration_schema": { + "additionalProperties": false, + "properties": { + "account_name": { + "description": "Name of your Privy organization account", + "type": "string" + } + }, + "required": [ + "account_name" + ], + "type": "object" + }, "provider_id": "prvdr_61UWLVjDZyzuP1BCY5EAi", "provider_name": "Privy", "scope": "account", @@ -9533,19 +9557,19 @@ "allowed_updates": [ { "direction": "any", - "service": "free" + "service": "b2b-essentials" }, { "direction": "any", - "service": "b2b-professional" + "service": "b2c-professional" }, { "direction": "any", - "service": "b2c-professional" + "service": "b2b-professional" }, { "direction": "any", - "service": "b2b-essentials" + "service": "free" } ], "availability": "available", @@ -9615,10 +9639,10 @@ "scope": "project", "service_id": "b2c-essentials", "updateable_to": [ - "free", - "b2b-professional", - "b2c-professional", "b2b-essentials", + "b2c-professional", + "b2b-professional", + "free", "b2c-essentials" ] }, @@ -9630,7 +9654,7 @@ }, { "direction": "any", - "service": "b2b-professional" + "service": "b2b-essentials" }, { "direction": "any", @@ -9638,7 +9662,7 @@ }, { "direction": "any", - "service": "b2b-essentials" + "service": "b2b-professional" } ], "availability": "available", @@ -9697,9 +9721,9 @@ "service_id": "free", "updateable_to": [ "b2c-essentials", - "b2b-professional", - "b2c-professional", "b2b-essentials", + "b2c-professional", + "b2b-professional", "free" ] }, @@ -9711,7 +9735,7 @@ }, { "direction": "any", - "service": "free" + "service": "b2b-essentials" }, { "direction": "any", @@ -9719,7 +9743,7 @@ }, { "direction": "any", - "service": "b2b-essentials" + "service": "free" } ], "availability": "available", @@ -9790,9 +9814,9 @@ "service_id": "b2c-professional", "updateable_to": [ "b2c-essentials", - "free", - "b2b-professional", "b2b-essentials", + "b2b-professional", + "free", "b2c-professional" ] }, @@ -9804,7 +9828,7 @@ }, { "direction": "any", - "service": "free" + "service": "b2c-professional" }, { "direction": "any", @@ -9812,7 +9836,7 @@ }, { "direction": "any", - "service": "b2c-professional" + "service": "free" } ], "availability": "available", @@ -9883,9 +9907,9 @@ "service_id": "b2b-essentials", "updateable_to": [ "b2c-essentials", - "free", - "b2b-professional", "b2c-professional", + "b2b-professional", + "free", "b2b-essentials" ] }, @@ -9897,7 +9921,7 @@ }, { "direction": "any", - "service": "free" + "service": "b2b-essentials" }, { "direction": "any", @@ -9905,7 +9929,7 @@ }, { "direction": "any", - "service": "b2b-essentials" + "service": "free" } ], "availability": "available", @@ -9976,9 +10000,9 @@ "service_id": "b2b-professional", "updateable_to": [ "b2c-essentials", - "free", - "b2c-professional", "b2b-essentials", + "b2c-professional", + "free", "b2b-professional" ] }, @@ -10006,9 +10030,9 @@ "is_default": null, "paid": null, "parent_services": [ - "developer", + "free", "startup", - "free" + "developer" ], "type": "free" } @@ -10136,7 +10160,19 @@ ], "type": "paid" }, - "provider_configuration_schema": null, + "provider_configuration_schema": { + "additionalProperties": false, + "properties": { + "account_name": { + "description": "Name of your Privy organization account", + "type": "string" + } + }, + "required": [ + "account_name" + ], + "type": "object" + }, "provider_id": "prvdr_61UWLVjDZyzuP1BCY5EAi", "provider_name": "Privy", "scope": "account", @@ -10179,7 +10215,19 @@ "paid_pricing": [], "type": "free" }, - "provider_configuration_schema": null, + "provider_configuration_schema": { + "additionalProperties": false, + "properties": { + "account_name": { + "description": "Name of your Privy organization account", + "type": "string" + } + }, + "required": [ + "account_name" + ], + "type": "object" + }, "provider_id": "prvdr_61UWLVjDZyzuP1BCY5EAi", "provider_name": "Privy", "scope": "account", @@ -10231,7 +10279,19 @@ ], "type": "paid" }, - "provider_configuration_schema": null, + "provider_configuration_schema": { + "additionalProperties": false, + "properties": { + "account_name": { + "description": "Name of your Privy organization account", + "type": "string" + } + }, + "required": [ + "account_name" + ], + "type": "object" + }, "provider_id": "prvdr_61UWLVjDZyzuP1BCY5EAi", "provider_name": "Privy", "scope": "account", @@ -10446,9 +10506,9 @@ "is_default": null, "paid": null, "parent_services": [ - "developer", + "free", "startup", - "free" + "developer" ], "type": "free" } @@ -10489,15 +10549,15 @@ "type": "object" }, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -10747,10 +10807,6 @@ }, { "allowed_updates": [ - { - "direction": "up", - "service": "business" - }, { "direction": "any", "service": "starter" @@ -10762,6 +10818,10 @@ { "direction": "up", "service": "pro" + }, + { + "direction": "up", + "service": "business" } ], "availability": "available", @@ -10803,18 +10863,14 @@ "scope": "account", "service_id": "starter", "updateable_to": [ - "business", "starter", "free", - "pro" + "pro", + "business" ] }, { "allowed_updates": [ - { - "direction": "up", - "service": "business" - }, { "direction": "down", "service": "free" @@ -10826,6 +10882,10 @@ { "direction": "down", "service": "starter" + }, + { + "direction": "up", + "service": "business" } ], "availability": "available", @@ -10867,14 +10927,18 @@ "scope": "account", "service_id": "pro", "updateable_to": [ - "business", "free", "pro", - "starter" + "starter", + "business" ] }, { "allowed_updates": [ + { + "direction": "any", + "service": "business" + }, { "direction": "down", "service": "free" @@ -10886,10 +10950,6 @@ { "direction": "down", "service": "starter" - }, - { - "direction": "any", - "service": "business" } ], "availability": "available", @@ -10931,29 +10991,29 @@ "scope": "account", "service_id": "business", "updateable_to": [ + "business", "free", "pro", - "starter", - "business" + "starter" ] }, { "allowed_updates": [ { "direction": "up", - "service": "business" + "service": "pro" }, { - "direction": "up", - "service": "starter" + "direction": "any", + "service": "free" }, { "direction": "up", - "service": "pro" + "service": "starter" }, { - "direction": "any", - "service": "free" + "direction": "up", + "service": "business" } ], "availability": "available", @@ -10983,10 +11043,10 @@ "scope": "account", "service_id": "free", "updateable_to": [ - "business", - "starter", "pro", - "free" + "free", + "starter", + "business" ] }, { @@ -11035,9 +11095,9 @@ "paid": null, "parent_services": [ "business", - "free", + "starter", "pro", - "starter" + "free" ], "type": "free" } @@ -11380,8 +11440,8 @@ "type": "freeform" }, "parent_services": [ - "growth", - "starter" + "starter", + "growth" ], "type": "paid" } @@ -11482,8 +11542,8 @@ "type": "freeform" }, "parent_services": [ - "growth", - "starter" + "starter", + "growth" ], "type": "paid" } @@ -11565,8 +11625,8 @@ "type": "freeform" }, "parent_services": [ - "growth", - "starter" + "starter", + "growth" ], "type": "paid" } @@ -11957,15 +12017,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -12010,11 +12070,11 @@ "allowed_updates": [ { "direction": "down", - "service": "hobbyist" + "service": "developer" }, { "direction": "down", - "service": "developer" + "service": "hobbyist" } ], "availability": "available", @@ -12023,15 +12083,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -12067,8 +12127,8 @@ "scope": "account", "service_id": "startup", "updateable_to": [ - "hobbyist", "developer", + "hobbyist", "startup" ] }, @@ -12089,15 +12149,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -12172,9 +12232,9 @@ "is_default": null, "paid": null, "parent_services": [ - "developer", + "hobbyist", "startup", - "hobbyist" + "developer" ], "type": "free" } @@ -12299,15 +12359,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -12360,15 +12420,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -12526,7 +12586,7 @@ ], "type": "paid" }, - "provider_configuration_schema": null, + "provider_configuration_schema": {}, "provider_id": "prvdr_61UnkAFYNcaD50TOY54Q4", "provider_name": "HeyGen", "scope": "project", @@ -12633,24 +12693,24 @@ { "allowed_updates": [ { - "direction": "down", - "service": "tier-1" + "direction": "up", + "service": "tier-5" }, { - "direction": "down", - "service": "tier-2" + "direction": "up", + "service": "tier-6" }, { - "direction": "up", - "service": "tier-4" + "direction": "down", + "service": "tier-2" }, { - "direction": "up", - "service": "tier-5" + "direction": "down", + "service": "tier-1" }, { "direction": "up", - "service": "tier-6" + "service": "tier-4" } ], "availability": "available", @@ -12698,11 +12758,11 @@ "scope": "account", "service_id": "tier-3", "updateable_to": [ - "tier-1", - "tier-2", - "tier-4", "tier-5", "tier-6", + "tier-2", + "tier-1", + "tier-4", "tier-3" ] }, @@ -12710,11 +12770,11 @@ "allowed_updates": [ { "direction": "down", - "service": "tier-1" + "service": "tier-5" }, { "direction": "down", - "service": "tier-2" + "service": "tier-4" }, { "direction": "down", @@ -12722,11 +12782,11 @@ }, { "direction": "down", - "service": "tier-4" + "service": "tier-2" }, { "direction": "down", - "service": "tier-5" + "service": "tier-1" } ], "availability": "available", @@ -12774,35 +12834,35 @@ "scope": "account", "service_id": "tier-6", "updateable_to": [ - "tier-1", - "tier-2", - "tier-3", - "tier-4", "tier-5", + "tier-4", + "tier-3", + "tier-2", + "tier-1", "tier-6" ] }, { "allowed_updates": [ { - "direction": "down", - "service": "tier-1" + "direction": "up", + "service": "tier-5" }, { "direction": "up", - "service": "tier-3" + "service": "tier-6" }, { - "direction": "up", - "service": "tier-4" + "direction": "down", + "service": "tier-1" }, { "direction": "up", - "service": "tier-5" + "service": "tier-4" }, { "direction": "up", - "service": "tier-6" + "service": "tier-3" } ], "availability": "available", @@ -12850,23 +12910,23 @@ "scope": "account", "service_id": "tier-2", "updateable_to": [ - "tier-1", - "tier-3", - "tier-4", "tier-5", "tier-6", + "tier-1", + "tier-4", + "tier-3", "tier-2" ] }, { "allowed_updates": [ { - "direction": "down", - "service": "tier-1" + "direction": "up", + "service": "tier-6" }, { "direction": "down", - "service": "tier-2" + "service": "tier-4" }, { "direction": "down", @@ -12874,11 +12934,11 @@ }, { "direction": "down", - "service": "tier-4" + "service": "tier-2" }, { - "direction": "up", - "service": "tier-6" + "direction": "down", + "service": "tier-1" } ], "availability": "available", @@ -12926,35 +12986,35 @@ "scope": "account", "service_id": "tier-5", "updateable_to": [ - "tier-1", - "tier-2", - "tier-3", - "tier-4", "tier-6", + "tier-4", + "tier-3", + "tier-2", + "tier-1", "tier-5" ] }, { "allowed_updates": [ { - "direction": "down", - "service": "tier-1" + "direction": "up", + "service": "tier-5" }, { - "direction": "down", - "service": "tier-2" + "direction": "up", + "service": "tier-6" }, { "direction": "down", "service": "tier-3" }, { - "direction": "up", - "service": "tier-5" + "direction": "down", + "service": "tier-2" }, { - "direction": "up", - "service": "tier-6" + "direction": "down", + "service": "tier-1" } ], "availability": "available", @@ -13002,11 +13062,11 @@ "scope": "account", "service_id": "tier-4", "updateable_to": [ - "tier-1", - "tier-2", - "tier-3", "tier-5", "tier-6", + "tier-3", + "tier-2", + "tier-1", "tier-4" ] }, @@ -13014,11 +13074,11 @@ "allowed_updates": [ { "direction": "up", - "service": "tier-2" + "service": "tier-6" }, { "direction": "up", - "service": "tier-3" + "service": "tier-5" }, { "direction": "up", @@ -13026,11 +13086,11 @@ }, { "direction": "up", - "service": "tier-5" + "service": "tier-3" }, { "direction": "up", - "service": "tier-6" + "service": "tier-2" } ], "availability": "available", @@ -13078,11 +13138,11 @@ "scope": "account", "service_id": "tier-1", "updateable_to": [ - "tier-2", - "tier-3", - "tier-4", - "tier-5", "tier-6", + "tier-5", + "tier-4", + "tier-3", + "tier-2", "tier-1" ] }, @@ -13239,11 +13299,11 @@ "allowed_updates": [ { "direction": "down", - "service": "basic" + "service": "scale" }, { "direction": "down", - "service": "scale" + "service": "basic" } ], "availability": "available", @@ -13291,8 +13351,8 @@ "scope": "account", "service_id": "enterprise", "updateable_to": [ - "basic", "scale", + "basic", "enterprise" ] }, @@ -13812,15 +13872,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -13862,15 +13922,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -14116,15 +14176,15 @@ "type": "object" }, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -14194,8 +14254,8 @@ "is_default": null, "paid": null, "parent_services": [ - "developer-plan", - "startup-plan" + "startup-plan", + "developer-plan" ], "type": "free" } @@ -14233,15 +14293,15 @@ "type": "object" }, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -14298,15 +14358,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -14354,11 +14414,11 @@ "allowed_updates": [ { "direction": "up", - "service": "pro" + "service": "standard" }, { "direction": "up", - "service": "standard" + "service": "pro" } ], "availability": "available", @@ -14367,19 +14427,19 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, - "description": "Hobby plan — 500 message credits/month, 1 AI agent, all AI models", + "description": "Hobby plan — 700 message credits/month, 1 AI agent, all AI models", "development": false, "group": null, "id": "prvsvc_61V0QJisPzBp7q5cM5DTM", @@ -14414,8 +14474,8 @@ "scope": "account", "service_id": "hobby", "updateable_to": [ - "pro", "standard", + "pro", "hobby" ] }, @@ -14454,9 +14514,9 @@ "is_default": null, "paid": null, "parent_services": [ - "standard", + "hobby", "pro", - "hobby" + "standard" ], "type": "free" }, @@ -14505,15 +14565,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -14593,15 +14653,15 @@ "type": "object" }, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -14679,15 +14739,15 @@ "type": "object" }, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -14759,10 +14819,10 @@ "is_default": null, "paid": null, "parent_services": [ - "enterprise-monthly", - "beginner-monthly", + "pro-monthly", "free", - "pro-monthly" + "beginner-monthly", + "enterprise-monthly" ], "type": "free" } @@ -14789,11 +14849,11 @@ }, { "direction": "any", - "service": "enterprise-monthly" + "service": "beginner-monthly" }, { "direction": "any", - "service": "beginner-monthly" + "service": "enterprise-monthly" } ], "availability": "available", @@ -14824,8 +14884,8 @@ "service_id": "free", "updateable_to": [ "pro-monthly", - "enterprise-monthly", "beginner-monthly", + "enterprise-monthly", "free" ] }, @@ -14833,15 +14893,15 @@ "allowed_updates": [ { "direction": "any", - "service": "free" + "service": "beginner-monthly" }, { "direction": "any", - "service": "enterprise-monthly" + "service": "free" }, { "direction": "any", - "service": "beginner-monthly" + "service": "enterprise-monthly" } ], "availability": "available", @@ -14883,9 +14943,9 @@ "scope": "account", "service_id": "pro-monthly", "updateable_to": [ + "beginner-monthly", "free", "enterprise-monthly", - "beginner-monthly", "pro-monthly" ] }, @@ -14897,11 +14957,11 @@ }, { "direction": "any", - "service": "enterprise-monthly" + "service": "free" }, { "direction": "any", - "service": "free" + "service": "enterprise-monthly" } ], "availability": "available", @@ -14944,8 +15004,8 @@ "service_id": "beginner-monthly", "updateable_to": [ "pro-monthly", - "enterprise-monthly", "free", + "enterprise-monthly", "beginner-monthly" ] }, @@ -14953,15 +15013,15 @@ "allowed_updates": [ { "direction": "any", - "service": "pro-monthly" + "service": "beginner-monthly" }, { "direction": "any", - "service": "free" + "service": "pro-monthly" }, { "direction": "any", - "service": "beginner-monthly" + "service": "free" } ], "availability": "available", @@ -15003,9 +15063,9 @@ "scope": "account", "service_id": "enterprise-monthly", "updateable_to": [ + "beginner-monthly", "pro-monthly", "free", - "beginner-monthly", "enterprise-monthly" ] }, @@ -15299,15 +15359,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -15545,8 +15605,8 @@ "is_default": true, "paid": null, "parent_services": [ - "schematic-growth", - "schematic-free" + "schematic-free", + "schematic-growth" ], "type": "free" } @@ -15616,15 +15676,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -15680,15 +15740,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -15805,15 +15865,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -15933,15 +15993,15 @@ ], "configuration_schema": {}, "constraints": [ - { - "mutual_exclusion_allowed_updates": true, - "type": "mutual_exclusion_allowed_updates" - }, { "count": { "at_most": 1 }, "type": "count" + }, + { + "mutual_exclusion_allowed_updates": true, + "type": "mutual_exclusion_allowed_updates" } ], "created": null, @@ -15990,6 +16050,109 @@ "updateable_to": [ "pay-as-you-go" ] + }, + { + "allowed_updates": [], + "availability": "available", + "categories": [ + "ecommerce" + ], + "configuration_schema": { + "additionalProperties": false, + "optional": [ + "store_name", + "plan" + ], + "properties": { + "plan": { + "description": "Selected Shopify catalog plan. Defaults to \"trial\" when omitted. The plan can be changed after provisioning by updating this field. \"trial\" is creation-only: a store can move from \"trial\" to a paid plan, never back.", + "enum": [ + "trial", + "basic", + "grow", + "advanced" + ], + "type": "string" + }, + "store_name": { + "description": "Add a store name. You can always change this later.", + "maxLength": 50, + "minLength": 1, + "title": "Store name", + "type": "string" + } + }, + "type": "object" + }, + "constraints": [], + "created": null, + "description": "Launch your own online store with Shopify. Build your store for free for 4 months. Only pay when you're ready to sell.", + "development": false, + "group": null, + "id": "prvsvc_61V8fBnYv2e6a7yWL5FFY", + "kind": "deployable", + "livemode": true, + "llm_context": "https://setup.shopify.com/provider/llm_context.md", + "object": "v2.provisioning.provider_service_detail", + "pricing": { + "component": null, + "paid": { + "description": "Start building for free for 4 months. Subscribe when you are ready to sell.", + "freeform": null, + "type": "free" + }, + "paid_pricing": [ + { + "configuration": { + "plan": "trial" + }, + "description": "Start building for free for 4 months. Subscribe when you are ready to sell.", + "freeform": null, + "is_default": true, + "type": "free" + }, + { + "configuration": { + "plan": "basic" + }, + "description": "For solo entrepreneurs. Owner account only. 2.9% + 30¢ online. 2% third-party fee.", + "freeform": "$1/mo for 3 months (new stores only), then $39/mo. Renews monthly.", + "is_default": null, + "type": "freeform" + }, + { + "configuration": { + "plan": "grow" + }, + "description": "For small teams. Owner account and 5 staff. 2.7% + 30¢ online. 1% third-party fee.", + "freeform": "$1/mo for 3 months (new stores only), then $105/mo. Renews monthly.", + "is_default": null, + "type": "freeform" + }, + { + "configuration": { + "plan": "advanced" + }, + "description": "For growing global businesses. Owner account and 15 staff. 2.5% + 30¢ online. 0.6% third-party fee.", + "freeform": "$1/mo for 3 months (new stores only), then $399/mo. Renews monthly.", + "is_default": null, + "type": "freeform" + } + ], + "type": "paid" + }, + "provider_configuration_schema": { + "additionalProperties": false, + "properties": {}, + "type": "object" + }, + "provider_id": "prvdr_61V3J0dhwegIR1fhV5Jx2", + "provider_name": "Shopify", + "scope": "project", + "service_id": "store", + "updateable_to": [ + "store" + ] } ], "source": "cache" diff --git a/crates/stackless-stripe-projects/tests/fixtures/command-surface.txt b/crates/stackless-stripe-projects/tests/fixtures/command-surface.txt index cd89e8f..129487f 100644 --- a/crates/stackless-stripe-projects/tests/fixtures/command-surface.txt +++ b/crates/stackless-stripe-projects/tests/fixtures/command-surface.txt @@ -1,10 +1,10 @@ # stripe projects command surface -# plugin-version: 0.30.0 +# plugin-version: 0.33.0 # DO NOT EDIT — regenerated by CI (mise run stripe-refresh) ===== stripe projects --help ===== ╭─────────────────────────────────────────────────────╮ -│ ⡜ Stripe Projects (v0.30.0) │ +│ ⡜ Stripe Projects (v0.33.0) │ │ │ │ Provision third-party services, manage credentials, │ │ and pull environment variables. │ @@ -1346,9 +1346,8 @@ Examples: 50 --provider neon $50/month ===== stripe projects spend --help ===== -stripe projects spend [provider] - -View charges on your account +stripe projects spend [provider] [--duration ] [--since ] [--until +] Positionals: provider Show charges for a specific provider [string] @@ -1371,6 +1370,20 @@ Options: --stream Enable streaming output animations [boolean] --debug Enable debug logging for Stripe API requests [boolean] + --duration Time range preset (e.g. 3m, 6m, 90d, all) [string] + --since Start date in ISO 8601 format (YYYY-MM-DD), + inclusive, rounded to start of month [string] + --until End date in ISO 8601 format (YYYY-MM-DD), + inclusive, rounded to end of month [string] + +Examples: + stripe projects spend Show charges (defaults to last 6 + months) + stripe projects spend --duration 3m Show last 3 months + stripe projects spend --duration all Show all-time charges + stripe projects spend --since 2026-01-01 Show charges for a specific date + --until 2026-03-31 range + stripe projects spend vercel Show charges for a specific provider ===== stripe projects feedback --help ===== stripe projects feedback diff --git a/crates/stackless-stripe-projects/tests/fixtures/plugin-version.txt b/crates/stackless-stripe-projects/tests/fixtures/plugin-version.txt index c25c8e5..be386c9 100644 --- a/crates/stackless-stripe-projects/tests/fixtures/plugin-version.txt +++ b/crates/stackless-stripe-projects/tests/fixtures/plugin-version.txt @@ -1 +1 @@ -0.30.0 +0.33.0 diff --git a/crates/stackless/src/authoring.rs b/crates/stackless/src/authoring.rs index 098ea74..2289680 100644 --- a/crates/stackless/src/authoring.rs +++ b/crates/stackless/src/authoring.rs @@ -8,7 +8,7 @@ use stackless_core::types::DnsName; use crate::error::Error; /// The Stripe Projects plugin version stackless is tested against. -pub const STRIPE_PROJECTS_PINNED: &str = "0.30.0"; +pub const STRIPE_PROJECTS_PINNED: &str = "0.33.0"; pub fn default_output_path() -> PathBuf { PathBuf::from("stackless.toml") diff --git a/scripts/generate_catalog_integrations.py b/scripts/generate_catalog_integrations.py index 86f7054..dfb9dea 100644 --- a/scripts/generate_catalog_integrations.py +++ b/scripts/generate_catalog_integrations.py @@ -109,6 +109,7 @@ "metronome/sandbox": "metronome", "supermemory/memory": "supermemory", "postalform/mail": "postalform", + "shopify/store": "shopify", "wix/headless": "wix", "base44_projects/app": "base44", "wordpress.com/site": "wordpress-com",