@sponson/adapters 0.0.0-stage → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +4 -2
  3. package/dist/algolia.d.ts +6 -0
  4. package/dist/algolia.js +151 -0
  5. package/dist/algolia.js.map +1 -0
  6. package/dist/clerk.d.ts +8 -0
  7. package/dist/clerk.js +107 -0
  8. package/dist/clerk.js.map +1 -0
  9. package/dist/cloudflare.d.ts +25 -0
  10. package/dist/cloudflare.js +310 -0
  11. package/dist/cloudflare.js.map +1 -0
  12. package/dist/common.d.ts +73 -0
  13. package/dist/common.js +140 -0
  14. package/dist/common.js.map +1 -0
  15. package/dist/digest.d.ts +46 -0
  16. package/dist/digest.js +129 -0
  17. package/dist/digest.js.map +1 -0
  18. package/dist/digitalocean.d.ts +5 -0
  19. package/dist/digitalocean.js +243 -0
  20. package/dist/digitalocean.js.map +1 -0
  21. package/dist/http-adapter-action.d.ts +63 -0
  22. package/dist/http-adapter-action.js +369 -0
  23. package/dist/http-adapter-action.js.map +1 -0
  24. package/dist/http-adapter-auth.d.ts +88 -0
  25. package/dist/http-adapter-auth.js +437 -0
  26. package/dist/http-adapter-auth.js.map +1 -0
  27. package/dist/http-adapter-client.d.ts +51 -0
  28. package/dist/http-adapter-client.js +156 -0
  29. package/dist/http-adapter-client.js.map +1 -0
  30. package/dist/http-adapter-graphql.d.ts +39 -0
  31. package/dist/http-adapter-graphql.js +157 -0
  32. package/dist/http-adapter-graphql.js.map +1 -0
  33. package/dist/http-adapter-list.d.ts +33 -0
  34. package/dist/http-adapter-list.js +421 -0
  35. package/dist/http-adapter-list.js.map +1 -0
  36. package/dist/http-adapter-resource.d.ts +29 -0
  37. package/dist/http-adapter-resource.js +462 -0
  38. package/dist/http-adapter-resource.js.map +1 -0
  39. package/dist/http-adapter-spec.d.ts +216 -0
  40. package/dist/http-adapter-spec.js +660 -0
  41. package/dist/http-adapter-spec.js.map +1 -0
  42. package/dist/http-adapter.d.ts +16 -0
  43. package/dist/http-adapter.js +28 -0
  44. package/dist/http-adapter.js.map +1 -0
  45. package/dist/http.d.ts +179 -0
  46. package/dist/http.js +352 -0
  47. package/dist/http.js.map +1 -0
  48. package/dist/index.d.ts +28 -0
  49. package/dist/index.js +45 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/launchdarkly.d.ts +15 -0
  52. package/dist/launchdarkly.js +337 -0
  53. package/dist/launchdarkly.js.map +1 -0
  54. package/dist/manual.d.ts +13 -0
  55. package/dist/manual.js +201 -0
  56. package/dist/manual.js.map +1 -0
  57. package/dist/neon.d.ts +8 -0
  58. package/dist/neon.js +167 -0
  59. package/dist/neon.js.map +1 -0
  60. package/dist/netlify.d.ts +18 -0
  61. package/dist/netlify.js +436 -0
  62. package/dist/netlify.js.map +1 -0
  63. package/dist/planetscale.d.ts +10 -0
  64. package/dist/planetscale.js +311 -0
  65. package/dist/planetscale.js.map +1 -0
  66. package/dist/providers.d.ts +26 -0
  67. package/dist/providers.js +36 -0
  68. package/dist/providers.js.map +1 -0
  69. package/dist/recipes.d.ts +167 -0
  70. package/dist/recipes.js +246 -0
  71. package/dist/recipes.js.map +1 -0
  72. package/dist/secrets.d.ts +72 -0
  73. package/dist/secrets.js +476 -0
  74. package/dist/secrets.js.map +1 -0
  75. package/dist/sigv4.d.ts +56 -0
  76. package/dist/sigv4.js +118 -0
  77. package/dist/sigv4.js.map +1 -0
  78. package/dist/supabase.d.ts +9 -0
  79. package/dist/supabase.js +385 -0
  80. package/dist/supabase.js.map +1 -0
  81. package/dist/vercel.d.ts +8 -0
  82. package/dist/vercel.js +475 -0
  83. package/dist/vercel.js.map +1 -0
  84. package/dist/xata.d.ts +8 -0
  85. package/dist/xata.js +142 -0
  86. package/dist/xata.js.map +1 -0
  87. package/dist/xml.d.ts +25 -0
  88. package/dist/xml.js +211 -0
  89. package/dist/xml.js.map +1 -0
  90. package/package.json +58 -3
  91. package/recipes/amplify.yaml +147 -0
  92. package/recipes/auth0.yaml +178 -0
  93. package/recipes/azure_swa.yaml +116 -0
  94. package/recipes/cloud_run.yaml +97 -0
  95. package/recipes/cloudflare.yaml +108 -0
  96. package/recipes/cockroachdb.yaml +132 -0
  97. package/recipes/configcat.yaml +123 -0
  98. package/recipes/datadog.yaml +136 -0
  99. package/recipes/firebase.yaml +84 -0
  100. package/recipes/flagsmith.yaml +98 -0
  101. package/recipes/fly.yaml +97 -0
  102. package/recipes/fly_machines.yaml +67 -0
  103. package/recipes/gcs.yaml +102 -0
  104. package/recipes/github.yaml +123 -0
  105. package/recipes/growthbook.yaml +124 -0
  106. package/recipes/heroku.yaml +98 -0
  107. package/recipes/honeycomb.yaml +89 -0
  108. package/recipes/inngest.yaml +97 -0
  109. package/recipes/kinde.yaml +120 -0
  110. package/recipes/mongodb_atlas.yaml +119 -0
  111. package/recipes/okta.yaml +177 -0
  112. package/recipes/posthog.yaml +127 -0
  113. package/recipes/postmark.yaml +105 -0
  114. package/recipes/postmark_server.yaml +93 -0
  115. package/recipes/prisma.yaml +150 -0
  116. package/recipes/qstash.yaml +134 -0
  117. package/recipes/railway.yaml +130 -0
  118. package/recipes/render.yaml +103 -0
  119. package/recipes/resend.yaml +130 -0
  120. package/recipes/s3.yaml +108 -0
  121. package/recipes/sendgrid.yaml +165 -0
  122. package/recipes/sentry.yaml +158 -0
  123. package/recipes/split.yaml +121 -0
  124. package/recipes/statsig.yaml +124 -0
  125. package/recipes/stripe.yaml +310 -0
  126. package/recipes/stytch.yaml +113 -0
  127. package/recipes/svix.yaml +109 -0
  128. package/recipes/turso.yaml +98 -0
  129. package/recipes/unleash.yaml +149 -0
  130. package/recipes/upstash.yaml +130 -0
  131. package/recipes/vercel.yaml +116 -0
  132. package/recipes/workos.yaml +70 -0
@@ -0,0 +1,178 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Auth0 Management API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: auth0
4
+ title: Auth0
5
+ category: auth
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Get Management API access tokens for production
9
+ url: https://auth0.com/docs/secure/tokens/access-tokens/management-api-access-tokens/get-management-api-access-tokens-for-production
10
+ - title: Management API v2, get a client
11
+ url: https://auth0.com/docs/api/management/v2/clients/get-clients-by-id
12
+ - title: Management API v2, update a client
13
+ url: https://auth0.com/docs/api/management/v2/clients/patch-clients-by-id
14
+ - title: Auth0 Terraform provider, configuration (conventional variable names)
15
+ url: https://raw.githubusercontent.com/auth0/terraform-provider-auth0/main/docs/index.md
16
+ api:
17
+ base_url_env: AUTH0_API_URL
18
+ auth:
19
+ oauth2_client_credentials:
20
+ token_url: /oauth/token
21
+ token_url_env: AUTH0_TOKEN_URL
22
+ client_id_env: AUTH0_CLIENT_ID
23
+ client_secret_env: AUTH0_CLIENT_SECRET
24
+ audience: /api/v2/
25
+ base_url_example: https://acme.eu.auth0.com/api/v2
26
+ detect:
27
+ deps: [auth0, "@auth0/"]
28
+ env: ["AUTH0_*"]
29
+ starter:
30
+ - op: callback_url
31
+ - op: logout_url
32
+ preview_url: url
33
+ where:
34
+ base_url: "`https://<tenant>.<region>.auth0.com/api/v2`: Auth0 Dashboard → Settings → General → Domain (the canonical domain, not a custom one)"
35
+ client_id: "Auth0 Dashboard → Applications → your application → Settings → Client ID"
36
+ url: "the exact callback URL the app sends as `redirect_uri` on a preview: its URL plus the callback path (a `{ from }` reference cannot carry a path)"
37
+ assumptions:
38
+ - id: AU1
39
+ verified: true
40
+ text: "A machine-to-machine application gets a Management API token from `POST https://<tenant>/oauth/token` (form-encoded `grant_type=client_credentials`, `client_id`, `client_secret`, `audience`); the audience is the Management API's identifier `https://<tenant>/api/v2/`. The answer has `access_token`, `expires_in` (86400 in the example) and `token_type: Bearer`. The recipe writes both as paths, so a plan's `base_url: https://<tenant>/api/v2` names the tenant once."
41
+ source: https://auth0.com/docs/secure/tokens/access-tokens/management-api-access-tokens/get-management-api-access-tokens-for-production
42
+ - id: AU2
43
+ verified: true
44
+ text: "`GET /api/v2/clients/{id}` answers the application with its `callbacks`, `allowed_logout_urls`, `web_origins` and `allowed_origins` arrays, which only a token with `read:clients` (or `read:client_keys`) receives. `fields` with `include_fields=true` limits the answer to the listed fields, so the recipe never reads the client secret."
45
+ source: https://auth0.com/docs/api/management/v2/clients/get-clients-by-id
46
+ - id: AU3
47
+ verified: true
48
+ text: "`PATCH /api/v2/clients/{id}` (scope `update:clients`) updates an application; `callbacks`, `allowed_logout_urls`, `web_origins` and `allowed_origins` are arrays of strings, and the body needs at least one property, so a write sends the one array alone."
49
+ source: https://auth0.com/docs/api/management/v2/clients/patch-clients-by-id
50
+ - id: AU4
51
+ verified: false
52
+ text: "An array sent in a PATCH replaces the stored array whole (it is not merged), so the recipe sends the full list read just before with one URL added or removed. The reference does not say so for these fields."
53
+ - id: AU5
54
+ verified: false
55
+ text: "Updates have no precondition (no ETag or version): the line locks the application (`lockOn`, one lock per tenant and client id, shared by the four ops) against other Sponson runs, and a console edit made between the read and the write can still be lost (the next apply then reports it as drift)."
56
+ - id: AU6
57
+ verified: false
58
+ text: "URLs are stored and read back exactly as written (no trailing slash added or case changed), so a URL identifies its entry."
59
+ - id: AU7
60
+ verified: true
61
+ text: "`AUTH0_CLIENT_ID` and `AUTH0_CLIENT_SECRET` are the variables the Auth0 Terraform provider reads for a machine-to-machine client."
62
+ source: https://raw.githubusercontent.com/auth0/terraform-provider-auth0/main/docs/index.md
63
+ - id: AU8
64
+ verified: false
65
+ text: "The audience stays the tenant's canonical domain when the tenant has a custom domain, so `base_url` names the canonical domain (`<tenant>.<region>.auth0.com`), not the custom one."
66
+ ops:
67
+ callback_url:
68
+ kind: list_item
69
+ title: Application callback URL
70
+ summary: "One URL in an Auth0 application's Allowed Callback URLs (`callbacks`), such as the preview's `/callback`; usually `{ from: <deploy line>.preview_url }` joined with the path in the app. Added by read-modify-write of the list under a lock on the application, removed on destroy; every other URL is left as it is. A URL removed in the Dashboard is drift (`missing`) that the next apply puts back."
71
+ covers: [auth0-callbacks]
72
+ params:
73
+ client_id:
74
+ type: string
75
+ required: true
76
+ description: The application's Client ID.
77
+ example: AbC123dEf456GhI789jKl012MnO345pQ
78
+ url:
79
+ type: string
80
+ required: true
81
+ pattern: "^https?://\\S+$"
82
+ description: "The callback URL, exactly as the application sends it as `redirect_uri`."
83
+ example: "https://${ctx.scope}.preview.example.app/callback"
84
+ http:
85
+ parent:
86
+ path: "/clients/{client_id}"
87
+ read_path: "/clients/{client_id}?fields=callbacks,allowed_logout_urls,web_origins,allowed_origins&include_fields=true"
88
+ list_path: /callbacks
89
+ item: { param: url }
90
+ sim:
91
+ objects:
92
+ "/clients/{client_id}": { callbacks: [https://app.example.com/callback], allowed_logout_urls: [https://app.example.com], web_origins: [https://app.example.com], allowed_origins: [] }
93
+ live:
94
+ params: { client_id: SPONSON_LIVE_AUTH0_APP_CLIENT_ID }
95
+ logout_url:
96
+ kind: list_item
97
+ title: Application allowed logout URL
98
+ summary: "One URL in an Auth0 application's Allowed Logout URLs (`allowed_logout_urls`), where `/v2/logout` may return the user, such as the preview's home page. Read-modify-write of the list under the application's lock; removed on destroy; every other URL is left as it is."
99
+ covers: [auth0-callbacks]
100
+ params:
101
+ client_id:
102
+ type: string
103
+ required: true
104
+ description: The application's Client ID.
105
+ example: AbC123dEf456GhI789jKl012MnO345pQ
106
+ url:
107
+ type: string
108
+ required: true
109
+ pattern: "^https?://\\S+$"
110
+ description: "The URL the application passes as `returnTo` on logout."
111
+ example: "https://${ctx.scope}.preview.example.app"
112
+ http:
113
+ parent:
114
+ path: "/clients/{client_id}"
115
+ read_path: "/clients/{client_id}?fields=callbacks,allowed_logout_urls,web_origins,allowed_origins&include_fields=true"
116
+ list_path: /allowed_logout_urls
117
+ item: { param: url }
118
+ sim:
119
+ objects:
120
+ "/clients/{client_id}": { callbacks: [https://app.example.com/callback], allowed_logout_urls: [https://app.example.com], web_origins: [https://app.example.com], allowed_origins: [] }
121
+ live:
122
+ params: { client_id: SPONSON_LIVE_AUTH0_APP_CLIENT_ID }
123
+ web_origin:
124
+ kind: list_item
125
+ title: Application allowed web origin
126
+ summary: "One origin in an Auth0 application's Allowed Web Origins (`web_origins`), which silent authentication and the SPA SDKs' token refresh need for the preview's origin. Read-modify-write of the list under the application's lock; removed on destroy; every other origin is left as it is."
127
+ covers: [auth0-callbacks]
128
+ params:
129
+ client_id:
130
+ type: string
131
+ required: true
132
+ description: The application's Client ID.
133
+ example: AbC123dEf456GhI789jKl012MnO345pQ
134
+ origin:
135
+ type: string
136
+ required: true
137
+ pattern: "^https?://[^/\\s]+$"
138
+ description: "The origin, scheme and host without a path (`https://pr-42.preview.example.app`)."
139
+ example: "https://${ctx.scope}.preview.example.app"
140
+ http:
141
+ parent:
142
+ path: "/clients/{client_id}"
143
+ read_path: "/clients/{client_id}?fields=callbacks,allowed_logout_urls,web_origins,allowed_origins&include_fields=true"
144
+ list_path: /web_origins
145
+ item: { param: origin }
146
+ sim:
147
+ objects:
148
+ "/clients/{client_id}": { callbacks: [https://app.example.com/callback], allowed_logout_urls: [https://app.example.com], web_origins: [https://app.example.com], allowed_origins: [] }
149
+ live:
150
+ params: { client_id: SPONSON_LIVE_AUTH0_APP_CLIENT_ID }
151
+ allowed_origin:
152
+ kind: list_item
153
+ title: Application allowed origin (CORS)
154
+ summary: "One origin in an Auth0 application's Allowed Origins (CORS, `allowed_origins`), for a preview that calls Auth0 from the browser across origins (embedded login, cross-origin authentication). Read-modify-write of the list under the application's lock; removed on destroy; every other origin is left as it is."
155
+ covers: [auth0-callbacks]
156
+ params:
157
+ client_id:
158
+ type: string
159
+ required: true
160
+ description: The application's Client ID.
161
+ example: AbC123dEf456GhI789jKl012MnO345pQ
162
+ origin:
163
+ type: string
164
+ required: true
165
+ pattern: "^https?://[^/\\s]+$"
166
+ description: "The origin, scheme and host without a path (`https://pr-42.preview.example.app`)."
167
+ example: "https://${ctx.scope}.preview.example.app"
168
+ http:
169
+ parent:
170
+ path: "/clients/{client_id}"
171
+ read_path: "/clients/{client_id}?fields=callbacks,allowed_logout_urls,web_origins,allowed_origins&include_fields=true"
172
+ list_path: /allowed_origins
173
+ item: { param: origin }
174
+ sim:
175
+ objects:
176
+ "/clients/{client_id}": { callbacks: [https://app.example.com/callback], allowed_logout_urls: [https://app.example.com], web_origins: [https://app.example.com], allowed_origins: [] }
177
+ live:
178
+ params: { client_id: SPONSON_LIVE_AUTH0_APP_CLIENT_ID }
@@ -0,0 +1,116 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Azure Static Web Apps (Azure Resource Manager) recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: azure_swa
4
+ title: Azure Static Web Apps
5
+ category: deploy
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Static Sites - Create Or Update Static Site Build App Settings
9
+ url: https://learn.microsoft.com/en-us/rest/api/appservice/static-sites/create-or-update-static-site-build-app-settings?view=rest-appservice-2024-04-01
10
+ - title: Static Sites - List Static Site Build App Settings
11
+ url: https://learn.microsoft.com/en-us/rest/api/appservice/static-sites/list-static-site-build-app-settings?view=rest-appservice-2024-04-01
12
+ - title: Static Sites - Get Static Site Builds
13
+ url: https://learn.microsoft.com/en-us/rest/api/appservice/static-sites/get-static-site-builds?view=rest-appservice-2024-04-01
14
+ - title: Preview environments in Azure Static Web Apps
15
+ url: https://learn.microsoft.com/en-us/azure/static-web-apps/preview-environments
16
+ - title: Configure application settings for Azure Static Web Apps
17
+ url: https://learn.microsoft.com/en-us/azure/static-web-apps/application-settings
18
+ - title: az account get-access-token
19
+ url: https://learn.microsoft.com/en-us/cli/azure/account#az-account-get-access-token
20
+ api:
21
+ base_url: https://management.azure.com
22
+ base_url_env: AZURE_SWA_API_URL
23
+ auth: { command: [az, account, get-access-token, --query, accessToken, -o, tsv] }
24
+ detect:
25
+ files: [staticwebapp.config.json]
26
+ env: ["AZURE_STATIC_WEB_APPS_*"]
27
+ starter:
28
+ - op: build_app_setting
29
+ values: { environment: "${ctx.pr.number}" }
30
+ where:
31
+ subscription_id: "`az account show --query id -o tsv`"
32
+ resource_group: "`az staticwebapp show --name <site> --query resourceGroup -o tsv`"
33
+ site: "`az staticwebapp list --query '[].name'`"
34
+ assumptions:
35
+ - id: AZ1
36
+ verified: true
37
+ text: "`PUT https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/staticSites/{name}/builds/{environmentName}/config/appsettings?api-version=2024-04-01` with `{ properties: { <name>: <value>, … } }` sets the app settings of one environment (build) and answers 200 with the same `properties`."
38
+ source: https://learn.microsoft.com/en-us/rest/api/appservice/static-sites/create-or-update-static-site-build-app-settings?view=rest-appservice-2024-04-01
39
+ - id: AZ2
40
+ verified: true
41
+ text: "The settings are read with `POST …/builds/{environmentName}/listAppSettings?api-version=2024-04-01` (no body), which answers a StringDictionary `{ id, properties }`; there is no GET, so the recipe reads with `parent.read_method: POST`."
42
+ source: https://learn.microsoft.com/en-us/rest/api/appservice/static-sites/list-static-site-build-app-settings?view=rest-appservice-2024-04-01
43
+ - id: AZ3
44
+ verified: true
45
+ text: "Each environment is a `builds/{environmentName}` resource: production is `default`, a pull request's preview environment is named by the pull request's number (`builds/12`)."
46
+ source: https://learn.microsoft.com/en-us/rest/api/appservice/static-sites/get-static-site-builds?view=rest-appservice-2024-04-01
47
+ - id: AZ4
48
+ verified: true
49
+ text: "A preview environment exists from the pull request's first deployment and is deleted, with its settings, when the pull request is closed; app settings reach the app's managed API (Functions), not the front-end build."
50
+ source: https://learn.microsoft.com/en-us/azure/static-web-apps/preview-environments
51
+ - id: AZ5
52
+ verified: true
53
+ text: "`az account get-access-token --query accessToken -o tsv` prints an Azure Resource Manager access token for the signed-in account (the default resource is `https://management.azure.com/`), sent as a bearer token."
54
+ source: https://learn.microsoft.com/en-us/cli/azure/account#az-account-get-access-token
55
+ - id: AZ6
56
+ verified: false
57
+ text: "The PUT replaces the whole settings map (a setting it is not sent is removed), so the recipe sends the map as read with the one entry changed (`send: collection`); there is no ETag on this resource, so only Sponson's own writers are serialised (the parent lock), and a setting added in the portal between the read and the write is lost."
58
+ - id: AZ7
59
+ verified: false
60
+ text: "Listing the settings of an environment that does not exist (not deployed yet, or deleted with its pull request) answers 404, which the recipe reads as no settings (`if_missing: empty`): plan shows a create before the first deploy, and destroy after the pull request closed is a no-op. A write before the first deploy fails with that 404; order the line after the deploy."
61
+ ops:
62
+ build_app_setting:
63
+ kind: list_item
64
+ title: App setting of a preview environment
65
+ summary: "One app setting of a Static Web App environment (a pull request's preview environment is named by its number), by read-modify-write of the environment's settings: read with `listAppSettings`, written whole with a PUT. Other settings are left alone and the environment is locked while Sponson writes it. Settings reach the app's managed API, not the front-end build (AZ4); the environment exists only after the PR's first deploy (AZ7)."
66
+ covers: [azure-swa-settings]
67
+ params:
68
+ subscription_id:
69
+ type: string
70
+ required: true
71
+ description: The Azure subscription id.
72
+ example: 00000000-0000-0000-0000-000000000000
73
+ resource_group:
74
+ type: string
75
+ required: true
76
+ description: The resource group of the static web app.
77
+ example: rg-web
78
+ site:
79
+ type: string
80
+ required: true
81
+ description: The static web app's name.
82
+ example: web
83
+ environment:
84
+ type: string
85
+ required: true
86
+ pattern: "^[a-zA-Z0-9]+$"
87
+ description: The environment's name (the pull request's number for its preview environment; `default` is production).
88
+ example: "42"
89
+ name:
90
+ type: string
91
+ required: true
92
+ description: The setting's name.
93
+ example: API_URL
94
+ value:
95
+ type: string
96
+ required: true
97
+ description: The setting's value.
98
+ example: "https://api-${ctx.scope}.example.app"
99
+ http:
100
+ parent:
101
+ path: "/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Web/staticSites/{site}/builds/{environment}/config/appsettings?api-version=2024-04-01"
102
+ read_path: "/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Web/staticSites/{site}/builds/{environment}/listAppSettings?api-version=2024-04-01"
103
+ read_method: POST
104
+ method: PUT
105
+ send: collection
106
+ if_missing: empty
107
+ list_path: /properties
108
+ shape: map
109
+ item: { param: name }
110
+ value: { param: value }
111
+ sim:
112
+ objects:
113
+ "/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.Web/staticSites/{site}/builds/{environment}/config/appsettings":
114
+ properties: { SHARED_SETTING: from-the-portal }
115
+ live:
116
+ params: { subscription_id: SPONSON_LIVE_AZURE_SUBSCRIPTION_ID, resource_group: SPONSON_LIVE_AZURE_RESOURCE_GROUP, site: SPONSON_LIVE_AZURE_SWA_SITE, environment: SPONSON_LIVE_AZURE_SWA_ENVIRONMENT }
@@ -0,0 +1,97 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Google Cloud Run Admin API v2 recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: cloud_run
4
+ title: Google Cloud Run
5
+ category: deploy
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: "REST Resource: projects.locations.services (Service, TrafficTarget)"
9
+ url: https://docs.cloud.google.com/run/docs/reference/rest/v2/projects.locations.services
10
+ - title: "Method: projects.locations.services.patch"
11
+ url: https://docs.cloud.google.com/run/docs/reference/rest/v2/projects.locations.services/patch
12
+ - title: "Method: projects.locations.services.get"
13
+ url: https://docs.cloud.google.com/run/docs/reference/rest/v2/projects.locations.services/get
14
+ - title: Rollbacks, gradual rollouts, and traffic migration (tags)
15
+ url: https://cloud.google.com/run/docs/rollouts-rollbacks-traffic-migration
16
+ - title: gcloud auth print-access-token
17
+ url: https://cloud.google.com/sdk/gcloud/reference/auth/print-access-token
18
+ api:
19
+ base_url: https://run.googleapis.com/v2
20
+ base_url_env: CLOUD_RUN_API_URL
21
+ auth: { command: [gcloud, auth, print-access-token] }
22
+ assumptions:
23
+ - id: CR1
24
+ verified: true
25
+ text: "`GET https://run.googleapis.com/v2/projects/{project}/locations/{location}/services/{service}` answers the Service with its `traffic` list and an `etag`, 'a system-generated fingerprint for this version of the resource' that 'may be used to detect modification conflict during updates'."
26
+ source: https://docs.cloud.google.com/run/docs/reference/rest/v2/projects.locations.services
27
+ - id: CR2
28
+ verified: true
29
+ text: "`PATCH https://run.googleapis.com/v2/{service.name}` takes a Service and an optional `updateMask` (fields to update), answers a long-running Operation, and needs the `cloud-platform` scope; the recipe sends `?updateMask=traffic` with `{ traffic, etag }` only, so the revision template is never written."
30
+ source: https://docs.cloud.google.com/run/docs/reference/rest/v2/projects.locations.services/patch
31
+ - id: CR3
32
+ verified: true
33
+ text: "A TrafficTarget is `{ type, revision, percent, tag }`; `type: TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION` sends traffic to the named `revision`, `percent` defaults to zero, and `tag` is 'a string to be part of the URI to exclusively reference this target', which is the per-PR URL."
34
+ source: https://docs.cloud.google.com/run/docs/reference/rest/v2/projects.locations.services
35
+ - id: CR4
36
+ verified: true
37
+ text: "`gcloud auth print-access-token` prints an OAuth access token for the active account, sent as a bearer token."
38
+ source: https://cloud.google.com/sdk/gcloud/reference/auth/print-access-token
39
+ - id: CR5
40
+ verified: false
41
+ text: "A patch whose `etag` no longer matches (a deploy changed the service after the read) is refused (409 ABORTED or 412) rather than applied: the line fails and the next apply reads again, so a concurrent `gcloud run deploy` is never overwritten. Sponson's own writers are serialised by the parent lock."
42
+ - id: CR6
43
+ verified: false
44
+ text: "The Service answered right after the patch's Operation is accepted already shows the new `traffic` (the desired state), so the re-read that confirms the write sees the tag; `percent` 0 is left out of the answer and `type` and `revision` are answered as sent."
45
+ - id: CR7
46
+ verified: false
47
+ text: "The revision a tag points to exists before the line applies: the PR's deploy creates it (`gcloud run deploy --no-traffic --revision-suffix <scope>` gives `<service>-<scope>`). Its environment variables are part of that revision, set by the deploy, not by this recipe; a deploy that tags (`--tag`) itself conflicts with this line's tag."
48
+ ops:
49
+ traffic_tag:
50
+ kind: list_item
51
+ title: Traffic tag of a revision
52
+ summary: "A tag (`pr-42`) in a service's `traffic` list pointing at the PR's revision with no traffic share, which gives the revision its own URL (`https://pr-42---<service>-<hash>.a.run.app`). Read-modify-write of `traffic` only (`updateMask=traffic`), sent with the service's `etag` so a concurrent deploy makes the write fail instead of being overwritten (CR5); other tags and the traffic split are left alone, and destroy removes only this tag. The revision itself, and its environment variables, are the deploy's (CR7)."
53
+ covers: []
54
+ params:
55
+ project:
56
+ type: string
57
+ required: true
58
+ description: The Google Cloud project id.
59
+ example: demo-project
60
+ region:
61
+ type: string
62
+ required: true
63
+ description: The service's region.
64
+ example: us-central1
65
+ service:
66
+ type: string
67
+ required: true
68
+ description: The Cloud Run service's name.
69
+ example: web
70
+ tag:
71
+ type: string
72
+ required: true
73
+ pattern: "^[a-z][a-z0-9-]*$"
74
+ description: The tag, the prefix of the revision's own URL.
75
+ example: "${ctx.scope}"
76
+ revision:
77
+ type: string
78
+ required: true
79
+ description: The revision the tag points to (made by the PR's deploy).
80
+ example: "web-${ctx.scope}"
81
+ http:
82
+ parent:
83
+ path: "/projects/{project}/locations/{region}/services/{service}?updateMask=traffic"
84
+ read_path: "/projects/{project}/locations/{region}/services/{service}"
85
+ method: PATCH
86
+ send: [etag]
87
+ list_path: /traffic
88
+ key_field: tag
89
+ item: { tag: { param: tag }, revision: { param: revision }, type: TRAFFIC_TARGET_ALLOCATION_TYPE_REVISION }
90
+ sim:
91
+ objects:
92
+ "/projects/{project}/locations/{region}/services/{service}":
93
+ name: projects/demo-project/locations/us-central1/services/web
94
+ etag: '"sim-etag-1"'
95
+ traffic: [{ type: TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST, percent: 100 }]
96
+ live:
97
+ params: { project: SPONSON_LIVE_GCP_PROJECT, region: SPONSON_LIVE_CLOUD_RUN_REGION, service: SPONSON_LIVE_CLOUD_RUN_SERVICE, revision: SPONSON_LIVE_CLOUD_RUN_REVISION }
@@ -0,0 +1,108 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Cloudflare API v4 recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: cloudflare
4
+ title: Cloudflare
5
+ category: dns
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Create DNS record
9
+ url: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/create/
10
+ - title: List DNS records
11
+ url: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/list/
12
+ - title: DNS record details
13
+ url: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/get/
14
+ - title: Update DNS record (PATCH)
15
+ url: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/edit/
16
+ - title: Delete DNS record
17
+ url: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/delete/
18
+ api:
19
+ base_url: https://api.cloudflare.com/client/v4
20
+ base_url_env: CLOUDFLARE_API_URL
21
+ auth: { bearer_env: CLOUDFLARE_API_TOKEN }
22
+ assumptions:
23
+ - id: CF1
24
+ verified: true
25
+ text: "Records are created with `POST /zones/{zone_id}/dns_records` (`type`, `name`, `content`, `ttl`, `proxied`, `comment`), authenticated with an API token as `Authorization: Bearer` that has DNS Write on the zone."
26
+ source: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/create/
27
+ - id: CF2
28
+ verified: true
29
+ text: "`GET /zones/{zone_id}/dns_records` answers `{ result: [...] }` and filters by `type` and `name.exact` (names compare case-insensitively)."
30
+ source: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/list/
31
+ - id: CF3
32
+ verified: true
33
+ text: "Single-record answers (create, details, update) carry the record under `result`, with its `id`."
34
+ source: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/get/
35
+ - id: CF4
36
+ verified: true
37
+ text: "`PATCH /zones/{zone_id}/dns_records/{id}` updates a record; `DELETE` on the same path removes it permanently."
38
+ source: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/delete/
39
+ - id: CF5
40
+ verified: false
41
+ text: "A PATCH needs `name` and `type` alongside the changed fields (the edit schema does not mark them optional), so every update sends both."
42
+ source: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/edit/
43
+ - id: CF6
44
+ verified: false
45
+ text: "The API returns `name` lower-cased as the full name and `content` as written, so `name` must be given lower-case and fully qualified, or every plan finds no record and tries to create one."
46
+ - id: CF7
47
+ verified: false
48
+ text: "A proxied record reports `ttl: 1` (automatic) whatever was sent, so `ttl` other than 1 with `proxied: true` shows as drift on every plan."
49
+ - id: CF8
50
+ verified: true
51
+ text: A CNAME cannot share its name with another record, so name and type identify it within the zone and a filtered list holds at most one, on its first page.
52
+ source: https://developers.cloudflare.com/api/resources/dns/subresources/records/methods/create/
53
+ ops:
54
+ dns_cname:
55
+ kind: resource
56
+ title: DNS CNAME record
57
+ summary: A CNAME record in a zone, such as `pr-42.preview.example.com` pointing at the preview deployment's host. Located by name and type; a console edit of its target, proxying, TTL or comment is drift.
58
+ covers: [cloudflare-dns]
59
+ params:
60
+ zone_id:
61
+ type: string
62
+ required: true
63
+ pattern: "^[0-9a-f]{32}$"
64
+ description: The zone's id (32 hex characters, on the zone's Overview page).
65
+ example: 023e105f4ecef8ad9ca31a8372d0c353
66
+ name:
67
+ type: string
68
+ required: true
69
+ pattern: "^[a-z0-9*_.-]+$"
70
+ description: The record's full name, lower-case, zone included.
71
+ example: "${ctx.scope}.preview.example.com"
72
+ target:
73
+ type: string
74
+ required: true
75
+ description: The host the name points at (the record's `content`).
76
+ example: preview-host.example.net
77
+ proxied:
78
+ type: boolean
79
+ default: false
80
+ description: Whether Cloudflare proxies the traffic.
81
+ ttl:
82
+ type: integer
83
+ default: 1
84
+ description: TTL in seconds (60–86400), or 1 for automatic.
85
+ comment:
86
+ type: string
87
+ description: A note on the record; no effect on DNS.
88
+ example: Managed by Sponson
89
+ http:
90
+ find:
91
+ path: "/zones/{zone_id}/dns_records?type=CNAME&name.exact={name}"
92
+ list_path: /result
93
+ match: { type: CNAME, name: { param: name } }
94
+ create: { path: "/zones/{zone_id}/dns_records" }
95
+ update:
96
+ method: PATCH
97
+ path: "/zones/{zone_id}/dns_records/{id}"
98
+ body: { type: CNAME, name: { param: name } }
99
+ delete: { path: "/zones/{zone_id}/dns_records/{id}" }
100
+ item_path: /result
101
+ fields:
102
+ content: { param: target }
103
+ proxied: { param: proxied }
104
+ ttl: { param: ttl }
105
+ comment: { param: comment }
106
+ outputs: { hostname: /name }
107
+ live:
108
+ params: { zone_id: SPONSON_LIVE_CLOUDFLARE_ZONE_ID, name: SPONSON_LIVE_CLOUDFLARE_NAME }
@@ -0,0 +1,132 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # CockroachDB Cloud API recipes (service account API key, bearer). Format and checklist: docs/adr/0020-recipes.md,
3
+ # CONTRIBUTING.md "Adding a recipe".
4
+ provider: cockroachdb
5
+ title: CockroachDB Cloud
6
+ category: databases
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Cloud API OpenAPI document (version 2026-09-15)
10
+ url: https://cockroachlabs.cloud/assets/docs/api/latest/openapi.json
11
+ - title: Use the CockroachDB Cloud API
12
+ url: https://docs.cockroachlabs.com/docs/cockroachcloud/cloud-api
13
+ api:
14
+ base_url: https://cockroachlabs.cloud/api/v1
15
+ base_url_env: COCKROACH_API_URL
16
+ auth: { bearer_env: COCKROACH_API_KEY }
17
+ headers: { Cc-Version: "2024-09-16" }
18
+ detect:
19
+ env: ["COCKROACH_*"]
20
+ db_hosts: [.cockroachlabs.cloud]
21
+ starter:
22
+ - op: database
23
+ values: { name: "app_${ctx.scope}" }
24
+ where:
25
+ cluster_id: "CockroachDB Cloud Console → your cluster (the UUID in its URL), or `ccloud cluster list`"
26
+ assumptions:
27
+ - id: CR1
28
+ verified: true
29
+ text: "The Cloud API authenticates with a service account's secret key as `Authorization: Bearer`; the key has the service account's roles."
30
+ source: https://docs.cockroachlabs.com/docs/cockroachcloud/cloud-api
31
+ - id: CR2
32
+ verified: true
33
+ text: "`Cc-Version` pins the API version (`YYYY-MM-DD`); without it the latest version answers, which may change under a client. The guide documents `2024-09-16`."
34
+ source: https://docs.cockroachlabs.com/docs/cockroachcloud/cloud-api
35
+ - id: CR3
36
+ verified: true
37
+ text: "`POST /clusters/{cluster_id}/databases` with `{ name }` creates a database and answers `{ name, table_count }`; `GET` on the same path lists `{ databases: [{ name, table_count }], pagination: { next_page } }`, paged with `pagination.page`; `DELETE /clusters/{cluster_id}/databases/{name}` deletes one. There is no GET of a single database."
38
+ source: https://cockroachlabs.cloud/assets/docs/api/latest/openapi.json
39
+ - id: CR4
40
+ verified: true
41
+ text: "`POST /clusters/{cluster_id}/sql-users` with `{ name, password }` (both required) creates a SQL user; `GET` on the same path lists `{ users: [{ name }], pagination: { next_page } }`; `DELETE /clusters/{cluster_id}/sql-users/{name}` deletes one. The password cannot be read back; `PUT …/sql-users/{name}/password` changes it."
42
+ source: https://cockroachlabs.cloud/assets/docs/api/latest/openapi.json
43
+ - id: CR5
44
+ verified: true
45
+ text: "A SQL user created through the API gets the SQL `admin` role by default, which the guide advises against for anything but administration."
46
+ source: https://docs.cockroachlabs.com/docs/cockroachcloud/cloud-api
47
+ - id: CR6
48
+ verified: true
49
+ text: "Creating or deleting databases and SQL users needs a cluster-level role of the service account (Cluster Admin, Cluster Operator or Cluster Data Accessor for databases; Cluster Admin for SQL users)."
50
+ source: https://cockroachlabs.cloud/assets/docs/api/latest/openapi.json
51
+ - id: CR7
52
+ verified: false
53
+ text: "`COCKROACH_API_KEY` is the variable the CockroachDB Terraform provider and `ccloud` read the key from."
54
+ - id: CR8
55
+ verified: false
56
+ text: "A second create of an existing database or SQL user is refused with 409 (ALREADY_EXISTS), and deleting one that is gone answers 404."
57
+ - id: CR9
58
+ verified: false
59
+ text: "Deleting a database drops it with its tables (CASCADE); the API offers no way to refuse when it holds data."
60
+ - id: CR10
61
+ verified: false
62
+ text: "The API takes database and user names that need quoting in SQL (`app_pr-42`, from a scope with `-`) and quotes them itself."
63
+ ops:
64
+ database:
65
+ kind: resource
66
+ title: Database in a cluster
67
+ summary: "A database per scope in a shared CockroachDB Cloud cluster, such as `app_pr_42`, instead of a cluster per preview. Located by its name in the cluster's list; only its existence is compared (the API exposes nothing else to set); destroy drops it with its data (CR9)."
68
+ covers: [cockroachdb-database]
69
+ params:
70
+ cluster_id:
71
+ type: string
72
+ required: true
73
+ description: The cluster's id (a UUID, shown in the Cloud Console's URL).
74
+ example: 9e3f1a52-63d1-4a4c-9b8f-2c8b7a0e5d11
75
+ name:
76
+ type: string
77
+ required: true
78
+ pattern: "^[A-Za-z_][A-Za-z0-9_.-]{0,62}$"
79
+ description: "The database's name; its identity. A name with `-` (a scope such as `pr-42`) needs quoting in SQL (CR10)."
80
+ example: "app_${ctx.scope}"
81
+ http:
82
+ find:
83
+ path: "/clusters/{cluster_id}/databases"
84
+ list_path: /databases
85
+ match: { name: { param: name } }
86
+ next: /pagination/next_page
87
+ cursor_param: pagination.page
88
+ create: { path: "/clusters/{cluster_id}/databases" }
89
+ delete: { path: "/clusters/{cluster_id}/databases/{id}" }
90
+ id_path: /name
91
+ sim:
92
+ id_from: name
93
+ live:
94
+ params: { cluster_id: SPONSON_LIVE_COCKROACH_CLUSTER_ID }
95
+ sql_user:
96
+ kind: resource
97
+ title: SQL user in a cluster
98
+ summary: "A SQL user per scope in a shared CockroachDB Cloud cluster, such as `app_pr_42`, with its password as an input (`{ secret: … }`, or a sensitive output of another line), sent on create only and never compared. It gets the SQL `admin` role (CR5): grant narrower privileges in SQL if previews must not administer the cluster. Located by its name; destroy deletes the user."
99
+ covers: []
100
+ params:
101
+ cluster_id:
102
+ type: string
103
+ required: true
104
+ description: The cluster's id (a UUID, shown in the Cloud Console's URL).
105
+ example: 9e3f1a52-63d1-4a4c-9b8f-2c8b7a0e5d11
106
+ name:
107
+ type: string
108
+ required: true
109
+ pattern: "^[A-Za-z_][A-Za-z0-9_.-]{0,62}$"
110
+ description: The user's name; its identity.
111
+ example: "app_${ctx.scope}"
112
+ password:
113
+ type: string
114
+ required: true
115
+ description: "The user's password, as a reference (`{ secret: env://… }`, or `{ from: <line>.<output> }` of a sensitive output); sent on create only (CR4)."
116
+ example: { secret: "env://PREVIEW_DB_PASSWORD" }
117
+ http:
118
+ find:
119
+ path: "/clusters/{cluster_id}/sql-users"
120
+ list_path: /users
121
+ match: { name: { param: name } }
122
+ next: /pagination/next_page
123
+ cursor_param: pagination.page
124
+ create:
125
+ path: "/clusters/{cluster_id}/sql-users"
126
+ body: { password: { param: password } }
127
+ delete: { path: "/clusters/{cluster_id}/sql-users/{id}" }
128
+ id_path: /name
129
+ sim:
130
+ id_from: name
131
+ live:
132
+ params: { cluster_id: SPONSON_LIVE_COCKROACH_CLUSTER_ID }