@homeflare/alchemy 0.48.0 → 0.49.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 (97) hide show
  1. package/README.md +11 -0
  2. package/dist/cloudflare/cloudflared-tunnel-api.d.ts +87 -0
  3. package/dist/cloudflare/cloudflared-tunnel-api.d.ts.map +1 -0
  4. package/dist/cloudflare/cloudflared-tunnel-form.d.ts +49 -0
  5. package/dist/cloudflare/cloudflared-tunnel-form.d.ts.map +1 -0
  6. package/dist/cloudflare/cloudflared-tunnel-harness.d.ts +45 -0
  7. package/dist/cloudflare/cloudflared-tunnel-harness.d.ts.map +1 -0
  8. package/dist/cloudflare/cloudflared-tunnel-lifecycle.d.ts +21 -0
  9. package/dist/cloudflare/cloudflared-tunnel-lifecycle.d.ts.map +1 -0
  10. package/dist/cloudflare/cloudflared-tunnel.d.ts +39 -0
  11. package/dist/cloudflare/cloudflared-tunnel.d.ts.map +1 -0
  12. package/dist/cloudflare/fake-access.d.ts +40 -0
  13. package/dist/cloudflare/fake-access.d.ts.map +1 -0
  14. package/dist/cloudflare/fake-mesh.d.ts +7 -2
  15. package/dist/cloudflare/fake-mesh.d.ts.map +1 -1
  16. package/dist/cloudflare/fake-tunnel.d.ts +48 -0
  17. package/dist/cloudflare/fake-tunnel.d.ts.map +1 -0
  18. package/dist/cloudflare/index.d.ts +15 -4
  19. package/dist/cloudflare/index.d.ts.map +1 -1
  20. package/dist/cloudflare/index.js +518 -7
  21. package/dist/cloudflare/index.js.map +15 -4
  22. package/dist/cloudflare/providers.d.ts +1 -1
  23. package/dist/cloudflare/providers.d.ts.map +1 -1
  24. package/dist/cloudflare/saas-oidc-api.d.ts +88 -0
  25. package/dist/cloudflare/saas-oidc-api.d.ts.map +1 -0
  26. package/dist/cloudflare/saas-oidc-consumer.d.ts +22 -0
  27. package/dist/cloudflare/saas-oidc-consumer.d.ts.map +1 -0
  28. package/dist/cloudflare/saas-oidc-error.d.ts +22 -0
  29. package/dist/cloudflare/saas-oidc-error.d.ts.map +1 -0
  30. package/dist/cloudflare/saas-oidc-form.d.ts +48 -0
  31. package/dist/cloudflare/saas-oidc-form.d.ts.map +1 -0
  32. package/dist/cloudflare/saas-oidc-harness.d.ts +50 -0
  33. package/dist/cloudflare/saas-oidc-harness.d.ts.map +1 -0
  34. package/dist/cloudflare/saas-oidc-lifecycle.d.ts +14 -0
  35. package/dist/cloudflare/saas-oidc-lifecycle.d.ts.map +1 -0
  36. package/dist/cloudflare/saas-oidc-props.d.ts +139 -0
  37. package/dist/cloudflare/saas-oidc-props.d.ts.map +1 -0
  38. package/dist/cloudflare/saas-oidc-team.d.ts +9 -0
  39. package/dist/cloudflare/saas-oidc-team.d.ts.map +1 -0
  40. package/dist/cloudflare/saas-oidc-wire.d.ts +58 -0
  41. package/dist/cloudflare/saas-oidc-wire.d.ts.map +1 -0
  42. package/dist/cloudflare/saas-oidc.d.ts +36 -0
  43. package/dist/cloudflare/saas-oidc.d.ts.map +1 -0
  44. package/dist/talos/cluster-adapter-errors.d.ts.map +1 -1
  45. package/dist/talos/cluster-adapter.d.ts +2 -1
  46. package/dist/talos/cluster-adapter.d.ts.map +1 -1
  47. package/dist/talos/index.js +19 -8
  48. package/dist/talos/index.js.map +6 -6
  49. package/dist/talos/mint-temp-file.d.ts.map +1 -1
  50. package/dist/talos/talosctl.d.ts +29 -0
  51. package/dist/talos/talosctl.d.ts.map +1 -1
  52. package/docs/cloudflared-tunnel.md +136 -0
  53. package/docs/credentials.md +1 -1
  54. package/docs/ownership.md +3 -2
  55. package/docs/saas-oidc.md +185 -0
  56. package/docs/talos-openbao-adapter.md +3 -2
  57. package/docs/upstream-conformance-families.md +1 -1
  58. package/docs/upstream-conformance.md +3 -0
  59. package/package.json +1 -1
  60. package/src/cloudflare/cloudflared-tunnel-api.ts +150 -0
  61. package/src/cloudflare/cloudflared-tunnel-edges.test.ts +179 -0
  62. package/src/cloudflare/cloudflared-tunnel-form.ts +78 -0
  63. package/src/cloudflare/cloudflared-tunnel-harness.ts +126 -0
  64. package/src/cloudflare/cloudflared-tunnel-lifecycle.ts +146 -0
  65. package/src/cloudflare/cloudflared-tunnel-state.test.ts +104 -0
  66. package/src/cloudflare/cloudflared-tunnel.test.ts +164 -0
  67. package/src/cloudflare/cloudflared-tunnel.ts +116 -0
  68. package/src/cloudflare/fake-access.ts +196 -0
  69. package/src/cloudflare/fake-mesh.ts +6 -3
  70. package/src/cloudflare/fake-tunnel.ts +148 -0
  71. package/src/cloudflare/index.ts +27 -4
  72. package/src/cloudflare/providers.ts +16 -3
  73. package/src/cloudflare/saas-oidc-api.ts +224 -0
  74. package/src/cloudflare/saas-oidc-consumer.ts +35 -0
  75. package/src/cloudflare/saas-oidc-error.ts +52 -0
  76. package/src/cloudflare/saas-oidc-form.test.ts +227 -0
  77. package/src/cloudflare/saas-oidc-form.ts +190 -0
  78. package/src/cloudflare/saas-oidc-harness.ts +140 -0
  79. package/src/cloudflare/saas-oidc-lifecycle.ts +150 -0
  80. package/src/cloudflare/saas-oidc-parse-error.test.ts +180 -0
  81. package/src/cloudflare/saas-oidc-props.ts +147 -0
  82. package/src/cloudflare/saas-oidc-read.test.ts +85 -0
  83. package/src/cloudflare/saas-oidc-stable.test.ts +38 -0
  84. package/src/cloudflare/saas-oidc-state.test.ts +128 -0
  85. package/src/cloudflare/saas-oidc-team.test.ts +88 -0
  86. package/src/cloudflare/saas-oidc-team.ts +70 -0
  87. package/src/cloudflare/saas-oidc-wire.ts +116 -0
  88. package/src/cloudflare/saas-oidc.test.ts +201 -0
  89. package/src/cloudflare/saas-oidc.ts +107 -0
  90. package/src/talos/cluster-adapter-errors.ts +2 -1
  91. package/src/talos/cluster-adapter-move.test.ts +2 -0
  92. package/src/talos/cluster-adapter.ts +2 -1
  93. package/src/talos/mint-temp-file.test.ts +44 -0
  94. package/src/talos/mint-temp-file.ts +16 -4
  95. package/src/talos/talosctl-probe-kill.test.ts +56 -0
  96. package/src/talos/talosctl.test.ts +54 -1
  97. package/src/talos/talosctl.ts +34 -2
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/cloudflare/r2-bucket-lock.ts", "../src/cloudflare/lock-rules.ts", "../src/cloudflare/mesh-node.ts", "../src/cloudflare/mesh-node-api.ts", "../src/cloudflare/mesh-node-form.ts", "../src/cloudflare/mesh-node-lifecycle.ts", "../src/cloudflare/mesh-node-token.ts", "../src/cloudflare/providers.ts", "../src/cloudflare/website.ts"],
3
+ "sources": ["../src/cloudflare/r2-bucket-lock.ts", "../src/cloudflare/lock-rules.ts", "../src/cloudflare/mesh-node.ts", "../src/cloudflare/mesh-node-api.ts", "../src/cloudflare/mesh-node-form.ts", "../src/cloudflare/mesh-node-lifecycle.ts", "../src/cloudflare/mesh-node-token.ts", "../src/cloudflare/cloudflared-tunnel.ts", "../src/cloudflare/cloudflared-tunnel-api.ts", "../src/cloudflare/cloudflared-tunnel-form.ts", "../src/cloudflare/cloudflared-tunnel-lifecycle.ts", "../src/cloudflare/saas-oidc.ts", "../src/cloudflare/saas-oidc-lifecycle.ts", "../src/cloudflare/saas-oidc-api.ts", "../src/cloudflare/saas-oidc-error.ts", "../src/cloudflare/saas-oidc-wire.ts", "../src/cloudflare/saas-oidc-form.ts", "../src/cloudflare/saas-oidc-team.ts", "../src/cloudflare/providers.ts", "../src/cloudflare/website.ts"],
4
4
  "sourcesContent": [
5
5
  "import { Unowned } from 'alchemy/AdoptPolicy';\nimport * as Provider from 'alchemy/Provider';\nimport { Resource, type ResourceClass } from 'alchemy/Resource';\n/**\n * `Cloudflare.R2BucketLock` — the lock rule set on one R2 bucket, declared rather than applied by\n * hand. A bucket lock is a RETENTION FLOOR: while a rule covers an object, no API call, no\n * lifecycle rule and no credential can delete it.\n *\n * 🔴 WHY THIS EXISTS AT ALL, AND WHAT IT REPLACES. a consuming stack shipped the retention floor as a\n * documented `wrangler r2 bucket lock add …` step in the runbook, on the measured ground that\n * `alchemy@2.0.0-beta.77` has no lock property anywhere in its R2 namespace. That measurement is\n * still true and it was the wrong conclusion: a step a human runs once is a step an Alchemy plan\n * can never show missing, so removing the floor by hand leaves that stack, its plan and\n * every gate completely green. The operator's rule (2026-09-15) is to cover such a gap with a\n * custom resource over the vendor's SDK, which is what this is.\n *\n * ★ OVER `@distilled.cloud/cloudflare/r2`, THE SAME SDK `Cloudflare.MeshNode` USES\n * (mesh-node-api.ts), NOT THE `cloudflare` NPM SDK THIS RESOURCE USED TO CALL. Measured\n * 2026-09-23 in distilled `1.0.0-rc.12` (the version alchemy@2.0.0-beta.79 pins),\n * `packages/cloudflare/src/services/r2.ts`: `getBucketLock` / `putBucketLock` cover the same\n * `GET`/`PUT /accounts/{account_id}/r2/buckets/{bucket_name}/lock` route the old SDK's\n * `r2.buckets.locks.get`/`.update` did — same `rules[]` body, the `cf-r2-jurisdiction` header\n * spelled as the `jurisdiction` field — but typed as `NoSuchBucket | InvalidRoute |\n * CloudflareRateLimited | CloudflareError` instead of an opaque HTTP status, so the \"bucket\n * does not exist yet\" case below is `catchTag('NoSuchBucket', …)`, never a status check. This\n * is also the swap that lets `client.ts` (the hand-rolled `cloudflare@4.5.0` wrapper) go away:\n * nothing else in the kit imported it.\n *\n * ⚠️ THE RULE SET IS A REPLACE, NOT A MERGE. `PUT` takes the WHOLE list, exactly like R2's\n * lifecycle rules — which is the trap a consuming stack records for\n * `homeflare-git`. Declaring this resource on a bucket whose live rules nobody has read would\n * drop them, so `read` fetches them first and `reconcile` refuses to write when they already\n * match.\n */\nimport type { CloudflareOpContext } from '@distilled.cloud/cloudflare/r2';\nimport { Credentials } from '@distilled.cloud/cloudflare/Credentials';\nimport * as r2 from '@distilled.cloud/cloudflare/r2';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport {\n type Jurisdiction,\n type R2LockRule,\n jurisdictionOf,\n rulesEqual,\n toBody,\n} from './lock-rules.ts';\nimport type { Providers } from './providers.ts';\n\nexport interface R2BucketLockProps {\n readonly accountId: string;\n readonly bucketName: string;\n /** ⚠️ Part of the bucket's IDENTITY, not a preference — a bucket in another jurisdiction is another bucket. */\n readonly jurisdiction?: Jurisdiction;\n /**\n * ⛔ REQUIRED, AND ITS EMPTY VALUE IS THE DANGEROUS ONE. `rules: []` means NO FLOOR: it is what\n * `delete` sends to take a lock off deliberately. An optional property would let a declaration\n * that simply forgot it send `[]` and silently unlock the repository, reporting success — the\n * same reasoning `LiteLLM.McpServer.allowedTools` is required for.\n */\n readonly rules: readonly R2LockRule[];\n}\n\nexport type R2BucketLockAttributes = {\n readonly bucketName: string;\n readonly jurisdiction: Jurisdiction;\n readonly rules: readonly R2LockRule[];\n};\n\nexport interface R2BucketLock extends Resource<\n 'Cloudflare.R2BucketLock',\n R2BucketLockProps,\n R2BucketLockAttributes,\n never,\n Providers\n> {}\n\n// ⚠️ EXPLICIT ANNOTATION, REQUIRED BY isolatedDeclarations. This file was internal to a\n// monorepo before it was published; a published package emits its .d.ts from a separate\n// tsc pass, so every exported symbol must state its type rather than infer it.\nexport const R2BucketLock: ResourceClass<R2BucketLock> = Resource<R2BucketLock>(\n 'Cloudflare.R2BucketLock',\n {\n /**\n * ⛔ RETAIN, AND ON THIS RESOURCE IT IS NOT THE ESTATE'S HABIT BUT THE WHOLE POINT. A lock is a\n * safety floor: removing the declaration must not remove the floor, because the state it\n * protects is the only physical backup of the estate's one Postgres. `delete` below is\n * implemented — a provider that cannot delete cannot be tested or corrected — and it is\n * reachable only by an operator running the CLI directly, past a deploy wrapper.\n */\n defaultRemovalPolicy: 'retain',\n },\n);\n\n/**\n * 🔴 A 404 HERE IS \"NOTHING TO ADOPT\", AND THE FIRST PLAN PROVED IT IS THE ORDINARY CASE.\n * Measured 2026-09-15, an alchemy plan --detailed on a bucket that does not\n * exist yet: `404 {\"success\":false,\"errors\":[{\"code\":10006,\"message\":\"The specified bucket does\n * not exist.\"}]}` — raised from `read`, during PLANNING, before anything is created. Alchemy\n * calls `read` for a resource with no prior state, so on every first deploy the lock is asked\n * about a bucket the same plan is about to create. Code 10006 is exactly distilled's\n * `NoSuchBucket` tag (r2.ts's error matcher, `[{ code: 10006 }]`), so `catchTag` on it is the\n * same case the status check used to cover — but by the vendor's own error identity, not by\n * reading a number off the response. Letting it propagate would make the resource impossible to\n * plan; swallowing every failure would also hide a 403 from a mis-scoped token, which is the\n * failure this estate spends the most time on. So: `NoSuchBucket` → absent, everything else\n * propagates with its SDK tag. Upstream `Cloudflare/R2/BucketSippy.ts` at beta.79 uses this\n * same catch-and-propagate shape; a blanket remap would hide those typed identities (S20/S21).\n *\n * ⛔ AN EMPTY LIVE RULE SET IS \"NO LOCK\", NOT \"A LOCK WITH NOTHING IN IT\", so it reads as absent\n * and the resource CREATES. The API answers `{}` for a bucket that has never been locked and\n * `{ rules: [] }` for one whose rules were removed; both mean the floor is gone.\n * ⚠️ `Unowned` UNTIL THIS STACK HAS WRITTEN IT. A lock someone applied by hand — which is exactly\n * what the retired runbook step did — must not be silently rewritten by a mistyped declaration;\n * the planner refuses until the resource is piped through `adopt(true)`, which is one line an\n * operator reads in the diff. An adopted rule set that already MATCHES then costs no request at\n * all, because `reconcile` below returns before writing.\n */\nexport const readLock = (\n props: { accountId: string; bucketName: string; jurisdiction?: Jurisdiction },\n written: boolean,\n): Effect.Effect<\n R2BucketLockAttributes | undefined,\n Exclude<r2.GetBucketLockError, { _tag: 'NoSuchBucket' }>,\n CloudflareOpContext\n> =>\n Effect.gen(function* () {\n const jurisdiction = jurisdictionOf(props);\n const live = yield* r2\n .getBucketLock({ accountId: props.accountId, bucketName: props.bucketName, jurisdiction })\n .pipe(Effect.catchTag('NoSuchBucket', () => Effect.succeed(undefined)));\n if (live === undefined) return undefined;\n const rules = (live.rules ?? []) as readonly R2LockRule[];\n if (rules.length === 0) return undefined;\n const attributes: R2BucketLockAttributes = {\n bucketName: props.bucketName,\n jurisdiction,\n rules,\n };\n return written ? attributes : Unowned(attributes);\n });\n\n/**\n * ⛔ A NO-OP MAKES NO REQUEST, AND ON A LOCK THAT MATTERS MORE THAN TIDINESS. `PUT` replaces the\n * rule set; re-sending an identical one on every deploy is a write against the only thing\n * standing between this repository and a delete, for no change. `output` is what the last deploy\n * persisted (or what `read` just adopted), so the comparison costs nothing either.\n * ⛔ `NoSuchBucket` IS NOT CAUGHT HERE. Unlike `read`, a `reconcile` that hits it means the bucket\n * this declaration names is genuinely gone — that is a real failure to surface, not a case to\n * paper over, so it propagates like every other typed SDK error `putBucketLock` can raise.\n */\nexport const reconcileLock = (\n news: R2BucketLockProps,\n output: R2BucketLockAttributes | undefined,\n): Effect.Effect<R2BucketLockAttributes, r2.PutBucketLockError, CloudflareOpContext> =>\n Effect.gen(function* () {\n const jurisdiction = jurisdictionOf(news);\n if (output !== undefined && rulesEqual(output.rules, news.rules)) return output;\n yield* r2.putBucketLock({\n accountId: news.accountId,\n bucketName: news.bucketName,\n jurisdiction,\n ...toBody(news.rules),\n });\n return { bucketName: news.bucketName, jurisdiction, rules: news.rules };\n });\n\n/**\n * ⛔ DELETING THIS RESOURCE MEANS PUTTING AN EMPTY RULE SET, WHICH IS UNLOCKING THE BUCKET. It is\n * implemented because the retirement path is real — R2 refuses to empty a bucket while any lock\n * rule is configured, so retiring the repository has to start here — and it is behind\n * `defaultRemovalPolicy: 'retain'` and the estate CLI's refusal of `destroy` precisely because\n * no ordinary deploy should ever reach it.\n * ⛔ IDEMPOTENT (S11): a missing bucket has nothing left to unlock. Catch only `NoSuchBucket`;\n * every other SDK error propagates, like upstream `R2/BucketSippy.ts` at beta.79.\n */\nexport const deleteLock = (\n output: R2BucketLockAttributes,\n accountId: string,\n): Effect.Effect<\n void,\n Exclude<r2.PutBucketLockError, { _tag: 'NoSuchBucket' }>,\n CloudflareOpContext\n> =>\n Effect.gen(function* () {\n yield* r2\n .putBucketLock({\n accountId,\n bucketName: output.bucketName,\n jurisdiction: output.jurisdiction,\n rules: [],\n })\n .pipe(Effect.catchTag('NoSuchBucket', () => Effect.void));\n });\n\nexport const R2BucketLockProvider = () =>\n Provider.effect(\n R2BucketLock,\n Effect.gen(function* () {\n // ★ CAPTURED AT BUILD, LIKE `MeshNodeProvider`. `Credentials` and `HttpClient.HttpClient`\n // are the whole of distilled's `CloudflareOpContext` (r2.ts re-exports the type); pinning\n // them here rather than reading ambient context inside each handler keeps this provider\n // correct whether or not the stack also merges `Cloudflare.providers()`.\n const services = Context.make(Credentials, yield* Credentials).pipe(\n Context.add(HttpClient.HttpClient, yield* HttpClient.HttpClient),\n );\n const run = <A, E>(effect: Effect.Effect<A, E, CloudflareOpContext>) =>\n Effect.provideContext(effect, services);\n\n return R2BucketLock.Provider.of({\n /**\n * ⛔ EMPTY, AND ALCHEMY'S OWN CONTRACT ASKS FOR EXACTLY THAT. `ProviderService.list`:\n * \"Resources with no native enumeration API (… sub-resources keyed entirely by a parent)\n * should return an empty array rather than throwing.\" There is no list-locks endpoint —\n * a lock is reachable only through the bucket that owns it — so enumerating would mean\n * listing every bucket in the account and GETting each one's lock.\n * ⛔⛔ AND `nuke.singleton` IS WHY THAT IS NOT MERELY UNAVAILABLE BUT UNWANTED. `list`\n * feeds `alchemy unsafe nuke`, which lists and then deletes; `delete` here is an UNLOCK.\n * A working enumeration would hand one command the ability to strip the retention floor\n * off every bucket in the account — the precise event a floor exists to make impossible.\n * `singleton` is the documented word for it: always-present configuration whose delete\n * resets rather than removes.\n */\n list: () => Effect.succeed([]),\n nuke: { singleton: true },\n read: Effect.fn(function* ({ olds, output }) {\n const props = olds ?? undefined;\n if (props === undefined) return undefined;\n return yield* run(readLock(props, output !== undefined));\n }),\n reconcile: Effect.fn(({ news, output }) => run(reconcileLock(news, output))),\n delete: Effect.fn(({ olds, output }) => run(deleteLock(output, olds.accountId))),\n });\n }),\n );\n",
6
6
  "/**\n * The vocabulary of an R2 lock rule, and the comparison that decides whether to write one.\n *\n * ★ ITS OWN MODULE FOR THE REASON `<estate>/litellm/src/equal.ts` IS ONE: the comparison is the\n * riskiest function in the provider and it deserves to be readable and testable without the\n * lifecycle around it. An equality that returned true for everything would make every reconcile\n * a no-op and the resource would silently never write anything — which looks exactly like a\n * converged stack.\n */\n/** Jurisdictions the API accepts, as the SDK declares them. */\nexport type Jurisdiction = 'default' | 'eu' | 'fedramp';\n\n/**\n * One lock rule. ⚠️ `prefix` OMITTED MEANS THE WHOLE BUCKET — the SDK documents an empty prefix as\n * the way to scope a rule to every object, and omitting the key is how that is spelled on the wire.\n */\nexport type R2LockRule = {\n readonly id: string;\n readonly enabled: boolean;\n readonly prefix?: string;\n readonly condition:\n | { readonly type: 'Age'; readonly maxAgeSeconds: number }\n | { readonly type: 'Date'; readonly date: string }\n | { readonly type: 'Indefinite' };\n};\n\n/** ⚠️ `undefined` and `'default'` are the same jurisdiction; normalise so a diff never sees both. */\nexport const jurisdictionOf = (props: { readonly jurisdiction?: Jurisdiction }): Jurisdiction =>\n props.jurisdiction ?? 'default';\n\n/**\n * ⛔ FIELD BY FIELD, IN ORDER, AND NOT `JSON.stringify` OF EITHER SIDE. The API returns rules in\n * its own key order and omits `prefix` where this repo may spell it `''`; a stringify compare\n * would report drift on every plan and PUT an identical body each deploy. Order across the list\n * IS compared, because the rule set is a replace and the list is what gets sent.\n */\nexport function rulesEqual(a: readonly R2LockRule[], b: readonly R2LockRule[]): boolean {\n if (a.length !== b.length) return false;\n return a.every((left, index) => {\n const right = b[index];\n if (right === undefined) return false;\n if (left.id !== right.id || left.enabled !== right.enabled) return false;\n if ((left.prefix ?? '') !== (right.prefix ?? '')) return false;\n const [lc, rc] = [left.condition, right.condition];\n if (lc.type !== rc.type) return false;\n if (lc.type === 'Age') return rc.type === 'Age' && lc.maxAgeSeconds === rc.maxAgeSeconds;\n if (lc.type === 'Date') return rc.type === 'Date' && lc.date === rc.date;\n return true;\n });\n}\n\n/**\n * The body the SDK sends. Split out so the test can assert the wire shape against one function.\n *\n * ⚠️ IT TAKES THE RULES, NOT THE PROPS, so this module names nothing from the resource — the\n * comparison and the vocabulary stay readable without the lifecycle around them.\n */\nexport const toBody = (rules: readonly R2LockRule[]): { rules: R2LockRule[] } => ({\n rules: rules.map((rule) => ({ ...rule })),\n});\n",
@@ -9,10 +9,21 @@
9
9
  "/**\n * `Cloudflare.MeshNode` — its props, its attributes, and the pure `diff` that decides between\n * update and replace.\n *\n * ⛔ THE ATTRIBUTES NEVER HOLD THE NODE TOKEN. That is the whole reason this resource exists:\n * Alchemy beta.79's `Cloudflare.Tunnel.WarpConnector` calls `getTunnelWarpConnectorToken` in\n * `toAttributes`, so EVERY read, reconcile and list fetches the token, and Alchemy state (which\n * is not encrypted) stores it — `Redacted` hides it from logs, not from the state store. It also\n * has no `ha` prop. The token is a runtime credential: fetch it on demand with\n * `fetchMeshNodeToken` (mesh-node-token.ts) and write it straight to the host.\n * ★ A BONUS OF NOT READING IT: the token endpoint needs a Write permission (\"Cloudflare One\n * Connectors Write\", per its API reference, read 2026-09-21), so a plan over this resource can\n * run on a read-only token. WarpConnector's cannot.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { MeshNodeError, type MeshNodeStatus } from './mesh-node-api.ts';\n\nexport interface MeshNodeProps {\n /**\n * The node's name in the account — and its identity when adopting (names are unique per\n * account). ★ Changing it RENAMES the node in place (`PATCH`): the id, the token, the\n * enrolled replicas and their Mesh IPs all survive, so a rename never re-enrols a host.\n */\n readonly name: string;\n /**\n * High availability (active-passive replicas sharing one node token).\n *\n * ⛔ CREATE-ONLY, AND REQUIRED FOR THAT REASON. Cloudflare's Mesh HA page: \"High availability\n * is set at node creation time and cannot be changed afterward.\" The PATCH body carries no\n * `ha`. So a change REPLACES the node — a new id, a new token, a new Mesh IP per replica —\n * and a default would silently fix an irreversible choice. The API's own default is `false`;\n * the dashboard's is `true`. Say which one you mean.\n * ★ A NODE WITH NO ROUTES — A DOOR — IS `ha: false`, AND A SECOND DOOR IS A SECOND NODE. HA\n * fails over the CIDR routes a node advertises; \"Nodes without routes do not benefit from HA\n * failover\", because the node's Mesh IP is tied to the individual replica (same page, \"When to\n * use\" and \"Considerations\", read 2026-09-21). After a failover the promoted replica answers\n * on its own Mesh IP, so nothing that dials a door's first replica follows it. Declare the\n * second door as its own `MeshNode` (own name, token, Mesh IP) and have callers list both.\n * ⚠️ `ha` NEEDS MASQUE on the node's device profile (same page).\n */\n readonly ha: boolean;\n}\n\nexport interface MeshNodeAttributes {\n /** The node's UUID (the `warp_connector` tunnel id). Pass it to `fetchMeshNodeToken`. */\n readonly id: string;\n /** The account the provider environment resolved when the node was written. */\n readonly accountId: string;\n readonly name: string;\n /** As of the last read or write; it moves on its own as replicas connect and drop. */\n readonly status: MeshNodeStatus | undefined;\n /**\n * The value the node was CREATED with (or declared with when adopted — see mesh-node.md).\n * ⚠️ No documented read returns it: `GET /warp_connector/{id}` has no `ha` field (API\n * reference, read 2026-09-21), so drift here cannot be observed, only prevented.\n */\n readonly ha: boolean;\n}\n\n/** ⛔ Fail closed on a declaration the API would accept but nobody meant. */\nexport const validateMeshNode = (props: MeshNodeProps): MeshNodeError | undefined => {\n if (typeof props.name !== 'string' || props.name.trim().length === 0) {\n return new MeshNodeError({ message: 'MeshNode `name` must be a non-empty string.' });\n }\n if (props.name !== props.name.trim()) {\n return new MeshNodeError({\n message: `MeshNode name \"${props.name}\" has leading or trailing whitespace; adoption matches names exactly.`,\n });\n }\n if (typeof props.ha !== 'boolean') {\n return new MeshNodeError({\n message: 'MeshNode `ha` must be true or false. It is create-only, so it has no default.',\n });\n }\n return undefined;\n};\n\n/**\n * update or replace, from the declaration and what was last written.\n *\n * - Account changed → `replace`, create-first: names are unique per ACCOUNT, so the two\n * generations cannot collide.\n * - `ha` changed → `replace`. ⛔ `deleteFirst` when the name stays the same: the new node needs\n * the name the old one holds, so create-first would fail with code 1013 (or, worse, a\n * create path that \"converged\" on the old node would record an `ha` it does not have).\n * Create-first when the name changes in the same edit, because then nothing collides.\n * ⚠️ UNDER THE DEFAULT `retain` (mesh-node.ts) the engine skips the old node's delete in both\n * orders: delete-first then REFUSES at the create (mesh-node-lifecycle.ts) until the deploy\n * opts in with `RemovalPolicy.destroy()`, and create-first leaves the old node live, unmanaged.\n * The answer here is the same under both policies: `diff` is not handed the policy (Provider.ts,\n * its input), and delete-first is exactly what lets the opted-in deploy reuse the name.\n * - Name changed alone → `update` (a PATCH rename).\n * - Otherwise → no opinion; the engine compares props.\n *\n * ★ An unresolved `ha` gives no answer here; `reconcile` re-checks it once resolved and refuses\n * rather than updating across a create-only change. An unresolved `name` next to a changed `ha`\n * counts as \"unchanged\", which picks the delete-first order that cannot collide.\n */\nexport const diffMeshNode = (\n news: Input<MeshNodeProps>,\n output: MeshNodeAttributes | undefined,\n accountId: string,\n): Diff | undefined => {\n if (output === undefined) return undefined;\n if (output.accountId !== accountId) return { action: 'replace' };\n const fields = news as { readonly name?: unknown; readonly ha?: unknown };\n const name = typeof fields.name === 'string' ? fields.name : undefined;\n if (typeof fields.ha === 'boolean' && fields.ha !== output.ha) {\n const sameName = name === undefined || name === output.name;\n return sameName ? { action: 'replace', deleteFirst: true } : { action: 'replace' };\n }\n if (name !== undefined && name !== output.name) return { action: 'update' };\n return undefined;\n};\n",
10
10
  "/**\n * read / reconcile / delete for `Cloudflare.MeshNode`, as plain Effects over the SDK calls in\n * mesh-node-api.ts. mesh-node.ts only wires them into Alchemy.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport * as Effect from 'effect/Effect';\nimport {\n MeshNodeError,\n type ObservedNode,\n createNode,\n deleteNode,\n findNodeByName,\n getNode,\n renameNode,\n} from './mesh-node-api.ts';\nimport { type MeshNodeAttributes, type MeshNodeProps, validateMeshNode } from './mesh-node-form.ts';\n\nconst attributesOf = (node: ObservedNode, accountId: string, ha: boolean): MeshNodeAttributes => ({\n id: node.id,\n accountId,\n name: node.name,\n status: node.status,\n ha,\n});\n\n/**\n * Owned: refresh by the stored id. Cold: find the live node by exact name and hand it back\n * `Unowned`, so Alchemy refuses to take it over until the stack says `adopt(true)` — the same\n * gate `Cloudflare.Tunnel.WarpConnector` uses, because a `warp_connector` carries no ownership\n * marker to prove it was ours.\n *\n * ⚠️ ADOPTION RECORDS THE DECLARED `ha`, BECAUSE NOTHING ELSE CAN. No documented read returns a\n * node's HA flag. Adopting an existing node with the wrong `ha` records the wrong value, and no\n * later plan can notice. Check the node's HA badge in the dashboard before adopting.\n */\nexport const readMeshNode = (\n accountId: string,\n olds: MeshNodeProps | undefined,\n output: MeshNodeAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const account = output?.accountId ?? accountId;\n if (output !== undefined) {\n const node = yield* getNode(account, output.id);\n if (node !== undefined) return attributesOf(node, account, output.ha);\n }\n const name = olds?.name ?? output?.name;\n const ha = olds?.ha ?? output?.ha;\n if (name === undefined || ha === undefined) return undefined;\n const match = yield* findNodeByName(account, name);\n return match === undefined ? undefined : Unowned(attributesOf(match, account, ha));\n });\n\n/**\n * ⛔ NEVER CONVERGE ON A NODE THIS RESOURCE DID NOT FIND BY ITS OWN ID. Alchemy's WarpConnector\n * answers `DuplicateTunnelName` by re-reading the node that holds the name and returning it.\n * Here that would be wrong in exactly the case that matters: an `ha` replace whose old node is\n * still alive (the default `retain` skips the delete-first teardown, mesh-node.ts) would\n * \"create\" by silently re-using the old node and record the new `ha` on it. Adoption belongs to\n * `read` and `adopt(true)`. An interrupted create is recovered through `read` too, which returns\n * the node `Unowned`, so the engine asks for `adopt(true)` there as well (as for WarpConnector).\n */\nconst nameTaken = (name: string, holder: string) =>\n new MeshNodeError({\n message:\n `A tunnel named \"${name}\" already exists (${holder}). Names are unique per account, and ` +\n 'this resource never takes over a node it did not create by its own id.',\n });\n\n/**\n * ★ HOW TO TAKE A NODE OVER WHILE THIS RESOURCE HAS A STATE ROW, WRITTEN ONCE. The plan asks\n * `read` to adopt only for a resource with NO row (Plan.ts, `oldState === undefined`) or for an\n * interrupted create's `creating` row. A replace's `replacing` row gets neither, so `adopt(true)`\n * alone changes nothing there and the create refuses again (measured in mesh-node-policy.test.ts).\n * `alchemy state rm` is Alchemy's \"Delete state records without deleting cloud resources\".\n */\nconst REOWN =\n \"drop this resource's state row (alchemy state rm, which leaves the node alone), then deploy \" +\n 'under adopt(true)';\n\n/**\n * 🔴 THE REFUSAL AN `ha` CHANGE MEETS UNDER THE DEFAULT, SO IT NAMES EVERY WAY ON. The plan says\n * `replace` (delete-first), the engine skips that delete because of `retain`, and the old node\n * still holds the name here. A node made outside the stack is caught earlier, by the plan's\n * adoption probe in `read`.\n * ⛔ BUT THE HOLDER IS NOT ALWAYS THE OLD NODE, AND `create` CANNOT TELL: the engine passes it no\n * `olds` (Apply.ts, the replace branch). An `ha` + `name` change is a create-first replace, and\n * there the holder of the NEW name is some other node, which `RemovalPolicy.destroy()` never\n * touches. So the old-node advice is conditional, and the other case says not to delete it: a\n * sentence that called any holder \"the old node\" would send an operator to delete a live node\n * and cut its replicas off the Mesh (measured 2026-09-21 in mesh-node-policy.test.ts).\n * ⚠️ REVERTING `ha` DOES NOT UNDO IT. The failed deploy leaves a `replacing` row, and the next\n * plan resumes that replace (Plan.ts, the `replacing` branch), so it refuses again. Dropping the\n * row and adopting is the way back. All measured in mesh-node-policy.test.ts.\n */\nconst oldNodeHoldsName = (name: string, holder: string) =>\n new MeshNodeError({\n message:\n `Mesh node \"${name}\" already exists (${holder}) and is not the node this resource tracks, ` +\n 'so it is left alone: names are unique per account. If this deploy changed `ha` and kept ' +\n \"the name, it is this resource's old node, kept by MeshNode's default \" +\n 'RemovalPolicy.retain because HA cannot change in place. To replace it (a new id, token and ' +\n 'Mesh IP per replica), deploy once with .pipe(RemovalPolicy.destroy()), which deletes it ' +\n `first, or delete it by hand. To keep it, reverting \\`ha\\` is not enough: ${REOWN}, with ` +\n 'the original `ha`. Otherwise it is another node: do not delete it. Choose another name, ' +\n `or, if it is meant to be this one, check its HA badge, then ${REOWN}.`,\n });\n\n/**\n * 🔴 A 1013 AFTER A CLEAN LOOKUP IS USUALLY THIS DEPLOY'S OWN NODE. distilled retries a POST on\n * a transport error or a 5xx (core `isTransientError`, measured in its source 2026-09-21), so a\n * create that succeeded but lost its response is sent again and answered 1013 by the node the\n * first attempt made. The lookup is repeated so the sentence names that node instead of blaming\n * another tunnel type; it is still not adopted here (see above), because a concurrent creator\n * looks the same and its `ha` is unknown.\n * ⚠️ `adopt(true)` ALONE ONLY RECOVERS A FIRST CREATE. Inside a replace (any `ha` change, either\n * order) the row is `replacing`, which the plan never probes, so the sentence names `REOWN` for\n * that case (measured in mesh-node-policy.test.ts).\n */\nconst raced = (accountId: string, name: string) =>\n findNodeByName(accountId, name).pipe(\n Effect.flatMap((holder) =>\n Effect.fail(\n holder === undefined\n ? // ⚠️ No live warp_connector of that name: another tunnel TYPE holds it (a cloudflared\n // tunnel), or — UNMEASURED — a node deleted moments ago (a delete-first `ha`\n // replace) still reserves it.\n nameTaken(name, 'not a live Mesh node: another tunnel type, or a just-deleted node')\n : new MeshNodeError({\n message:\n `Mesh node \"${name}\" (${holder.id}) appeared between this deploy's lookup and its ` +\n 'create. The likeliest cause is this deploy itself: the SDK retries a create whose ' +\n 'response was lost. If so, check its HA badge matches the declared `ha`, then ' +\n 'adopt it: adopt(true) after a first create, but after a replace ' +\n `${REOWN}, because adopt(true) alone does not act on a replace's row. ` +\n 'Otherwise another creator raced this one.',\n }),\n ),\n ),\n );\n\nconst createFresh = (accountId: string, news: MeshNodeProps) =>\n Effect.gen(function* () {\n const existing = yield* findNodeByName(accountId, news.name);\n if (existing !== undefined) return yield* Effect.fail(oldNodeHoldsName(news.name, existing.id));\n const node = yield* createNode(accountId, news.name, news.ha).pipe(\n Effect.catchTag('DuplicateTunnelName', () => raced(accountId, news.name)),\n );\n return attributesOf(node, accountId, news.ha);\n });\n\nexport const reconcileMeshNode = (\n accountId: string,\n news: MeshNodeProps,\n output: MeshNodeAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const invalid = validateMeshNode(news);\n if (invalid !== undefined) return yield* Effect.fail(invalid);\n const owned = output !== undefined && output.accountId === accountId ? output : undefined;\n const live = owned === undefined ? undefined : yield* getNode(accountId, owned.id);\n // ★ Gone out of band (or a replace's new generation): create, under the same refusals.\n if (owned === undefined || live === undefined) return yield* createFresh(accountId, news);\n if (owned.ha !== news.ha) {\n // ⛔ The plan would have said `replace`; it can only reach here with `ha` unresolved then.\n return yield* Effect.fail(\n new MeshNodeError({\n message: `MeshNode \"${news.name}\": \\`ha\\` is create-only (stored ${owned.ha}, declared ${news.ha}). Plan again so the change is a replace.`,\n }),\n );\n }\n if (live.name === news.name) return attributesOf(live, accountId, owned.ha);\n const renamed = yield* renameNode(accountId, live.id, news.name).pipe(\n Effect.catchTag('DuplicateTunnelName', () =>\n Effect.fail(nameTaken(news.name, 'held by another tunnel')),\n ),\n );\n return attributesOf(renamed, accountId, owned.ha);\n });\n\n/**\n * ⚠️ Cloudflare may refuse to delete a node while replicas are connected (Alchemy's WarpConnector\n * notes \"Deleting requires all connector sessions to be down\"; unmeasured here). Stop the\n * client on every replica first. A node already gone is a successful delete.\n */\nexport const deleteMeshNode = (output: MeshNodeAttributes) =>\n deleteNode(output.accountId, output.id);\n",
11
11
  "/**\n * `fetchMeshNodeToken` — a Mesh node's enrolment token, fetched on demand for a one-off Bun\n * `mesh-enroll` step. ⛔ Not a Resource, and never an attribute: see mesh-node-form.ts for why\n * the token must stay out of Alchemy state.\n *\n * ⛔ WHERE THE VALUE MAY GO. Straight into a root-owned `0600` file on the node's host, which the\n * enrolment reads (`warp-cli --accept-tos connector new <token>`, per the Mesh get-started page,\n * read 2026-09-21). Nowhere else: not Alchemy state, not a secrets store (a runtime credential\n * belongs to the host that runs it), not a log line, not a CI variable. It is returned\n * `Redacted`, so an accidental `console.log` or `Effect.log` prints `<redacted>`; call\n * `Redacted.value` only at the write.\n * ⚠️ `warp-cli` takes the token as an ARGUMENT, so it is visible in the host's process list for\n * the life of that command. Run the enrolment as root on the node itself, never over a shell\n * whose history is kept.\n * ⚠️ THE ENDPOINT NEEDS A WRITE PERMISSION. Its API reference (read 2026-09-21) accepts\n * \"Cloudflare One Connectors Write\", \"Cloudflare One Connector: cloudflared Write\" or\n * \"Cloudflare Tunnel Write\"; the Mesh get-started guide also names \"Cloudflare One Connector:\n * WARP Write\". Connectors Write is in both lists. Mint a short-lived token for this step alone;\n * a read-only plan token gets a 403 here.\n *\n * ★ Requires distilled `Credentials` and an `HttpClient`, not Alchemy's CloudflareEnvironment: a\n * Bun script has no Alchemy profile, so the account id is an argument. Provide\n * `Credentials.fromApiToken({ apiToken })` (from `@distilled.cloud/cloudflare/Credentials`) and\n * `FetchHttpClient.layer`. ⛔ Not `Credentials.fromEnv()`: it falls back to the Global API Key\n * (`CLOUDFLARE_API_KEY` + `CLOUDFLARE_EMAIL`) when no token is set — and `refuseUnsafe` below\n * refuses that key rather than trusting the caller to have read this.\n */\nimport { Credentials } from '@distilled.cloud/cloudflare/Credentials';\nimport * as zeroTrust from '@distilled.cloud/cloudflare/zero-trust';\nimport * as Effect from 'effect/Effect';\nimport * as Redacted from 'effect/Redacted';\nimport { MeshNodeError, findNodeByName } from './mesh-node-api.ts';\n\n/** Which node: by id (the `MeshNode` attribute) or by its exact name. */\nexport type MeshNodeRef = { readonly accountId: string } & (\n | { readonly id: string; readonly name?: undefined }\n | { readonly name: string; readonly id?: undefined }\n);\n\n/**\n * ⛔ REFUSED BEFORE ANY REQUEST IS SENT:\n * - `DISTILLED_DEBUG_HTTP` set. distilled's Cloudflare protocol then `console.error`s the first\n * 400 characters of EVERY response body (protocol.ts, measured in 1.0.0-rc.12) — the token\n * response is `{\"success\":true,…,\"result\":\"<token>\"}`, so the whole token lands on stderr and in\n * whatever captures it. The check reads `process.env` because that is what distilled reads.\n * - The Global API Key (`apiKey` credentials). Account-wide and long-lived: the one credential a\n * one-off enrolment must never use.\n * - An EMPTY API token. A denied secrets-store grant renders empty, and Cloudflare then answers\n * like a bad permission — which would send the operator to widen a grant that was fine\n * (client.ts says the same for `CLOUDFLARE_API_TOKEN`).\n */\nconst refuseUnsafe = Effect.gen(function* () {\n if ((globalThis.process?.env?.['DISTILLED_DEBUG_HTTP'] ?? '') !== '') {\n return yield* Effect.fail(\n new MeshNodeError({\n message:\n 'Refusing to fetch a Mesh node token while DISTILLED_DEBUG_HTTP is set: the SDK would print the token to stderr. Unset it and run again.',\n }),\n );\n }\n const credentials = yield* yield* Credentials;\n if (credentials.type === 'apiKey') {\n return yield* Effect.fail(\n new MeshNodeError({\n message:\n 'Refusing the Global API Key for a Mesh node token. Mint a short-lived API token with \"Cloudflare One Connectors Write\" and pass it with fromApiToken.',\n }),\n );\n }\n if (credentials.type === 'apiToken' && Redacted.value(credentials.apiToken).length === 0) {\n return yield* Effect.fail(\n new MeshNodeError({\n message:\n 'The Cloudflare API token is EMPTY. That is a missing or denied secrets grant, not a Cloudflare permission: re-mint the token before changing any grant.',\n }),\n );\n }\n});\n\nconst resolveId = (ref: MeshNodeRef) => {\n if (ref.id !== undefined) return Effect.succeed(ref.id);\n const name = ref.name;\n return Effect.flatMap(findNodeByName(ref.accountId, name), (node) =>\n node === undefined\n ? Effect.fail(new MeshNodeError({ message: `No live Mesh node named \"${name}\".` }))\n : Effect.succeed(node.id),\n );\n};\n\n/**\n * ⛔ An EMPTY token fails closed: `warp-cli connector new \"\"` would not enrol, and a script that\n * wrote an empty file would report success over a host that never joined.\n * ⛔ The failures made here name the node and account and quote no response body, because the\n * success body IS the token. Other SDK errors pass through; they come only from non-2xx\n * responses, which carry an error envelope, not a token.\n */\nexport const fetchMeshNodeToken = (ref: MeshNodeRef) =>\n Effect.gen(function* () {\n yield* refuseUnsafe;\n const nodeId = yield* resolveId(ref);\n const token = yield* zeroTrust\n .getTunnelWarpConnectorToken({ accountId: ref.accountId, tunnelId: nodeId })\n .pipe(\n Effect.catchTag('TunnelNotFound', () =>\n Effect.fail(\n new MeshNodeError({\n message: `Mesh node ${nodeId} does not exist in account ${ref.accountId}.`,\n }),\n ),\n ),\n Effect.catchTag('Forbidden', () =>\n Effect.fail(\n new MeshNodeError({\n message: `Reading Mesh node ${nodeId}'s token was refused (403). The endpoint needs a Write permission such as \"Cloudflare One Connectors Write\", on a token scoped to account ${ref.accountId}.`,\n }),\n ),\n ),\n );\n if (typeof token !== 'string' || token.length === 0) {\n return yield* Effect.fail(\n new MeshNodeError({ message: `Mesh node ${nodeId} returned an empty token.` }),\n );\n }\n return Redacted.make(token);\n });\n",
12
- "/**\n * The HomeFlare Cloudflare provider collection, in the shape `LiteLLM.providers()` has.\n *\n * ⚠️ THIS IS NOT `alchemy/Cloudflare`'s COLLECTION AND DOES NOT REPLACE IT. A stack that declares\n * both a `Cloudflare.R2.Bucket` and this lock merges the two:\n * `Layer.mergeAll(Cloudflare.providers(), HomeflareCloudflare.providers())` — the shape\n * a consuming stack names for one talking to more than one account.\n *\n * ★ ONE CREDENTIAL PATH NOW, NOT TWO. Until 2026-09-23 `R2BucketLock` ran over the `cloudflare`\n * npm SDK with its own `CLOUDFLARE_API_TOKEN` read (`client.ts`, since deleted) while `MeshNode`\n * ran over `@distilled.cloud/cloudflare` through `Cloudflare.CloudflareApiLive()`. Both\n * providers now call distilled, so both resolve credentials and the account the same way\n * `Cloudflare.providers()` does (an Alchemy profile, or `CLOUDFLARE_API_TOKEN` +\n * `CLOUDFLARE_ACCOUNT_ID` in CI) — a lock and the Gateway rules that reference the same bucket\n * land in the same account.\n * ⚠️ THE LAYER IS BUILT TWICE ON PURPOSE, once per provider, even when the stack also merges\n * `Cloudflare.providers()`. `Layer.provide`, not `provideMerge`: each feeds its own provider\n * only and adds nothing to the stack's context.\n */\nimport * as Cloudflare from 'alchemy/Cloudflare';\nimport * as Provider from 'alchemy/Provider';\nimport * as Layer from 'effect/Layer';\nimport { MeshNode, MeshNodeProvider } from './mesh-node.ts';\nimport { R2BucketLock, R2BucketLockProvider } from './r2-bucket-lock.ts';\n\nexport class Providers extends Provider.ProviderCollection<Providers>()('HomeflareCloudflare') {}\n\n// ⛔ `orDie` ON BOTH, as `Cloudflare.providers()` ends with: a stack's providers layer must not\n// fail, and a profile that cannot resolve is a defect carrying Alchemy's own sentence.\nexport const providers = () =>\n Layer.effect(Providers, Provider.collection([R2BucketLock, MeshNode])).pipe(\n Layer.provide(\n Layer.mergeAll(\n R2BucketLockProvider().pipe(Layer.provide(Cloudflare.CloudflareApiLive()), Layer.orDie),\n MeshNodeProvider().pipe(Layer.provide(Cloudflare.CloudflareApiLive()), Layer.orDie),\n ),\n ),\n );\n",
12
+ "/**\n * `Cloudflare.CloudflaredTunnel` — a Cloudflare Tunnel (the cloudflared kind, `cfd_tunnel`),\n * declared without its connector token ever touching Alchemy state. Usage: docs/cloudflared-tunnel.md.\n *\n * 🔴 WHY THIS EXISTS ALONGSIDE `Cloudflare.Tunnel.Tunnel`. Alchemy beta.81's resource stores the\n * token as a `Redacted` attribute, and the state encoder writes it out in plaintext. See\n * cloudflared-tunnel-form.ts, which also says what this resource leaves to other declarations.\n *\n * ★ REMOVAL POLICY: `retain` BY DEFAULT — see the ★ on `CloudflaredTunnel` below.\n */\nimport { Credentials } from '@distilled.cloud/cloudflare/Credentials';\nimport * as Cloudflare from 'alchemy/Cloudflare';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport { Resource, type ResourceClass } from 'alchemy/Resource';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport {\n type CloudflaredTunnelAttributes,\n type CloudflaredTunnelProps,\n diffCloudflaredTunnel,\n validateCloudflaredTunnel,\n} from './cloudflared-tunnel-form.ts';\nimport {\n deleteCloudflaredTunnel,\n readCloudflaredTunnel,\n reconcileCloudflaredTunnel,\n} from './cloudflared-tunnel-lifecycle.ts';\nimport type { Providers } from './providers.ts';\n\nexport interface CloudflaredTunnel extends Resource<\n 'Cloudflare.CloudflaredTunnel',\n CloudflaredTunnelProps,\n CloudflaredTunnelAttributes,\n never,\n Providers\n> {}\n\n/**\n * ★ `defaultRemovalPolicy: 'retain'`, LIKE EVERY KIT RESOURCE WHOSE DELETION BREAKS ITS CONSUMERS\n * (MeshNode, the `Bao.*` families, `R2BucketLock`; the reasoning is written once in\n * proxmox/resource.ts). Deleting a tunnel drops its remote configuration (ingress rules) and\n * orphans every DNS record that targets `<id>.cfargotunnel.com`, and a new tunnel has a new id\n * and a new token. So dropping the declaration, or `alchemy destroy`, leaves the tunnel live.\n * ⚠️ IT ALSO SKIPS THE OLD TUNNEL'S DELETE IN AN ACCOUNT-CHANGE REPLACE (create-first), which\n * leaves the old tunnel live and unmanaged (Apply.ts, `deleteOldGenerations`, beta.81).\n * ⛔ `delete` STAYS FULLY IMPLEMENTED. Retain is Terraform's `prevent_destroy`, not a stub: it runs\n * the moment a declaration opts in with `.pipe(RemovalPolicy.destroy())`.\n */\nexport const CloudflaredTunnel: ResourceClass<CloudflaredTunnel> = Resource<CloudflaredTunnel>(\n 'Cloudflare.CloudflaredTunnel',\n { defaultRemovalPolicy: 'retain' },\n);\n\n/** What every call below needs: the account, credentials and HTTP — Alchemy's own trio. */\nexport type CloudflaredTunnelServices =\n | Cloudflare.CloudflareEnvironment\n | Credentials\n | HttpClient.HttpClient;\n\n/** The account id, resolved the way every `alchemy/Cloudflare` provider resolves it. */\nconst accountId = Effect.gen(function* () {\n const resolved = yield* yield* Cloudflare.CloudflareEnvironment;\n return resolved.accountId;\n});\n\n/**\n * ★ `Provider.effect`, CAPTURING THE THREE SERVICES AT BUILD, exactly as `MeshNodeProvider` does\n * (mesh-node.ts says why): correct whether or not the stack also merges `Cloudflare.providers()`.\n */\nexport const CloudflaredTunnelProvider = () =>\n Provider.effect(\n CloudflaredTunnel,\n Effect.gen(function* () {\n const services = Context.make(\n Cloudflare.CloudflareEnvironment,\n yield* Cloudflare.CloudflareEnvironment,\n ).pipe(\n Context.add(Credentials, yield* Credentials),\n Context.add(HttpClient.HttpClient, yield* HttpClient.HttpClient),\n );\n const run = <A, E>(effect: Effect.Effect<A, E, CloudflaredTunnelServices>) =>\n Effect.provideContext(effect, services);\n\n return CloudflaredTunnel.Provider.of({\n stables: ['id', 'accountId'],\n /**\n * ⛔ EMPTY, AND `nuke.skip`. Alchemy's own `Cloudflare.Tunnel.Tunnel` already lists every\n * `cfd_tunnel` in the account for `alchemy unsafe nuke` (and fetches each token to do\n * it); a second listing here would make nuke delete each tunnel twice. `read` does not\n * depend on this.\n */\n list: () => Effect.succeed([]),\n nuke: { skip: true },\n /** ⛔ Validate at plan time too, so a bad declaration never reaches a replace. */\n diff: ({ news, output }) =>\n run(\n Effect.gen(function* () {\n const invalid = isResolved(news) ? validateCloudflaredTunnel(news) : undefined;\n if (invalid !== undefined) return yield* Effect.fail(invalid);\n return diffCloudflaredTunnel(news, output, yield* accountId);\n }),\n ),\n read: ({ olds, output }) =>\n run(Effect.flatMap(accountId, (account) => readCloudflaredTunnel(account, olds, output))),\n reconcile: ({ news, output }) =>\n run(\n Effect.flatMap(accountId, (account) =>\n reconcileCloudflaredTunnel(account, news, output),\n ),\n ),\n delete: ({ output }) => run(deleteCloudflaredTunnel(output)),\n });\n }),\n );\n",
13
+ "/**\n * The `cfd_tunnel` calls a Cloudflare Tunnel (the cloudflared kind) needs, and nothing else.\n *\n * ★ OVER `@distilled.cloud/cloudflare`, THE SDK ALCHEMY'S OWN CLOUDFLARE PROVIDERS USE, the same\n * one mesh-node-api.ts calls. The operations are `createTunnelCloudflared` (POST\n * `/accounts/{account_id}/cfd_tunnel`), `getTunnelCloudflared`, `patchTunnelCloudflared`,\n * `deleteTunnelCloudflared` and the account-wide `listTunnels` (GET `/accounts/{id}/tunnels`,\n * filtered to `cfd_tunnel`), all in distilled `1.0.0-rc.13` `src/services/zero_trust.ts`\n * (read 2026-10-09).\n *\n * ⛔ NO FUNCTION IN THIS FILE CALLS `getTunnelCloudflaredToken`, AND `observe` COPIES NAMED FIELDS\n * ONLY. Alchemy's own `Cloudflare.Tunnel.Tunnel` (beta.81, Tunnel/Tunnel.ts `reconcile`, `read`\n * and `list`) fetches the token on every call and stores it as a `Redacted` attribute, which\n * `encodeState` (State/StateEncoding.ts) writes into state as `{\"__redacted__\": \"<token>\"}`:\n * plaintext, and the token runs the tunnel. This package never holds it, so there is nothing to\n * leak. See cloudflared-tunnel-form.ts.\n * ⚠️ `createTunnelCloudflared` takes no `tunnelSecret` here either: the schema calls the field the\n * password for a locally-managed tunnel and marks it optional (distilled rc.13 zero_trust.ts,\n * `CreateTunnelCloudflaredRequest`); that Cloudflare generates one when it is omitted is\n * UNVERIFIED, and a remotely managed tunnel needs no secret on our side.\n */\nimport * as zeroTrust from '@distilled.cloud/cloudflare/zero-trust';\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\nimport * as Stream from 'effect/Stream';\n\n/** A live tunnel as this package sees it. ⛔ There is no token or secret field, on purpose. */\nexport interface ObservedTunnel {\n readonly id: string;\n readonly name: string;\n /** `undefined` when the response omitted it; only an explicit `local` is refused (lifecycle). */\n readonly configSrc: 'cloudflare' | 'local' | undefined;\n}\n\n/** A refusal or failure, carrying the sentence an operator needs. Never a token. */\nexport class CloudflaredTunnelError extends Data.TaggedError('CloudflaredTunnelError')<{\n readonly message: string;\n}> {}\n\ntype Raw = {\n readonly id?: string | null;\n readonly name?: string | null;\n readonly configSrc?: string | null;\n readonly tunType?: string | null;\n readonly deletedAt?: string | null;\n};\n\n/**\n * ⚠️ A DELETED TUNNEL IS NOT A TUNNEL. Cloudflare keeps deleted tunnels (every response carries\n * `deleted_at`), so a `GET` by id can answer for one; adopting that tombstone would PATCH it.\n * ⚠️ NOR IS ANOTHER TUNNEL TYPE. `/tunnels` lists every type, and a `warp_connector` (a Mesh node)\n * shares the account's name space. An item whose `tun_type` is anything but `cfd_tunnel` is not\n * ours even when the name matches. A response with no `tun_type` is accepted: it came from a\n * `cfd_tunnel`-scoped call.\n */\nexport const observe = (raw: Raw): ObservedTunnel | undefined => {\n if (typeof raw.id !== 'string' || raw.id.length === 0) return undefined;\n if (raw.deletedAt !== undefined && raw.deletedAt !== null) return undefined;\n if (typeof raw.tunType === 'string' && raw.tunType !== 'cfd_tunnel') return undefined;\n const configSrc =\n raw.configSrc === 'cloudflare' || raw.configSrc === 'local' ? raw.configSrc : undefined;\n return { id: raw.id, name: raw.name ?? '', configSrc };\n};\n\n/** The tunnel with this id, or `undefined` when it is gone (404 / code 1002, or soft-deleted). */\nexport const getTunnel = (accountId: string, tunnelId: string) =>\n zeroTrust.getTunnelCloudflared({ accountId, tunnelId }).pipe(\n Effect.map(observe),\n Effect.catchTag('TunnelNotFound', () => Effect.succeed(undefined)),\n );\n\n/**\n * The live cloudflared tunnel with exactly this name, or `undefined`.\n *\n * ★ `name` and `tunTypes` are sent as filters, but the match is re-checked here: the filter's\n * exactness is not documented, and a prefix match would adopt the wrong tunnel.\n * ⛔ Two live matches is refused rather than resolved. Names are unique per account (the create\n * answers code 1013 otherwise), so two means something this package does not understand.\n */\nexport const findTunnelByName = (accountId: string, name: string) =>\n zeroTrust.listTunnels.items({ accountId, name, isDeleted: false, tunTypes: ['cfd_tunnel'] }).pipe(\n Stream.map(observe),\n Stream.filter((t): t is ObservedTunnel => t !== undefined && t.name === name),\n Stream.runCollect,\n Effect.flatMap((chunk) => {\n const matches = Array.from(chunk);\n if (matches.length <= 1) return Effect.succeed(matches[0]);\n const ids = matches.map((t) => t.id).join(', ');\n return Effect.fail(\n new CloudflaredTunnelError({\n message: `Tunnel name \"${name}\" matches ${matches.length} live cloudflared tunnels (${ids}). Names are unique per account, so refusing to guess which one is meant.`,\n }),\n );\n }),\n );\n\n/**\n * ⛔ ALWAYS `configSrc: 'cloudflare'` (remotely managed), NEVER OMITTED. The dashboard and the\n * ingress rules a consumer later PUTs are the remote configuration; a `local` tunnel ignores\n * them. The SDK types the field as optional with two values (zero_trust.ts\n * `CreateTunnelCloudflaredRequest`) and does not say which is the default, so it is sent\n * explicitly. Create-only (the PATCH body has `name` and `tunnelSecret` only).\n */\nexport const createTunnel = (accountId: string, name: string) =>\n zeroTrust.createTunnelCloudflared({ accountId, name, configSrc: 'cloudflare' }).pipe(\n Effect.flatMap((raw) => {\n const tunnel = observe(raw);\n return tunnel === undefined\n ? Effect.fail(\n new CloudflaredTunnelError({ message: `Creating tunnel \"${name}\" returned no id.` }),\n )\n : Effect.succeed(tunnel);\n }),\n );\n\n/** ★ A rename is a PATCH: same id, same connectors, same `<id>.cfargotunnel.com` target. */\nexport const renameTunnel = (accountId: string, tunnelId: string, name: string) =>\n zeroTrust\n .patchTunnelCloudflared({ accountId, tunnelId, name })\n .pipe(Effect.map((raw) => observe(raw) ?? { id: tunnelId, name, configSrc: undefined }));\n\n/**\n * ⚠️ The SDK types `deleteTunnelCloudflared`'s errors as `CloudflareOpError`, which has no\n * `NotFound`, yet a 404 reaches the caller as `NotFound` at runtime (protocol.ts `HTTP_STATUS_MAP`,\n * measured through the fake). So the tag is matched by name, not by `catchTag`'s typed union.\n */\nconst isNotFound = (error: { readonly _tag: string }) => error._tag === 'NotFound';\n\n/**\n * ⛔ DELETE BY THE STORED ID ONLY, AFTER A READ BY THAT ID. Never by name: a name can have been\n * re-used by another tunnel since this one was written, and a delete cannot be taken back. An\n * empty id is refused before any request (a blank path segment would address the collection).\n * ★ Idempotent: a tunnel already gone or soft-deleted is a successful delete, including one that\n * vanishes between the read and the DELETE (see `isNotFound`). The API refuses a\n * delete while connectors are attached (\"The tunnel must have no active connections\", SDK doc on\n * `deleteTunnelCloudflared`); that error passes through, since the operator must stop cloudflared.\n */\nexport const deleteTunnel = (accountId: string, tunnelId: string) =>\n Effect.gen(function* () {\n if (tunnelId.length === 0) {\n return yield* Effect.fail(\n new CloudflaredTunnelError({ message: 'Refusing to delete a tunnel with an empty id.' }),\n );\n }\n const live = yield* getTunnel(accountId, tunnelId);\n if (live === undefined) return;\n yield* zeroTrust\n .deleteTunnelCloudflared({ accountId, tunnelId })\n .pipe(Effect.catchIf(isNotFound, () => Effect.void));\n });\n",
14
+ "/**\n * `Cloudflare.CloudflaredTunnel` — its props, its attributes, and the pure `diff`.\n *\n * ⛔ THE ATTRIBUTES NEVER HOLD A TOKEN OR A SECRET. That is the reason this resource exists next to\n * Alchemy's `Cloudflare.Tunnel.Tunnel` (beta.81): its `reconcile`, `read` and `list` all call\n * `getTunnelCloudflaredToken` and keep the result as `token: Redacted<string>`, which\n * `encodeState` persists as `{\"__redacted__\": \"<token>\"}` in the (unencrypted) state store. That\n * token is what `cloudflared tunnel run --token` accepts: whoever reads state can run a connector\n * for the tunnel and receive its traffic. It also makes every PLAN need a token with a Write\n * permission. This resource never calls the endpoint, so a plan runs on a read token.\n * ★ THE CONNECTOR'S TOKEN IS A RUNTIME CREDENTIAL, NOT INFRASTRUCTURE STATE. Whatever installs the\n * connector fetches it at that moment and hands it straight to the workload's secret store.\n * Nothing here does, and nothing here exports a helper that would.\n * ★ SCOPE: the tunnel object only. No ingress rules, no routes, no DNS, no connector Deployment:\n * those are separate declarations that name this tunnel's `id`.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport type { Input } from 'alchemy/Input';\nimport { CloudflaredTunnelError } from './cloudflared-tunnel-api.ts';\n\nexport interface CloudflaredTunnelProps {\n /**\n * The tunnel's name in the account — and its identity when adopting (names are unique per\n * account). ★ Changing it RENAMES the tunnel in place (`PATCH`): the id, the connectors and the\n * `<id>.cfargotunnel.com` target survive.\n */\n readonly name: string;\n}\n\nexport interface CloudflaredTunnelAttributes {\n /** The tunnel's UUID: the `<id>.cfargotunnel.com` target and the key for ingress rules. */\n readonly id: string;\n /**\n * The account the provider environment resolved when the tunnel was written. Not a secret; it\n * is what `delete` and `read` address, so they never act on the current environment's account.\n */\n readonly accountId: string;\n readonly name: string;\n // ★ No `status`: it moves on its own as connectors attach and drop, so a stored copy would make\n // every `alchemy drift` report this tunnel as drifted. Health is the API's or the metrics' to say.\n}\n\n/** ⛔ Fail closed on a declaration the API would accept but nobody meant. */\nexport const validateCloudflaredTunnel = (\n props: CloudflaredTunnelProps,\n): CloudflaredTunnelError | undefined => {\n if (typeof props.name !== 'string' || props.name.trim().length === 0) {\n return new CloudflaredTunnelError({\n message: 'CloudflaredTunnel `name` must be a non-empty string.',\n });\n }\n if (props.name !== props.name.trim()) {\n return new CloudflaredTunnelError({\n message: `CloudflaredTunnel name \"${props.name}\" has leading or trailing whitespace; adoption matches names exactly.`,\n });\n }\n return undefined;\n};\n\n/**\n * update or replace, from the declaration and what was last written.\n *\n * - Account changed → `replace`, create-first: names are unique per ACCOUNT, so the two\n * generations cannot collide.\n * - Name changed → `update` (a PATCH rename).\n * - Otherwise → no opinion; the engine compares props.\n */\nexport const diffCloudflaredTunnel = (\n news: Input<CloudflaredTunnelProps>,\n output: CloudflaredTunnelAttributes | undefined,\n accountId: string,\n): Diff | undefined => {\n if (output === undefined) return undefined;\n if (output.accountId !== accountId) return { action: 'replace' };\n const name = (news as { readonly name?: unknown }).name;\n if (typeof name === 'string' && name !== output.name) return { action: 'update' };\n return undefined;\n};\n",
15
+ "/**\n * read / reconcile / delete for `Cloudflare.CloudflaredTunnel`, as plain Effects over the SDK calls\n * in cloudflared-tunnel-api.ts. cloudflared-tunnel.ts only wires them into Alchemy.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport * as Effect from 'effect/Effect';\nimport {\n CloudflaredTunnelError,\n type ObservedTunnel,\n createTunnel,\n deleteTunnel,\n findTunnelByName,\n getTunnel,\n renameTunnel,\n} from './cloudflared-tunnel-api.ts';\nimport {\n type CloudflaredTunnelAttributes,\n type CloudflaredTunnelProps,\n validateCloudflaredTunnel,\n} from './cloudflared-tunnel-form.ts';\n\nconst attributesOf = (tunnel: ObservedTunnel, accountId: string): CloudflaredTunnelAttributes => ({\n id: tunnel.id,\n accountId,\n name: tunnel.name,\n});\n\n/**\n * Owned: refresh by the stored id. Cold (no stored row): find the live tunnel by exact name and\n * hand it back `Unowned`, so Alchemy refuses to take it over until the stack says `adopt(true)` —\n * a `cfd_tunnel` carries no ownership marker to prove it was ours. Adoption is idempotent: the\n * adopted row holds the id, so every later `read` takes the owned path and writes nothing.\n *\n * ⛔ A STORED ID THAT NO LONGER RESOLVES IS \"MISSING\", NEVER A NAME PROBE. Drift (alchemy beta.81\n * src/Drift.ts) passes the row's output and treats whatever `read` returns as already ours\n * (`stripUnowned`), then repairs by reconciling onto it. A by-name fallback there would write a\n * same-named tunnel somebody else made into this row, bypassing `adopt(true)`; a later rename\n * would PATCH it and `RemovalPolicy.destroy` would DELETE it. Returning `undefined` makes Drift\n * report \"missing\" and recreate through `reconcile`, where `createFresh` names the holder.\n */\nexport const readCloudflaredTunnel = (\n accountId: string,\n olds: CloudflaredTunnelProps | undefined,\n output: CloudflaredTunnelAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const account = output?.accountId ?? accountId;\n if (output !== undefined) {\n const tunnel = yield* getTunnel(account, output.id);\n return tunnel === undefined ? undefined : attributesOf(tunnel, account);\n }\n const name = olds?.name;\n if (name === undefined) return undefined;\n const match = yield* findTunnelByName(account, name);\n return match === undefined ? undefined : Unowned(attributesOf(match, account));\n });\n\n/**\n * ⛔ NEVER CONVERGE ON A TUNNEL THIS RESOURCE DID NOT FIND BY ITS OWN ID. Alchemy's own Tunnel\n * answers a duplicate-name failure by re-reading the holder and returning it (when its `adopt`\n * prop is set). Here adoption belongs to `read` and `adopt(true)` alone, where it is visible in\n * the plan. The holder is named, never deleted: it may be a live tunnel that serves traffic.\n */\nconst nameHeld = (name: string, holder: string) =>\n new CloudflaredTunnelError({\n message:\n `A tunnel named \"${name}\" already exists (${holder}) and is not the tunnel this resource ` +\n 'tracks, so it is left alone: names are unique per account. If it is meant to be this ' +\n 'one, deploy under adopt(true). Otherwise choose another name; do not delete it.',\n });\n\n/**\n * 🔴 A 1013 AFTER A CLEAN LOOKUP IS USUALLY THIS DEPLOY'S OWN TUNNEL. distilled retries a POST on a\n * transport error or a 5xx, so a create that succeeded but lost its response is sent again and\n * answered 1013 by the tunnel the first attempt made. The lookup is repeated so the sentence\n * names that tunnel; it is still not adopted here, because a concurrent creator looks the same.\n */\nconst raced = (accountId: string, name: string) =>\n findTunnelByName(accountId, name).pipe(\n Effect.flatMap((holder) =>\n Effect.fail(\n holder === undefined\n ? nameHeld(\n name,\n 'not a live cloudflared tunnel: another tunnel type, or one just deleted',\n )\n : new CloudflaredTunnelError({\n message:\n `Tunnel \"${name}\" (${holder.id}) appeared between this deploy's lookup and its ` +\n 'create. The likeliest cause is this deploy itself: the SDK retries a create ' +\n 'whose response was lost. If so, deploy again under adopt(true). Otherwise ' +\n 'another creator raced this one.',\n }),\n ),\n ),\n );\n\nconst createFresh = (accountId: string, name: string) =>\n Effect.gen(function* () {\n const existing = yield* findTunnelByName(accountId, name);\n if (existing !== undefined) return yield* Effect.fail(nameHeld(name, existing.id));\n const tunnel = yield* createTunnel(accountId, name).pipe(\n Effect.catchTag('DuplicateTunnelName', () => raced(accountId, name)),\n );\n return attributesOf(tunnel, accountId);\n });\n\n/**\n * ⛔ `local` IS REFUSED. A tunnel's `config_src` is create-only, and adoption by name could land on\n * a locally configured tunnel, whose ingress is a YAML file on its origin: a consumer's remote\n * rules would be accepted by the API and never applied. The tunnel is left untouched.\n */\nconst requireRemote = (tunnel: ObservedTunnel) =>\n tunnel.configSrc === 'local'\n ? Effect.fail(\n new CloudflaredTunnelError({\n message: `Tunnel \"${tunnel.name}\" (${tunnel.id}) is locally configured (config_src local), which cannot be changed after creation. CloudflaredTunnel manages remotely configured tunnels only; create a new tunnel under another name.`,\n }),\n )\n : Effect.void;\n\nexport const reconcileCloudflaredTunnel = (\n accountId: string,\n news: CloudflaredTunnelProps,\n output: CloudflaredTunnelAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const invalid = validateCloudflaredTunnel(news);\n if (invalid !== undefined) return yield* Effect.fail(invalid);\n const owned = output !== undefined && output.accountId === accountId ? output : undefined;\n const live = owned === undefined ? undefined : yield* getTunnel(accountId, owned.id);\n // ★ Gone out of band (or a replace's new generation): create, under the same refusals.\n if (owned === undefined || live === undefined) return yield* createFresh(accountId, news.name);\n yield* requireRemote(live);\n if (live.name === news.name) return attributesOf(live, accountId);\n const renamed = yield* renameTunnel(accountId, live.id, news.name).pipe(\n Effect.catchTag('DuplicateTunnelName', () =>\n Effect.fail(nameHeld(news.name, 'held by another tunnel')),\n ),\n );\n return attributesOf(renamed, accountId);\n });\n\n/** By the stored id and account only (cloudflared-tunnel-api.ts, `deleteTunnel`). */\nexport const deleteCloudflaredTunnel = (output: CloudflaredTunnelAttributes) =>\n deleteTunnel(output.accountId, output.id);\n",
16
+ "/**\n * `HomeFlare.Access.SaasOidcApplication` — a Cloudflare Access SaaS application that makes Access an\n * OIDC identity provider for one relying party (OpenBao's JWT/OIDC auth, Headlamp, a Kubernetes\n * API server). Usage and the secret rules: docs/saas-oidc.md.\n *\n * 🔴 WHY THIS EXISTS ALONGSIDE `Cloudflare.Access.Application`. Alchemy beta.81's Application has\n * no `saas_app` field (its source mentions `saas` only as a type and in two comments), and\n * `@distilled.cloud/cloudflare` rc.13 decodes a saas app's GET and create response WITHOUT it, so\n * the client id cannot be read back through either. See saas-oidc-api.ts.\n *\n * ⛔ THE TYPE ID IS A STATE KEY, AND THIS ONE WAS MOVED, NOT RENAMED. It is the string the\n * homeflare-openbao stack's live `OpenBaoOidcSaas` row was written under. A different string would\n * make the next plan see a new resource and an orphaned one: a second OIDC app, with a new\n * client id and `aud`, and a delete of the live one unless it were retained. `tests` pin it.\n *\n * ★ `retain` BY DEFAULT, like every kit resource whose deletion breaks its consumers: deleting the\n * app deletes the client id every relying party is configured with. The openbao declaration\n * already pipes `RemovalPolicy.retain()`; `delete` stays fully implemented for a declaration\n * that opts in with `.pipe(RemovalPolicy.destroy())`.\n */\nimport { Credentials } from '@distilled.cloud/cloudflare/Credentials';\nimport * as Cloudflare from 'alchemy/Cloudflare';\nimport * as Provider from 'alchemy/Provider';\nimport { Resource, type ResourceClass } from 'alchemy/Resource';\nimport * as Context from 'effect/Context';\nimport * as Effect from 'effect/Effect';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport type { Providers } from './providers.ts';\nimport { deleteSaasOidc, readSaasOidc, reconcileSaasOidc } from './saas-oidc-lifecycle.ts';\nimport type { SaasOidcApplicationAttributes, SaasOidcApplicationProps } from './saas-oidc-form.ts';\n\nexport interface SaasOidcApplication extends Resource<\n 'HomeFlare.Access.SaasOidcApplication',\n SaasOidcApplicationProps,\n SaasOidcApplicationAttributes,\n never,\n Providers\n> {}\n\nexport const SaasOidcApplication: ResourceClass<SaasOidcApplication> =\n Resource<SaasOidcApplication>('HomeFlare.Access.SaasOidcApplication', {\n defaultRemovalPolicy: 'retain',\n });\n\nconst accountId = Effect.gen(function* () {\n const resolved = yield* yield* Cloudflare.CloudflareEnvironment;\n return resolved.accountId;\n});\n\n/**\n * ★ `Provider.effect`, CAPTURING THE THREE SERVICES AT BUILD, as MeshNodeProvider does\n * (mesh-node.ts): correct whether or not the stack also merges `Cloudflare.providers()`.\n */\nexport const SaasOidcApplicationProvider = () =>\n Provider.effect(\n SaasOidcApplication,\n Effect.gen(function* () {\n const services = Context.make(\n Cloudflare.CloudflareEnvironment,\n yield* Cloudflare.CloudflareEnvironment,\n ).pipe(\n Context.add(Credentials, yield* Credentials),\n Context.add(HttpClient.HttpClient, yield* HttpClient.HttpClient),\n );\n // ★ Only the three captured services are provided; `createPhysicalName` still takes\n // InstanceId, Stack and Stage from the engine at call time, as in every Alchemy provider.\n const run = <A, E, R>(effect: Effect.Effect<A, E, R>) =>\n Effect.provideContext(effect, services);\n\n return SaasOidcApplication.Provider.of({\n /**\n * ★ THE ISSUER AND EVERY ENDPOINT ARE STABLE. They depend only on the team domain and the\n * client id, and `checkTeam` refuses any team other than the app's own host, so no update\n * of this app can move them. Alchemy's plan (Plan.ts `withStables`, beta.81) keeps ONLY\n * the listed attributes resolved for consumers of an updated resource; without them, a\n * redirect-URI change would leave `app.issuer` unresolved and re-plan every consumer\n * (the Talos KubeAuthenticationConfig, so a kube-apiserver restart on each node).\n * ⚠️ `teamDomain` is NOT listed: state written by the openbao copy has no such attribute,\n * so a consumer would read `undefined` on the first plan after the move.\n */\n stables: [\n 'applicationId',\n 'aud',\n 'clientId',\n 'accountId',\n 'issuer',\n 'authorizationEndpoint',\n 'tokenEndpoint',\n 'jwksEndpoint',\n 'userinfoEndpoint',\n 'configurationEndpoint',\n ],\n /**\n * ⛔ EMPTY, AND `nuke.skip`. Alchemy's own `Cloudflare.Access.Application` already lists\n * every Access application in the account for `alchemy unsafe nuke` (its `list`, saas\n * apps included); a second listing here would make nuke delete each app twice.\n */\n list: () => Effect.succeed([]),\n nuke: { skip: true },\n read: ({ id, olds, output }) =>\n run(Effect.flatMap(accountId, (account) => readSaasOidc(account, id, olds, output))),\n reconcile: ({ id, news, output }) =>\n run(Effect.flatMap(accountId, (account) => reconcileSaasOidc(account, id, news, output))),\n delete: ({ output }) => run(deleteSaasOidc(output)),\n });\n }),\n );\n",
17
+ "/**\n * read / reconcile / delete for `HomeFlare.Access.SaasOidcApplication`, as plain Effects over the\n * calls in saas-oidc-api.ts. saas-oidc.ts only wires them into Alchemy.\n */\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { createPhysicalName } from 'alchemy/PhysicalName';\nimport * as Effect from 'effect/Effect';\nimport { createApp, deleteApp, findSaasByName, getApp, updateApp } from './saas-oidc-api.ts';\nimport { SaasOidcError } from './saas-oidc-error.ts';\nimport {\n type SaasOidcApplicationAttributes,\n type SaasOidcApplicationProps,\n checkTeam,\n needsSync,\n toAttributes,\n validateSaasOidc,\n writeBody,\n} from './saas-oidc-form.ts';\nimport { checkTeamBeforeCreate } from './saas-oidc-team.ts';\nimport type { ObservedApp } from './saas-oidc-wire.ts';\n\nconst resolveName = (id: string, name: string | undefined) =>\n name !== undefined && name !== '' ? Effect.succeed(name) : createPhysicalName({ id });\n\n/**\n * The live app: by the known id when there is one, else by exact name. A saas app found by id that\n * is NOT type `saas` is refused, never rewritten: `applicationId` pointing at a self-hosted app\n * would otherwise have its type overwritten by the update.\n */\nconst observe = (\n accountId: string,\n knownId: string | undefined,\n name: string,\n options: { readonly byIdOnly?: boolean } = {},\n) =>\n Effect.gen(function* () {\n const byId = knownId === undefined ? undefined : yield* getApp(accountId, knownId);\n if (byId !== undefined) {\n if (byId.type !== 'saas') {\n return yield* Effect.fail(\n new SaasOidcError({\n message: `Access application ${knownId} is type \"${byId.type ?? 'unknown'}\", not \"saas\". Refusing to rewrite it as a SaaS OIDC app.`,\n }),\n );\n }\n return byId;\n }\n // ★ An OWNED row's app that is gone stays gone: another saas app with the same name is not it,\n // and its applicationId and clientId (declared stables) must not change under the row.\n if (options.byIdOnly === true) return undefined;\n const foundId = yield* findSaasByName(accountId, name);\n return foundId === undefined ? undefined : yield* getApp(accountId, foundId);\n });\n\n/**\n * Owned: refresh by the stored id ONLY (gone is `undefined`, never a same-named app). Cold: find the app by `applicationId` or exact name and hand it\n * back `Unowned`, so Alchemy refuses to take it over until the stack says `adopt(true)`.\n * ★ `olds` MAY PREDATE `teamDomain` (state written by the openbao copy of this resource): the host\n * Cloudflare reports for the app stands in, so the first plan after the move can still read.\n */\nexport const readSaasOidc = (\n accountId: string,\n id: string,\n olds: SaasOidcApplicationProps | undefined,\n output: SaasOidcApplicationAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const name = yield* resolveName(id, output?.name ?? olds?.name);\n const owned = output?.applicationId;\n const observed = yield* observe(accountId, owned ?? olds?.applicationId, name, {\n byIdOnly: owned !== undefined,\n });\n if (observed === undefined) return undefined;\n const attributes = toAttributes(\n observed,\n output?.accountId ?? accountId,\n name,\n output?.teamDomain ?? olds?.teamDomain,\n );\n if (attributes === undefined) return undefined;\n return owned !== undefined ? attributes : Unowned(attributes);\n });\n\nconst missing = (what: string) =>\n Effect.fail(\n new SaasOidcError({\n message: `SaaS OIDC ${what} succeeded but the GET did not return the app.`,\n }),\n );\n\n/** observe, ensure (create or sync), read back. A no-op when nothing drifted. */\nexport const reconcileSaasOidc = (\n accountId: string,\n id: string,\n news: SaasOidcApplicationProps,\n output: SaasOidcApplicationAttributes | undefined,\n) =>\n Effect.gen(function* () {\n const invalid = validateSaasOidc(news);\n if (invalid !== undefined) return yield* Effect.fail(invalid);\n const name = yield* resolveName(id, news.name);\n // ★ A stored applicationId means the row owns THAT app: if it is gone, create (or refuse),\n // never adopt whatever app now carries the name. Only a cold run may look up by name.\n const ownedId = output?.applicationId;\n let observed: ObservedApp | undefined = yield* observe(\n accountId,\n ownedId ?? news.applicationId,\n name,\n { byIdOnly: ownedId !== undefined },\n );\n // ⛔ BEFORE ANY WRITE TO AN EXISTING APP: a wrong team domain must not cost it an update.\n // A FIRST CREATE has no app to compare with, so it is checked against the account's Zero\n // Trust organization instead (`checkTeamBeforeCreate`, below). ⚠️ That read needs a token\n // permission the app write does not (saas-oidc-team.ts): where it cannot be read, a wrong\n // `teamDomain` still writes the app and then fails the post-write check, leaving a live app\n // with no state row. docs/saas-oidc.md says what to do.\n const wrongTeam =\n observed === undefined ? undefined : checkTeam(news.teamDomain, observed, output?.teamDomain);\n if (wrongTeam !== undefined) return yield* Effect.fail(wrongTeam);\n const write = writeBody(news, name);\n if (observed === undefined) {\n const wrongOrganization = yield* checkTeamBeforeCreate(accountId, news.teamDomain);\n if (wrongOrganization !== undefined) return yield* Effect.fail(wrongOrganization);\n const createdId = yield* createApp(accountId, write);\n observed = yield* getApp(accountId, createdId);\n if (observed === undefined) return yield* missing('create');\n } else if (needsSync(news, observed, name)) {\n const appId = observed.id;\n if (appId === undefined) {\n return yield* Effect.fail(new SaasOidcError({ message: 'SaaS OIDC app has no id.' }));\n }\n yield* updateApp(accountId, appId, write);\n observed = yield* getApp(accountId, appId);\n if (observed === undefined) return yield* missing('update');\n }\n const teamAfterWrite = checkTeam(news.teamDomain, observed, output?.teamDomain);\n if (teamAfterWrite !== undefined) return yield* Effect.fail(teamAfterWrite);\n const attributes = toAttributes(observed, accountId, name, news.teamDomain);\n if (attributes === undefined) {\n return yield* Effect.fail(\n new SaasOidcError({\n message: 'Cloudflare returned a SaaS OIDC application without an id, aud or client id.',\n }),\n );\n }\n return attributes;\n });\n\nexport const deleteSaasOidc = (output: SaasOidcApplicationAttributes) =>\n deleteApp(output.accountId, output.applicationId);\n",
18
+ "/**\n * The Access application calls a SaaS OIDC application needs, and nothing else.\n *\n * ★ OVER `@distilled.cloud/cloudflare` FOR EVERY WRITE AND FOR THE NAME LOOKUP; ONE RAW READ. Alchemy\n * `2.0.0-beta.81`'s `Cloudflare.Access.Application` has no `saas_app` at all (Application.ts:\n * `saas` appears only as an app type and in two doc comments), so this resource exists. The SDK\n * does carry `saasApp` on the REQUEST (`AccessApplicationsCreateRequestSaasAppOIDCSaaSApp`,\n * zero_trust.ts, `@distilled.cloud/cloudflare@1.0.0-rc.13`), so create and update go through it.\n * ⚠️ BUT ITS RESPONSES DROP IT. Measured 2026-10-09 against rc.13 with a fake API that answered a\n * full `saas_app` object: `getAccessApplicationForAccount` and `createAccessApplicationForAccount`\n * both decoded to `{ domain, type, id, aud, name, policies }`, with no `saasApp`. The response\n * union is discriminated by field names (`T.UnionCases`) and a saas app falls in a variant with\n * no `saas_app`. So the OIDC client id, redirect URIs, scopes and grant types can only be read\n * from the wire JSON, and `getApp` below does that through the `HttpClient` the SDK itself runs\n * on, with the SDK's own credentials and base URL. It invents no path: it is the same\n * `GET /accounts/{account_id}/access/apps/{app_id}` the SDK's get verb sends.\n *\n * ⛔ THE CLIENT SECRET NEVER REACHES THIS PACKAGE'S MEMORY. The field doc on the SDK schema says\n * \"The application client secret, only returned on POST request\". The create response is\n * decoded by the SDK, whose `saasApp`-less variant drops it, and `getApp` copies named fields\n * only and never reads `client_secret`. TWO paths remain where the SDK itself holds or prints the\n * raw body (`@distilled.cloud/cloudflare@1.0.0-rc.13`, protocol.ts):\n * 1. `DISTILLED_DEBUG_HTTP` makes it `console.error` the first 400 characters of every response\n * (line 334), so `refuseDebugHttp` stops a write while it is set. THIS ONE STAYS AN\n * OPERATOR-ONLY SWITCH: the package can refuse to run, it cannot stop the SDK printing.\n * 2. A response that fails schema validation becomes `CloudflareParseError({ body, cause })`\n * (lines 452-457), whose `body` is the whole parsed response, create response included.\n * ⛔ SANITISED HERE: every SDK call of this resource (create, update, list, delete, and the\n * organization read in saas-oidc-team.ts) catches that tag at the call and fails with a\n * `SaasOidcError` that names the operation and nothing from the response. Why, when it can\n * happen at all, and what rc.13 will and will not raise: saas-oidc-error.ts (`withheld`).\n * The raw `get` below is not an SDK call and never builds that error.\n * A PUBLIC PKCE client (`allowPkceWithoutClientSecret: true`) has no secret at all, so neither\n * path exposes one for the Headlamp app.\n */\nimport { Credentials, formatHeaders } from '@distilled.cloud/cloudflare/Credentials';\nimport * as zeroTrust from '@distilled.cloud/cloudflare/zero-trust';\nimport * as Effect from 'effect/Effect';\nimport * as Schedule from 'effect/Schedule';\nimport * as Stream from 'effect/Stream';\nimport * as HttpClient from 'effect/http/HttpClient';\nimport * as HttpClientRequest from 'effect/http/HttpClientRequest';\nimport { SaasOidcError, withheld } from './saas-oidc-error.ts';\nimport { type AppWrite, parseApp, record } from './saas-oidc-wire.ts';\n\n/**\n * The two SDK writes, as properties of an object so saas-oidc-parse-error.test.ts can stand in for\n * one: rc.13 cannot be made to fail a create or update with `CloudflareParseError` (the ⚠️ in\n * saas-oidc-error.ts), and the catch on those two calls still has to be proven wired. Nothing\n * else assigns here.\n */\nexport const sdkWrites = {\n create: zeroTrust.createAccessApplicationForAccount,\n update: zeroTrust.updateAccessApplicationForAccount,\n};\n\nconst TRANSIENT_STATUS = (status: number): boolean =>\n status === 403 || status === 429 || status >= 500;\n\n/**\n * ★ UPSTREAM'S OWN RETRY, EXACTLY (alchemy beta.81 `Cloudflare/Access/Application.ts`, the comment\n * and `retryTransientAccessError` above `ApplicationProvider`): `AccessReferenceNotFound` is a\n * freshly created policy still propagating, and `Forbidden` is how Cloudflare throttles a valid\n * token. Capped exponential, about 45 seconds, then fail. The raw read joins it for the same\n * statuses so it is throttled the way the SDK calls are.\n */\nexport const retryTransient = <A, E extends { readonly _tag: string }, R>(\n effect: Effect.Effect<A, E, R>,\n) =>\n effect.pipe(\n Effect.retry({\n while: (e) =>\n e._tag === 'AccessReferenceNotFound' ||\n e._tag === 'Forbidden' ||\n (e._tag === 'SaasOidcError' &&\n TRANSIENT_STATUS((e as unknown as SaasOidcError).status ?? 0)),\n schedule: Schedule.max([\n Schedule.min([Schedule.exponential('1 second', 1.5), Schedule.spaced('5 seconds')]),\n Schedule.recurs(12),\n ]),\n }),\n );\n\n/**\n * ⛔ REFUSED BEFORE ANY WRITE while `DISTILLED_DEBUG_HTTP` is set: the SDK would print the create\n * response, which carries the client secret, to stderr (protocol.ts). Read from `process.env`\n * because that is what distilled reads, as mesh-node-token.ts does.\n */\nexport const refuseDebugHttp = Effect.suspend(() =>\n (globalThis.process?.env?.['DISTILLED_DEBUG_HTTP'] ?? '') === ''\n ? Effect.void\n : Effect.fail(\n new SaasOidcError({\n message:\n 'Refusing to write a SaaS OIDC application while DISTILLED_DEBUG_HTTP is set: the SDK would print the response, which carries the client secret, to stderr. Unset it and run again.',\n }),\n ),\n);\n\n/**\n * The app with this id, from the wire JSON, or `undefined` when it is gone (404).\n * ⚠️ Error messages carry the reason and the status only: the request holds the bearer token.\n */\nexport const getApp = (accountId: string, appId: string) =>\n Effect.gen(function* () {\n const credentials = yield* yield* Credentials;\n const client = yield* HttpClient.HttpClient;\n const request = HttpClientRequest.get(\n `${credentials.apiBaseUrl}/accounts/${accountId}/access/apps/${appId}`,\n ).pipe(HttpClientRequest.setHeaders(formatHeaders(credentials)), HttpClientRequest.acceptJson);\n const transport = (cause: { readonly message: string }) =>\n new SaasOidcError({ message: `Access application GET failed: ${cause.message}`, status: 0 });\n const response = yield* client.execute(request).pipe(Effect.mapError(transport));\n if (response.status === 404) return undefined;\n if (response.status < 200 || response.status >= 300) {\n return yield* Effect.fail(\n new SaasOidcError({\n message: `Access application GET returned ${response.status}`,\n status: response.status,\n }),\n );\n }\n const text = yield* response.text.pipe(Effect.mapError(transport));\n const body = yield* Effect.try({\n try: () => record(JSON.parse(text) as unknown),\n catch: () =>\n new SaasOidcError({ message: 'Access application GET was not JSON', status: 200 }),\n });\n const result = record(body?.['result']);\n return result === undefined ? undefined : parseApp(result);\n }).pipe(retryTransient);\n\n/**\n * The live saas app with exactly this name, or `undefined`. The list payload carries no `saas_app`\n * (see the ⚠️ above), so the caller re-reads the match by id with `getApp`.\n * ⛔ Two matches are refused rather than resolved: Access does not enforce unique app names, so two\n * means this package cannot tell which one is meant.\n */\nexport const findSaasByName = (accountId: string, name: string) =>\n zeroTrust.listAccessApplicationsForAccount.items({ accountId }).pipe(\n Stream.filter((app) => app.type === 'saas' && app.name === name),\n Stream.runCollect,\n Effect.catchTag('CloudflareParseError', withheld('application list')),\n retryTransient,\n Effect.flatMap((chunk) => {\n const ids = Array.from(chunk).flatMap((app) => (app.id == null ? [] : [app.id]));\n if (ids.length <= 1) return Effect.succeed(ids[0]);\n return Effect.fail(\n new SaasOidcError({\n message: `SaaS application name \"${name}\" matches ${ids.length} live apps (${ids.join(', ')}). Refusing to guess which one is meant; set \\`applicationId\\`.`,\n }),\n );\n }),\n );\n\nconst sdkBody = (write: AppWrite) => ({\n type: 'saas' as const,\n name: write.name,\n sessionDuration: write.sessionDuration,\n ...(write.allowedIdps === undefined ? {} : { allowedIdps: [...write.allowedIdps] }),\n ...(write.autoRedirectToIdentity === undefined\n ? {}\n : { autoRedirectToIdentity: write.autoRedirectToIdentity }),\n appLauncherVisible: write.appLauncherVisible,\n policies: [...write.policies],\n saasApp: {\n authType: write.saasApp.authType,\n redirectUris: [...write.saasApp.redirectUris],\n scopes: [...write.saasApp.scopes],\n grantTypes: [...write.saasApp.grantTypes],\n accessTokenLifetime: write.saasApp.accessTokenLifetime,\n allowPkceWithoutClientSecret: write.saasApp.allowPkceWithoutClientSecret,\n ...(write.saasApp.refreshTokenOptions === undefined\n ? {}\n : { refreshTokenOptions: write.saasApp.refreshTokenOptions }),\n ...(write.saasApp.appLauncherUrl === undefined\n ? {}\n : { appLauncherUrl: write.saasApp.appLauncherUrl }),\n } satisfies zeroTrust.AccessApplicationsCreateRequestSaasAppOIDCSaaSApp,\n});\n\n/** Create the app and return its id. The response's other fields are not trusted: re-read it. */\nexport const createApp = (accountId: string, write: AppWrite) =>\n refuseDebugHttp.pipe(\n Effect.andThen(\n sdkWrites\n .create({ accountId, ...sdkBody(write) })\n .pipe(\n Effect.catchTag('CloudflareParseError', withheld('application create')),\n retryTransient,\n ),\n ),\n Effect.flatMap((created) =>\n created.id == null\n ? Effect.fail(new SaasOidcError({ message: 'SaaS OIDC create did not return an id.' }))\n : Effect.succeed(created.id),\n ),\n );\n\nexport const updateApp = (accountId: string, appId: string, write: AppWrite) =>\n refuseDebugHttp.pipe(\n Effect.andThen(\n sdkWrites\n .update({ accountId, appId, ...sdkBody(write) })\n .pipe(\n Effect.catchTag('CloudflareParseError', withheld('application update')),\n retryTransient,\n ),\n ),\n Effect.asVoid,\n );\n\n/**\n * Idempotent: an app already gone is a successful delete. Any other failure propagates, except\n * that a response the SDK cannot validate is withheld (see `withheld`).\n */\nexport const deleteApp = (accountId: string, appId: string) =>\n zeroTrust.deleteAccessApplicationForAccount({ accountId, appId }).pipe(\n Effect.asVoid,\n Effect.catchTags({\n AccessApplicationNotFound: () => Effect.void,\n CloudflareParseError: withheld('application delete'),\n }),\n );\n",
19
+ "/**\n * The one error type of the SaaS OIDC resource, and the handler that keeps the SDK's raw\n * `CloudflareParseError` (which can hold the client secret) from leaving this package.\n *\n * ⛔ THE LEAK. A response that fails schema validation becomes `CloudflareParseError({ body, cause })`\n * (`@distilled.cloud/cloudflare@1.0.0-rc.13` protocol.ts:452-457). `body` is the WHOLE parsed\n * response, a create response included, and for a confidential client that carries\n * `client_secret`; `cause` is a SchemaError that quotes the values that failed. A raw SDK error\n * that reached an engine log or a trace would print both.\n * ★ THE FIX is at every SDK call of this resource (create, update, list, delete, and the\n * organization read in saas-oidc-team.ts): `Effect.catchTag('CloudflareParseError', withheld(…))`,\n * applied before the retry or any other combinator sees the error. It is the house's catchTag\n * doctrine (a typed tag, no status or message sniffing), and the replacement carries the\n * operation name and nothing from the response. The raw `get` in saas-oidc-api.ts is not an SDK\n * call and never builds this error.\n * ⚠️ WHEN IT CAN HAPPEN AT ALL. The error exists only under STRICT response validation (core\n * response-validation.ts:29 \"without one, every call is lenient\", :104-116); neither Alchemy nor\n * this kit turns strict on, so a consumer's own `ResponseValidation.strict` is what makes it\n * reachable. MEASURED 2026-10-09 against rc.13 under strict validation: a create or update\n * response decodes against `S.Unknown` (`AccessApplicationsCreateResult`, zero_trust.ts:35025;\n * `AccessApplicationsUpdateResult`, :210218), which accepts anything, so the real SDK cannot\n * raise it for those two today (the tests reach them through `sdkWrites` in saas-oidc-api.ts).\n * List (a non-array result, :147403-147410), delete (`id` typed, :61601-61607) and the organization\n * read (:169193-169253) are typed and can. The catch is on all five so a later SDK that types the\n * union cannot start leaking.\n * ⛔ `DISTILLED_DEBUG_HTTP` is the other raw-body path and stays an operator-only switch: the\n * package refuses a write while it is set (`refuseDebugHttp`), it cannot stop the SDK printing.\n */\nimport * as Data from 'effect/Data';\nimport * as Effect from 'effect/Effect';\n\n/** A refusal or failure, carrying the sentence an operator needs. Never a secret. */\nexport class SaasOidcError extends Data.TaggedError('SaasOidcError')<{\n readonly message: string;\n /** The HTTP status when the failure is a response (0 for a transport failure). */\n readonly status?: number;\n}> {}\n\n/**\n * ⛔ THE ONLY THING THIS PACKAGE SAYS ABOUT A RESPONSE THE SDK COULD NOT VALIDATE. The handler for\n * `Effect.catchTag('CloudflareParseError', …)`: it never touches the error it replaces, because\n * that error's `body` is the whole response (a create's carries `client_secret`) and its `cause`\n * quotes the values that failed. The message names the operation only. No status is claimed: the\n * SDK builds this error after its failure branch (protocol.ts:359, :452), so the response was a\n * success as far as HTTP and the envelope go, and nothing more is known.\n */\nexport const withheld = (operation: string) => (): Effect.Effect<never, SaasOidcError> =>\n Effect.fail(\n new SaasOidcError({\n message: `Access ${operation} succeeded at the HTTP level but the SDK could not validate the response. The response is withheld on purpose (a create response carries the client secret). Check the application in the Zero Trust dashboard, then run again.`,\n }),\n );\n",
20
+ "/**\n * The wire shapes of a SaaS OIDC application, and the parse that reads them: pure, no Effect, no\n * request. saas-oidc-api.ts says why the read is raw JSON (the SDK's responses drop `saas_app`).\n *\n * ⛔ `parseApp` COPIES NAMED FIELDS ONLY, and `client_secret` is not among them: an `ObservedApp`\n * has nowhere to hold the client secret, so no code path can persist or log one.\n */\n\n/** The OIDC half of a live SaaS app, from the wire JSON. */\nexport interface ObservedSaas {\n readonly authType: string | undefined;\n readonly clientId: string | undefined;\n readonly redirectUris: ReadonlyArray<string> | undefined;\n readonly scopes: ReadonlyArray<string> | undefined;\n readonly grantTypes: ReadonlyArray<string> | undefined;\n readonly accessTokenLifetime: string | undefined;\n readonly refreshTokenLifetime: string | undefined;\n readonly allowPkceWithoutClientSecret: boolean | undefined;\n}\n\n/** A live Access application as this package sees it. ⛔ There is no secret field, on purpose. */\nexport interface ObservedApp {\n readonly id: string | undefined;\n readonly aud: string | undefined;\n readonly type: string | undefined;\n readonly name: string | undefined;\n readonly domain: string | undefined;\n readonly createdAt: string | undefined;\n readonly updatedAt: string | undefined;\n readonly policyIds: ReadonlyArray<string>;\n readonly saasApp: ObservedSaas | undefined;\n}\n\n/** What a create or an update sends. Built by `writeBody` in saas-oidc-form.ts. */\nexport interface AppWrite {\n readonly name: string;\n readonly sessionDuration: string;\n readonly allowedIdps: ReadonlyArray<string> | undefined;\n readonly autoRedirectToIdentity: boolean | undefined;\n readonly appLauncherVisible: boolean;\n readonly policies: ReadonlyArray<string>;\n readonly saasApp: {\n readonly authType: 'oidc';\n readonly redirectUris: ReadonlyArray<string>;\n readonly scopes: ReadonlyArray<string>;\n readonly grantTypes: ReadonlyArray<string>;\n readonly accessTokenLifetime: string;\n readonly refreshTokenOptions?: { readonly lifetime: string };\n readonly allowPkceWithoutClientSecret: boolean;\n readonly appLauncherUrl: string | undefined;\n };\n}\n\nconst str = (value: unknown): string | undefined => (typeof value === 'string' ? value : undefined);\nconst bool = (value: unknown): boolean | undefined =>\n typeof value === 'boolean' ? value : undefined;\nconst strings = (value: unknown): ReadonlyArray<string> | undefined =>\n Array.isArray(value) && value.every((item) => typeof item === 'string') ? value : undefined;\nexport const record = (value: unknown): Record<string, unknown> | undefined =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n\n/**\n * The policy ids in the order Access applies them. Each policy on the wire carries a `precedence`\n * (\"The order of execution for this policy\", zero_trust.ts:13630-13631), and the declared list is\n * \"in ascending order of precedence\" (zero_trust.ts:9774). Sorted by it only when EVERY entry\n * reports one (ties keep array order); otherwise the array order stands, since a partial ranking\n * says nothing about where the unranked entries sit.\n */\nconst policyIds = (policies: ReadonlyArray<unknown>): ReadonlyArray<string> => {\n const entries = policies.flatMap((policy, index) => {\n const id = str(record(policy)?.['id']);\n const precedence = record(policy)?.['precedence'];\n return id === undefined\n ? []\n : [{ id, index, precedence: typeof precedence === 'number' ? precedence : undefined }];\n });\n if (!entries.every((entry) => entry.precedence !== undefined)) return entries.map((e) => e.id);\n return [...entries]\n .sort((a, b) => (a.precedence ?? 0) - (b.precedence ?? 0) || a.index - b.index)\n .map((entry) => entry.id);\n};\n\n/**\n * Named fields only, from Cloudflare's snake_case JSON. ⛔ `client_secret` is not among them, and\n * `public_key`, `custom_claims` and `hybrid_and_implicit_options` are not used by this resource.\n */\nexport const parseApp = (raw: Record<string, unknown>): ObservedApp => {\n const saas = record(raw['saas_app']);\n const refresh = record(saas?.['refresh_token_options']);\n const policies = Array.isArray(raw['policies']) ? raw['policies'] : [];\n return {\n id: str(raw['id']),\n aud: str(raw['aud']),\n type: str(raw['type']),\n name: str(raw['name']),\n domain: str(raw['domain']),\n createdAt: str(raw['created_at']),\n updatedAt: str(raw['updated_at']),\n policyIds: policyIds(policies),\n saasApp:\n saas === undefined\n ? undefined\n : {\n authType: str(saas['auth_type']),\n clientId: str(saas['client_id']),\n redirectUris: strings(saas['redirect_uris']),\n scopes: strings(saas['scopes']),\n grantTypes: strings(saas['grant_types']),\n accessTokenLifetime: str(saas['access_token_lifetime']),\n refreshTokenLifetime: str(refresh?.['lifetime']),\n allowPkceWithoutClientSecret: bool(saas['allow_pkce_without_client_secret']),\n },\n };\n};\n",
21
+ "/**\n * `HomeFlare.Access.SaasOidcApplication` — the pure helpers the lifecycle uses: validation, the\n * issuer URLs, drift, the write body and the attribute mapping. The props and attributes types are\n * declared in saas-oidc-props.ts and re-exported here.\n *\n * ⛔ THE ATTRIBUTES NEVER HOLD THE CLIENT SECRET, and nothing here can: `ObservedApp`\n * (saas-oidc-api.ts) has no field for it. A client that authenticates with PKCE alone declares\n * `allowPkceWithoutClientSecret: true`, and then no secret exists to keep.\n * ★ THE ISSUER IS `https://<teamDomain>/cdn-cgi/access/sso/oidc/<clientId>`. The team domain is a\n * prop; the openbao copy this came from hardcoded one account's. Cloudflare also reports the\n * same host in the app's own `domain` field (Alchemy's `Access.Application` doc: for `saas`\n * \"Cloudflare uses the OIDC issuer\"), and `checkTeam` refuses a declaration that disagrees with it.\n */\nimport { SaasOidcError } from './saas-oidc-error.ts';\nimport type { SaasOidcApplicationAttributes, SaasOidcApplicationProps } from './saas-oidc-props.ts';\nimport type { AppWrite, ObservedApp } from './saas-oidc-wire.ts';\n\n// The declared shape lives in saas-oidc-props.ts (every field documented there); re-exported so\n// the module that always held it keeps answering to its old name.\nexport type {\n OidcGrantType,\n OidcScope,\n SaasOidcApplicationAttributes,\n SaasOidcApplicationProps,\n} from './saas-oidc-props.ts';\n\nconst HOSTNAME = /^[a-z0-9]+(?:[.-][a-z0-9]+)+$/;\n\n/** ⛔ Fail closed on a declaration the API would accept but nobody meant. */\nexport const validateSaasOidc = (props: SaasOidcApplicationProps): SaasOidcError | undefined => {\n const fail = (message: string) => new SaasOidcError({ message });\n if (typeof props.teamDomain !== 'string' || !HOSTNAME.test(props.teamDomain)) {\n return fail(\n `SaasOidcApplication \\`teamDomain\\` must be a bare lowercase hostname such as \"example.cloudflareaccess.com\" (no scheme, no path); got ${JSON.stringify(props.teamDomain)}.`,\n );\n }\n if (props.name !== undefined && props.name.trim() === '') {\n return fail('SaasOidcApplication `name`, when set, must not be empty.');\n }\n if (\n props.saasApp.allowPkceWithoutClientSecret === true &&\n !props.saasApp.grantTypes.includes('authorization_code_with_pkce')\n ) {\n return fail(\n '`allowPkceWithoutClientSecret: true` only applies to the `authorization_code_with_pkce` grant; add it to `grantTypes` or drop the flag.',\n );\n }\n return undefined;\n};\n\nexport const issuerFor = (teamDomain: string, clientId: string): string =>\n `https://${teamDomain}/cdn-cgi/access/sso/oidc/${clientId}`;\n\nconst hostOf = (domain: string): string => domain.split('/')[0]?.toLowerCase() ?? '';\n\n/**\n * ⛔ A DECLARED TEAM DOMAIN THAT DISAGREES WITH THE APP'S OWN `domain` is refused: the issuer in\n * the attributes would point at a different team than the one that holds the app.\n * ⛔ WHEN CLOUDFLARE REPORTS NO `domain` there is nothing to compare, so the team the row already\n * recorded (`knownTeam`, from state) stands in: a declaration that moves it is refused, because\n * `issuer` is a declared stable and must not move under the row. With no `knownTeam` either\n * (a first create or a cold adoption) there is nothing to compare against and the declaration\n * stands.\n */\nexport const checkTeam = (\n teamDomain: string,\n app: ObservedApp,\n knownTeam?: string,\n): SaasOidcError | undefined => {\n if (app.domain === undefined) {\n return knownTeam === undefined || knownTeam.toLowerCase() === teamDomain.toLowerCase()\n ? undefined\n : new SaasOidcError({\n message: `SaasOidcApplication declares teamDomain \"${teamDomain}\" but the row recorded \"${knownTeam}\" and Cloudflare reports no domain for the app to confirm the change. The issuer cannot move; revert the declaration.`,\n });\n }\n return hostOf(app.domain) === teamDomain.toLowerCase()\n ? undefined\n : new SaasOidcError({\n message: `SaasOidcApplication declares teamDomain \"${teamDomain}\" but Cloudflare reports the app on \"${hostOf(app.domain)}\".`,\n });\n};\n\nconst refreshLifetime = (props: SaasOidcApplicationProps): string =>\n props.saasApp.refreshTokenLifetime ?? '';\n\n/** The request body for a create or an update. */\nexport const writeBody = (news: SaasOidcApplicationProps, name: string): AppWrite => ({\n name,\n sessionDuration: news.sessionDuration ?? '24h',\n allowedIdps: news.allowedIdps,\n autoRedirectToIdentity: news.autoRedirectToIdentity,\n appLauncherVisible: news.appLauncherVisible ?? false,\n policies: news.policies,\n saasApp: {\n authType: 'oidc',\n redirectUris: news.saasApp.redirectUris,\n scopes: news.saasApp.scopes,\n grantTypes: news.saasApp.grantTypes,\n accessTokenLifetime: news.saasApp.accessTokenLifetime,\n ...(news.saasApp.refreshTokenLifetime === undefined\n ? {}\n : { refreshTokenOptions: { lifetime: news.saasApp.refreshTokenLifetime } }),\n allowPkceWithoutClientSecret: news.saasApp.allowPkceWithoutClientSecret ?? false,\n appLauncherUrl: news.saasApp.appLauncherUrl,\n },\n});\n\nconst sameSet = (a: ReadonlyArray<string>, b: ReadonlyArray<string> | undefined): boolean =>\n JSON.stringify([...a].sort()) === JSON.stringify([...(b ?? [])].sort());\n\n/**\n * ⛔ ORDER MATTERS FOR POLICIES: `policies` is \"in ascending order of precedence\" (the SDK's own\n * field doc, zero_trust.ts:9774), so `[a, b]` and `[b, a]` are different declarations. A set\n * compare here would let a reorder go unnoticed and leave the live precedence unchanged.\n * `parseApp` hands the observed ids over already ordered by the API's `precedence`.\n */\nconst sameList = (a: ReadonlyArray<string>, b: ReadonlyArray<string> | undefined): boolean => {\n const other = b ?? [];\n return a.length === other.length && a.every((item, index) => item === other[index]);\n};\n\n/**\n * Does the live app differ from the declaration? Compared: name, auth type, redirect URIs, scopes,\n * grant types (each as a set), both lifetimes, the PKCE flag and the policies IN ORDER — the\n * openbao copy's set plus the policy order, kept so adopting a live app is not newly a write.\n * ⚠️ NOT COMPARED: `sessionDuration`, `allowedIdps`, `autoRedirectToIdentity`, `appLauncherVisible`,\n * `appLauncherUrl`. They are written on create and on any sync, but a change to only those does\n * not trigger one. Carried over unchanged; see saas-oidc.md.\n */\nexport const needsSync = (\n news: SaasOidcApplicationProps,\n observed: ObservedApp,\n name: string,\n): boolean => {\n const saas = observed.saasApp;\n return (\n (observed.name ?? '') !== name ||\n (saas?.authType ?? '') !== 'oidc' ||\n !sameSet(news.saasApp.redirectUris, saas?.redirectUris) ||\n !sameSet(news.saasApp.scopes, saas?.scopes) ||\n !sameSet(news.saasApp.grantTypes, saas?.grantTypes) ||\n (saas?.accessTokenLifetime ?? '') !== news.saasApp.accessTokenLifetime ||\n refreshLifetime(news) !== (saas?.refreshTokenLifetime ?? '') ||\n (saas?.allowPkceWithoutClientSecret ?? false) !==\n (news.saasApp.allowPkceWithoutClientSecret ?? false) ||\n !sameList(news.policies, observed.policyIds)\n );\n};\n\n/**\n * The attributes of a live app, or `undefined` when it lacks an id, an `aud` or a client id (so it\n * is not a SaaS OIDC app this package can describe). `teamDomain` is the declared one, or, for a\n * read that has no declaration (state written before the prop existed), the host Cloudflare reports.\n */\nexport const toAttributes = (\n app: ObservedApp,\n accountId: string,\n fallbackName: string,\n declaredTeam: string | undefined,\n): SaasOidcApplicationAttributes | undefined => {\n const clientId = app.saasApp?.clientId;\n const teamDomain = declaredTeam ?? (app.domain === undefined ? undefined : hostOf(app.domain));\n if (app.id === undefined || app.aud === undefined || clientId === undefined) return undefined;\n if (teamDomain === undefined || teamDomain === '') return undefined;\n const issuer = issuerFor(teamDomain, clientId);\n const saas = app.saasApp;\n return {\n applicationId: app.id,\n aud: app.aud,\n domain: app.domain ?? issuer.replace('https://', ''),\n name: app.name ?? fallbackName,\n accountId,\n teamDomain,\n clientId,\n issuer,\n authorizationEndpoint: `${issuer}/authorization`,\n tokenEndpoint: `${issuer}/token`,\n jwksEndpoint: `${issuer}/jwks`,\n userinfoEndpoint: `${issuer}/userinfo`,\n configurationEndpoint: `${issuer}/.well-known/openid-configuration`,\n redirectUris: saas?.redirectUris ?? [],\n scopes: saas?.scopes ?? [],\n grantTypes: saas?.grantTypes ?? [],\n accessTokenLifetime: saas?.accessTokenLifetime ?? '',\n refreshTokenLifetime: saas?.refreshTokenLifetime ?? '',\n createdAt: app.createdAt,\n updatedAt: app.updatedAt,\n };\n};\n",
22
+ "/**\n * The team-domain check that runs BEFORE a first create, so a wrong `teamDomain` is refused with no\n * write instead of after one (saas-oidc-lifecycle.ts says where it is called).\n *\n * ★ THE ACCOUNT'S TEAM DOMAIN CAN BE READ BEFORE ANY APP EXISTS: `GET /accounts/{account_id}/access/\n * organizations` returns the Zero Trust organization, whose `auth_domain` is \"The unique subdomain\n * assigned to your Zero Trust organization\" (`@distilled.cloud/cloudflare@1.0.0-rc.13`\n * `listOrganizationsForAccount`, zero_trust.ts:168990 for the path and :238313-238324 for the\n * operation; `ListOrganizationsResponse.authDomain`, :169161). Alchemy's own\n * `Cloudflare/Access/Organization.ts` (beta.81, `observe`, line 338) reads it the same way. The\n * app's OIDC issuer host is that same domain, which is what `checkTeam` already compares after\n * the write.\n * ⚠️ BUT IT IS A DIFFERENT API-TOKEN PERMISSION than the one that writes the app. Cloudflare's docs\n * (developers.cloudflare.com/fundamentals/api/reference/permissions, read 2026-10-09) give\n * `Access: Organizations Read` (or `Access: Organizations, Identity Providers, and Groups Read`)\n * for this endpoint, while creating an Access application needs `Access: Apps and Policies\n * Write` (the same docs, e.g. cloudflare-one/access-controls/ai-controls/secure-mcp-servers). So\n * a token minted only for the app write may be refused here.\n * ★ THEREFORE THE CHECK IS ADVISORY WHERE IT CANNOT READ, AND STRICT WHERE IT CAN. Refused only when\n * the organization was read and names a different domain. A token that may not read it\n * (`Unauthorized`, `Forbidden`) or an account with no organization (`OrganizationNotFound`)\n * gives no answer, and the create goes on exactly as before, with the post-write `checkTeam`\n * still the backstop: that is the documented known limit (docs/saas-oidc.md). Any other failure\n * (a throttle, a 5xx) fails the run here, before a write; run again.\n * ★ ONE ATTEMPT (`Retry.none`, retry.ts:48-51). The SDK's default policy retries \"Authentication\n * error\", which is what a token without the permission gets, up to eight times with backoff\n * (core retry.ts `makeDefault`): a first create would wait for an answer it can never get.\n */\nimport * as Retry from '@distilled.cloud/cloudflare/Retry';\nimport * as zeroTrust from '@distilled.cloud/cloudflare/zero-trust';\nimport * as Effect from 'effect/Effect';\nimport { SaasOidcError, withheld } from './saas-oidc-error.ts';\n\n/**\n * ⚠️ WHY A TAG PREDICATE AND NOT `catchTags` for these three. `Forbidden` is built by the SDK's 4xx\n * mapping (protocol.ts:164-169 for code 10001, :423-436 for a bare 403) but is not in this\n * operation's declared error union (it lists `Unauthorized`, `OrganizationNotFound`, … only), so\n * `catchTags({ Forbidden })` does not typecheck. Which of `Unauthorized` (code 10000 \"Authentication\n * error\", protocol.ts:155-158) and `Forbidden` (10001 \"Method not allowed for token\") a token\n * without the permission gets was not measured live, so both stand. Tag equality only: no status,\n * no message, as `retryTransient` in saas-oidc-api.ts does.\n */\nconst cannotRead = (error: { readonly _tag: string }): boolean =>\n error._tag === 'Unauthorized' ||\n error._tag === 'Forbidden' ||\n error._tag === 'OrganizationNotFound';\n\n/** The organization's `auth_domain`, or `undefined` when this token cannot read it or there is none. */\nconst readAuthDomain = (accountId: string) =>\n zeroTrust.listOrganizationsForAccount({ accountId }).pipe(\n Retry.none,\n Effect.map((organization) => organization.authDomain ?? undefined),\n Effect.catchTag('CloudflareParseError', withheld('organization read')),\n Effect.catchIf(cannotRead, () => Effect.succeed(undefined)),\n );\n\n/**\n * A refusal when the account's organization is read and its domain is not the declared one; else\n * `undefined` (it matches, or it could not be read, see above). Hostnames compare case-insensitively.\n */\nexport const checkTeamBeforeCreate = (accountId: string, teamDomain: string) =>\n readAuthDomain(accountId).pipe(\n Effect.map((authDomain) =>\n authDomain === undefined || authDomain.toLowerCase() === teamDomain.toLowerCase()\n ? undefined\n : new SaasOidcError({\n message: `SaasOidcApplication declares teamDomain \"${teamDomain}\" but this account's Zero Trust organization is \"${authDomain}\". Refused before any write; no application was created.`,\n }),\n ),\n );\n",
23
+ "/**\n * The HomeFlare Cloudflare provider collection, in the shape `LiteLLM.providers()` has.\n *\n * ⚠️ THIS IS NOT `alchemy/Cloudflare`'s COLLECTION AND DOES NOT REPLACE IT. A stack that declares\n * both a `Cloudflare.R2.Bucket` and this lock merges the two:\n * `Layer.mergeAll(Cloudflare.providers(), HomeflareCloudflare.providers())` — the shape\n * a consuming stack names for one talking to more than one account.\n *\n * ★ ONE CREDENTIAL PATH NOW, NOT TWO. Until 2026-09-23 `R2BucketLock` ran over the `cloudflare`\n * npm SDK with its own `CLOUDFLARE_API_TOKEN` read (`client.ts`, since deleted) while `MeshNode`\n * ran over `@distilled.cloud/cloudflare` through `Cloudflare.CloudflareApiLive()`. Both\n * providers now call distilled, so both resolve credentials and the account the same way\n * `Cloudflare.providers()` does (an Alchemy profile, or `CLOUDFLARE_API_TOKEN` +\n * `CLOUDFLARE_ACCOUNT_ID` in CI) — a lock and the Gateway rules that reference the same bucket\n * land in the same account.\n * ⚠️ THE LAYER IS BUILT ONCE PER PROVIDER ON PURPOSE, even when the stack also merges\n * `Cloudflare.providers()`. `Layer.provide`, not `provideMerge`: each feeds its own provider\n * only and adds nothing to the stack's context.\n */\nimport * as Cloudflare from 'alchemy/Cloudflare';\nimport * as Provider from 'alchemy/Provider';\nimport * as Layer from 'effect/Layer';\nimport { CloudflaredTunnel, CloudflaredTunnelProvider } from './cloudflared-tunnel.ts';\nimport { MeshNode, MeshNodeProvider } from './mesh-node.ts';\nimport { R2BucketLock, R2BucketLockProvider } from './r2-bucket-lock.ts';\nimport { SaasOidcApplication, SaasOidcApplicationProvider } from './saas-oidc.ts';\n\nexport class Providers extends Provider.ProviderCollection<Providers>()('HomeflareCloudflare') {}\n\n// ⛔ `orDie` ON EACH of the four, as `Cloudflare.providers()` ends with: a stack's providers layer must not\n// fail, and a profile that cannot resolve is a defect carrying Alchemy's own sentence.\nexport const providers = () =>\n Layer.effect(\n Providers,\n Provider.collection([R2BucketLock, MeshNode, CloudflaredTunnel, SaasOidcApplication]),\n ).pipe(\n Layer.provide(\n Layer.mergeAll(\n R2BucketLockProvider().pipe(Layer.provide(Cloudflare.CloudflareApiLive()), Layer.orDie),\n MeshNodeProvider().pipe(Layer.provide(Cloudflare.CloudflareApiLive()), Layer.orDie),\n CloudflaredTunnelProvider().pipe(\n Layer.provide(Cloudflare.CloudflareApiLive()),\n Layer.orDie,\n ),\n SaasOidcApplicationProvider().pipe(\n Layer.provide(Cloudflare.CloudflareApiLive()),\n Layer.orDie,\n ),\n ),\n ),\n );\n",
13
24
  "/**\n * House defaults on Alchemy's Website stacks — Astro and Vite, which is what we ship.\n *\n * ★ NOT Nextjs. The blog measured Website.Nextjs planning as **create** because it\n * hashes source; adopt is `Worker`, not Nextjs. Do not add a Nextjs helper until an\n * app actually deploys that way.\n *\n * ⛔ `disable_nodejs_process_v2` ON ASTRO IS LOAD-BEARING. Workerd process-v2 (compat\n * date 2025-09-15) makes every Astro page return `[object Object]`. Vite/TanStack\n * Start does not need that flag; it needs `nodejs_compat` (Alchemy already defaults\n * it on Vite).\n *\n * ⛔ Alchemy injects `@alchemy.run/frontend-frameworks/astro`. Do not add\n * `@astrojs/cloudflare` — that is the wrangler path.\n *\n * ★ Alchemy's Website props are an Effect-wrapped InputProps union. We do not re-export\n * that type — isolatedDeclarations cannot name it. Extra Alchemy fields still pass\n * through via the rest bag; the assertion is only at the call into Alchemy.\n */\nimport * as AdoptPolicy from 'alchemy/AdoptPolicy';\nimport * as Cloudflare from 'alchemy/Cloudflare';\nimport * as RemovalPolicy from 'alchemy/RemovalPolicy';\n\nconst ASTRO_FLAGS: readonly string[] = ['nodejs_compat', 'disable_nodejs_process_v2'];\n\nexport interface AstroWebsiteProps {\n readonly rootDir?: string;\n readonly name?: string;\n readonly sessionKVBindingName?: string | false;\n readonly compatibility?: { readonly flags?: readonly string[]; readonly date?: string };\n}\n\nexport interface ViteWebsiteProps {\n readonly rootDir?: string;\n readonly name?: string;\n}\n\n/**\n * `Cloudflare.Website.Astro` with house flags, no auto session KV, adopt+retain.\n *\n * const site = yield* astroWebsite('subnetcalc', { rootDir });\n */\nexport function astroWebsite(\n name: string,\n props: AstroWebsiteProps,\n): ReturnType<typeof Cloudflare.Website.Astro> {\n const flags = props.compatibility?.flags ?? ASTRO_FLAGS;\n const merged = {\n ...props,\n name: props.name ?? name,\n sessionKVBindingName: props.sessionKVBindingName ?? false,\n compatibility: { ...props.compatibility, flags: [...flags] },\n };\n return Cloudflare.Website.Astro(name, merged as never).pipe(\n AdoptPolicy.adopt(true),\n RemovalPolicy.retain(),\n );\n}\n\n/**\n * `Cloudflare.Website.Vite` — TanStack Start, SolidStart, static Vite.\n * `nodejs_compat` is Alchemy's Vite default; we do not add process-v2 here.\n *\n * const app = yield* viteWebsite('aimto', { rootDir: webRoot });\n */\nexport function viteWebsite(\n name: string,\n props?: ViteWebsiteProps,\n): ReturnType<typeof Cloudflare.Website.Vite> {\n const merged = { ...props, name: props?.name ?? name };\n return Cloudflare.Website.Vite(name, merged as never).pipe(\n AdoptPolicy.adopt(true),\n RemovalPolicy.retain(),\n );\n}\n"
14
25
  ],
15
- "mappings": "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",
16
- "debugId": "95ACB917FDFEA2D864756E2164756E21",
26
+ "mappings": ";AAAA;AACA;AACA;AAiCA;AACA;AACA;AACA;AACA;;;ACZO,IAAM,iBAAiB,CAAC,UAC7B,MAAM,gBAAgB;AAQjB,SAAS,UAAU,CAAC,GAA0B,GAAmC;AAAA,EACtF,IAAI,EAAE,WAAW,EAAE;AAAA,IAAQ,OAAO;AAAA,EAClC,OAAO,EAAE,MAAM,CAAC,MAAM,UAAU;AAAA,IAC9B,MAAM,QAAQ,EAAE;AAAA,IAChB,IAAI,UAAU;AAAA,MAAW,OAAO;AAAA,IAChC,IAAI,KAAK,OAAO,MAAM,MAAM,KAAK,YAAY,MAAM;AAAA,MAAS,OAAO;AAAA,IACnE,KAAK,KAAK,UAAU,SAAS,MAAM,UAAU;AAAA,MAAK,OAAO;AAAA,IACzD,OAAO,IAAI,MAAM,CAAC,KAAK,WAAW,MAAM,SAAS;AAAA,IACjD,IAAI,GAAG,SAAS,GAAG;AAAA,MAAM,OAAO;AAAA,IAChC,IAAI,GAAG,SAAS;AAAA,MAAO,OAAO,GAAG,SAAS,SAAS,GAAG,kBAAkB,GAAG;AAAA,IAC3E,IAAI,GAAG,SAAS;AAAA,MAAQ,OAAO,GAAG,SAAS,UAAU,GAAG,SAAS,GAAG;AAAA,IACpE,OAAO;AAAA,GACR;AAAA;AASI,IAAM,SAAS,CAAC,WAA2D;AAAA,EAChF,OAAO,MAAM,IAAI,CAAC,UAAU,KAAK,KAAK,EAAE;AAC1C;;;ADqBO,IAAM,eAA4C,SACvD,2BACA;AAAA,EAQE,sBAAsB;AACxB,CACF;AA0BO,IAAM,WAAW,CACtB,OACA,YAMO,WAAI,UAAU,GAAG;AAAA,EACtB,MAAM,eAAe,eAAe,KAAK;AAAA,EACzC,MAAM,OAAO,OACV,iBAAc,EAAE,WAAW,MAAM,WAAW,YAAY,MAAM,YAAY,aAAa,CAAC,EACxF,KAAY,gBAAS,gBAAgB,MAAa,eAAQ,SAAS,CAAC,CAAC;AAAA,EACxE,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,MAAM,QAAS,KAAK,SAAS,CAAC;AAAA,EAC9B,IAAI,MAAM,WAAW;AAAA,IAAG;AAAA,EACxB,MAAM,aAAqC;AAAA,IACzC,YAAY,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AAAA,EACA,OAAO,UAAU,aAAa,QAAQ,UAAU;AAAA,CACjD;AAWI,IAAM,gBAAgB,CAC3B,MACA,WAEO,WAAI,UAAU,GAAG;AAAA,EACtB,MAAM,eAAe,eAAe,IAAI;AAAA,EACxC,IAAI,WAAW,aAAa,WAAW,OAAO,OAAO,KAAK,KAAK;AAAA,IAAG,OAAO;AAAA,EACzE,OAAU,iBAAc;AAAA,IACtB,WAAW,KAAK;AAAA,IAChB,YAAY,KAAK;AAAA,IACjB;AAAA,OACG,OAAO,KAAK,KAAK;AAAA,EACtB,CAAC;AAAA,EACD,OAAO,EAAE,YAAY,KAAK,YAAY,cAAc,OAAO,KAAK,MAAM;AAAA,CACvE;AAWI,IAAM,aAAa,CACxB,QACA,cAMO,WAAI,UAAU,GAAG;AAAA,EACtB,OACG,iBAAc;AAAA,IACb;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,cAAc,OAAO;AAAA,IACrB,OAAO,CAAC;AAAA,EACV,CAAC,EACA,KAAY,gBAAS,gBAAgB,MAAa,WAAI,CAAC;AAAA,CAC3D;AAEI,IAAM,uBAAuB,MACzB,gBACP,cACO,WAAI,UAAU,GAAG;AAAA,EAKtB,MAAM,WAAmB,aAAK,aAAa,OAAO,WAAW,EAAE,KACrD,YAAe,uBAAY,OAAkB,qBAAU,CACjE;AAAA,EACA,MAAM,MAAM,CAAO,YACV,sBAAe,SAAQ,QAAQ;AAAA,EAExC,OAAO,aAAa,SAAS,GAAG;AAAA,IAc9B,MAAM,MAAa,eAAQ,CAAC,CAAC;AAAA,IAC7B,MAAM,EAAE,WAAW,KAAK;AAAA,IACxB,MAAa,UAAG,UAAU,GAAG,MAAM,UAAU;AAAA,MAC3C,MAAM,QAAQ,QAAQ;AAAA,MACtB,IAAI,UAAU;AAAA,QAAW;AAAA,MACzB,OAAO,OAAO,IAAI,SAAS,OAAO,WAAW,SAAS,CAAC;AAAA,KACxD;AAAA,IACD,WAAkB,UAAG,GAAG,MAAM,aAAa,IAAI,cAAc,MAAM,MAAM,CAAC,CAAC;AAAA,IAC3E,QAAe,UAAG,GAAG,MAAM,aAAa,IAAI,WAAW,QAAQ,KAAK,SAAS,CAAC,CAAC;AAAA,EACjF,CAAC;AAAA,CACF,CACH;;AE7NF,wBAAS;AACT;AACA;AACA;AACA,qBAAS;AACT;AACA;AACA;;;ACGA;AACA;AACA;AACA;AAAA;AAaO,MAAM,sBAA2B,iBAAY,eAAe,EAEhE;AAAC;AASJ,IAAM,WAA8B,CAAC,YAAY,YAAY,WAAW,MAAM;AAOvE,IAAM,UAAU,CAAC,QAAuC;AAAA,EAC7D,IAAI,OAAO,IAAI,OAAO,YAAY,IAAI,GAAG,WAAW;AAAA,IAAG;AAAA,EACvD,IAAI,IAAI,cAAc,aAAa,IAAI,cAAc;AAAA,IAAM;AAAA,EAC3D,MAAM,SACJ,OAAO,IAAI,WAAW,YAAY,SAAS,SAAS,IAAI,MAAM,IACzD,IAAI,SACL;AAAA,EACN,OAAO,EAAE,IAAI,IAAI,IAAI,MAAM,IAAI,QAAQ,IAAI,OAAO;AAAA;AAI7C,IAAM,UAAU,CAAC,WAAmB,WAC/B,iCAAuB,EAAE,WAAW,UAAU,OAAO,CAAC,EAAE,KACzD,YAAI,OAAO,GACX,iBAAS,kBAAkB,MAAa,gBAAQ,SAAS,CAAC,CACnE;AAUK,IAAM,iBAAiB,CAAC,WAAmB,SACtC,mCAAyB,MAAM,EAAE,WAAW,MAAM,WAAW,MAAM,CAAC,EAAE,KACvE,WAAI,OAAO,GACX,cAAO,CAAC,SAA+B,SAAS,aAAa,KAAK,SAAS,IAAI,GAC/E,mBACA,gBAAQ,CAAC,UAAU;AAAA,EACxB,MAAM,UAAU,MAAM,KAAK,KAAK;AAAA,EAChC,IAAI,QAAQ,UAAU;AAAA,IAAG,OAAc,gBAAQ,QAAQ,EAAE;AAAA,EACzD,MAAM,MAAM,QAAQ,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,KAAK,IAAI;AAAA,EACpD,OAAc,aACZ,IAAI,cAAc;AAAA,IAChB,SAAS,mBAAmB,iBAAiB,QAAQ,sBAAsB;AAAA,EAC7E,CAAC,CACH;AAAA,CACD,CACH;AAOK,IAAM,aAAa,CAAC,WAAmB,MAAc,OAChD,oCAA0B,EAAE,WAAW,MAAM,GAAG,CAAC,EAAE,KACpD,gBAAQ,CAAC,QAAQ;AAAA,EACtB,MAAM,OAAO,QAAQ,GAAG;AAAA,EACxB,OAAO,SAAS,YACL,aACL,IAAI,cAAc,EAAE,SAAS,uBAAuB,wBAAwB,CAAC,CAC/E,IACO,gBAAQ,IAAI;AAAA,CACxB,CACH;AAGK,IAAM,aAAa,CAAC,WAAmB,QAAgB,SAEzD,mCAAyB,EAAE,WAAW,UAAU,QAAQ,KAAK,CAAC,EAC9D,KAAY,YAAI,CAAC,QAAQ,QAAQ,GAAG,KAAK,EAAE,IAAI,QAAQ,MAAM,QAAQ,UAAU,CAAC,CAAC;AAG/E,IAAM,aAAa,CAAC,WAAmB,WAClC,oCAA0B,EAAE,WAAW,UAAU,OAAO,CAAC,EAAE,KAC5D,gBACA,iBAAS,kBAAkB,MAAa,YAAI,CACrD;;;AClEK,IAAM,mBAAmB,CAAC,UAAoD;AAAA,EACnF,IAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,EAAE,WAAW,GAAG;AAAA,IACpE,OAAO,IAAI,cAAc,EAAE,SAAS,8CAA8C,CAAC;AAAA,EACrF;AAAA,EACA,IAAI,MAAM,SAAS,MAAM,KAAK,KAAK,GAAG;AAAA,IACpC,OAAO,IAAI,cAAc;AAAA,MACvB,SAAS,kBAAkB,MAAM;AAAA,IACnC,CAAC;AAAA,EACH;AAAA,EACA,IAAI,OAAO,MAAM,OAAO,WAAW;AAAA,IACjC,OAAO,IAAI,cAAc;AAAA,MACvB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EACA;AAAA;AAwBK,IAAM,eAAe,CAC1B,MACA,QACA,cACqB;AAAA,EACrB,IAAI,WAAW;AAAA,IAAW;AAAA,EAC1B,IAAI,OAAO,cAAc;AAAA,IAAW,OAAO,EAAE,QAAQ,UAAU;AAAA,EAC/D,MAAM,SAAS;AAAA,EACf,MAAM,OAAO,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;AAAA,EAC7D,IAAI,OAAO,OAAO,OAAO,aAAa,OAAO,OAAO,OAAO,IAAI;AAAA,IAC7D,MAAM,WAAW,SAAS,aAAa,SAAS,OAAO;AAAA,IACvD,OAAO,WAAW,EAAE,QAAQ,WAAW,aAAa,KAAK,IAAI,EAAE,QAAQ,UAAU;AAAA,EACnF;AAAA,EACA,IAAI,SAAS,aAAa,SAAS,OAAO;AAAA,IAAM,OAAO,EAAE,QAAQ,SAAS;AAAA,EAC1E;AAAA;;;AC7GF,oBAAS;AACT;AAYA,IAAM,eAAe,CAAC,MAAoB,WAAmB,QAAqC;AAAA,EAChG,IAAI,KAAK;AAAA,EACT;AAAA,EACA,MAAM,KAAK;AAAA,EACX,QAAQ,KAAK;AAAA,EACb;AACF;AAYO,IAAM,eAAe,CAC1B,WACA,MACA,WAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,UAAU,QAAQ,aAAa;AAAA,EACrC,IAAI,WAAW,WAAW;AAAA,IACxB,MAAM,OAAO,OAAO,QAAQ,SAAS,OAAO,EAAE;AAAA,IAC9C,IAAI,SAAS;AAAA,MAAW,OAAO,aAAa,MAAM,SAAS,OAAO,EAAE;AAAA,EACtE;AAAA,EACA,MAAM,OAAO,MAAM,QAAQ,QAAQ;AAAA,EACnC,MAAM,KAAK,MAAM,MAAM,QAAQ;AAAA,EAC/B,IAAI,SAAS,aAAa,OAAO;AAAA,IAAW;AAAA,EAC5C,MAAM,QAAQ,OAAO,eAAe,SAAS,IAAI;AAAA,EACjD,OAAO,UAAU,YAAY,YAAY,SAAQ,aAAa,OAAO,SAAS,EAAE,CAAC;AAAA,CAClF;AAWH,IAAM,YAAY,CAAC,MAAc,WAC/B,IAAI,cAAc;AAAA,EAChB,SACE,mBAAmB,yBAAyB,gDAC5C;AACJ,CAAC;AASH,IAAM,QACJ,iGACA;AAiBF,IAAM,mBAAmB,CAAC,MAAc,WACtC,IAAI,cAAc;AAAA,EAChB,SACE,cAAc,yBAAyB,uDACvC,6FACA,0EACA,gGACA,6FACA,4EAA4E,iBAC5E,6FACA,+DAA+D;AACnE,CAAC;AAaH,IAAM,QAAQ,CAAC,WAAmB,SAChC,eAAe,WAAW,IAAI,EAAE,KACvB,gBAAQ,CAAC,WACP,aACL,WAAW,YAIP,UAAU,MAAM,mEAAmE,IACnF,IAAI,cAAc;AAAA,EAChB,SACE,cAAc,UAAU,OAAO,uDAC/B,uFACA,kFACA,qEACA,GAAG,uEACH;AACJ,CAAC,CACP,CACF,CACF;AAEF,IAAM,cAAc,CAAC,WAAmB,SAC/B,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAW,OAAO,eAAe,WAAW,KAAK,IAAI;AAAA,EAC3D,IAAI,aAAa;AAAA,IAAW,OAAO,OAAc,aAAK,iBAAiB,KAAK,MAAM,SAAS,EAAE,CAAC;AAAA,EAC9F,MAAM,OAAO,OAAO,WAAW,WAAW,KAAK,MAAM,KAAK,EAAE,EAAE,KACrD,iBAAS,uBAAuB,MAAM,MAAM,WAAW,KAAK,IAAI,CAAC,CAC1E;AAAA,EACA,OAAO,aAAa,MAAM,WAAW,KAAK,EAAE;AAAA,CAC7C;AAEI,IAAM,oBAAoB,CAC/B,WACA,MACA,WAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,UAAU,iBAAiB,IAAI;AAAA,EACrC,IAAI,YAAY;AAAA,IAAW,OAAO,OAAc,aAAK,OAAO;AAAA,EAC5D,MAAM,QAAQ,WAAW,aAAa,OAAO,cAAc,YAAY,SAAS;AAAA,EAChF,MAAM,OAAO,UAAU,YAAY,YAAY,OAAO,QAAQ,WAAW,MAAM,EAAE;AAAA,EAEjF,IAAI,UAAU,aAAa,SAAS;AAAA,IAAW,OAAO,OAAO,YAAY,WAAW,IAAI;AAAA,EACxF,IAAI,MAAM,OAAO,KAAK,IAAI;AAAA,IAExB,OAAO,OAAc,aACnB,IAAI,cAAc;AAAA,MAChB,SAAS,aAAa,KAAK,wCAAwC,MAAM,gBAAgB,KAAK;AAAA,IAChG,CAAC,CACH;AAAA,EACF;AAAA,EACA,IAAI,KAAK,SAAS,KAAK;AAAA,IAAM,OAAO,aAAa,MAAM,WAAW,MAAM,EAAE;AAAA,EAC1E,MAAM,UAAU,OAAO,WAAW,WAAW,KAAK,IAAI,KAAK,IAAI,EAAE,KACxD,iBAAS,uBAAuB,MAC9B,aAAK,UAAU,KAAK,MAAM,wBAAwB,CAAC,CAC5D,CACF;AAAA,EACA,OAAO,aAAa,SAAS,WAAW,MAAM,EAAE;AAAA,CACjD;AAOI,IAAM,iBAAiB,CAAC,WAC7B,WAAW,OAAO,WAAW,OAAO,EAAE;;;AHtIjC,IAAM,WAAoC,UAAmB,uBAAuB;AAAA,EACzF,sBAAsB;AACxB,CAAC;AASD,IAAM,YAAmB,YAAI,UAAU,GAAG;AAAA,EACxC,MAAM,WAAW,OAAO,OAAkB;AAAA,EAC1C,OAAO,SAAS;AAAA,CACjB;AASM,IAAM,mBAAmB,MACrB,iBACP,UACO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAmB,cACZ,kCACX,OAAkB,gCACpB,EAAE,KACQ,aAAI,cAAa,OAAO,YAAW,GACnC,aAAe,wBAAY,OAAkB,sBAAU,CACjE;AAAA,EACA,MAAM,MAAM,CAAO,YACV,uBAAe,SAAQ,QAAQ;AAAA,EAExC,OAAO,SAAS,SAAS,GAAG;AAAA,IAC1B,SAAS,CAAC,MAAM,aAAa,IAAI;AAAA,IAMjC,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,IAC7B,MAAM,EAAE,MAAM,KAAK;AAAA,IAOnB,MAAM,GAAG,MAAM,aACb,IACS,YAAI,UAAU,GAAG;AAAA,MACtB,MAAM,UAAU,WAAW,IAAI,IAAI,iBAAiB,IAAI,IAAI;AAAA,MAC5D,IAAI,YAAY;AAAA,QAAW,OAAO,OAAc,aAAK,OAAO;AAAA,MAC5D,OAAO,aAAa,MAAM,QAAQ,OAAO,SAAS;AAAA,KACnD,CACH;AAAA,IACF,MAAM,GAAG,MAAM,aACb,IAAW,gBAAQ,WAAW,CAAC,YAAY,aAAa,SAAS,MAAM,MAAM,CAAC,CAAC;AAAA,IACjF,WAAW,GAAG,MAAM,aAClB,IAAW,gBAAQ,WAAW,CAAC,YAAY,kBAAkB,SAAS,MAAM,MAAM,CAAC,CAAC;AAAA,IACtF,QAAQ,GAAG,aAAa,IAAI,eAAe,MAAM,CAAC;AAAA,EACpD,CAAC;AAAA,CACF,CACH;;AI5FF,wBAAS;AACT;AACA;AACA;AAqBA,IAAM,eAAsB,YAAI,UAAU,GAAG;AAAA,EAC3C,KAAK,WAAW,SAAS,MAAM,2BAA2B,QAAQ,IAAI;AAAA,IACpE,OAAO,OAAc,aACnB,IAAI,cAAc;AAAA,MAChB,SACE;AAAA,IACJ,CAAC,CACH;AAAA,EACF;AAAA,EACA,MAAM,cAAc,OAAO,OAAO;AAAA,EAClC,IAAI,YAAY,SAAS,UAAU;AAAA,IACjC,OAAO,OAAc,aACnB,IAAI,cAAc;AAAA,MAChB,SACE;AAAA,IACJ,CAAC,CACH;AAAA,EACF;AAAA,EACA,IAAI,YAAY,SAAS,cAAuB,eAAM,YAAY,QAAQ,EAAE,WAAW,GAAG;AAAA,IACxF,OAAO,OAAc,aACnB,IAAI,cAAc;AAAA,MAChB,SACE;AAAA,IACJ,CAAC,CACH;AAAA,EACF;AAAA,CACD;AAED,IAAM,YAAY,CAAC,QAAqB;AAAA,EACtC,IAAI,IAAI,OAAO;AAAA,IAAW,OAAc,gBAAQ,IAAI,EAAE;AAAA,EACtD,MAAM,OAAO,IAAI;AAAA,EACjB,OAAc,gBAAQ,eAAe,IAAI,WAAW,IAAI,GAAG,CAAC,SAC1D,SAAS,YACE,aAAK,IAAI,cAAc,EAAE,SAAS,4BAA4B,SAAS,CAAC,CAAC,IACzE,gBAAQ,KAAK,EAAE,CAC5B;AAAA;AAUK,IAAM,qBAAqB,CAAC,QAC1B,YAAI,UAAU,GAAG;AAAA,EACtB,OAAO;AAAA,EACP,MAAM,SAAS,OAAO,UAAU,GAAG;AAAA,EACnC,MAAM,QAAQ,OACX,uCAA4B,EAAE,WAAW,IAAI,WAAW,UAAU,OAAO,CAAC,EAC1E,KACQ,iBAAS,kBAAkB,MACzB,aACL,IAAI,cAAc;AAAA,IAChB,SAAS,aAAa,oCAAoC,IAAI;AAAA,EAChE,CAAC,CACH,CACF,GACO,iBAAS,aAAa,MACpB,aACL,IAAI,cAAc;AAAA,IAChB,SAAS,qBAAqB,mJAAmJ,IAAI;AAAA,EACvL,CAAC,CACH,CACF,CACF;AAAA,EACF,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AAAA,IACnD,OAAO,OAAc,aACnB,IAAI,cAAc,EAAE,SAAS,aAAa,kCAAkC,CAAC,CAC/E;AAAA,EACF;AAAA,EACA,OAAgB,cAAK,KAAK;AAAA,CAC3B;;AClHH,wBAAS;AACT;AACA,uBAAS;AACT;AACA,qBAAS;AACT;AACA;AACA;;;ACIA;AACA;AACA;AACA;AAAA;AAWO,MAAM,+BAAoC,kBAAY,wBAAwB,EAElF;AAAC;AAkBG,IAAM,WAAU,CAAC,QAAyC;AAAA,EAC/D,IAAI,OAAO,IAAI,OAAO,YAAY,IAAI,GAAG,WAAW;AAAA,IAAG;AAAA,EACvD,IAAI,IAAI,cAAc,aAAa,IAAI,cAAc;AAAA,IAAM;AAAA,EAC3D,IAAI,OAAO,IAAI,YAAY,YAAY,IAAI,YAAY;AAAA,IAAc;AAAA,EACrE,MAAM,YACJ,IAAI,cAAc,gBAAgB,IAAI,cAAc,UAAU,IAAI,YAAY;AAAA,EAChF,OAAO,EAAE,IAAI,IAAI,IAAI,MAAM,IAAI,QAAQ,IAAI,UAAU;AAAA;AAIhD,IAAM,YAAY,CAAC,YAAmB,aACjC,gCAAqB,EAAE,uBAAW,SAAS,CAAC,EAAE,KAC/C,YAAI,QAAO,GACX,iBAAS,kBAAkB,MAAa,gBAAQ,SAAS,CAAC,CACnE;AAUK,IAAM,mBAAmB,CAAC,YAAmB,SACxC,uBAAY,MAAM,EAAE,uBAAW,MAAM,WAAW,OAAO,UAAU,CAAC,YAAY,EAAE,CAAC,EAAE,KACpF,YAAI,QAAO,GACX,eAAO,CAAC,MAA2B,MAAM,aAAa,EAAE,SAAS,IAAI,GACrE,oBACA,gBAAQ,CAAC,UAAU;AAAA,EACxB,MAAM,UAAU,MAAM,KAAK,KAAK;AAAA,EAChC,IAAI,QAAQ,UAAU;AAAA,IAAG,OAAc,gBAAQ,QAAQ,EAAE;AAAA,EACzD,MAAM,MAAM,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,IAAI;AAAA,EAC9C,OAAc,aACZ,IAAI,uBAAuB;AAAA,IACzB,SAAS,gBAAgB,iBAAiB,QAAQ,oCAAoC;AAAA,EACxF,CAAC,CACH;AAAA,CACD,CACH;AASK,IAAM,eAAe,CAAC,YAAmB,SACpC,mCAAwB,EAAE,uBAAW,MAAM,WAAW,aAAa,CAAC,EAAE,KACvE,gBAAQ,CAAC,QAAQ;AAAA,EACtB,MAAM,SAAS,SAAQ,GAAG;AAAA,EAC1B,OAAO,WAAW,YACP,aACL,IAAI,uBAAuB,EAAE,SAAS,oBAAoB,wBAAwB,CAAC,CACrF,IACO,gBAAQ,MAAM;AAAA,CAC1B,CACH;AAGK,IAAM,eAAe,CAAC,YAAmB,UAAkB,SAE7D,kCAAuB,EAAE,uBAAW,UAAU,KAAK,CAAC,EACpD,KAAY,YAAI,CAAC,QAAQ,SAAQ,GAAG,KAAK,EAAE,IAAI,UAAU,MAAM,WAAW,UAAU,CAAC,CAAC;AAO3F,IAAM,aAAa,CAAC,UAAqC,MAAM,SAAS;AAWjE,IAAM,eAAe,CAAC,YAAmB,aACvC,YAAI,UAAU,GAAG;AAAA,EACtB,IAAI,SAAS,WAAW,GAAG;AAAA,IACzB,OAAO,OAAc,aACnB,IAAI,uBAAuB,EAAE,SAAS,gDAAgD,CAAC,CACzF;AAAA,EACF;AAAA,EACA,MAAM,OAAO,OAAO,UAAU,YAAW,QAAQ;AAAA,EACjD,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,OACG,mCAAwB,EAAE,uBAAW,SAAS,CAAC,EAC/C,KAAY,gBAAQ,YAAY,MAAa,YAAI,CAAC;AAAA,CACtD;;;AC1GI,IAAM,4BAA4B,CACvC,UACuC;AAAA,EACvC,IAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,EAAE,WAAW,GAAG;AAAA,IACpE,OAAO,IAAI,uBAAuB;AAAA,MAChC,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EACA,IAAI,MAAM,SAAS,MAAM,KAAK,KAAK,GAAG;AAAA,IACpC,OAAO,IAAI,uBAAuB;AAAA,MAChC,SAAS,2BAA2B,MAAM;AAAA,IAC5C,CAAC;AAAA,EACH;AAAA,EACA;AAAA;AAWK,IAAM,wBAAwB,CACnC,MACA,QACA,eACqB;AAAA,EACrB,IAAI,WAAW;AAAA,IAAW;AAAA,EAC1B,IAAI,OAAO,cAAc;AAAA,IAAW,OAAO,EAAE,QAAQ,UAAU;AAAA,EAC/D,MAAM,OAAQ,KAAqC;AAAA,EACnD,IAAI,OAAO,SAAS,YAAY,SAAS,OAAO;AAAA,IAAM,OAAO,EAAE,QAAQ,SAAS;AAAA,EAChF;AAAA;;;ACxEF,oBAAS;AACT;AAgBA,IAAM,gBAAe,CAAC,QAAwB,gBAAoD;AAAA,EAChG,IAAI,OAAO;AAAA,EACX;AAAA,EACA,MAAM,OAAO;AACf;AAeO,IAAM,wBAAwB,CACnC,YACA,MACA,WAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,UAAU,QAAQ,aAAa;AAAA,EACrC,IAAI,WAAW,WAAW;AAAA,IACxB,MAAM,SAAS,OAAO,UAAU,SAAS,OAAO,EAAE;AAAA,IAClD,OAAO,WAAW,YAAY,YAAY,cAAa,QAAQ,OAAO;AAAA,EACxE;AAAA,EACA,MAAM,OAAO,MAAM;AAAA,EACnB,IAAI,SAAS;AAAA,IAAW;AAAA,EACxB,MAAM,QAAQ,OAAO,iBAAiB,SAAS,IAAI;AAAA,EACnD,OAAO,UAAU,YAAY,YAAY,SAAQ,cAAa,OAAO,OAAO,CAAC;AAAA,CAC9E;AAQH,IAAM,WAAW,CAAC,MAAc,WAC9B,IAAI,uBAAuB;AAAA,EACzB,SACE,mBAAmB,yBAAyB,iDAC5C,0FACA;AACJ,CAAC;AAQH,IAAM,SAAQ,CAAC,YAAmB,SAChC,iBAAiB,YAAW,IAAI,EAAE,KACzB,gBAAQ,CAAC,WACP,aACL,WAAW,YACP,SACE,MACA,yEACF,IACA,IAAI,uBAAuB;AAAA,EACzB,SACE,WAAW,UAAU,OAAO,uDAC5B,iFACA,+EACA;AACJ,CAAC,CACP,CACF,CACF;AAEF,IAAM,eAAc,CAAC,YAAmB,SAC/B,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAW,OAAO,iBAAiB,YAAW,IAAI;AAAA,EACxD,IAAI,aAAa;AAAA,IAAW,OAAO,OAAc,aAAK,SAAS,MAAM,SAAS,EAAE,CAAC;AAAA,EACjF,MAAM,SAAS,OAAO,aAAa,YAAW,IAAI,EAAE,KAC3C,iBAAS,uBAAuB,MAAM,OAAM,YAAW,IAAI,CAAC,CACrE;AAAA,EACA,OAAO,cAAa,QAAQ,UAAS;AAAA,CACtC;AAOH,IAAM,gBAAgB,CAAC,WACrB,OAAO,cAAc,UACV,aACL,IAAI,uBAAuB;AAAA,EACzB,SAAS,WAAW,OAAO,UAAU,OAAO;AAC9C,CAAC,CACH,IACO;AAEN,IAAM,6BAA6B,CACxC,YACA,MACA,WAEO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,UAAU,0BAA0B,IAAI;AAAA,EAC9C,IAAI,YAAY;AAAA,IAAW,OAAO,OAAc,aAAK,OAAO;AAAA,EAC5D,MAAM,QAAQ,WAAW,aAAa,OAAO,cAAc,aAAY,SAAS;AAAA,EAChF,MAAM,OAAO,UAAU,YAAY,YAAY,OAAO,UAAU,YAAW,MAAM,EAAE;AAAA,EAEnF,IAAI,UAAU,aAAa,SAAS;AAAA,IAAW,OAAO,OAAO,aAAY,YAAW,KAAK,IAAI;AAAA,EAC7F,OAAO,cAAc,IAAI;AAAA,EACzB,IAAI,KAAK,SAAS,KAAK;AAAA,IAAM,OAAO,cAAa,MAAM,UAAS;AAAA,EAChE,MAAM,UAAU,OAAO,aAAa,YAAW,KAAK,IAAI,KAAK,IAAI,EAAE,KAC1D,iBAAS,uBAAuB,MAC9B,aAAK,SAAS,KAAK,MAAM,wBAAwB,CAAC,CAC3D,CACF;AAAA,EACA,OAAO,cAAa,SAAS,UAAS;AAAA,CACvC;AAGI,IAAM,0BAA0B,CAAC,WACtC,aAAa,OAAO,WAAW,OAAO,EAAE;;;AH/FnC,IAAM,oBAAsD,UACjE,gCACA,EAAE,sBAAsB,SAAS,CACnC;AASA,IAAM,aAAmB,YAAI,UAAU,GAAG;AAAA,EACxC,MAAM,WAAW,OAAO,OAAkB;AAAA,EAC1C,OAAO,SAAS;AAAA,CACjB;AAMM,IAAM,4BAA4B,MAC9B,iBACP,mBACO,YAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAmB,cACZ,mCACX,OAAkB,iCACpB,EAAE,KACQ,aAAI,cAAa,OAAO,YAAW,GACnC,aAAe,wBAAY,OAAkB,sBAAU,CACjE;AAAA,EACA,MAAM,MAAM,CAAO,YACV,uBAAe,SAAQ,QAAQ;AAAA,EAExC,OAAO,kBAAkB,SAAS,GAAG;AAAA,IACnC,SAAS,CAAC,MAAM,WAAW;AAAA,IAO3B,MAAM,MAAa,gBAAQ,CAAC,CAAC;AAAA,IAC7B,MAAM,EAAE,MAAM,KAAK;AAAA,IAEnB,MAAM,GAAG,MAAM,aACb,IACS,YAAI,UAAU,GAAG;AAAA,MACtB,MAAM,UAAU,YAAW,IAAI,IAAI,0BAA0B,IAAI,IAAI;AAAA,MACrE,IAAI,YAAY;AAAA,QAAW,OAAO,OAAc,aAAK,OAAO;AAAA,MAC5D,OAAO,sBAAsB,MAAM,QAAQ,OAAO,UAAS;AAAA,KAC5D,CACH;AAAA,IACF,MAAM,GAAG,MAAM,aACb,IAAW,gBAAQ,YAAW,CAAC,YAAY,sBAAsB,SAAS,MAAM,MAAM,CAAC,CAAC;AAAA,IAC1F,WAAW,GAAG,MAAM,aAClB,IACS,gBAAQ,YAAW,CAAC,YACzB,2BAA2B,SAAS,MAAM,MAAM,CAClD,CACF;AAAA,IACF,QAAQ,GAAG,aAAa,IAAI,wBAAwB,MAAM,CAAC;AAAA,EAC7D,CAAC;AAAA,CACF,CACH;;AI/FF,wBAAS;AACT;AACA;AACA,qBAAS;AACT;AACA;AACA;;;ACtBA,oBAAS;AACT;AACA;;;AC6BA,wBAAS;AACT;AACA;AACA;AACA;AACA;AACA;;;ACbA;AACA;AAAA;AAGO,MAAM,sBAA2B,kBAAY,eAAe,EAIhE;AAAC;AAUG,IAAM,WAAW,CAAC,cAAsB,MACtC,aACL,IAAI,cAAc;AAAA,EAChB,SAAS,UAAU;AACrB,CAAC,CACH;;;ACEF,IAAM,MAAM,CAAC,WAAwC,OAAO,WAAU,WAAW,SAAQ;AACzF,IAAM,OAAO,CAAC,WACZ,OAAO,WAAU,YAAY,SAAQ;AACvC,IAAM,UAAU,CAAC,WACf,MAAM,QAAQ,MAAK,KAAK,OAAM,MAAM,CAAC,SAAS,OAAO,SAAS,QAAQ,IAAI,SAAQ;AAC7E,IAAM,SAAS,CAAC,WACrB,OAAO,WAAU,YAAY,WAAU,QAAQ,CAAC,MAAM,QAAQ,MAAK,IAC9D,SACD;AASN,IAAM,YAAY,CAAC,aAA4D;AAAA,EAC7E,MAAM,UAAU,SAAS,QAAQ,CAAC,QAAQ,UAAU;AAAA,IAClD,MAAM,KAAK,IAAI,OAAO,MAAM,IAAI,KAAK;AAAA,IACrC,MAAM,aAAa,OAAO,MAAM,IAAI;AAAA,IACpC,OAAO,OAAO,YACV,CAAC,IACD,CAAC,EAAE,IAAI,OAAO,YAAY,OAAO,eAAe,WAAW,aAAa,UAAU,CAAC;AAAA,GACxF;AAAA,EACD,IAAI,CAAC,QAAQ,MAAM,CAAC,UAAU,MAAM,eAAe,SAAS;AAAA,IAAG,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,EAC7F,OAAO,CAAC,GAAG,OAAO,EACf,KAAK,CAAC,GAAG,OAAO,EAAE,cAAc,MAAM,EAAE,cAAc,MAAM,EAAE,QAAQ,EAAE,KAAK,EAC7E,IAAI,CAAC,UAAU,MAAM,EAAE;AAAA;AAOrB,IAAM,WAAW,CAAC,QAA8C;AAAA,EACrE,MAAM,OAAO,OAAO,IAAI,WAAW;AAAA,EACnC,MAAM,UAAU,OAAO,OAAO,wBAAwB;AAAA,EACtD,MAAM,WAAW,MAAM,QAAQ,IAAI,WAAW,IAAI,IAAI,cAAc,CAAC;AAAA,EACrE,OAAO;AAAA,IACL,IAAI,IAAI,IAAI,KAAK;AAAA,IACjB,KAAK,IAAI,IAAI,MAAM;AAAA,IACnB,MAAM,IAAI,IAAI,OAAO;AAAA,IACrB,MAAM,IAAI,IAAI,OAAO;AAAA,IACrB,QAAQ,IAAI,IAAI,SAAS;AAAA,IACzB,WAAW,IAAI,IAAI,aAAa;AAAA,IAChC,WAAW,IAAI,IAAI,aAAa;AAAA,IAChC,WAAW,UAAU,QAAQ;AAAA,IAC7B,SACE,SAAS,YACL,YACA;AAAA,MACE,UAAU,IAAI,KAAK,YAAY;AAAA,MAC/B,UAAU,IAAI,KAAK,YAAY;AAAA,MAC/B,cAAc,QAAQ,KAAK,gBAAgB;AAAA,MAC3C,QAAQ,QAAQ,KAAK,SAAS;AAAA,MAC9B,YAAY,QAAQ,KAAK,cAAc;AAAA,MACvC,qBAAqB,IAAI,KAAK,wBAAwB;AAAA,MACtD,sBAAsB,IAAI,UAAU,WAAW;AAAA,MAC/C,8BAA8B,KAAK,KAAK,mCAAmC;AAAA,IAC7E;AAAA,EACR;AAAA;;;AF/DK,IAAM,YAAY;AAAA,EACvB,QAAkB;AAAA,EAClB,QAAkB;AACpB;AAEA,IAAM,mBAAmB,CAAC,WACxB,WAAW,OAAO,WAAW,OAAO,UAAU;AASzC,IAAM,iBAAiB,CAC5B,YAEA,QAAO,KACE,eAAM;AAAA,EACX,OAAO,CAAC,MACN,EAAE,SAAS,6BACX,EAAE,SAAS,eACV,EAAE,SAAS,mBACV,iBAAkB,EAA+B,UAAU,CAAC;AAAA,EAChE,UAAmB,aAAI;AAAA,IACZ,aAAI,CAAU,qBAAY,YAAY,GAAG,GAAY,gBAAO,WAAW,CAAC,CAAC;AAAA,IACzE,gBAAO,EAAE;AAAA,EACpB,CAAC;AACH,CAAC,CACH;AAOK,IAAM,kBAAyB,iBAAQ,OAC3C,WAAW,SAAS,MAAM,2BAA2B,QAAQ,KACnD,gBACA,cACL,IAAI,cAAc;AAAA,EAChB,SACE;AACJ,CAAC,CACH,CACN;AAMO,IAAM,SAAS,CAAC,YAAmB,UACjC,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,cAAc,OAAO,OAAO;AAAA,EAClC,MAAM,SAAS,OAAkB;AAAA,EACjC,MAAM,UAA4B,sBAChC,GAAG,YAAY,uBAAuB,0BAAyB,OACjE,EAAE,KAAuB,6BAAW,cAAc,WAAW,CAAC,GAAqB,4BAAU;AAAA,EAC7F,MAAM,YAAY,CAAC,UACjB,IAAI,cAAc,EAAE,SAAS,kCAAkC,MAAM,WAAW,QAAQ,EAAE,CAAC;AAAA,EAC7F,MAAM,WAAW,OAAO,OAAO,QAAQ,OAAO,EAAE,KAAY,kBAAS,SAAS,CAAC;AAAA,EAC/E,IAAI,SAAS,WAAW;AAAA,IAAK;AAAA,EAC7B,IAAI,SAAS,SAAS,OAAO,SAAS,UAAU,KAAK;AAAA,IACnD,OAAO,OAAc,cACnB,IAAI,cAAc;AAAA,MAChB,SAAS,mCAAmC,SAAS;AAAA,MACrD,QAAQ,SAAS;AAAA,IACnB,CAAC,CACH;AAAA,EACF;AAAA,EACA,MAAM,OAAO,OAAO,SAAS,KAAK,KAAY,kBAAS,SAAS,CAAC;AAAA,EACjE,MAAM,OAAO,OAAc,aAAI;AAAA,IAC7B,KAAK,MAAM,OAAO,KAAK,MAAM,IAAI,CAAY;AAAA,IAC7C,OAAO,MACL,IAAI,cAAc,EAAE,SAAS,uCAAuC,QAAQ,IAAI,CAAC;AAAA,EACrF,CAAC;AAAA,EACD,MAAM,SAAS,OAAO,OAAO,SAAS;AAAA,EACtC,OAAO,WAAW,YAAY,YAAY,SAAS,MAAM;AAAA,CAC1D,EAAE,KAAK,cAAc;AAQjB,IAAM,iBAAiB,CAAC,YAAmB,SACtC,4CAAiC,MAAM,EAAE,sBAAU,CAAC,EAAE,KACvD,eAAO,CAAC,QAAQ,IAAI,SAAS,UAAU,IAAI,SAAS,IAAI,GACxD,oBACA,kBAAS,wBAAwB,SAAS,kBAAkB,CAAC,GACpE,gBACO,iBAAQ,CAAC,UAAU;AAAA,EACxB,MAAM,MAAM,MAAM,KAAK,KAAK,EAAE,QAAQ,CAAC,QAAS,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAE;AAAA,EAC/E,IAAI,IAAI,UAAU;AAAA,IAAG,OAAc,iBAAQ,IAAI,EAAE;AAAA,EACjD,OAAc,cACZ,IAAI,cAAc;AAAA,IAChB,SAAS,0BAA0B,iBAAiB,IAAI,qBAAqB,IAAI,KAAK,IAAI;AAAA,EAC5F,CAAC,CACH;AAAA,CACD,CACH;AAEF,IAAM,UAAU,CAAC,WAAqB;AAAA,EACpC,MAAM;AAAA,EACN,MAAM,MAAM;AAAA,EACZ,iBAAiB,MAAM;AAAA,KACnB,MAAM,gBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,CAAC,GAAG,MAAM,WAAW,EAAE;AAAA,KAC7E,MAAM,2BAA2B,YACjC,CAAC,IACD,EAAE,wBAAwB,MAAM,uBAAuB;AAAA,EAC3D,oBAAoB,MAAM;AAAA,EAC1B,UAAU,CAAC,GAAG,MAAM,QAAQ;AAAA,EAC5B,SAAS;AAAA,IACP,UAAU,MAAM,QAAQ;AAAA,IACxB,cAAc,CAAC,GAAG,MAAM,QAAQ,YAAY;AAAA,IAC5C,QAAQ,CAAC,GAAG,MAAM,QAAQ,MAAM;AAAA,IAChC,YAAY,CAAC,GAAG,MAAM,QAAQ,UAAU;AAAA,IACxC,qBAAqB,MAAM,QAAQ;AAAA,IACnC,8BAA8B,MAAM,QAAQ;AAAA,OACxC,MAAM,QAAQ,wBAAwB,YACtC,CAAC,IACD,EAAE,qBAAqB,MAAM,QAAQ,oBAAoB;AAAA,OACzD,MAAM,QAAQ,mBAAmB,YACjC,CAAC,IACD,EAAE,gBAAgB,MAAM,QAAQ,eAAe;AAAA,EACrD;AACF;AAGO,IAAM,YAAY,CAAC,YAAmB,UAC3C,gBAAgB,KACP,iBACL,UACG,OAAO,EAAE,0BAAc,QAAQ,KAAK,EAAE,CAAC,EACvC,KACQ,kBAAS,wBAAwB,SAAS,oBAAoB,CAAC,GACtE,cACF,CACJ,GACO,iBAAQ,CAAC,YACd,QAAQ,MAAM,OACH,cAAK,IAAI,cAAc,EAAE,SAAS,yCAAyC,CAAC,CAAC,IAC7E,iBAAQ,QAAQ,EAAE,CAC/B,CACF;AAEK,IAAM,YAAY,CAAC,YAAmB,OAAe,UAC1D,gBAAgB,KACP,iBACL,UACG,OAAO,EAAE,uBAAW,UAAU,QAAQ,KAAK,EAAE,CAAC,EAC9C,KACQ,kBAAS,wBAAwB,SAAS,oBAAoB,CAAC,GACtE,cACF,CACJ,GACO,eACT;AAMK,IAAM,YAAY,CAAC,YAAmB,UACjC,6CAAkC,EAAE,uBAAW,MAAM,CAAC,EAAE,KACzD,iBACA,mBAAU;AAAA,EACf,2BAA2B,MAAa;AAAA,EACxC,sBAAsB,SAAS,oBAAoB;AACrD,CAAC,CACH;;;AGrMF,IAAM,WAAW;AAGV,IAAM,mBAAmB,CAAC,UAA+D;AAAA,EAC9F,MAAM,SAAO,CAAC,YAAoB,IAAI,cAAc,EAAE,QAAQ,CAAC;AAAA,EAC/D,IAAI,OAAO,MAAM,eAAe,YAAY,CAAC,SAAS,KAAK,MAAM,UAAU,GAAG;AAAA,IAC5E,OAAO,OACL,yIAAyI,KAAK,UAAU,MAAM,UAAU,IAC1K;AAAA,EACF;AAAA,EACA,IAAI,MAAM,SAAS,aAAa,MAAM,KAAK,KAAK,MAAM,IAAI;AAAA,IACxD,OAAO,OAAK,0DAA0D;AAAA,EACxE;AAAA,EACA,IACE,MAAM,QAAQ,iCAAiC,QAC/C,CAAC,MAAM,QAAQ,WAAW,SAAS,8BAA8B,GACjE;AAAA,IACA,OAAO,OACL,yIACF;AAAA,EACF;AAAA,EACA;AAAA;AAGK,IAAM,YAAY,CAAC,YAAoB,aAC5C,WAAW,sCAAsC;AAEnD,IAAM,SAAS,CAAC,WAA2B,OAAO,MAAM,GAAG,EAAE,IAAI,YAAY,KAAK;AAW3E,IAAM,YAAY,CACvB,YACA,KACA,cAC8B;AAAA,EAC9B,IAAI,IAAI,WAAW,WAAW;AAAA,IAC5B,OAAO,cAAc,aAAa,UAAU,YAAY,MAAM,WAAW,YAAY,IACjF,YACA,IAAI,cAAc;AAAA,MAChB,SAAS,4CAA4C,qCAAqC;AAAA,IAC5F,CAAC;AAAA,EACP;AAAA,EACA,OAAO,OAAO,IAAI,MAAM,MAAM,WAAW,YAAY,IACjD,YACA,IAAI,cAAc;AAAA,IAChB,SAAS,4CAA4C,kDAAkD,OAAO,IAAI,MAAM;AAAA,EAC1H,CAAC;AAAA;AAGP,IAAM,kBAAkB,CAAC,UACvB,MAAM,QAAQ,wBAAwB;AAGjC,IAAM,YAAY,CAAC,MAAgC,UAA4B;AAAA,EACpF;AAAA,EACA,iBAAiB,KAAK,mBAAmB;AAAA,EACzC,aAAa,KAAK;AAAA,EAClB,wBAAwB,KAAK;AAAA,EAC7B,oBAAoB,KAAK,sBAAsB;AAAA,EAC/C,UAAU,KAAK;AAAA,EACf,SAAS;AAAA,IACP,UAAU;AAAA,IACV,cAAc,KAAK,QAAQ;AAAA,IAC3B,QAAQ,KAAK,QAAQ;AAAA,IACrB,YAAY,KAAK,QAAQ;AAAA,IACzB,qBAAqB,KAAK,QAAQ;AAAA,OAC9B,KAAK,QAAQ,yBAAyB,YACtC,CAAC,IACD,EAAE,qBAAqB,EAAE,UAAU,KAAK,QAAQ,qBAAqB,EAAE;AAAA,IAC3E,8BAA8B,KAAK,QAAQ,gCAAgC;AAAA,IAC3E,gBAAgB,KAAK,QAAQ;AAAA,EAC/B;AACF;AAEA,IAAM,UAAU,CAAC,GAA0B,MACzC,KAAK,UAAU,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,KAAK,UAAU,CAAC,GAAI,KAAK,CAAC,CAAE,EAAE,KAAK,CAAC;AAQxE,IAAM,WAAW,CAAC,GAA0B,MAAkD;AAAA,EAC5F,MAAM,QAAQ,KAAK,CAAC;AAAA,EACpB,OAAO,EAAE,WAAW,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,UAAU,SAAS,MAAM,MAAM;AAAA;AAW7E,IAAM,YAAY,CACvB,MACA,UACA,SACY;AAAA,EACZ,MAAM,OAAO,SAAS;AAAA,EACtB,QACG,SAAS,QAAQ,QAAQ,SACzB,MAAM,YAAY,QAAQ,UAC3B,CAAC,QAAQ,KAAK,QAAQ,cAAc,MAAM,YAAY,KACtD,CAAC,QAAQ,KAAK,QAAQ,QAAQ,MAAM,MAAM,KAC1C,CAAC,QAAQ,KAAK,QAAQ,YAAY,MAAM,UAAU,MACjD,MAAM,uBAAuB,QAAQ,KAAK,QAAQ,uBACnD,gBAAgB,IAAI,OAAO,MAAM,wBAAwB,QACxD,MAAM,gCAAgC,YACpC,KAAK,QAAQ,gCAAgC,UAChD,CAAC,SAAS,KAAK,UAAU,SAAS,SAAS;AAAA;AASxC,IAAM,eAAe,CAC1B,KACA,YACA,cACA,iBAC8C;AAAA,EAC9C,MAAM,WAAW,IAAI,SAAS;AAAA,EAC9B,MAAM,aAAa,iBAAiB,IAAI,WAAW,YAAY,YAAY,OAAO,IAAI,MAAM;AAAA,EAC5F,IAAI,IAAI,OAAO,aAAa,IAAI,QAAQ,aAAa,aAAa;AAAA,IAAW;AAAA,EAC7E,IAAI,eAAe,aAAa,eAAe;AAAA,IAAI;AAAA,EACnD,MAAM,SAAS,UAAU,YAAY,QAAQ;AAAA,EAC7C,MAAM,OAAO,IAAI;AAAA,EACjB,OAAO;AAAA,IACL,eAAe,IAAI;AAAA,IACnB,KAAK,IAAI;AAAA,IACT,QAAQ,IAAI,UAAU,OAAO,QAAQ,YAAY,EAAE;AAAA,IACnD,MAAM,IAAI,QAAQ;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,uBAAuB,GAAG;AAAA,IAC1B,eAAe,GAAG;AAAA,IAClB,cAAc,GAAG;AAAA,IACjB,kBAAkB,GAAG;AAAA,IACrB,uBAAuB,GAAG;AAAA,IAC1B,cAAc,MAAM,gBAAgB,CAAC;AAAA,IACrC,QAAQ,MAAM,UAAU,CAAC;AAAA,IACzB,YAAY,MAAM,cAAc,CAAC;AAAA,IACjC,qBAAqB,MAAM,uBAAuB;AAAA,IAClD,sBAAsB,MAAM,wBAAwB;AAAA,IACpD,WAAW,IAAI;AAAA,IACf,WAAW,IAAI;AAAA,EACjB;AAAA;;;AChKF;AACA;AACA;AAYA,IAAM,aAAa,CAAC,UAClB,MAAM,SAAS,kBACf,MAAM,SAAS,eACf,MAAM,SAAS;AAGjB,IAAM,iBAAiB,CAAC,eACZ,uCAA4B,EAAE,sBAAU,CAAC,EAAE,KAC7C,YACC,aAAI,CAAC,iBAAiB,aAAa,cAAc,SAAS,GAC1D,kBAAS,wBAAwB,SAAS,mBAAmB,CAAC,GAC9D,iBAAQ,YAAY,MAAa,iBAAQ,SAAS,CAAC,CAC5D;AAMK,IAAM,wBAAwB,CAAC,YAAmB,eACvD,eAAe,UAAS,EAAE,KACjB,aAAI,CAAC,eACV,eAAe,aAAa,WAAW,YAAY,MAAM,WAAW,YAAY,IAC5E,YACA,IAAI,cAAc;AAAA,EAChB,SAAS,4CAA4C,8DAA8D;AACrH,CAAC,CACP,CACF;;;ALhDF,IAAM,cAAc,CAAC,IAAY,SAC/B,SAAS,aAAa,SAAS,KAAY,iBAAQ,IAAI,IAAI,mBAAmB,EAAE,GAAG,CAAC;AAOtF,IAAM,WAAU,CACd,YACA,SACA,MACA,UAA2C,CAAC,MAErC,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,YAAY,YAAY,YAAY,OAAO,OAAO,YAAW,OAAO;AAAA,EACjF,IAAI,SAAS,WAAW;AAAA,IACtB,IAAI,KAAK,SAAS,QAAQ;AAAA,MACxB,OAAO,OAAc,cACnB,IAAI,cAAc;AAAA,QAChB,SAAS,sBAAsB,oBAAoB,KAAK,QAAQ;AAAA,MAClE,CAAC,CACH;AAAA,IACF;AAAA,IACA,OAAO;AAAA,EACT;AAAA,EAGA,IAAI,QAAQ,aAAa;AAAA,IAAM;AAAA,EAC/B,MAAM,UAAU,OAAO,eAAe,YAAW,IAAI;AAAA,EACrD,OAAO,YAAY,YAAY,YAAY,OAAO,OAAO,YAAW,OAAO;AAAA,CAC5E;AAQI,IAAM,eAAe,CAC1B,YACA,IACA,MACA,WAEO,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,OAAO,OAAO,YAAY,IAAI,QAAQ,QAAQ,MAAM,IAAI;AAAA,EAC9D,MAAM,QAAQ,QAAQ;AAAA,EACtB,MAAM,WAAW,OAAO,SAAQ,YAAW,SAAS,MAAM,eAAe,MAAM;AAAA,IAC7E,UAAU,UAAU;AAAA,EACtB,CAAC;AAAA,EACD,IAAI,aAAa;AAAA,IAAW;AAAA,EAC5B,MAAM,aAAa,aACjB,UACA,QAAQ,aAAa,YACrB,MACA,QAAQ,cAAc,MAAM,UAC9B;AAAA,EACA,IAAI,eAAe;AAAA,IAAW;AAAA,EAC9B,OAAO,UAAU,YAAY,aAAa,SAAQ,UAAU;AAAA,CAC7D;AAEH,IAAM,UAAU,CAAC,SACR,cACL,IAAI,cAAc;AAAA,EAChB,SAAS,aAAa;AACxB,CAAC,CACH;AAGK,IAAM,oBAAoB,CAC/B,YACA,IACA,MACA,WAEO,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,UAAU,iBAAiB,IAAI;AAAA,EACrC,IAAI,YAAY;AAAA,IAAW,OAAO,OAAc,cAAK,OAAO;AAAA,EAC5D,MAAM,OAAO,OAAO,YAAY,IAAI,KAAK,IAAI;AAAA,EAG7C,MAAM,UAAU,QAAQ;AAAA,EACxB,IAAI,WAAoC,OAAO,SAC7C,YACA,WAAW,KAAK,eAChB,MACA,EAAE,UAAU,YAAY,UAAU,CACpC;AAAA,EAOA,MAAM,YACJ,aAAa,YAAY,YAAY,UAAU,KAAK,YAAY,UAAU,QAAQ,UAAU;AAAA,EAC9F,IAAI,cAAc;AAAA,IAAW,OAAO,OAAc,cAAK,SAAS;AAAA,EAChE,MAAM,QAAQ,UAAU,MAAM,IAAI;AAAA,EAClC,IAAI,aAAa,WAAW;AAAA,IAC1B,MAAM,oBAAoB,OAAO,sBAAsB,YAAW,KAAK,UAAU;AAAA,IACjF,IAAI,sBAAsB;AAAA,MAAW,OAAO,OAAc,cAAK,iBAAiB;AAAA,IAChF,MAAM,YAAY,OAAO,UAAU,YAAW,KAAK;AAAA,IACnD,WAAW,OAAO,OAAO,YAAW,SAAS;AAAA,IAC7C,IAAI,aAAa;AAAA,MAAW,OAAO,OAAO,QAAQ,QAAQ;AAAA,EAC5D,EAAO,SAAI,UAAU,MAAM,UAAU,IAAI,GAAG;AAAA,IAC1C,MAAM,QAAQ,SAAS;AAAA,IACvB,IAAI,UAAU,WAAW;AAAA,MACvB,OAAO,OAAc,cAAK,IAAI,cAAc,EAAE,SAAS,2BAA2B,CAAC,CAAC;AAAA,IACtF;AAAA,IACA,OAAO,UAAU,YAAW,OAAO,KAAK;AAAA,IACxC,WAAW,OAAO,OAAO,YAAW,KAAK;AAAA,IACzC,IAAI,aAAa;AAAA,MAAW,OAAO,OAAO,QAAQ,QAAQ;AAAA,EAC5D;AAAA,EACA,MAAM,iBAAiB,UAAU,KAAK,YAAY,UAAU,QAAQ,UAAU;AAAA,EAC9E,IAAI,mBAAmB;AAAA,IAAW,OAAO,OAAc,cAAK,cAAc;AAAA,EAC1E,MAAM,aAAa,aAAa,UAAU,YAAW,MAAM,KAAK,UAAU;AAAA,EAC1E,IAAI,eAAe,WAAW;AAAA,IAC5B,OAAO,OAAc,cACnB,IAAI,cAAc;AAAA,MAChB,SAAS;AAAA,IACX,CAAC,CACH;AAAA,EACF;AAAA,EACA,OAAO;AAAA,CACR;AAEI,IAAM,iBAAiB,CAAC,WAC7B,UAAU,OAAO,WAAW,OAAO,aAAa;;;AD9G3C,IAAM,sBACX,UAA8B,wCAAwC;AAAA,EACpE,sBAAsB;AACxB,CAAC;AAEH,IAAM,aAAmB,aAAI,UAAU,GAAG;AAAA,EACxC,MAAM,WAAW,OAAO,OAAkB;AAAA,EAC1C,OAAO,SAAS;AAAA,CACjB;AAMM,IAAM,8BAA8B,MAChC,iBACP,qBACO,aAAI,UAAU,GAAG;AAAA,EACtB,MAAM,WAAmB,cACZ,mCACX,OAAkB,iCACpB,EAAE,KACQ,aAAI,cAAa,OAAO,YAAW,GACnC,aAAe,wBAAY,OAAkB,sBAAU,CACjE;AAAA,EAGA,MAAM,MAAM,CAAU,YACb,wBAAe,SAAQ,QAAQ;AAAA,EAExC,OAAO,oBAAoB,SAAS,GAAG;AAAA,IAWrC,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAMA,MAAM,MAAa,iBAAQ,CAAC,CAAC;AAAA,IAC7B,MAAM,EAAE,MAAM,KAAK;AAAA,IACnB,MAAM,GAAG,IAAI,MAAM,aACjB,IAAW,iBAAQ,YAAW,CAAC,YAAY,aAAa,SAAS,IAAI,MAAM,MAAM,CAAC,CAAC;AAAA,IACrF,WAAW,GAAG,IAAI,MAAM,aACtB,IAAW,iBAAQ,YAAW,CAAC,YAAY,kBAAkB,SAAS,IAAI,MAAM,MAAM,CAAC,CAAC;AAAA,IAC1F,QAAQ,GAAG,aAAa,IAAI,eAAe,MAAM,CAAC;AAAA,EACpD,CAAC;AAAA,CACF,CACH;;AOvFF;AACA;AACA;AAMO,MAAM,kBAA2B,6BAA8B,EAAE,qBAAqB,EAAE;AAAC;AAIzF,IAAM,YAAY,MACjB,aACJ,WACS,qBAAW,CAAC,cAAc,UAAU,mBAAmB,mBAAmB,CAAC,CACtF,EAAE,KACM,cACE,eACJ,qBAAqB,EAAE,KAAW,cAAmB,8BAAkB,CAAC,GAAS,WAAK,GACtF,iBAAiB,EAAE,KAAW,cAAmB,8BAAkB,CAAC,GAAS,WAAK,GAClF,0BAA0B,EAAE,KACpB,cAAmB,8BAAkB,CAAC,GACtC,WACR,GACA,4BAA4B,EAAE,KACtB,cAAmB,8BAAkB,CAAC,GACtC,WACR,CACF,CACF,CACF;;AC/BF;AACA;AACA;AAEA,IAAM,cAAiC,CAAC,iBAAiB,2BAA2B;AAmB7E,SAAS,YAAY,CAC1B,MACA,OAC6C;AAAA,EAC7C,MAAM,QAAQ,MAAM,eAAe,SAAS;AAAA,EAC5C,MAAM,SAAS;AAAA,OACV;AAAA,IACH,MAAM,MAAM,QAAQ;AAAA,IACpB,sBAAsB,MAAM,wBAAwB;AAAA,IACpD,eAAe,KAAK,MAAM,eAAe,OAAO,CAAC,GAAG,KAAK,EAAE;AAAA,EAC7D;AAAA,EACA,OAAkB,oBAAQ,MAAM,MAAM,MAAe,EAAE,KACzC,kBAAM,IAAI,GACR,qBAAO,CACvB;AAAA;AASK,SAAS,WAAW,CACzB,MACA,OAC4C;AAAA,EAC5C,MAAM,SAAS,KAAK,OAAO,MAAM,OAAO,QAAQ,KAAK;AAAA,EACrD,OAAkB,oBAAQ,KAAK,MAAM,MAAe,EAAE,KACxC,kBAAM,IAAI,GACR,qBAAO,CACvB;AAAA;",
27
+ "debugId": "C4EF78F8A64F7A6064756E2164756E21",
17
28
  "names": []
18
29
  }
@@ -3,6 +3,6 @@ import * as Layer from 'effect/Layer';
3
3
  declare const Providers_base: Provider.ProviderCollection<Providers, "HomeflareCloudflare">;
4
4
  export declare class Providers extends Providers_base {
5
5
  }
6
- export declare const providers: () => Layer.Layer<Providers, never, import("alchemy").AuthProviders | import("effect/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/http/HttpClient").HttpClient | import("effect/Path").Path>;
6
+ export declare const providers: () => Layer.Layer<Providers, never, import("alchemy").AuthProviders | import("effect/process/ChildProcessSpawner").ChildProcessSpawner | import("effect/Crypto").Crypto | import("effect/FileSystem").FileSystem | import("effect/http/HttpClient").HttpClient | import("effect/Path").Path | import("alchemy").Stack | import("alchemy").Stage>;
7
7
  export {};
8
8
  //# sourceMappingURL=providers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/cloudflare/providers.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;;AAItC,qBAAa,SAAU,SAAQ,cAA+D;CAAG;AAIjG,eAAO,MAAM,SAAS,8RAQnB,CAAC"}
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/cloudflare/providers.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;;AAMtC,qBAAa,SAAU,SAAQ,cAA+D;CAAG;AAIjG,eAAO,MAAM,SAAS,kVAmBnB,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * The Access application calls a SaaS OIDC application needs, and nothing else.
3
+ *
4
+ * ★ OVER `@distilled.cloud/cloudflare` FOR EVERY WRITE AND FOR THE NAME LOOKUP; ONE RAW READ. Alchemy
5
+ * `2.0.0-beta.81`'s `Cloudflare.Access.Application` has no `saas_app` at all (Application.ts:
6
+ * `saas` appears only as an app type and in two doc comments), so this resource exists. The SDK
7
+ * does carry `saasApp` on the REQUEST (`AccessApplicationsCreateRequestSaasAppOIDCSaaSApp`,
8
+ * zero_trust.ts, `@distilled.cloud/cloudflare@1.0.0-rc.13`), so create and update go through it.
9
+ * ⚠️ BUT ITS RESPONSES DROP IT. Measured 2026-10-09 against rc.13 with a fake API that answered a
10
+ * full `saas_app` object: `getAccessApplicationForAccount` and `createAccessApplicationForAccount`
11
+ * both decoded to `{ domain, type, id, aud, name, policies }`, with no `saasApp`. The response
12
+ * union is discriminated by field names (`T.UnionCases`) and a saas app falls in a variant with
13
+ * no `saas_app`. So the OIDC client id, redirect URIs, scopes and grant types can only be read
14
+ * from the wire JSON, and `getApp` below does that through the `HttpClient` the SDK itself runs
15
+ * on, with the SDK's own credentials and base URL. It invents no path: it is the same
16
+ * `GET /accounts/{account_id}/access/apps/{app_id}` the SDK's get verb sends.
17
+ *
18
+ * ⛔ THE CLIENT SECRET NEVER REACHES THIS PACKAGE'S MEMORY. The field doc on the SDK schema says
19
+ * "The application client secret, only returned on POST request". The create response is
20
+ * decoded by the SDK, whose `saasApp`-less variant drops it, and `getApp` copies named fields
21
+ * only and never reads `client_secret`. TWO paths remain where the SDK itself holds or prints the
22
+ * raw body (`@distilled.cloud/cloudflare@1.0.0-rc.13`, protocol.ts):
23
+ * 1. `DISTILLED_DEBUG_HTTP` makes it `console.error` the first 400 characters of every response
24
+ * (line 334), so `refuseDebugHttp` stops a write while it is set. THIS ONE STAYS AN
25
+ * OPERATOR-ONLY SWITCH: the package can refuse to run, it cannot stop the SDK printing.
26
+ * 2. A response that fails schema validation becomes `CloudflareParseError({ body, cause })`
27
+ * (lines 452-457), whose `body` is the whole parsed response, create response included.
28
+ * ⛔ SANITISED HERE: every SDK call of this resource (create, update, list, delete, and the
29
+ * organization read in saas-oidc-team.ts) catches that tag at the call and fails with a
30
+ * `SaasOidcError` that names the operation and nothing from the response. Why, when it can
31
+ * happen at all, and what rc.13 will and will not raise: saas-oidc-error.ts (`withheld`).
32
+ * The raw `get` below is not an SDK call and never builds that error.
33
+ * A PUBLIC PKCE client (`allowPkceWithoutClientSecret: true`) has no secret at all, so neither
34
+ * path exposes one for the Headlamp app.
35
+ */
36
+ import { Credentials } from '@distilled.cloud/cloudflare/Credentials';
37
+ import * as zeroTrust from '@distilled.cloud/cloudflare/zero-trust';
38
+ import * as Effect from 'effect/Effect';
39
+ import * as HttpClient from 'effect/http/HttpClient';
40
+ import { SaasOidcError } from './saas-oidc-error.ts';
41
+ import { type AppWrite } from './saas-oidc-wire.ts';
42
+ /**
43
+ * The two SDK writes, as properties of an object so saas-oidc-parse-error.test.ts can stand in for
44
+ * one: rc.13 cannot be made to fail a create or update with `CloudflareParseError` (the ⚠️ in
45
+ * saas-oidc-error.ts), and the catch on those two calls still has to be proven wired. Nothing
46
+ * else assigns here.
47
+ */
48
+ export declare const sdkWrites: {
49
+ create: import("@distilled.cloud/core/api").OperationMethod<zeroTrust.CreateAccessApplicationForAccountRequest, zeroTrust.AccessApplicationsCreateResult, zeroTrust.CreateAccessApplicationForAccountError, zeroTrust.CloudflareOpContext>;
50
+ update: import("@distilled.cloud/core/api").OperationMethod<zeroTrust.UpdateAccessApplicationForAccountRequest, zeroTrust.AccessApplicationsUpdateResult, zeroTrust.UpdateAccessApplicationForAccountError, zeroTrust.CloudflareOpContext>;
51
+ };
52
+ /**
53
+ * ★ UPSTREAM'S OWN RETRY, EXACTLY (alchemy beta.81 `Cloudflare/Access/Application.ts`, the comment
54
+ * and `retryTransientAccessError` above `ApplicationProvider`): `AccessReferenceNotFound` is a
55
+ * freshly created policy still propagating, and `Forbidden` is how Cloudflare throttles a valid
56
+ * token. Capped exponential, about 45 seconds, then fail. The raw read joins it for the same
57
+ * statuses so it is throttled the way the SDK calls are.
58
+ */
59
+ export declare const retryTransient: <A, E extends {
60
+ readonly _tag: string;
61
+ }, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
62
+ /**
63
+ * ⛔ REFUSED BEFORE ANY WRITE while `DISTILLED_DEBUG_HTTP` is set: the SDK would print the create
64
+ * response, which carries the client secret, to stderr (protocol.ts). Read from `process.env`
65
+ * because that is what distilled reads, as mesh-node-token.ts does.
66
+ */
67
+ export declare const refuseDebugHttp: Effect.Effect<void, SaasOidcError, never>;
68
+ /**
69
+ * The app with this id, from the wire JSON, or `undefined` when it is gone (404).
70
+ * ⚠️ Error messages carry the reason and the status only: the request holds the bearer token.
71
+ */
72
+ export declare const getApp: (accountId: string, appId: string) => Effect.Effect<import("./saas-oidc-wire.ts").ObservedApp | undefined, SaasOidcError | import("@distilled.cloud/cloudflare/Credentials").CredentialsError, Credentials | HttpClient.HttpClient>;
73
+ /**
74
+ * The live saas app with exactly this name, or `undefined`. The list payload carries no `saas_app`
75
+ * (see the ⚠️ above), so the caller re-reads the match by id with `getApp`.
76
+ * ⛔ Two matches are refused rather than resolved: Access does not enforce unique app names, so two
77
+ * means this package cannot tell which one is meant.
78
+ */
79
+ export declare const findSaasByName: (accountId: string, name: string) => Effect.Effect<string | undefined, zeroTrust.AccessReferenceNotFound | import("@distilled.cloud/cloudflare/Errors").CloudflareError | import("@distilled.cloud/cloudflare/Errors").CloudflareHttpError | import("@distilled.cloud/cloudflare/Errors").CloudflareRateLimited | import("@distilled.cloud/caddy").ConfigError | import("effect/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InvalidRoute | import("@distilled.cloud/cloudflare/Credentials").OAuthRefreshError | SaasOidcError | import("@distilled.cloud/cloudflare/Errors").UnknownCloudflareError | import("@distilled.cloud/grafana").DefaultErrors, zeroTrust.CloudflareOpContext>;
80
+ /** Create the app and return its id. The response's other fields are not trusted: re-read it. */
81
+ export declare const createApp: (accountId: string, write: AppWrite) => Effect.Effect<string, zeroTrust.AccessDestinationConflict | zeroTrust.AccessReferenceNotFound | import("@distilled.cloud/cloudflare/Errors").CloudflareError | import("@distilled.cloud/cloudflare/Errors").CloudflareHttpError | import("@distilled.cloud/cloudflare/Errors").CloudflareRateLimited | import("@distilled.cloud/caddy").ConfigError | import("effect/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InvalidRoute | import("@distilled.cloud/cloudflare/Credentials").OAuthRefreshError | SaasOidcError | import("@distilled.cloud/cloudflare/Errors").UnknownCloudflareError | import("@distilled.cloud/grafana").DefaultErrors, zeroTrust.CloudflareOpContext>;
82
+ export declare const updateApp: (accountId: string, appId: string, write: AppWrite) => Effect.Effect<void, zeroTrust.AccessReferenceNotFound | import("@distilled.cloud/cloudflare/Errors").CloudflareError | import("@distilled.cloud/cloudflare/Errors").CloudflareHttpError | import("@distilled.cloud/cloudflare/Errors").CloudflareRateLimited | import("@distilled.cloud/caddy").ConfigError | import("effect/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InvalidRoute | import("@distilled.cloud/cloudflare/Credentials").OAuthRefreshError | SaasOidcError | import("@distilled.cloud/cloudflare/Errors").UnknownCloudflareError | import("@distilled.cloud/grafana").DefaultErrors, zeroTrust.CloudflareOpContext>;
83
+ /**
84
+ * Idempotent: an app already gone is a successful delete. Any other failure propagates, except
85
+ * that a response the SDK cannot validate is withheld (see `withheld`).
86
+ */
87
+ export declare const deleteApp: (accountId: string, appId: string) => Effect.Effect<void, import("@distilled.cloud/cloudflare/Errors").CloudflareError | import("@distilled.cloud/cloudflare/Errors").CloudflareHttpError | import("@distilled.cloud/cloudflare/Errors").CloudflareRateLimited | import("@distilled.cloud/caddy").ConfigError | import("effect/http/HttpClientError").HttpClientError | import("@distilled.cloud/cloudflare/Errors").InvalidRoute | import("@distilled.cloud/cloudflare/Credentials").OAuthRefreshError | SaasOidcError | import("@distilled.cloud/cloudflare/Errors").UnknownCloudflareError | import("@distilled.cloud/grafana").DefaultErrors, zeroTrust.CloudflareOpContext>;
88
+ //# sourceMappingURL=saas-oidc-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saas-oidc-api.d.ts","sourceRoot":"","sources":["../../src/cloudflare/saas-oidc-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,WAAW,EAAiB,MAAM,yCAAyC,CAAC;AACrF,OAAO,KAAK,SAAS,MAAM,wCAAwC,CAAC;AACpE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,UAAU,MAAM,wBAAwB,CAAC;AAErD,OAAO,EAAE,aAAa,EAAY,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,KAAK,QAAQ,EAAoB,MAAM,qBAAqB,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,SAAS;IACpB,MAAM;IACN,MAAM;CACP,CAAC;AAKF;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,UAC9D,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,2BAc7B,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,eAAe,2CAS3B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,cAAe,MAAM,SAAS,MAAM,kMA2B9B,CAAC;AAE1B;;;;;GAKG;AACH,eAAO,MAAM,cAAc,cAAe,MAAM,QAAQ,MAAM,iqBAe3D,CAAC;AA4BJ,iGAAiG;AACjG,eAAO,MAAM,SAAS,cAAe,MAAM,SAAS,QAAQ,2rBAezD,CAAC;AAEJ,eAAO,MAAM,SAAS,cAAe,MAAM,SAAS,MAAM,SAAS,QAAQ,mpBAWxE,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,SAAS,cAAe,MAAM,SAAS,MAAM,+mBAOvD,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A test-only downstream resource for saas-oidc-stable.test.ts: it takes the app's issuer as a prop
3
+ * and counts how often the engine reconciles it, so a test can tell "planned noop" from "planned
4
+ * update" for a consumer of `app.issuer`. ⛔ TEST-ONLY. No provider imports this file.
5
+ */
6
+ import * as Provider from 'alchemy/Provider';
7
+ import { Resource } from 'alchemy/Resource';
8
+ export interface IssuerConsumer extends Resource<'Test.IssuerConsumer', {
9
+ readonly issuer: string;
10
+ readonly jwks: string;
11
+ }, {
12
+ readonly issuer: string;
13
+ readonly jwks: string;
14
+ }> {
15
+ }
16
+ export declare const IssuerConsumer: import("alchemy").ResourceClass<IssuerConsumer>;
17
+ /** The provider, and the list of props it was reconciled with (one entry per reconcile). */
18
+ export declare const consumerProvider: () => {
19
+ layer: import("effect/Layer").Layer<Provider.Provider<IssuerConsumer>, never, never>;
20
+ reconciled: string[];
21
+ };
22
+ //# sourceMappingURL=saas-oidc-consumer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saas-oidc-consumer.d.ts","sourceRoot":"","sources":["../../src/cloudflare/saas-oidc-consumer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAG5C,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,qBAAqB,EACrB;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAClD;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CACnD;CAAG;AACJ,eAAO,MAAM,cAAc,iDAAkD,CAAC;AAE9E,4FAA4F;AAC5F,eAAO,MAAM,gBAAgB;;;CAiB5B,CAAC"}
@@ -0,0 +1,22 @@
1
+ import * as Effect from 'effect/Effect';
2
+ declare const SaasOidcError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
3
+ readonly _tag: "SaasOidcError";
4
+ } & Readonly<A>;
5
+ /** A refusal or failure, carrying the sentence an operator needs. Never a secret. */
6
+ export declare class SaasOidcError extends SaasOidcError_base<{
7
+ readonly message: string;
8
+ /** The HTTP status when the failure is a response (0 for a transport failure). */
9
+ readonly status?: number;
10
+ }> {
11
+ }
12
+ /**
13
+ * ⛔ THE ONLY THING THIS PACKAGE SAYS ABOUT A RESPONSE THE SDK COULD NOT VALIDATE. The handler for
14
+ * `Effect.catchTag('CloudflareParseError', …)`: it never touches the error it replaces, because
15
+ * that error's `body` is the whole response (a create's carries `client_secret`) and its `cause`
16
+ * quotes the values that failed. The message names the operation only. No status is claimed: the
17
+ * SDK builds this error after its failure branch (protocol.ts:359, :452), so the response was a
18
+ * success as far as HTTP and the envelope go, and nothing more is known.
19
+ */
20
+ export declare const withheld: (operation: string) => () => Effect.Effect<never, SaasOidcError>;
21
+ export {};
22
+ //# sourceMappingURL=saas-oidc-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saas-oidc-error.d.ts","sourceRoot":"","sources":["../../src/cloudflare/saas-oidc-error.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;;;;AAExC,qFAAqF;AACrF,qBAAa,aAAc,SAAQ,mBAAkC;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,cAAe,MAAM,WAAS,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,aAAa,CAKlF,CAAC"}