@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,123 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # ConfigCat Public Management API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ provider: configcat
4
+ title: ConfigCat
5
+ category: flags
6
+ verified_on: "2026-10-11"
7
+ docs:
8
+ - title: Public Management API
9
+ url: https://api.configcat.com/docs/
10
+ - title: OpenAPI spec (get value v2, replace value v2, targeting rule models)
11
+ url: https://api.configcat.com/docs/v1/swagger.json
12
+ - title: Terraform provider (credential variable names)
13
+ url: https://github.com/configcat/terraform-provider-configcat/blob/main/docs/index.md
14
+ api:
15
+ base_url: https://api.configcat.com
16
+ base_url_env: CONFIGCAT_API_URL
17
+ auth: { basic: { user_env: CONFIGCAT_BASIC_AUTH_USERNAME, password_env: CONFIGCAT_BASIC_AUTH_PASSWORD } }
18
+ detect:
19
+ deps: [configcat-js, configcat-js-ssr, configcat-js-full, configcat-node, configcat-react]
20
+ env: ["CONFIGCAT_*"]
21
+ starter:
22
+ - op: targeting_rule
23
+ values: { match: "${ctx.scope}" }
24
+ where:
25
+ environment_id: "ConfigCat Dashboard → your product → Environments → the preview environment's ID"
26
+ setting_id: "ConfigCat Public Management API `GET /v1/configs/{configId}/settings`: the flag's `settingId`"
27
+ assumptions:
28
+ - id: CC1
29
+ verified: true
30
+ text: "The API is at `https://api.configcat.com`, authenticated with Basic auth from Public API credentials (the Terraform provider's `CONFIGCAT_BASIC_AUTH_USERNAME` / `CONFIGCAT_BASIC_AUTH_PASSWORD`)."
31
+ source: https://api.configcat.com/docs/v1/swagger.json
32
+ - id: CC2
33
+ verified: true
34
+ text: "`GET /v2/environments/{environmentId}/settings/{settingId}/value` answers the setting's value in the environment bare: `defaultValue`, `targetingRules`, `percentageEvaluationAttribute` and read-only fields."
35
+ source: https://api.configcat.com/docs/v1/swagger.json
36
+ - id: CC3
37
+ verified: true
38
+ text: "`PUT` on the same path replaces the value: only `defaultValue`, `targetingRules` and `percentageEvaluationAttribute` can be set, and one not sent is reset. So those three are sent, as read, with the rule list changed (`parent.send`)."
39
+ source: https://api.configcat.com/docs/v1/swagger.json
40
+ - id: CC4
41
+ verified: true
42
+ text: "A targeting rule is `{ conditions: [{ userCondition: { comparisonAttribute, comparator, comparisonValue: { stringValue, doubleValue, listValue: [{ value, hint }] } } }], value | percentageOptions }`; rules have no id and are evaluated in order. `isOneOf` and `sensitiveIsOneOf` compare a user attribute with a list."
43
+ source: https://api.configcat.com/docs/v1/swagger.json
44
+ - id: CC5
45
+ verified: true
46
+ text: "Answers carry every member of a value (`boolValue`, `stringValue`, `intValue`, `doubleValue`, `predefinedVariationId`) and of a comparison value, the unused ones null; the rule is written with the same nulls so that it reads back equal."
47
+ source: https://api.configcat.com/docs/v1/swagger.json
48
+ - id: CC6
49
+ verified: false
50
+ text: "A condition is read back with `segmentCondition` and `prerequisiteFlagCondition` null and each list value's `hint` null, exactly as written; otherwise every plan shows the rule as changed."
51
+ - id: CC7
52
+ verified: false
53
+ text: "In a product with \"Config changes require a reason\" or approvals turned on, the PUT without `reason` / `bypassApproval` is refused (the query parameters are not sent); the apply then fails."
54
+ source: https://api.configcat.com/docs/v1/swagger.json
55
+ ops:
56
+ targeting_rule:
57
+ kind: list_item
58
+ title: Feature flag targeting rule
59
+ summary: "A targeting rule on a boolean feature flag in one environment that serves a value to the preview, such as `IF Preview IS ONE OF [pr-42] THEN true`. Added after the flag's other rules (evaluated in order: an earlier matching rule wins); the default value and the other rules are written back as read. Identified by the matched value; a console edit of its condition or served value is drift. Destroy removes the rule."
60
+ covers: [configcat-rule]
61
+ params:
62
+ environment_id:
63
+ type: string
64
+ required: true
65
+ description: The environment's id (a UUID).
66
+ example: 08d8becf-d4d9-4c66-8b48-6ac74cd95fba
67
+ setting_id:
68
+ type: string
69
+ required: true
70
+ pattern: "^[0-9]+$"
71
+ description: The feature flag's numeric setting id.
72
+ example: "12345"
73
+ attribute:
74
+ type: string
75
+ required: true
76
+ description: The user attribute the rule compares (a custom attribute such as `Preview`, or `Identifier`, `Email`).
77
+ example: Preview
78
+ comparator:
79
+ type: string
80
+ default: isOneOf
81
+ enum: [isOneOf, sensitiveIsOneOf]
82
+ description: "`isOneOf`, or `sensitiveIsOneOf` (hashed in the config JSON)."
83
+ match:
84
+ type: string
85
+ required: true
86
+ description: The attribute value that is the preview; it identifies the rule.
87
+ example: "${ctx.scope}"
88
+ serve:
89
+ type: boolean
90
+ default: true
91
+ description: The value the flag serves to the preview.
92
+ http:
93
+ parent:
94
+ path: "/v2/environments/{environment_id}/settings/{setting_id}/value"
95
+ method: PUT
96
+ send: [defaultValue, percentageEvaluationAttribute]
97
+ list_path: /targetingRules
98
+ key_field: /conditions/0/userCondition/comparisonValue/listValue/0/value
99
+ item:
100
+ conditions:
101
+ - userCondition:
102
+ comparisonAttribute: { param: attribute }
103
+ comparator: { param: comparator }
104
+ comparisonValue:
105
+ stringValue: null
106
+ doubleValue: null
107
+ listValue: [{ value: { param: match }, hint: null }]
108
+ segmentCondition: null
109
+ prerequisiteFlagCondition: null
110
+ value:
111
+ boolValue: { param: serve }
112
+ stringValue: null
113
+ intValue: null
114
+ doubleValue: null
115
+ predefinedVariationId: null
116
+ sim:
117
+ objects:
118
+ "/v2/environments/{environment_id}/settings/{setting_id}/value":
119
+ defaultValue: { boolValue: false, stringValue: null, intValue: null, doubleValue: null, predefinedVariationId: null }
120
+ targetingRules: []
121
+ percentageEvaluationAttribute: null
122
+ live:
123
+ params: { environment_id: SPONSON_LIVE_CONFIGCAT_ENVIRONMENT_ID, setting_id: SPONSON_LIVE_CONFIGCAT_SETTING_ID }
@@ -0,0 +1,136 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Datadog API recipes: every request carries the API key and the application key. Format and checklist:
3
+ # docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
4
+ provider: datadog
5
+ title: Datadog
6
+ category: observability
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Synthetics API reference
10
+ url: https://docs.datadoghq.com/api/latest/synthetics/
11
+ - title: API authentication (keys, sites)
12
+ url: https://docs.datadoghq.com/api/latest/authentication/
13
+ - title: Datadog API v1 OpenAPI document (datadog-api-client)
14
+ url: https://github.com/DataDog/datadog-api-client-typescript/blob/master/.generator/schemas/v1/openapi.yaml
15
+ api:
16
+ base_url: https://api.datadoghq.com/api/v1
17
+ base_url_env: DATADOG_API_URL
18
+ auth:
19
+ headers:
20
+ - { header: DD-API-KEY, value_env: DD_API_KEY }
21
+ - { header: DD-APPLICATION-KEY, value_env: DD_APP_KEY }
22
+ detect:
23
+ deps: [dd-trace, "@datadog/"]
24
+ env: ["DD_*", "DATADOG_*"]
25
+ starter:
26
+ - op: synthetic_api_test
27
+ preview_url: url
28
+ values: { name: "preview ${ctx.scope}", locations: ["aws:us-east-2"] }
29
+ assumptions:
30
+ - id: DD1
31
+ verified: true
32
+ text: "Requests carry the API key in `DD-API-KEY` and the application key in `DD-APPLICATION-KEY`; Datadog's clients read them from `DD_API_KEY` and `DD_APP_KEY`. Reading needs the application key too."
33
+ source: https://github.com/DataDog/datadog-api-client-typescript/blob/master/.generator/schemas/v1/openapi.yaml
34
+ - id: DD2
35
+ verified: true
36
+ text: "Each Datadog site has its own API host (`https://api.datadoghq.eu`, `https://api.us3.datadoghq.com`, …): outside US1, set `base_url` in the provider block."
37
+ source: https://docs.datadoghq.com/api/latest/authentication/
38
+ - id: DD3
39
+ verified: true
40
+ text: "`POST /synthetics/tests/api` creates an API test from `name`, `type` (`api`), `config` (`request` with `method` and `url`, `assertions`), `locations`, `options` and `message` (all required), with `subtype` (`http`), `status` (`live` or `paused`) and `tags`, answering it with its `public_id`."
41
+ source: https://github.com/DataDog/datadog-api-client-typescript/blob/master/.generator/schemas/v1/openapi.yaml
42
+ - id: DD4
43
+ verified: true
44
+ text: "`GET /synthetics/tests/api/{public_id}` answers one API test; `PUT` on the same path replaces its configuration with a whole test definition."
45
+ source: https://github.com/DataDog/datadog-api-client-typescript/blob/master/.generator/schemas/v1/openapi.yaml
46
+ - id: DD5
47
+ verified: true
48
+ text: "`GET /synthetics/tests` lists every test as `{ tests: [...] }` (`page_size` and `page_number` are optional); `POST /synthetics/tests/delete` with `{ public_ids: [...] }` deletes tests."
49
+ source: https://github.com/DataDog/datadog-api-client-typescript/blob/master/.generator/schemas/v1/openapi.yaml
50
+ - id: DD6
51
+ verified: false
52
+ text: "Without `page_size` the list answers every test of the organisation, and a test name identifies one test (two with one name make `find` refuse with PARAM_INVALID)."
53
+ - id: DD7
54
+ verified: false
55
+ text: "The fields compared (`config.request`, `config.assertions`, `locations`, `options.tick_every`, `message`, `status`, `tags`) are answered as they were sent, in order."
56
+ - id: DD8
57
+ verified: false
58
+ text: "Deleting a test that is already gone answers 404 (destroy treats it as done)."
59
+ - id: DD9
60
+ verified: false
61
+ text: "Deployment events (`datadog-ci deployment mark`, the DORA deployments API) are not a recipe: they are listed with a POST over a time window and cannot be deleted, so a later run could not find the one it made."
62
+ ops:
63
+ synthetic_api_test:
64
+ kind: resource
65
+ title: Synthetic API test against a preview
66
+ summary: "A Synthetic HTTP test per scope that checks the preview's URL from Datadog's locations, such as `preview pr-42` asserting status 200, with its own monitor. Located by its name; a console edit of its request, assertions, locations, frequency, message, status or tags is drift; destroy deletes the test (and its monitor)."
67
+ covers: [datadog-marker]
68
+ params:
69
+ name:
70
+ type: string
71
+ required: true
72
+ description: The test's name; its identity (DD6).
73
+ example: "preview ${ctx.scope}"
74
+ url:
75
+ type: string
76
+ required: true
77
+ pattern: "^https?://\\S+$"
78
+ description: The URL to request (the preview's URL, or a health path on it).
79
+ example: "https://${ctx.scope}.preview.example.app/api/health"
80
+ method:
81
+ type: string
82
+ default: GET
83
+ enum: [GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS]
84
+ description: The HTTP method.
85
+ assertions:
86
+ type: list
87
+ default: [{ type: statusCode, operator: is, target: 200 }]
88
+ description: "The assertions, each `{ type, operator, target }` (`statusCode`, `responseTime`, `body`, `header`)."
89
+ locations:
90
+ type: list
91
+ required: true
92
+ description: "The managed locations to run from, such as `aws:us-east-2` or `aws:eu-central-1`."
93
+ example: ["aws:us-east-2"]
94
+ tick_every:
95
+ type: integer
96
+ default: 300
97
+ description: How often the test runs, in seconds.
98
+ message:
99
+ type: string
100
+ default: Preview check managed by Sponson
101
+ description: The notification message of the test's monitor.
102
+ status:
103
+ type: string
104
+ default: live
105
+ enum: [live, paused]
106
+ description: "`paused` keeps the test without running it."
107
+ tags:
108
+ type: list
109
+ default: ["managed-by:sponson"]
110
+ description: The test's tags.
111
+ http:
112
+ find:
113
+ path: /synthetics/tests
114
+ list_path: /tests
115
+ match: { name: { param: name } }
116
+ read: { path: "/synthetics/tests/api/{id}" }
117
+ create:
118
+ path: /synthetics/tests/api
119
+ body: { type: api, subtype: http }
120
+ update:
121
+ method: PUT
122
+ body: { type: api, subtype: http }
123
+ delete:
124
+ method: POST
125
+ path: /synthetics/tests/delete
126
+ body: { public_ids: ["{id}"] }
127
+ id_path: /public_id
128
+ fields:
129
+ /config/request/method: { param: method }
130
+ /config/request/url: { param: url }
131
+ /config/assertions: { param: assertions }
132
+ locations: { param: locations }
133
+ /options/tick_every: { param: tick_every }
134
+ message: { param: message }
135
+ status: { param: status }
136
+ tags: { param: tags }
@@ -0,0 +1,84 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Firebase Authentication (Identity Toolkit admin API) recipes. Format and checklist: docs/adr/0020-recipes.md,
3
+ # CONTRIBUTING.md "Adding a recipe".
4
+ provider: firebase
5
+ title: Firebase Authentication
6
+ category: auth
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Identity Toolkit API v2, projects.getConfig
10
+ url: https://docs.cloud.google.com/identity-platform/docs/reference/rest/v2/projects/getConfig
11
+ - title: Identity Toolkit API v2, projects.updateConfig
12
+ url: https://docs.cloud.google.com/identity-platform/docs/reference/rest/v2/projects/updateConfig
13
+ - title: Identity Toolkit API v2, Config
14
+ url: https://docs.cloud.google.com/identity-platform/docs/reference/rest/v2/Config
15
+ - title: Authenticate for using REST
16
+ url: https://docs.cloud.google.com/docs/authentication/rest
17
+ api:
18
+ base_url: https://identitytoolkit.googleapis.com/admin/v2
19
+ base_url_env: FIREBASE_API_URL
20
+ auth: { command: [gcloud, auth, print-access-token] }
21
+ detect:
22
+ deps: [firebase, firebase-admin]
23
+ env: ["FIREBASE_*"]
24
+ files: [firebase.json, .firebaserc]
25
+ starter:
26
+ - op: authorized_domain
27
+ where:
28
+ project: "`projects.default` in .firebaserc, or `firebase projects:list`"
29
+ domain: "the preview's host name without `https://` (Firebase takes a domain, so a `{ from }` URL cannot be used)"
30
+ assumptions:
31
+ - id: FB1
32
+ verified: true
33
+ text: "`GET https://identitytoolkit.googleapis.com/admin/v2/projects/{project}/config` answers the project's Auth `Config`."
34
+ source: https://docs.cloud.google.com/identity-platform/docs/reference/rest/v2/projects/getConfig
35
+ - id: FB2
36
+ verified: true
37
+ text: "`PATCH …/admin/v2/projects/{project}/config?updateMask=authorizedDomains` updates the config with a `Config` body; fields set in the body but not named in `updateMask` are ignored, so the write changes the authorized domains only. It needs one of the `identitytoolkit`, `firebase` or `cloud-platform` OAuth scopes."
38
+ source: https://docs.cloud.google.com/identity-platform/docs/reference/rest/v2/projects/updateConfig
39
+ - id: FB3
40
+ verified: true
41
+ text: "`authorizedDomains` is a list of strings, the domains authorized for OAuth redirects; the masked field is replaced by the list sent, so the recipe sends the whole list read just before with one domain added or removed."
42
+ source: https://docs.cloud.google.com/identity-platform/docs/reference/rest/v2/Config
43
+ - id: FB4
44
+ verified: true
45
+ text: "`gcloud auth print-access-token` prints an access token for the signed-in credentials, used as `Authorization: Bearer`. With user credentials some APIs also need the quota project in `x-goog-user-project`: add `headers: { x-goog-user-project: <project> }` to the `providers.http.firebase` block (not needed with a service account; a block's headers are part of its identity)."
46
+ source: https://docs.cloud.google.com/docs/authentication/rest
47
+ - id: FB5
48
+ verified: false
49
+ text: "The config has no precondition the update honours (no etag in `Config`): the line locks the project's config (`lockOn`) against other Sponson runs; a console edit made between the read and the write can still be lost."
50
+ - id: FB6
51
+ verified: false
52
+ text: "An authorized domain is a host name without scheme, port or path, and is read back exactly as written."
53
+ - id: FB7
54
+ verified: false
55
+ text: "The same admin v2 config holds the authorized domains of a Firebase Authentication project with or without the Identity Platform upgrade."
56
+ ops:
57
+ authorized_domain:
58
+ kind: list_item
59
+ title: Authorized domain
60
+ summary: "One domain in Firebase Authentication's authorized domains (Authentication, Settings, Authorized domains), which OAuth sign-in redirects and email links require, such as the preview deployment's host. Read-modify-write of the list with `updateMask=authorizedDomains` under a lock on the project's config; removed on destroy; every other domain (localhost, the Firebase hosting domains) is left as it is."
61
+ covers: [firebase-auth-domains]
62
+ params:
63
+ project:
64
+ type: string
65
+ required: true
66
+ description: The Google Cloud / Firebase project id.
67
+ example: demo-project
68
+ domain:
69
+ type: string
70
+ required: true
71
+ pattern: "^[A-Za-z0-9.-]+$"
72
+ description: "The domain, a host name without scheme or path (`pr-42.preview.example.app`, `my-app-git-feat-x.vercel.app`)."
73
+ example: "${ctx.scope}.preview.example.app"
74
+ http:
75
+ parent:
76
+ path: "/projects/{project}/config?updateMask=authorizedDomains"
77
+ read_path: "/projects/{project}/config"
78
+ list_path: /authorizedDomains
79
+ item: { param: domain }
80
+ sim:
81
+ objects:
82
+ "/projects/{project}/config": { name: projects/demo-project/config, authorizedDomains: [localhost, demo-project.firebaseapp.com, demo-project.web.app] }
83
+ live:
84
+ params: { project: SPONSON_LIVE_FIREBASE_PROJECT }
@@ -0,0 +1,98 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Flagsmith Admin (management) API recipes, authenticated by an organisation API key. Format and checklist:
3
+ # docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
4
+ provider: flagsmith
5
+ title: Flagsmith
6
+ category: flags
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Admin API authentication
10
+ url: https://docs.flagsmith.com/integrating-with-flagsmith/flagsmith-api-overview/admin-api/authentication
11
+ - title: Admin API overview
12
+ url: https://docs.flagsmith.com/integrating-with-flagsmith/flagsmith-api-overview/admin-api
13
+ - title: Flagsmith API OpenAPI document
14
+ url: https://api.flagsmith.com/api/v1/swagger.json
15
+ api:
16
+ base_url: https://api.flagsmith.com/api/v1
17
+ base_url_env: FLAGSMITH_API_URL
18
+ auth: { header: Authorization, value_env: FLAGSMITH_MASTER_API_KEY, prefix: "Api-Key " }
19
+ detect:
20
+ deps: [flagsmith, flagsmith-nodejs, react-native-flagsmith]
21
+ env: ["FLAGSMITH_*"]
22
+ starter:
23
+ - op: environment
24
+ values: { name: "preview-${ctx.scope}" }
25
+ where:
26
+ project_id: "Flagsmith → your project: the number in its URL (`/project/<id>/…`)"
27
+ assumptions:
28
+ - id: FS1
29
+ verified: true
30
+ text: "The Admin API authenticates with an organisation API key as `Authorization: Api-Key <key>`; Bearer is only for short-lived OIDC-exchanged tokens."
31
+ source: https://docs.flagsmith.com/integrating-with-flagsmith/flagsmith-api-overview/admin-api/authentication
32
+ - id: FS2
33
+ verified: true
34
+ text: "`POST /environments/` creates an environment from `name` and `project` (both required) and optional `description`, answering it (201) with its integer `id` and its `api_key`, the client-side environment key SDKs use."
35
+ source: https://api.flagsmith.com/api/v1/swagger.json
36
+ - id: FS3
37
+ verified: true
38
+ text: "`GET /environments/?project=<id>` lists a project's environments as `{ count, next, previous, results: [...] }`."
39
+ source: https://api.flagsmith.com/api/v1/swagger.json
40
+ - id: FS4
41
+ verified: true
42
+ text: "`GET`, `PATCH` and `DELETE /environments/{api_key}/` read, update and delete one environment (DELETE answers 204)."
43
+ source: https://api.flagsmith.com/api/v1/swagger.json
44
+ - id: FS5
45
+ verified: false
46
+ text: "A project has fewer environments than one page of the list holds, so `find` sees them all (`next` is a URL, which the http adapter does not follow)."
47
+ - id: FS6
48
+ verified: false
49
+ text: "A new environment starts with every feature of the project at its default state, and environment names are unique in a project in practice (two with one name make `find` refuse with PARAM_INVALID)."
50
+ - id: FS7
51
+ verified: false
52
+ text: "`description` is answered as sent; `FLAGSMITH_MASTER_API_KEY` is the variable Flagsmith's Terraform provider reads the key from."
53
+ - id: FS8
54
+ verified: false
55
+ text: "Identity and segment overrides are not recipes: Edge projects (Flagsmith SaaS's default) address identities by UUID through `edge-identities`, and a segment override is two objects (a feature segment, then a feature state) created in order."
56
+ - id: FS9
57
+ verified: false
58
+ text: "The create takes the project id as a string of digits (`\"1234\"`) as well as a number, as Django REST Framework's primary-key fields do."
59
+ ops:
60
+ environment:
61
+ kind: resource
62
+ title: Environment per preview
63
+ summary: "A Flagsmith environment per scope in a project, such as `preview-pr-42`, starting from the project's default flag states; its client-side environment key is the output `api_key` for the preview's SDK configuration. Located by name in the project; a console edit of its description is drift; destroy deletes the environment with its flag states and identities."
64
+ covers: [flagsmith-override]
65
+ params:
66
+ project_id:
67
+ type: string
68
+ required: true
69
+ pattern: "^[0-9]+$"
70
+ description: The project's id, digits as a string (FS9).
71
+ example: "1234"
72
+ name:
73
+ type: string
74
+ required: true
75
+ description: The environment's name; its identity in the project (FS6).
76
+ example: "preview-${ctx.scope}"
77
+ description:
78
+ type: string
79
+ default: Preview environment managed by Sponson
80
+ description: The environment's description.
81
+ http:
82
+ find:
83
+ path: "/environments/?project={project_id}"
84
+ list_path: /results
85
+ match: { name: { param: name } }
86
+ read: { path: "/environments/{id}/" }
87
+ create:
88
+ path: /environments/
89
+ body: { project: { param: project_id } }
90
+ update: { method: PATCH }
91
+ id_path: /api_key
92
+ fields:
93
+ description: { param: description }
94
+ outputs:
95
+ api_key: /api_key
96
+ environment_id: /id
97
+ live:
98
+ params: { project_id: SPONSON_LIVE_FLAGSMITH_PROJECT_ID }
@@ -0,0 +1,97 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Fly.io GraphQL API recipes (app secrets). Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ # The per-PR app itself is in fly_machines.yaml (the Machines API has another base URL).
4
+ provider: fly
5
+ title: Fly.io
6
+ category: deploy
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Secrets and Fly Apps
10
+ url: https://docs.fly.io/apps/secrets
11
+ - title: superfly/fly-go resource_secrets.go (the GraphQL operations flyctl sends)
12
+ url: https://github.com/superfly/fly-go/blob/main/resource_secrets.go
13
+ - title: superfly/fly-go schema.graphql
14
+ url: https://github.com/superfly/fly-go/blob/main/schema.graphql
15
+ - title: Working with the Machines API (tokens)
16
+ url: https://docs.fly.io/machines/api/working-with-machines-api/
17
+ api:
18
+ base_url: https://api.fly.io/graphql
19
+ base_url_env: FLY_API_URL
20
+ auth: { bearer_env: FLY_API_TOKEN }
21
+ detect:
22
+ env: ["FLY_*"]
23
+ files: [fly.toml]
24
+ starter:
25
+ - op: secret
26
+ where:
27
+ app: "the preview's Fly app: `app` in fly.toml, or the app per pull request a `fly_machines.app` line creates"
28
+ assumptions:
29
+ - id: FS1
30
+ verified: true
31
+ text: "`query ($appName: String!) { app(name: $appName) { secrets { name digest createdAt } } }` lists an app's secrets; values are never returned (Fly does not allow reading plain-text secret values), so the recipe locates a secret by name and compares nothing else."
32
+ source: https://github.com/superfly/fly-go/blob/main/resource_secrets.go
33
+ - id: FS2
34
+ verified: true
35
+ text: "`mutation($input: SetSecretsInput!) { setSecrets(input: $input) { release { id version } } }` with `{ appId, secrets: [{ key, value }] }` sets secrets; `unsetSecrets(input: { appId, keys })` removes them."
36
+ source: https://github.com/superfly/fly-go/blob/main/resource_secrets.go
37
+ - id: FS3
38
+ verified: true
39
+ text: "Plain-text values cannot be read back (`fly secrets list` shows a name, a digest and a date), so a value changed in the console, or in the plan, is not detected: drift is the secret's presence only. To change a value, run `sponson apply --destroy` for the scope (or remove the line, apply, and add it back)."
40
+ source: https://docs.fly.io/apps/secrets
41
+ - id: FS4
42
+ verified: true
43
+ text: "Machines read secrets as environment variables at boot: a secret set without updating the Machines (as `fly secrets set --stage` does) reaches only Machines started or updated afterwards. Order the line before the PR's `fly deploy`; after a later change run `fly secrets deploy`."
44
+ source: https://docs.fly.io/apps/secrets
45
+ - id: FS5
46
+ verified: false
47
+ text: "A token from `fly tokens create` (`FlyV1 fm2_…`) or `fly auth token` is accepted as `Authorization: Bearer <token>` by the GraphQL API, as the Machines API documents for the same `FLY_API_TOKEN`. (fly-go itself sends a macaroon as `FlyV1 <token>`; a block that needs that sends it with `auth: { header: Authorization, value_env: FLY_API_TOKEN }`.)"
48
+ - id: FS6
49
+ verified: false
50
+ text: "Unsetting a secret of an app that no longer exists answers an error saying the app could not be found, which the adapter reads as already gone; unsetting an absent key of an existing app is not an error. setSecrets through GraphQL updates no Machines (FS4). Current flyctl sets secrets through the Machines API instead; the GraphQL mutations are not marked deprecated."
51
+ ops:
52
+ secret:
53
+ kind: resource
54
+ title: App secret
55
+ summary: "One secret of a Fly app (typically the app made for the pull request, `fly_machines.app`), set with `setSecrets` and removed with `unsetSecrets`. Fly never returns secret values, so only its presence is compared: a secret unset in the console is recreated, a changed value is not noticed (FS3). Machines see it once started or updated after the write: order it before the PR's deploy (FS4)."
56
+ covers: [fly-secrets]
57
+ params:
58
+ app:
59
+ type: string
60
+ required: true
61
+ description: The app's name.
62
+ example: "demo-${ctx.scope}"
63
+ name:
64
+ type: string
65
+ required: true
66
+ pattern: "^[A-Za-z_][A-Za-z0-9_]*$"
67
+ description: The secret's name (the environment variable the Machines see).
68
+ example: DATABASE_URL
69
+ value:
70
+ type: string
71
+ required: true
72
+ description: The secret's value, normally a `{ secret }` reference. It is sent, never stored or compared.
73
+ example: "postgres://app@db.internal:5432/${ctx.scope}"
74
+ http:
75
+ find:
76
+ graphql:
77
+ query: "query ($appName: String!) { app(name: $appName) { secrets { name digest createdAt } } }"
78
+ variables: { appName: { param: app } }
79
+ data_path: /app/secrets
80
+ match: { name: { param: name } }
81
+ create:
82
+ graphql:
83
+ query: "mutation($input: SetSecretsInput!) { setSecrets(input: $input) { release { id version } } }"
84
+ variables: { input: { appId: { param: app }, secrets: [{ key: { param: name }, value: { param: value } }] } }
85
+ idempotent: true
86
+ delete:
87
+ graphql:
88
+ query: "mutation($input: UnsetSecretsInput!) { unsetSecrets(input: $input) { release { id version } } }"
89
+ variables: { input: { appId: { param: app }, keys: [{ param: name }] } }
90
+ sim:
91
+ provider: fly
92
+ seed: { apps: { demo-pr-42: {} } }
93
+ present: /apps/demo-pr-42/secrets/DATABASE_URL
94
+ drift:
95
+ missing: { secrets.demo-pr-42.DATABASE_URL: delete }
96
+ live:
97
+ params: { app: SPONSON_LIVE_FLY_APP }
@@ -0,0 +1,67 @@
1
+ # yaml-language-server: $schema=../../../schema/recipe.schema.json
2
+ # Fly.io Machines API recipes (the per-PR app). Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
3
+ # The app's secrets are in fly.yaml (the GraphQL API has another base URL).
4
+ provider: fly_machines
5
+ title: Fly.io Machines API
6
+ category: deploy
7
+ verified_on: "2026-10-11"
8
+ docs:
9
+ - title: Working with the Machines API
10
+ url: https://docs.fly.io/machines/api/working-with-machines-api/
11
+ - title: Apps resource
12
+ url: https://docs.fly.io/machines/api/apps-resource/
13
+ api:
14
+ base_url: https://api.machines.dev/v1
15
+ base_url_env: FLY_MACHINES_API_URL
16
+ auth: { bearer_env: FLY_API_TOKEN }
17
+ assumptions:
18
+ - id: FM1
19
+ verified: true
20
+ text: "The Machines API's public base URL is `https://api.machines.dev`, its paths start with `/v1`, and requests carry `Authorization: Bearer <fly_api_token>`."
21
+ source: https://docs.fly.io/machines/api/working-with-machines-api/
22
+ - id: FM2
23
+ verified: true
24
+ text: "`POST /v1/apps` with `app_name` (unique) and `org_slug` (both required) and optional `network` creates an app and answers 201 with `id` and `created_at`."
25
+ source: https://docs.fly.io/machines/api/apps-resource/
26
+ - id: FM3
27
+ verified: true
28
+ text: "`GET /v1/apps/{app_name}` answers the app (`id`, `name`, `status`, `organization { name, slug }`), so the name the plan chooses locates it."
29
+ source: https://docs.fly.io/machines/api/apps-resource/
30
+ - id: FM4
31
+ verified: true
32
+ text: "`DELETE /v1/apps/{app_name}` answers 202; Machines should be stopped first, and `?force=true` stops them and deletes the app at once, which is what the recipe's destroy sends (a PR app has the PR's Machines running)."
33
+ source: https://docs.fly.io/machines/api/apps-resource/
34
+ - id: FM5
35
+ verified: false
36
+ text: "Deleting the app deletes its secrets and its Machines with it; an app name another organization holds is refused with a conflict, which the adapter then fails to read (403/404) and reports."
37
+ ops:
38
+ app:
39
+ kind: resource
40
+ title: App
41
+ summary: "A Fly app per pull request, such as `demo-${ctx.scope}`, in an organization: created with `POST /v1/apps`, located by its name, deleted with its Machines on destroy (`?force=true`). Its secrets are `fly.secret` lines; the Machines are what the PR's `fly deploy` creates. Nothing about the app is compared besides its existence."
42
+ covers: [fly-secrets]
43
+ params:
44
+ app_name:
45
+ type: string
46
+ required: true
47
+ pattern: "^[a-z0-9][a-z0-9-]*$"
48
+ description: The app's name, unique across Fly.io.
49
+ example: "demo-${ctx.scope}"
50
+ org_slug:
51
+ type: string
52
+ required: true
53
+ description: The organization the app is created in.
54
+ example: acme
55
+ network:
56
+ type: string
57
+ description: A private network (6PN) to put the app on.
58
+ http:
59
+ read: { path: "/apps/{app_name}" }
60
+ create:
61
+ path: /apps
62
+ body: { app_name: { param: app_name }, org_slug: { param: org_slug }, network: { param: network } }
63
+ delete: { path: "/apps/{app_name}?force=true" }
64
+ sim:
65
+ id_from: app_name
66
+ live:
67
+ params: { org_slug: SPONSON_LIVE_FLY_ORG }