@sponson/adapters 0.4.0 → 0.6.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.
- package/README.md +1 -1
- package/dist/algolia.d.ts +6 -0
- package/dist/algolia.js +151 -0
- package/dist/algolia.js.map +1 -0
- package/dist/clerk.js +1 -1
- package/dist/clerk.js.map +1 -1
- package/dist/cloud_run.d.ts +12 -0
- package/dist/cloud_run.js +469 -0
- package/dist/cloud_run.js.map +1 -0
- package/dist/cloudflare.d.ts +25 -0
- package/dist/cloudflare.js +310 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/common.d.ts +2 -6
- package/dist/common.js.map +1 -1
- package/dist/digest.d.ts +46 -0
- package/dist/digest.js +129 -0
- package/dist/digest.js.map +1 -0
- package/dist/digitalocean.d.ts +5 -0
- package/dist/digitalocean.js +243 -0
- package/dist/digitalocean.js.map +1 -0
- package/dist/http-adapter-action.d.ts +63 -0
- package/dist/http-adapter-action.js +369 -0
- package/dist/http-adapter-action.js.map +1 -0
- package/dist/http-adapter-auth.d.ts +94 -0
- package/dist/http-adapter-auth.js +440 -0
- package/dist/http-adapter-auth.js.map +1 -0
- package/dist/http-adapter-client.d.ts +57 -0
- package/dist/http-adapter-client.js +163 -0
- package/dist/http-adapter-client.js.map +1 -0
- package/dist/http-adapter-graphql.d.ts +39 -0
- package/dist/http-adapter-graphql.js +157 -0
- package/dist/http-adapter-graphql.js.map +1 -0
- package/dist/http-adapter-list.d.ts +33 -0
- package/dist/http-adapter-list.js +421 -0
- package/dist/http-adapter-list.js.map +1 -0
- package/dist/http-adapter-resource.d.ts +29 -0
- package/dist/http-adapter-resource.js +462 -0
- package/dist/http-adapter-resource.js.map +1 -0
- package/dist/http-adapter-spec.d.ts +216 -0
- package/dist/http-adapter-spec.js +660 -0
- package/dist/http-adapter-spec.js.map +1 -0
- package/dist/http-adapter.d.ts +16 -0
- package/dist/http-adapter.js +28 -0
- package/dist/http-adapter.js.map +1 -0
- package/dist/http.d.ts +61 -2
- package/dist/http.js +79 -18
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.js +26 -2
- package/dist/index.js.map +1 -1
- package/dist/launchdarkly.d.ts +15 -0
- package/dist/launchdarkly.js +337 -0
- package/dist/launchdarkly.js.map +1 -0
- package/dist/manual.d.ts +13 -0
- package/dist/manual.js +201 -0
- package/dist/manual.js.map +1 -0
- package/dist/neon.js +1 -1
- package/dist/neon.js.map +1 -1
- package/dist/netlify.d.ts +18 -0
- package/dist/netlify.js +436 -0
- package/dist/netlify.js.map +1 -0
- package/dist/planetscale.d.ts +10 -0
- package/dist/planetscale.js +311 -0
- package/dist/planetscale.js.map +1 -0
- package/dist/providers.d.ts +28 -0
- package/dist/providers.js +41 -0
- package/dist/providers.js.map +1 -0
- package/dist/recipes.d.ts +167 -0
- package/dist/recipes.js +246 -0
- package/dist/recipes.js.map +1 -0
- package/dist/secrets.d.ts +44 -0
- package/dist/secrets.js +279 -0
- package/dist/secrets.js.map +1 -1
- package/dist/sigv4.d.ts +56 -0
- package/dist/sigv4.js +118 -0
- package/dist/sigv4.js.map +1 -0
- package/dist/supabase.d.ts +9 -0
- package/dist/supabase.js +385 -0
- package/dist/supabase.js.map +1 -0
- package/dist/vercel.js +1 -1
- package/dist/vercel.js.map +1 -1
- package/dist/xata.d.ts +8 -0
- package/dist/xata.js +142 -0
- package/dist/xata.js.map +1 -0
- package/dist/xml.d.ts +25 -0
- package/dist/xml.js +211 -0
- package/dist/xml.js.map +1 -0
- package/package.json +11 -4
- package/recipes/amplify.yaml +147 -0
- package/recipes/auth0.yaml +178 -0
- package/recipes/azure_swa.yaml +116 -0
- package/recipes/cloudflare.yaml +108 -0
- package/recipes/cockroachdb.yaml +132 -0
- package/recipes/configcat.yaml +123 -0
- package/recipes/datadog.yaml +136 -0
- package/recipes/firebase.yaml +84 -0
- package/recipes/flagsmith.yaml +98 -0
- package/recipes/fly.yaml +97 -0
- package/recipes/fly_machines.yaml +67 -0
- package/recipes/gcs.yaml +102 -0
- package/recipes/github.yaml +124 -0
- package/recipes/growthbook.yaml +124 -0
- package/recipes/heroku.yaml +98 -0
- package/recipes/honeycomb.yaml +89 -0
- package/recipes/inngest.yaml +97 -0
- package/recipes/kinde.yaml +120 -0
- package/recipes/mongodb_atlas.yaml +119 -0
- package/recipes/okta.yaml +177 -0
- package/recipes/posthog.yaml +127 -0
- package/recipes/postmark.yaml +105 -0
- package/recipes/postmark_server.yaml +93 -0
- package/recipes/prisma.yaml +150 -0
- package/recipes/qstash.yaml +134 -0
- package/recipes/railway.yaml +130 -0
- package/recipes/render.yaml +103 -0
- package/recipes/resend.yaml +130 -0
- package/recipes/s3.yaml +108 -0
- package/recipes/sendgrid.yaml +165 -0
- package/recipes/sentry.yaml +158 -0
- package/recipes/split.yaml +121 -0
- package/recipes/statsig.yaml +124 -0
- package/recipes/stripe.yaml +310 -0
- package/recipes/stytch.yaml +113 -0
- package/recipes/svix.yaml +109 -0
- package/recipes/turso.yaml +98 -0
- package/recipes/unleash.yaml +149 -0
- package/recipes/upstash.yaml +130 -0
- package/recipes/vercel.yaml +116 -0
- package/recipes/workos.yaml +70 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
# yaml-language-server: $schema=../../../schema/recipe.schema.json
|
|
2
|
+
# Stripe API v1 recipes: a test-mode webhook endpoint per preview, and the catalog objects (products, prices,
|
|
3
|
+
# coupons) a preview's code expects. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
|
|
4
|
+
provider: stripe
|
|
5
|
+
title: Stripe
|
|
6
|
+
category: webhooks
|
|
7
|
+
verified_on: "2026-10-11"
|
|
8
|
+
docs:
|
|
9
|
+
- title: Authentication
|
|
10
|
+
url: https://docs.stripe.com/api/authentication
|
|
11
|
+
- title: API keys
|
|
12
|
+
url: https://docs.stripe.com/keys
|
|
13
|
+
- title: Idempotent requests
|
|
14
|
+
url: https://docs.stripe.com/api/idempotent_requests
|
|
15
|
+
- title: Create a webhook endpoint
|
|
16
|
+
url: https://docs.stripe.com/api/webhook_endpoints/create
|
|
17
|
+
- title: List all webhook endpoints
|
|
18
|
+
url: https://docs.stripe.com/api/webhook_endpoints/list
|
|
19
|
+
- title: Update a webhook endpoint
|
|
20
|
+
url: https://docs.stripe.com/api/webhook_endpoints/update
|
|
21
|
+
- title: Delete a webhook endpoint
|
|
22
|
+
url: https://docs.stripe.com/api/webhook_endpoints/delete
|
|
23
|
+
- title: Webhooks in the Dashboard (endpoint limit)
|
|
24
|
+
url: https://docs.stripe.com/development/dashboard/webhooks
|
|
25
|
+
- title: Create a product
|
|
26
|
+
url: https://docs.stripe.com/api/products/create
|
|
27
|
+
- title: Retrieve a product
|
|
28
|
+
url: https://docs.stripe.com/api/products/retrieve
|
|
29
|
+
- title: Delete a product
|
|
30
|
+
url: https://docs.stripe.com/api/products/delete
|
|
31
|
+
- title: Create a price
|
|
32
|
+
url: https://docs.stripe.com/api/prices/create
|
|
33
|
+
- title: List all prices
|
|
34
|
+
url: https://docs.stripe.com/api/prices/list
|
|
35
|
+
- title: Update a price
|
|
36
|
+
url: https://docs.stripe.com/api/prices/update
|
|
37
|
+
- title: Create a coupon
|
|
38
|
+
url: https://docs.stripe.com/api/coupons/create
|
|
39
|
+
api:
|
|
40
|
+
base_url: https://api.stripe.com/v1
|
|
41
|
+
base_url_env: STRIPE_API_URL
|
|
42
|
+
auth: { bearer_env: STRIPE_SECRET_KEY }
|
|
43
|
+
encoding: form
|
|
44
|
+
# A preview must never create live-mode objects (ST2): live keys are refused unless this variable says otherwise.
|
|
45
|
+
refuse_credential: { prefixes: [sk_live_, rk_live_], unless_env: SPONSON_STRIPE_ALLOW_LIVE }
|
|
46
|
+
detect:
|
|
47
|
+
deps: [stripe, "@stripe/"]
|
|
48
|
+
env: ["STRIPE_*"]
|
|
49
|
+
starter:
|
|
50
|
+
- op: webhook_endpoint
|
|
51
|
+
preview_url: url
|
|
52
|
+
values: { name: "preview-${ctx.scope}" }
|
|
53
|
+
assumptions:
|
|
54
|
+
- id: ST1
|
|
55
|
+
verified: true
|
|
56
|
+
text: "The API takes a secret or restricted key as `Authorization: Bearer` (besides Basic), and request bodies form-encoded with bracketed nesting (`metadata[k]=v`, `enabled_events[0]=x`)."
|
|
57
|
+
source: https://docs.stripe.com/api/authentication
|
|
58
|
+
- id: ST2
|
|
59
|
+
verified: true
|
|
60
|
+
text: "Test-mode keys start with `sk_test_` / `rk_test_`, live-mode keys with `sk_live_` / `rk_live_`; objects of one mode are invisible to the other. The recipe refuses the live prefixes (`refuse_credential`) unless `SPONSON_STRIPE_ALLOW_LIVE=1`."
|
|
61
|
+
source: https://docs.stripe.com/keys
|
|
62
|
+
- id: ST3
|
|
63
|
+
verified: false
|
|
64
|
+
text: "Organization keys (`sk_org_…`) and platform-managed keys do not carry the mode in their prefix, so they are not refused: use a test-mode secret or restricted key for previews."
|
|
65
|
+
- id: ST4
|
|
66
|
+
verified: true
|
|
67
|
+
text: "Every v1 `POST` accepts an `Idempotency-Key` (up to 255 characters, kept at least 24 hours); a retry with the same key returns the saved answer, and different parameters under one key are an error."
|
|
68
|
+
source: https://docs.stripe.com/api/idempotent_requests
|
|
69
|
+
- id: ST5
|
|
70
|
+
verified: true
|
|
71
|
+
text: "`POST /v1/webhook_endpoints` takes `url`, `enabled_events` (`['*']` for all), `description`, `metadata` and `api_version`, and answers the endpoint with its signing `secret` populated."
|
|
72
|
+
source: https://docs.stripe.com/api/webhook_endpoints/create
|
|
73
|
+
- id: ST6
|
|
74
|
+
verified: true
|
|
75
|
+
text: "`GET /v1/webhook_endpoints` (`limit` 1–100) answers `{ data: [...], has_more }` whose endpoints carry `url`, `enabled_events`, `description` and `metadata` but no `secret`: the secret exists only in the create's answer, hence the `once` output."
|
|
76
|
+
source: https://docs.stripe.com/api/webhook_endpoints/list
|
|
77
|
+
- id: ST7
|
|
78
|
+
verified: true
|
|
79
|
+
text: "An account has at most 16 webhook endpoints, so one list page of 100 holds them all, and at most 16 previews (minus the account's own endpoints) can have one at a time."
|
|
80
|
+
source: https://docs.stripe.com/development/dashboard/webhooks
|
|
81
|
+
- id: ST8
|
|
82
|
+
verified: true
|
|
83
|
+
text: "`POST /v1/webhook_endpoints/{id}` updates `url`, `enabled_events`, `description`, `metadata` and `disabled` in place (the secret is not rotated); `DELETE` on the same path removes the endpoint."
|
|
84
|
+
source: https://docs.stripe.com/api/webhook_endpoints/update
|
|
85
|
+
- id: ST9
|
|
86
|
+
verified: false
|
|
87
|
+
text: "The API returns `url`, `enabled_events` and `description` as they were written, `enabled_events` in the order sent, so an unchanged endpoint hashes the same on every plan."
|
|
88
|
+
- id: ST10
|
|
89
|
+
verified: true
|
|
90
|
+
text: "`POST /v1/products` takes a client-chosen `id` (unique among the account's products), `name`, `description` and `active`; `GET /v1/products/{id}` retrieves it; `POST /v1/products/{id}` updates it."
|
|
91
|
+
source: https://docs.stripe.com/api/products/create
|
|
92
|
+
- id: ST11
|
|
93
|
+
verified: true
|
|
94
|
+
text: "A product can be deleted only while it has no prices, and prices are never deleted, so the recipe archives a product (`active: false`) instead of deleting it."
|
|
95
|
+
source: https://docs.stripe.com/api/products/delete
|
|
96
|
+
- id: ST12
|
|
97
|
+
verified: false
|
|
98
|
+
text: "An archived product is still returned by `GET /v1/products/{id}`, and its id stays taken: re-applying a destroyed scope finds it and reactivates it as an adopted object, which a later destroy leaves active."
|
|
99
|
+
- id: ST13
|
|
100
|
+
verified: true
|
|
101
|
+
text: "`POST /v1/prices` takes `product`, `currency`, `unit_amount`, `recurring[interval]`, `nickname`, `lookup_key` and `transfer_lookup_key` (which moves the lookup key from the price holding it to the new one)."
|
|
102
|
+
source: https://docs.stripe.com/api/prices/create
|
|
103
|
+
- id: ST14
|
|
104
|
+
verified: true
|
|
105
|
+
text: "`GET /v1/prices` lists active prices by default, filters by up to 10 `lookup_keys` and by `active`, and answers `{ data: [...] }`."
|
|
106
|
+
source: https://docs.stripe.com/api/prices/list
|
|
107
|
+
- id: ST15
|
|
108
|
+
verified: true
|
|
109
|
+
text: "A price's amount, currency and interval cannot change after creation; `POST /v1/prices/{id}` updates `active`, `nickname`, `lookup_key` and `metadata`. There is no delete: the recipe archives (`active: false`)."
|
|
110
|
+
source: https://docs.stripe.com/api/prices/update
|
|
111
|
+
- id: ST16
|
|
112
|
+
verified: true
|
|
113
|
+
text: "`POST /v1/coupons` takes a client-chosen `id`, `name`, `percent_off` or `amount_off` with `currency`, `duration` (`once`, `repeating`, `forever`) and `duration_in_months`."
|
|
114
|
+
source: https://docs.stripe.com/api/coupons/create
|
|
115
|
+
- id: ST17
|
|
116
|
+
verified: false
|
|
117
|
+
text: "`GET /v1/coupons/{id}` retrieves a coupon, `POST /v1/coupons/{id}` updates its `name` and `metadata` only (the discount is fixed), and `DELETE /v1/coupons/{id}` deletes it, after which its id may be used again."
|
|
118
|
+
source: https://docs.stripe.com/api/coupons/create
|
|
119
|
+
- id: ST18
|
|
120
|
+
verified: false
|
|
121
|
+
text: "Stripe must reach the endpoint's URL over public HTTPS: a preview behind deployment protection (Vercel Authentication) receives no events unless it lets Stripe's requests through."
|
|
122
|
+
ops:
|
|
123
|
+
webhook_endpoint:
|
|
124
|
+
kind: resource
|
|
125
|
+
title: Webhook endpoint
|
|
126
|
+
summary: "A test-mode webhook endpoint that sends the preview's events to its own URL, `url` + `path` (usually `{ from: web.preview_url }`). Identified by `name` in its metadata, so a new preview URL updates the endpoint in place. Its signing `secret` exists only in the answer that creates the endpoint: it is a once-only output, passed in the same run to the line that writes it (`STRIPE_WEBHOOK_SECRET` in a `vercel.env` or `netlify.env` line); later runs keep what that line holds, and `apply --recreate <line>` rotates it. Writing the secret rebuilds the preview, and a preview URL output follows the newest deploy, so the next run moves the endpoint to the rebuilt deploy's URL (in place: the secret is kept); a URL that does not change per deploy (`https://deploy-preview-${ctx.pr.number}--<site>.netlify.app`, a custom preview domain) avoids that update. A console edit of its URL, events or description is drift."
|
|
127
|
+
covers: [stripe-webhook]
|
|
128
|
+
params:
|
|
129
|
+
name:
|
|
130
|
+
type: string
|
|
131
|
+
required: true
|
|
132
|
+
pattern: "^[A-Za-z0-9_.:/-]{1,200}$"
|
|
133
|
+
description: "The endpoint's identity, kept in its metadata (`sponson_endpoint`): unique per preview, such as `preview-${ctx.scope}`."
|
|
134
|
+
example: "preview-${ctx.scope}"
|
|
135
|
+
url:
|
|
136
|
+
type: string
|
|
137
|
+
required: true
|
|
138
|
+
pattern: "^https://\\S*[^/]$"
|
|
139
|
+
description: "The preview's base URL, without a trailing slash: usually `{ from: <vercel.env or netlify.env line>.preview_url }`."
|
|
140
|
+
example: https://pr-42.preview.example.com
|
|
141
|
+
path:
|
|
142
|
+
type: string
|
|
143
|
+
default: /api/webhooks/stripe
|
|
144
|
+
pattern: "^/\\S*$"
|
|
145
|
+
description: The path of the app's webhook handler, appended to `url`.
|
|
146
|
+
enabled_events:
|
|
147
|
+
type: list
|
|
148
|
+
default: ["*"]
|
|
149
|
+
description: "The event types sent to the endpoint (`['*']`: all that need no explicit selection)."
|
|
150
|
+
example: [checkout.session.completed, customer.subscription.updated]
|
|
151
|
+
description:
|
|
152
|
+
type: string
|
|
153
|
+
description: A note shown in the Dashboard.
|
|
154
|
+
example: Preview webhook managed by Sponson
|
|
155
|
+
api_version:
|
|
156
|
+
type: string
|
|
157
|
+
description: The Stripe API version events are rendered with (the account's default when left out); sent on create only.
|
|
158
|
+
http:
|
|
159
|
+
find:
|
|
160
|
+
path: /webhook_endpoints?limit=100
|
|
161
|
+
list_path: /data
|
|
162
|
+
match: { /metadata/sponson_endpoint: { param: name } }
|
|
163
|
+
create:
|
|
164
|
+
path: /webhook_endpoints
|
|
165
|
+
idempotency_key: true
|
|
166
|
+
body: { api_version: { param: api_version } }
|
|
167
|
+
update: { method: POST, path: "/webhook_endpoints/{id}" }
|
|
168
|
+
delete: { path: "/webhook_endpoints/{id}" }
|
|
169
|
+
fields:
|
|
170
|
+
url: { join: [{ param: url }, { param: path }] }
|
|
171
|
+
enabled_events: { param: enabled_events }
|
|
172
|
+
description: { param: description }
|
|
173
|
+
outputs:
|
|
174
|
+
secret: { path: /secret, sensitive: true, once: true }
|
|
175
|
+
live:
|
|
176
|
+
params: { url: SPONSON_LIVE_STRIPE_WEBHOOK_URL }
|
|
177
|
+
product:
|
|
178
|
+
kind: resource
|
|
179
|
+
title: Product
|
|
180
|
+
summary: "A test-mode product with an id of the plan's choosing, such as `pro-pr-42`, for the prices a preview sells. Destroy archives it (`active: false`): Stripe never deletes a product that has prices. A console edit of its name, description or active flag is drift."
|
|
181
|
+
covers: [stripe-test-objects]
|
|
182
|
+
params:
|
|
183
|
+
product_id:
|
|
184
|
+
type: string
|
|
185
|
+
required: true
|
|
186
|
+
pattern: "^[A-Za-z0-9_-]{1,100}$"
|
|
187
|
+
description: The product's id, unique in the account, such as `pro-${ctx.scope}`.
|
|
188
|
+
example: "pro-${ctx.scope}"
|
|
189
|
+
name:
|
|
190
|
+
type: string
|
|
191
|
+
required: true
|
|
192
|
+
description: The product's name, shown to customers.
|
|
193
|
+
example: Pro (preview)
|
|
194
|
+
description:
|
|
195
|
+
type: string
|
|
196
|
+
description: The product's description, shown to customers.
|
|
197
|
+
http:
|
|
198
|
+
read: { path: "/products/{product_id}" }
|
|
199
|
+
create: { path: /products, idempotency_key: true, body: { id: { param: product_id } } }
|
|
200
|
+
update: { method: POST }
|
|
201
|
+
delete: { method: POST, body: { active: false } }
|
|
202
|
+
fields:
|
|
203
|
+
name: { param: name }
|
|
204
|
+
description: { param: description }
|
|
205
|
+
active: true
|
|
206
|
+
price:
|
|
207
|
+
kind: resource
|
|
208
|
+
title: Price
|
|
209
|
+
summary: "A test-mode price found by its lookup key (`lookup`, such as `pro-monthly-pr-42`, which the preview's code looks the price up by), product, currency, amount and interval. Amount, currency and interval cannot change on a Stripe price, so changing them in the plan makes a new price that takes over the lookup key, and the old one stays until the scope is destroyed. Destroy archives it (`active: false`): Stripe never deletes a price. A console edit of its nickname is drift."
|
|
210
|
+
covers: [stripe-test-objects]
|
|
211
|
+
params:
|
|
212
|
+
lookup:
|
|
213
|
+
type: string
|
|
214
|
+
required: true
|
|
215
|
+
pattern: "^\\S{1,200}$"
|
|
216
|
+
description: "The price's `lookup_key`: what the preview's code looks it up by, unique among active prices. (Named `lookup` because a plan key ending in `_key` must hold a secret reference.)"
|
|
217
|
+
example: "pro-monthly-${ctx.scope}"
|
|
218
|
+
product:
|
|
219
|
+
type: string
|
|
220
|
+
required: true
|
|
221
|
+
description: "The product's id: usually `{ from: <stripe.product line>.id }`."
|
|
222
|
+
example: pro-pr-42
|
|
223
|
+
currency:
|
|
224
|
+
type: string
|
|
225
|
+
required: true
|
|
226
|
+
pattern: "^[a-z]{3}$"
|
|
227
|
+
description: Three-letter ISO currency code, lower-case.
|
|
228
|
+
example: usd
|
|
229
|
+
unit_amount:
|
|
230
|
+
type: integer
|
|
231
|
+
required: true
|
|
232
|
+
description: The amount in the currency's smallest unit (1500 is 15.00 USD).
|
|
233
|
+
example: 1500
|
|
234
|
+
interval:
|
|
235
|
+
type: string
|
|
236
|
+
enum: [day, week, month, year]
|
|
237
|
+
description: The billing interval of a recurring price; left out, the price is one-time.
|
|
238
|
+
example: month
|
|
239
|
+
nickname:
|
|
240
|
+
type: string
|
|
241
|
+
description: A note on the price, hidden from customers.
|
|
242
|
+
example: Pro monthly (preview)
|
|
243
|
+
http:
|
|
244
|
+
find:
|
|
245
|
+
path: "/prices?lookup_keys[]={lookup}&active=true&limit=10"
|
|
246
|
+
list_path: /data
|
|
247
|
+
match:
|
|
248
|
+
lookup_key: { param: lookup }
|
|
249
|
+
active: true
|
|
250
|
+
product: { param: product }
|
|
251
|
+
currency: { param: currency }
|
|
252
|
+
unit_amount: { param: unit_amount }
|
|
253
|
+
/recurring/interval: { param: interval }
|
|
254
|
+
create: { path: /prices, idempotency_key: true, body: { transfer_lookup_key: true } }
|
|
255
|
+
update: { method: POST, path: "/prices/{id}" }
|
|
256
|
+
delete: { method: POST, path: "/prices/{id}", body: { active: false } }
|
|
257
|
+
fields:
|
|
258
|
+
nickname: { param: nickname }
|
|
259
|
+
live:
|
|
260
|
+
params: { product: SPONSON_LIVE_STRIPE_PRODUCT }
|
|
261
|
+
coupon:
|
|
262
|
+
kind: resource
|
|
263
|
+
title: Coupon
|
|
264
|
+
summary: "A test-mode coupon with an id of the plan's choosing, such as `preview-pr-42`, for the discount codes a preview accepts. Its discount (percent or amount, duration) is set on create and fixed after; give the line a new `coupon_id` to change it. Destroy deletes it. A console edit of its name is drift."
|
|
265
|
+
covers: [stripe-test-objects]
|
|
266
|
+
params:
|
|
267
|
+
coupon_id:
|
|
268
|
+
type: string
|
|
269
|
+
required: true
|
|
270
|
+
pattern: "^[A-Za-z0-9_-]{1,100}$"
|
|
271
|
+
description: The coupon's id (the code applied to a customer), unique in the account.
|
|
272
|
+
example: "preview-${ctx.scope}"
|
|
273
|
+
percent_off:
|
|
274
|
+
type: number
|
|
275
|
+
description: The discount in percent (0–100); or set `amount_off` and `currency`.
|
|
276
|
+
example: 25
|
|
277
|
+
amount_off:
|
|
278
|
+
type: integer
|
|
279
|
+
description: The discount in the currency's smallest unit, with `currency`.
|
|
280
|
+
currency:
|
|
281
|
+
type: string
|
|
282
|
+
pattern: "^[a-z]{3}$"
|
|
283
|
+
description: The currency of `amount_off`.
|
|
284
|
+
duration:
|
|
285
|
+
type: string
|
|
286
|
+
enum: [once, repeating, forever]
|
|
287
|
+
default: once
|
|
288
|
+
description: How long the discount applies to a subscription.
|
|
289
|
+
duration_in_months:
|
|
290
|
+
type: integer
|
|
291
|
+
description: "For `duration: repeating`, the number of months."
|
|
292
|
+
name:
|
|
293
|
+
type: string
|
|
294
|
+
description: The name shown to customers (the id when left out).
|
|
295
|
+
example: Preview discount
|
|
296
|
+
http:
|
|
297
|
+
read: { path: "/coupons/{coupon_id}" }
|
|
298
|
+
create:
|
|
299
|
+
path: /coupons
|
|
300
|
+
idempotency_key: true
|
|
301
|
+
body:
|
|
302
|
+
id: { param: coupon_id }
|
|
303
|
+
percent_off: { param: percent_off }
|
|
304
|
+
amount_off: { param: amount_off }
|
|
305
|
+
currency: { param: currency }
|
|
306
|
+
duration: { param: duration }
|
|
307
|
+
duration_in_months: { param: duration_in_months }
|
|
308
|
+
update: { method: POST }
|
|
309
|
+
fields:
|
|
310
|
+
name: { param: name }
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# yaml-language-server: $schema=../../../schema/recipe.schema.json
|
|
2
|
+
# Stytch Programmatic Workspace Actions (management API v3) recipes. Format and checklist: docs/adr/0020-recipes.md,
|
|
3
|
+
# CONTRIBUTING.md "Adding a recipe".
|
|
4
|
+
provider: stytch
|
|
5
|
+
title: Stytch
|
|
6
|
+
category: auth
|
|
7
|
+
verified_on: "2026-10-11"
|
|
8
|
+
docs:
|
|
9
|
+
- title: Create redirect URL (PWA v3)
|
|
10
|
+
url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/create-redirect-url
|
|
11
|
+
- title: Get all redirect URLs (PWA v3)
|
|
12
|
+
url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/get-all-redirect-urls
|
|
13
|
+
- title: Get redirect URL (PWA v3)
|
|
14
|
+
url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/get-redirect-url
|
|
15
|
+
- title: Update redirect URL (PWA v3)
|
|
16
|
+
url: https://stytch.com/docs/workspace-management/pwa/update-redirect-url
|
|
17
|
+
- title: Delete redirect URL (PWA v3)
|
|
18
|
+
url: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/delete-redirect-url
|
|
19
|
+
- title: Limits
|
|
20
|
+
url: https://stytch.com/docs/workspace-management/pwa/limits
|
|
21
|
+
api:
|
|
22
|
+
base_url: https://management.stytch.com
|
|
23
|
+
base_url_env: STYTCH_MANAGEMENT_API_URL
|
|
24
|
+
auth: { basic: { user_env: STYTCH_WORKSPACE_KEY_ID, password_env: STYTCH_WORKSPACE_KEY_SECRET } }
|
|
25
|
+
detect:
|
|
26
|
+
deps: [stytch, "@stytch/"]
|
|
27
|
+
env: ["STYTCH_*"]
|
|
28
|
+
starter:
|
|
29
|
+
- op: redirect_url
|
|
30
|
+
where:
|
|
31
|
+
project_slug: "Stytch Dashboard → Project settings: the project's slug"
|
|
32
|
+
url: "the exact redirect URL Stytch receives on a preview: its URL plus the path (a `{ from }` reference cannot carry a path)"
|
|
33
|
+
assumptions:
|
|
34
|
+
- id: SY1
|
|
35
|
+
verified: true
|
|
36
|
+
text: "The management API authenticates with HTTP Basic: a workspace management key's id as the user and its secret as the password."
|
|
37
|
+
source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/create-redirect-url
|
|
38
|
+
- id: SY2
|
|
39
|
+
verified: true
|
|
40
|
+
text: "`POST /pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls` takes `url`, `valid_types` (`{ type, is_default }`, type one of LOGIN, INVITE, SIGNUP, RESET_PASSWORD, DISCOVERY) and `do_not_promote_defaults`, and answers `{ request_id, redirect_url: { url, valid_types }, status_code }`."
|
|
41
|
+
source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/create-redirect-url
|
|
42
|
+
- id: SY3
|
|
43
|
+
verified: true
|
|
44
|
+
text: "`GET …/redirect_urls/all` answers `{ redirect_urls: [{ url, valid_types }], … }`: every redirect URL of the environment, unpaged."
|
|
45
|
+
source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/get-all-redirect-urls
|
|
46
|
+
- id: SY4
|
|
47
|
+
verified: true
|
|
48
|
+
text: "A redirect URL has no id of its own: `GET`, `PUT` (to change `valid_types`) and `DELETE` on `…/redirect_urls?url=<url>` address it by its URL."
|
|
49
|
+
source: https://stytch.com/docs/api-reference/pwa/api/v3/redirect-urls/delete-redirect-url
|
|
50
|
+
- id: SY5
|
|
51
|
+
verified: true
|
|
52
|
+
text: "A project has at most 100 redirect URLs."
|
|
53
|
+
source: https://stytch.com/docs/workspace-management/pwa/limits
|
|
54
|
+
- id: SY6
|
|
55
|
+
verified: false
|
|
56
|
+
text: "`do_not_promote_defaults: true` on create and delete keeps a preview URL from becoming, or a deletion from changing, the environment's default for a type."
|
|
57
|
+
- id: SY7
|
|
58
|
+
verified: false
|
|
59
|
+
text: "A `PUT` body that also carries `url` (the adapter sends the identifying field back on PUT) is accepted, and `valid_types` read back in the order and form they were written, so an unchanged URL hashes the same on every plan."
|
|
60
|
+
- id: SY8
|
|
61
|
+
verified: false
|
|
62
|
+
text: "Each redirect URL is its own object (no shared list to rewrite), so concurrent previews need no lock."
|
|
63
|
+
ops:
|
|
64
|
+
redirect_url:
|
|
65
|
+
kind: resource
|
|
66
|
+
title: Redirect URL
|
|
67
|
+
summary: "One redirect URL of a Stytch project environment, such as the preview's `/authenticate`, with the redirect types it is valid for (by default login and sign-up, never the default). Found by its URL; a console edit of its types is drift and is put back with a `PUT`. Destroy deletes it without promoting another URL to default."
|
|
68
|
+
covers: [stytch-redirect]
|
|
69
|
+
params:
|
|
70
|
+
project_slug:
|
|
71
|
+
type: string
|
|
72
|
+
required: true
|
|
73
|
+
pattern: "^[A-Za-z0-9_-]+$"
|
|
74
|
+
description: The project's slug.
|
|
75
|
+
example: my-app
|
|
76
|
+
environment_slug:
|
|
77
|
+
type: string
|
|
78
|
+
required: true
|
|
79
|
+
pattern: "^[A-Za-z0-9_-]+$"
|
|
80
|
+
description: The environment's slug within the project (its test environment, or a custom one).
|
|
81
|
+
example: test
|
|
82
|
+
url:
|
|
83
|
+
type: string
|
|
84
|
+
required: true
|
|
85
|
+
pattern: "^https?://\\S+$"
|
|
86
|
+
description: "The redirect URL, exactly as Stytch will receive it (`https://pr-42.preview.example.com/authenticate`)."
|
|
87
|
+
example: "https://${ctx.scope}.preview.example.com/authenticate"
|
|
88
|
+
valid_types:
|
|
89
|
+
type: list
|
|
90
|
+
default: [{ type: LOGIN, is_default: false }, { type: SIGNUP, is_default: false }]
|
|
91
|
+
description: "The redirect types the URL is valid for: `{ type, is_default }` with type LOGIN, INVITE, SIGNUP, RESET_PASSWORD or DISCOVERY."
|
|
92
|
+
http:
|
|
93
|
+
find:
|
|
94
|
+
path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls/all"
|
|
95
|
+
list_path: /redirect_urls
|
|
96
|
+
match: { url: { param: url } }
|
|
97
|
+
create:
|
|
98
|
+
path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls"
|
|
99
|
+
body: { do_not_promote_defaults: true }
|
|
100
|
+
update:
|
|
101
|
+
method: PUT
|
|
102
|
+
path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls?url={id}"
|
|
103
|
+
body: { do_not_promote_defaults: true }
|
|
104
|
+
delete:
|
|
105
|
+
path: "/pwa/v3/projects/{project_slug}/environments/{environment_slug}/redirect_urls?url={id}&do_not_promote_defaults=true"
|
|
106
|
+
item_path: /redirect_url
|
|
107
|
+
id_path: /url
|
|
108
|
+
fields:
|
|
109
|
+
valid_types: { param: valid_types }
|
|
110
|
+
sim:
|
|
111
|
+
item_query: url
|
|
112
|
+
live:
|
|
113
|
+
params: { project_slug: SPONSON_LIVE_STYTCH_PROJECT_SLUG, environment_slug: SPONSON_LIVE_STYTCH_ENVIRONMENT_SLUG }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# yaml-language-server: $schema=../../../schema/recipe.schema.json
|
|
2
|
+
# Svix API recipes (Svix as the webhook sender). Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md
|
|
3
|
+
# "Adding a recipe".
|
|
4
|
+
provider: svix
|
|
5
|
+
title: Svix
|
|
6
|
+
category: webhooks
|
|
7
|
+
verified_on: "2026-10-11"
|
|
8
|
+
docs:
|
|
9
|
+
- title: Svix API reference (OpenAPI document)
|
|
10
|
+
url: https://api.svix.com/api/v1/openapi.json
|
|
11
|
+
- title: Svix API reference
|
|
12
|
+
url: https://api.svix.com/docs
|
|
13
|
+
- title: Entities overview (ids and uids)
|
|
14
|
+
url: https://docs.svix.com/overview
|
|
15
|
+
api:
|
|
16
|
+
base_url: https://api.us.svix.com
|
|
17
|
+
base_url_env: SVIX_API_URL
|
|
18
|
+
auth: { bearer_env: SVIX_AUTH_TOKEN }
|
|
19
|
+
detect:
|
|
20
|
+
deps: [svix]
|
|
21
|
+
env: ["SVIX_*"]
|
|
22
|
+
starter:
|
|
23
|
+
- op: endpoint
|
|
24
|
+
values: { uid: "sponson-${ctx.env}-${ctx.scope}" }
|
|
25
|
+
where:
|
|
26
|
+
app_id: "Svix dashboard → Applications: the application (customer) the previews receive as"
|
|
27
|
+
url: "the preview's webhook handler: its URL plus the handler's path (a `{ from }` reference cannot carry a path)"
|
|
28
|
+
assumptions:
|
|
29
|
+
- id: SV1
|
|
30
|
+
verified: true
|
|
31
|
+
text: "`POST /api/v1/app/{app_id}/endpoint` creates an endpoint from `url` (required), `uid`, `description`, `eventTypes`, `channels`, `disabled`, `metadata` and an optional `secret`, answering 201 with the endpoint (`id` `ep_…`, `uid`, `url`, …); requests carry `Authorization: Bearer <token>`."
|
|
32
|
+
source: https://api.svix.com/api/v1/openapi.json
|
|
33
|
+
- id: SV2
|
|
34
|
+
verified: true
|
|
35
|
+
text: "`GET`, `PATCH` (partial update) and `DELETE` (204) `/api/v1/app/{app_id}/endpoint/{endpoint_id}` take the endpoint's id or its uid, and `{app_id}` takes the application's id or uid."
|
|
36
|
+
source: https://api.svix.com/api/v1/openapi.json
|
|
37
|
+
- id: SV3
|
|
38
|
+
verified: true
|
|
39
|
+
text: "A uid is 1–256 characters of letters, digits, `-`, `_` and `.`, unique among the application's endpoints, so it names the endpoint for every later request and a create whose answer was lost is found by it."
|
|
40
|
+
source: https://api.svix.com/api/v1/openapi.json
|
|
41
|
+
- id: SV4
|
|
42
|
+
verified: true
|
|
43
|
+
text: "The endpoint's signing secret is generated when `secret` is null (recommended) and is readable at any time with `GET …/endpoint/{endpoint_id}/secret` (`{ key }`): it is not a once-only value, and this recipe does not output it (an endpoint answer does not carry it)."
|
|
44
|
+
source: https://api.svix.com/api/v1/openapi.json
|
|
45
|
+
- id: SV5
|
|
46
|
+
verified: true
|
|
47
|
+
text: "The API is served per region (`api.us.svix.com`, `api.eu.svix.com`, `api.ca.svix.com`, `api.au.svix.com`), and POST requests accept an `idempotency-key` header."
|
|
48
|
+
source: https://api.svix.com/api/v1/openapi.json
|
|
49
|
+
- id: SV6
|
|
50
|
+
verified: false
|
|
51
|
+
text: "A create with a uid that exists already answers 409 (the specification lists 409 for every operation without saying when), after which the endpoint is found by its uid and claimed only if an earlier intent of the same scope named it."
|
|
52
|
+
- id: SV7
|
|
53
|
+
verified: false
|
|
54
|
+
text: "`description` is answered as sent (default empty), `disabled` as a boolean and `eventTypes` as the list sent, so they compare without drift on every plan; accounts outside the US region set `base_url` in the provider block."
|
|
55
|
+
ops:
|
|
56
|
+
endpoint:
|
|
57
|
+
kind: resource
|
|
58
|
+
title: Application endpoint
|
|
59
|
+
summary: An endpoint of a Svix application delivering to an environment's URL, named by a uid such as `preview-pr-42`. Its URL, description, event type filter and disabled flag are compared for drift; its signing secret stays readable in Svix (SV4).
|
|
60
|
+
covers: [svix-endpoint]
|
|
61
|
+
params:
|
|
62
|
+
app_id:
|
|
63
|
+
type: string
|
|
64
|
+
required: true
|
|
65
|
+
description: The application's id (`app_…`) or uid.
|
|
66
|
+
example: acme-customer
|
|
67
|
+
uid:
|
|
68
|
+
type: string
|
|
69
|
+
required: true
|
|
70
|
+
pattern: "^[a-zA-Z0-9\\-_.]{1,256}$"
|
|
71
|
+
description: The endpoint's uid, unique in the application; its identity.
|
|
72
|
+
example: "sponson-${ctx.env}-${ctx.scope}"
|
|
73
|
+
url:
|
|
74
|
+
type: string
|
|
75
|
+
required: true
|
|
76
|
+
pattern: "^https?://\\S+$"
|
|
77
|
+
description: The URL messages are delivered to.
|
|
78
|
+
example: "https://${ctx.scope}.preview.example.com/api/webhooks"
|
|
79
|
+
description:
|
|
80
|
+
type: string
|
|
81
|
+
default: ""
|
|
82
|
+
description: A note shown in the Svix dashboard and the application portal.
|
|
83
|
+
event_types:
|
|
84
|
+
type: list
|
|
85
|
+
description: Event type names to deliver (all when left out); at least one when set.
|
|
86
|
+
disabled:
|
|
87
|
+
type: boolean
|
|
88
|
+
default: false
|
|
89
|
+
description: Whether deliveries are paused.
|
|
90
|
+
secret:
|
|
91
|
+
type: string
|
|
92
|
+
description: "A signing secret to use instead of a generated one (`whsec_…`); sent on create only. Use `{ secret: … }`."
|
|
93
|
+
http:
|
|
94
|
+
read: { path: "/api/v1/app/{app_id}/endpoint/{uid}" }
|
|
95
|
+
create:
|
|
96
|
+
path: "/api/v1/app/{app_id}/endpoint"
|
|
97
|
+
body: { uid: { param: uid }, secret: { param: secret } }
|
|
98
|
+
idempotency_key: true
|
|
99
|
+
update: { method: PATCH, path: "/api/v1/app/{app_id}/endpoint/{id}" }
|
|
100
|
+
delete: { path: "/api/v1/app/{app_id}/endpoint/{id}" }
|
|
101
|
+
fields:
|
|
102
|
+
url: { param: url }
|
|
103
|
+
description: { param: description }
|
|
104
|
+
disabled: { param: disabled }
|
|
105
|
+
eventTypes: { param: event_types }
|
|
106
|
+
sim:
|
|
107
|
+
id_from: uid
|
|
108
|
+
live:
|
|
109
|
+
params: { app_id: SPONSON_LIVE_SVIX_APP_ID }
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# yaml-language-server: $schema=../../../schema/recipe.schema.json
|
|
2
|
+
# Turso Platform API recipes. Format and checklist: docs/adr/0020-recipes.md, CONTRIBUTING.md "Adding a recipe".
|
|
3
|
+
provider: turso
|
|
4
|
+
title: Turso
|
|
5
|
+
category: databases
|
|
6
|
+
verified_on: "2026-10-11"
|
|
7
|
+
docs:
|
|
8
|
+
- title: Create database
|
|
9
|
+
url: https://docs.turso.tech/api-reference/databases/create
|
|
10
|
+
- title: Retrieve database
|
|
11
|
+
url: https://docs.turso.tech/api-reference/databases/retrieve
|
|
12
|
+
- title: Delete database
|
|
13
|
+
url: https://docs.turso.tech/api-reference/databases/delete
|
|
14
|
+
- title: Branching
|
|
15
|
+
url: https://docs.turso.tech/features/branching
|
|
16
|
+
api:
|
|
17
|
+
base_url: https://api.turso.tech/v1
|
|
18
|
+
base_url_env: TURSO_API_URL
|
|
19
|
+
auth: { bearer_env: TURSO_API_TOKEN }
|
|
20
|
+
detect:
|
|
21
|
+
deps: ["@libsql/client", "@tursodatabase/"]
|
|
22
|
+
env: ["TURSO_*"]
|
|
23
|
+
db_hosts: [.turso.io]
|
|
24
|
+
starter:
|
|
25
|
+
- op: database_branch
|
|
26
|
+
values: { name: "app-${ctx.scope}" }
|
|
27
|
+
where:
|
|
28
|
+
organization: "`turso org list`"
|
|
29
|
+
parent: "the database to branch from: `turso db list`"
|
|
30
|
+
assumptions:
|
|
31
|
+
- id: TU1
|
|
32
|
+
verified: true
|
|
33
|
+
text: "`POST /v1/organizations/{organizationSlug}/databases` with `name`, `group` and `seed: { type: database, name: <parent> }` creates a database copied from the parent, authenticated with a Platform API token as `Authorization: Bearer`."
|
|
34
|
+
source: https://docs.turso.tech/api-reference/databases/create
|
|
35
|
+
- id: TU2
|
|
36
|
+
verified: true
|
|
37
|
+
text: "The create answers `{ database: { DbId, Hostname, Name } }` (capitalised), and 409 when a database with that name exists already."
|
|
38
|
+
source: https://docs.turso.tech/api-reference/databases/create
|
|
39
|
+
- id: TU3
|
|
40
|
+
verified: true
|
|
41
|
+
text: "`GET /v1/organizations/{organizationSlug}/databases/{databaseName}` answers `{ database: { Name, DbId, Hostname, group, … } }`, and 404 when there is none."
|
|
42
|
+
source: https://docs.turso.tech/api-reference/databases/retrieve
|
|
43
|
+
- id: TU4
|
|
44
|
+
verified: true
|
|
45
|
+
text: "`DELETE` on the same path removes the database; 404 when it is gone."
|
|
46
|
+
source: https://docs.turso.tech/api-reference/databases/delete
|
|
47
|
+
- id: TU5
|
|
48
|
+
verified: true
|
|
49
|
+
text: Database names are lower-case letters, numbers and dashes, at most 64 characters, unique in the organization; the group must exist already.
|
|
50
|
+
source: https://docs.turso.tech/api-reference/databases/create
|
|
51
|
+
- id: TU6
|
|
52
|
+
verified: false
|
|
53
|
+
text: The database is usable as soon as the create answers (no readiness to poll for a branch of a database in the same group).
|
|
54
|
+
ops:
|
|
55
|
+
database_branch:
|
|
56
|
+
kind: resource
|
|
57
|
+
title: Database branch
|
|
58
|
+
summary: "A database copied from a parent database (a branch), named for the scope, such as `app-pr-42` from `app`. Its hostname is an output for the app's connection URL. Only its existence is compared for drift: changing `group` or `parent` later does not move or re-copy a database that exists."
|
|
59
|
+
covers: [turso-database]
|
|
60
|
+
params:
|
|
61
|
+
organization:
|
|
62
|
+
type: string
|
|
63
|
+
required: true
|
|
64
|
+
description: The organization (or user account) slug.
|
|
65
|
+
example: acme
|
|
66
|
+
name:
|
|
67
|
+
type: string
|
|
68
|
+
required: true
|
|
69
|
+
pattern: "^[a-z0-9-]{1,64}$"
|
|
70
|
+
description: The new database's name.
|
|
71
|
+
example: "app-${ctx.scope}"
|
|
72
|
+
parent:
|
|
73
|
+
type: string
|
|
74
|
+
required: true
|
|
75
|
+
description: The parent database to copy.
|
|
76
|
+
example: app
|
|
77
|
+
group:
|
|
78
|
+
type: string
|
|
79
|
+
default: default
|
|
80
|
+
description: The group the database is placed in; it must exist.
|
|
81
|
+
timestamp:
|
|
82
|
+
type: string
|
|
83
|
+
description: Copy the parent as it was at this ISO 8601 time (point-in-time recovery window only).
|
|
84
|
+
http:
|
|
85
|
+
read: { path: "/organizations/{organization}/databases/{name}" }
|
|
86
|
+
create:
|
|
87
|
+
path: "/organizations/{organization}/databases"
|
|
88
|
+
body:
|
|
89
|
+
name: { param: name }
|
|
90
|
+
group: { param: group }
|
|
91
|
+
seed: { type: database, name: { param: parent }, timestamp: { param: timestamp } }
|
|
92
|
+
item_path: /database
|
|
93
|
+
id_path: /Name
|
|
94
|
+
outputs: { hostname: /Hostname, db_id: /DbId }
|
|
95
|
+
sim:
|
|
96
|
+
id_from: name
|
|
97
|
+
live:
|
|
98
|
+
params: { organization: SPONSON_LIVE_TURSO_ORGANIZATION, parent: SPONSON_LIVE_TURSO_DATABASE }
|