@astrale-os/adapter-cloudflare 0.5.0-beta.2 → 0.5.0-beta.21

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 (120) hide show
  1. package/README.md +11 -0
  2. package/dist/adapter.d.ts +17 -0
  3. package/dist/adapter.js +140 -0
  4. package/dist/build/artifact.d.ts +8 -0
  5. package/dist/build/artifact.js +179 -0
  6. package/dist/build/closure.d.ts +2 -0
  7. package/dist/build/closure.js +22 -0
  8. package/dist/build/codegen/execution.d.ts +13 -0
  9. package/dist/build/codegen/execution.js +44 -0
  10. package/dist/build/codegen/gateway.d.ts +1 -0
  11. package/dist/build/codegen/gateway.js +7 -0
  12. package/dist/build/codegen/merge.d.ts +0 -1
  13. package/dist/build/codegen/merge.js +0 -1
  14. package/dist/build/codegen/wrangler.d.ts +10 -1
  15. package/dist/build/codegen/wrangler.js +22 -16
  16. package/dist/build/configuration.d.ts +9 -0
  17. package/dist/build/configuration.js +40 -0
  18. package/dist/build/frontend-v1.d.ts +16 -0
  19. package/dist/build/frontend-v1.js +133 -0
  20. package/dist/build/index.d.ts +8 -16
  21. package/dist/build/index.js +8 -16
  22. package/dist/build/metafile.d.ts +5 -0
  23. package/dist/build/metafile.js +14 -0
  24. package/dist/build/parse-output.d.ts +0 -1
  25. package/dist/build/parse-output.js +0 -1
  26. package/dist/build/qualification.d.ts +5 -0
  27. package/dist/build/qualification.js +7 -0
  28. package/dist/build/roots.d.ts +2 -0
  29. package/dist/build/roots.js +34 -0
  30. package/dist/build/wrangler-cli.d.ts +53 -34
  31. package/dist/build/wrangler-cli.js +284 -111
  32. package/dist/cloudflare.d.ts +1 -34
  33. package/dist/cloudflare.js +1 -205
  34. package/dist/deploy/deploy.d.ts +17 -0
  35. package/dist/deploy/deploy.js +207 -0
  36. package/dist/deploy/index.d.ts +1 -0
  37. package/dist/deploy/index.js +1 -0
  38. package/dist/identity/binding.d.ts +2 -0
  39. package/dist/identity/binding.js +2 -0
  40. package/dist/identity/material.d.ts +7 -0
  41. package/dist/identity/material.js +42 -0
  42. package/dist/identity/secret.d.ts +3 -0
  43. package/dist/identity/secret.js +7 -0
  44. package/dist/identity/source.d.ts +26 -0
  45. package/dist/identity/source.js +98 -0
  46. package/dist/index.d.ts +3 -4
  47. package/dist/index.js +3 -4
  48. package/dist/params.d.ts +18 -5
  49. package/dist/params.js +0 -1
  50. package/dist/release/index.d.ts +2 -0
  51. package/dist/release/index.js +2 -0
  52. package/dist/release/namespace.d.ts +2 -0
  53. package/dist/release/namespace.js +6 -0
  54. package/dist/release/target.d.ts +2 -0
  55. package/dist/release/target.js +9 -0
  56. package/dist/worker/entry.d.ts +34 -0
  57. package/dist/worker/entry.js +157 -0
  58. package/dist/worker/fetch.d.ts +11 -0
  59. package/dist/worker/fetch.js +31 -0
  60. package/dist/worker/gateway.d.ts +15 -0
  61. package/dist/worker/gateway.js +26 -0
  62. package/dist/worker/identity.d.ts +7 -0
  63. package/dist/worker/identity.js +23 -0
  64. package/dist/worker/index.d.ts +3 -48
  65. package/dist/worker/index.js +3 -186
  66. package/package.json +21 -18
  67. package/template/astrale.config.ts +19 -11
  68. package/dist/build/codegen/identity.d.ts +0 -10
  69. package/dist/build/codegen/identity.d.ts.map +0 -1
  70. package/dist/build/codegen/identity.js +0 -32
  71. package/dist/build/codegen/identity.js.map +0 -1
  72. package/dist/build/codegen/merge.d.ts.map +0 -1
  73. package/dist/build/codegen/merge.js.map +0 -1
  74. package/dist/build/codegen/worker.d.ts +0 -12
  75. package/dist/build/codegen/worker.d.ts.map +0 -1
  76. package/dist/build/codegen/worker.js +0 -40
  77. package/dist/build/codegen/worker.js.map +0 -1
  78. package/dist/build/codegen/wrangler.d.ts.map +0 -1
  79. package/dist/build/codegen/wrangler.js.map +0 -1
  80. package/dist/build/frontend.d.ts +0 -26
  81. package/dist/build/frontend.d.ts.map +0 -1
  82. package/dist/build/frontend.js +0 -263
  83. package/dist/build/frontend.js.map +0 -1
  84. package/dist/build/index.d.ts.map +0 -1
  85. package/dist/build/index.js.map +0 -1
  86. package/dist/build/parse-output.d.ts.map +0 -1
  87. package/dist/build/parse-output.js.map +0 -1
  88. package/dist/build/prepare.d.ts +0 -18
  89. package/dist/build/prepare.d.ts.map +0 -1
  90. package/dist/build/prepare.js +0 -117
  91. package/dist/build/prepare.js.map +0 -1
  92. package/dist/build/worker.d.ts +0 -15
  93. package/dist/build/worker.d.ts.map +0 -1
  94. package/dist/build/worker.js +0 -21
  95. package/dist/build/worker.js.map +0 -1
  96. package/dist/build/wrangler-cli.d.ts.map +0 -1
  97. package/dist/build/wrangler-cli.js.map +0 -1
  98. package/dist/cloudflare.d.ts.map +0 -1
  99. package/dist/cloudflare.js.map +0 -1
  100. package/dist/index.d.ts.map +0 -1
  101. package/dist/index.js.map +0 -1
  102. package/dist/params.d.ts.map +0 -1
  103. package/dist/params.js.map +0 -1
  104. package/dist/worker/index.d.ts.map +0 -1
  105. package/dist/worker/index.js.map +0 -1
  106. package/src/build/codegen/identity.ts +0 -40
  107. package/src/build/codegen/merge.ts +0 -92
  108. package/src/build/codegen/worker.ts +0 -53
  109. package/src/build/codegen/wrangler.ts +0 -127
  110. package/src/build/frontend.ts +0 -324
  111. package/src/build/index.ts +0 -29
  112. package/src/build/parse-output.ts +0 -60
  113. package/src/build/prepare.ts +0 -161
  114. package/src/build/worker.ts +0 -37
  115. package/src/build/wrangler-cli.ts +0 -362
  116. package/src/cloudflare.ts +0 -251
  117. package/src/index.ts +0 -21
  118. package/src/params.ts +0 -92
  119. package/src/worker/index.ts +0 -276
  120. package/src/worker/tsconfig.json +0 -10
package/src/params.ts DELETED
@@ -1,92 +0,0 @@
1
- /**
2
- * `CloudflareParams` — the provider-typed config for the Cloudflare adapter.
3
- *
4
- * One type covers every env; per-env differences are just optional fields. The
5
- * same `{ dev, prod, canary, … }` map a developer writes in `astrale.config.ts`
6
- * is `Record<string, CloudflareParams>`.
7
- *
8
- * Three deployment shapes fall out of which fields are set:
9
- * • dev (local) — `watch()` runs `wrangler dev`; URL = http://localhost:<port>
10
- * • dev-remote — `deploy()` with no `route`; URL = https://<name>.<sub>.workers.dev
11
- * • remote (custom) — `deploy()` with `route`; URL = https://<route>
12
- */
13
- export interface CloudflareParams {
14
- /**
15
- * Custom route / hostname for a routed deploy, e.g. `'crm.acme.dev'`. Omit to
16
- * deploy to `*.workers.dev`. Ignored by local `wrangler dev` (URL is
17
- * localhost) but still used as the placeholder base domain there.
18
- */
19
- readonly route?: string
20
- /** Gitignored secrets file (whole contents = secrets), e.g. `'.env.dev'`. */
21
- readonly secrets?: string
22
- /** Local dev port for `wrangler dev`. Default 8787. */
23
- readonly port?: number
24
- /**
25
- * Public URL the dev server is reached through (tunnel / sandbox preview /
26
- * reverse proxy), e.g. `'https://my-box.preview.dev'`. Sets BOTH effects an
27
- * off-localhost dev needs: wrangler binds 0.0.0.0 (reachable from outside)
28
- * and `WORKER_URL` is pinned to this URL so the worker's per-request-Host
29
- * identity (`iss`) doesn't drift to the proxy's internal hostname.
30
- * Usually injected by `pnpm dev --host <url>` rather than written here.
31
- */
32
- readonly host?: string
33
- /** Override the Worker name. Default: derived from the domain origin. */
34
- readonly workerName?: string
35
- /** Run `wrangler dev --remote` (edge isolate) instead of local workerd. */
36
- readonly remote?: boolean
37
- /** Extra plain (non-secret) vars to inject as Worker vars. */
38
- readonly vars?: Record<string, string>
39
- /**
40
- * Escape hatch: raw wrangler config deep-merged over the generated base. Use
41
- * it to declare extra bindings the adapter has no typed field for — KV, R2,
42
- * D1, queues, service bindings, extra `compatibility_flags`, cron `triggers`,
43
- * Hyperdrive, etc.
44
- *
45
- * wrangler: {
46
- * kv_namespaces: [{ binding: 'CACHE', id: '<kv-id>' }],
47
- * r2_buckets: [{ binding: 'FILES', bucket_name: 'my-bucket' }],
48
- * }
49
- *
50
- * The merge is additive: arrays concat (so your `services` keep the adapter's
51
- * `SELF` binding and your `compatibility_flags` keep `nodejs_compat`); new
52
- * keys are added as-is. The adapter-owned keys `name`, `main`, `assets`,
53
- * `routes` are rejected — use `workerName` and `route` instead. Avoid
54
- * the reserved binding names `SELF` and `ASSETS`. Durable Objects need extra
55
- * worker-entry codegen and aren't supported through this field yet.
56
- */
57
- readonly wrangler?: Record<string, unknown>
58
- /**
59
- * Route subrequests to platform INSTANCE hostnames through a service binding
60
- * instead of the public edge. A Cloudflare Worker can't fetch a same-zone
61
- * hostname served by another Worker — Cloudflare 522s it — so without this the
62
- * worker can't fetch an instance kernel's JWKS to verify an inbound credential
63
- * from that instance, and every cross-instance call (install hooks, the GUI's
64
- * delegated calls, authed views) fails with `2002 Credential verification
65
- * failed`. Point it at a router Worker that reaches those hosts internally
66
- * (Astrale: `admin-router`).
67
- *
68
- * Wires BOTH halves the SDK needs: the `<binding>` service binding in the
69
- * generated wrangler config, and a `routeSubrequest` predicate in the worker
70
- * entry that diverts matching hosts through it.
71
- *
72
- * ON BY DEFAULT (targets `admin-router`) so every adapter-deployed worker can
73
- * verify cross-instance credentials out of the box. Pass `false` to disable
74
- * (a domain that never talks to another instance, or an account without a
75
- * router), or an object to override the target / binding name / host match.
76
- *
77
- * router: false // opt out
78
- * router: { service: 'my-router' } // override target
79
- */
80
- readonly router?:
81
- | false
82
- | {
83
- /** Router Worker the binding targets. Default `'admin-router'`. */
84
- readonly service?: string
85
- /** Service-binding name the worker reads. Default `'ROUTER'`. */
86
- readonly binding?: string
87
- /** Host suffix that marks an instance host. Default `'.astrale.ai'`. */
88
- readonly hostSuffix?: string
89
- /** Min dot-separated label count for an instance host. Default `4` (`<slug>.<region>.astrale.ai`). */
90
- readonly minLabels?: number
91
- }
92
- }
@@ -1,276 +0,0 @@
1
- import type { schema } from '@astrale-os/kernel-dsl/v1'
2
- import type { DomainDefinition, FrontendRoute } from '@astrale-os/sdk'
3
- import type {
4
- Application,
5
- RemoteDeploymentInput,
6
- RemoteIdentityInput,
7
- } from '@astrale-os/sdk/server'
8
- import type { ExecutionContext, Hono } from 'hono'
9
-
10
- import { issuer } from '@astrale-os/kernel-core/auth'
11
- import { createRemoteApplication } from '@astrale-os/sdk/server'
12
- import { Hono as HonoApplication } from 'hono'
13
-
14
- const MAXIMUM_CACHED_APPLICATIONS = 4
15
- const APPLICATION_CLOSE_TIMEOUT_MS = 5_000
16
-
17
- /** SDK-owned Publication manifest passed through without adapter interpretation. */
18
- export type CloudflareManifest = NonNullable<RemoteDeploymentInput['manifest']>
19
-
20
- export interface Fetcher {
21
- fetch(request: Request): Response | Promise<Response>
22
- }
23
-
24
- /** Adapter-owned bindings generated into every Cloudflare deployment. */
25
- export interface CloudflareAdapterBindings {
26
- /** Native Cloudflare deployment metadata (direct Wrangler deployments). */
27
- readonly ASTRALE_VERSION_METADATA?: {
28
- readonly id: string
29
- readonly tag: string
30
- readonly timestamp: string
31
- }
32
- /** Content-derived generation supplied by providers without version metadata. */
33
- readonly ASTRALE_DEPLOYMENT_VERSION?: string
34
- readonly WORKER_URL?: string
35
- readonly ASSETS?: Fetcher
36
- readonly SELF?: Fetcher
37
- readonly FRONTEND_DEV_URL?: string
38
- readonly DISPATCHER?: { get(name: string): Fetcher }
39
- }
40
-
41
- export interface CloudflareWorkerConfig<
42
- S extends schema.DomainSchema,
43
- Bindings extends CloudflareAdapterBindings,
44
- Environment,
45
- > {
46
- readonly definition: DomainDefinition<S>
47
- readonly privateKey:
48
- | RemoteIdentityInput['privateKey']
49
- | ((bindings: Bindings) => RemoteIdentityInput['privateKey'])
50
- /** Compose provider bindings into the exact Domain environment once per deployment generation. */
51
- readonly resolveEnvironment?: (bindings: Bindings) => Environment | Promise<Environment>
52
- readonly manifest?: CloudflareManifest
53
- readonly resolveUrl?: (bindings: Bindings, requestOrigin: string) => string
54
- readonly selfBinding?: (bindings: Bindings) => Fetcher | null | undefined
55
- readonly routeSubrequest?: (url: URL, bindings: Bindings) => Fetcher | null | undefined
56
- }
57
-
58
- export interface CloudflareWorker<Environment> {
59
- fetch(
60
- request: Request,
61
- environment: Environment,
62
- executionContext?: ExecutionContext,
63
- ): Response | Promise<Response>
64
- }
65
-
66
- interface CachedApplication<Environment> {
67
- readonly origin: string
68
- readonly application: Application<Hono>
69
- readonly environment: Environment
70
- }
71
-
72
- /**
73
- * Compose one Cloudflare module Worker without request-global mutation.
74
- * Environment bindings are captured once per immutable serving URL/version generation.
75
- */
76
- export function cloudflareWorkerEntry<
77
- Bindings extends CloudflareAdapterBindings,
78
- Environment = Bindings,
79
- >() {
80
- return function <const S extends schema.DomainSchema>(
81
- config: CloudflareWorkerConfig<S, Bindings, Environment>,
82
- ): CloudflareWorker<Bindings> {
83
- const applications = new Map<string, CachedApplication<Environment>>()
84
-
85
- const applicationFor = async (
86
- url: string,
87
- bindings: Bindings,
88
- ): Promise<CachedApplication<Environment>> => {
89
- const version = deploymentVersion(bindings)
90
- const key = `${url}\u0000${version}`
91
- const cached = applications.get(key)
92
- if (cached !== undefined) return cached
93
- const environment =
94
- config.resolveEnvironment === undefined
95
- ? (bindings as unknown as Environment)
96
- : await config.resolveEnvironment(bindings)
97
-
98
- const routedFetch: typeof fetch = async (input, init) => {
99
- const request = new Request(input, init)
100
- const destination = new URL(request.url)
101
- if (destination.origin === new URL(url).origin) {
102
- const self = config.selfBinding?.(bindings)
103
- if (self !== null && self !== undefined) return self.fetch(request)
104
- return serving.app.fetch(request, environment)
105
- }
106
- const route = config.routeSubrequest?.(destination, bindings)
107
- return route === null || route === undefined ? fetch(request) : route.fetch(request)
108
- }
109
-
110
- const deploymentQuery = `deployment=${encodeURIComponent(version)}`
111
- const issuerId = issuer.accept(url)
112
- const host = new HonoApplication()
113
- host.get('/meta', (context) =>
114
- context.json({
115
- origin: config.definition.schema.origin,
116
- issuer: issuerId,
117
- schemaRevision: config.definition.domain.$.revision,
118
- deploymentVersion: version,
119
- }),
120
- )
121
- const serving: Application<Hono> = createRemoteApplication({
122
- definition: config.definition,
123
- environment,
124
- identity: {
125
- issuer: issuerId,
126
- subject: config.definition.schema.origin,
127
- privateKey:
128
- typeof config.privateKey === 'function'
129
- ? config.privateKey(bindings)
130
- : config.privateKey,
131
- fetch: routedFetch,
132
- ...(new URL(url).protocol === 'http:' ? { allowInsecureHttp: true } : {}),
133
- },
134
- deployment: {
135
- bundleHref: `${url}/.well-known/astrale/domain.bundle.json?${deploymentQuery}`,
136
- endpoint: { http: `${url}/invoke` },
137
- ...(config.definition.frontend === undefined
138
- ? {}
139
- : { frontendBaseHref: url, frontendCacheKey: version }),
140
- ...(config.manifest === undefined ? {} : { manifest: config.manifest }),
141
- installation: {
142
- href: `${url}/install`,
143
- method: 'POST',
144
- authentication: { kind: 'public' },
145
- },
146
- },
147
- app: host,
148
- server: {
149
- path: '/invoke',
150
- health: { probe: { check: () => true } },
151
- limits: {
152
- maximumRequestBytes: 1024 * 1024,
153
- maximumPublicationBytes: 1024 * 1024,
154
- maximumBundleBytes: 8 * 1024 * 1024,
155
- maximumInstallationBytes: 8 * 1024 * 1024,
156
- invocationTimeoutMs: 30_000,
157
- },
158
- },
159
- })
160
- const created = Object.freeze({
161
- origin: new URL(url).origin,
162
- application: serving,
163
- environment,
164
- })
165
- if (applications.size >= MAXIMUM_CACHED_APPLICATIONS) {
166
- const oldest = applications.keys().next().value
167
- if (oldest !== undefined) {
168
- const evicted = applications.get(oldest)
169
- applications.delete(oldest)
170
- if (evicted !== undefined) {
171
- try {
172
- await closeEvictedApplication(evicted.application)
173
- } catch (cause) {
174
- await serving
175
- .close({ deadline: Date.now() + APPLICATION_CLOSE_TIMEOUT_MS })
176
- .catch(() => undefined)
177
- throw cause
178
- }
179
- }
180
- }
181
- }
182
- applications.set(key, created)
183
- return created
184
- }
185
-
186
- return Object.freeze({
187
- async fetch(request: Request, bindings: Bindings, executionContext?: ExecutionContext) {
188
- const requestUrl = new URL(request.url)
189
- if (config.definition.frontend !== undefined) {
190
- const asset = await frontendAsset(config.definition, bindings, requestUrl, request)
191
- if (asset !== undefined) return asset
192
- }
193
- const publicUrl = canonicalServingUrl(
194
- config.resolveUrl?.(bindings, requestOrigin(requestUrl, request)) ??
195
- bindings.WORKER_URL ??
196
- requestOrigin(requestUrl, request),
197
- )
198
- const serving = await applicationFor(publicUrl, bindings)
199
- return serving.application.app.fetch(request, serving.environment, executionContext)
200
- },
201
- })
202
- }
203
- }
204
-
205
- /** Close an evicted Application before its environment bindings leave the cache. */
206
- export function closeEvictedApplication(
207
- application: Pick<Application, 'close'>,
208
- now: () => number = Date.now,
209
- ): Promise<void> {
210
- return application.close({ deadline: now() + APPLICATION_CLOSE_TIMEOUT_MS })
211
- }
212
-
213
- async function frontendAsset<S extends schema.DomainSchema>(
214
- definition: DomainDefinition<S>,
215
- environment: CloudflareAdapterBindings,
216
- url: URL,
217
- request: Request,
218
- ): Promise<Response | undefined> {
219
- const frontend = definition.frontend
220
- if (frontend === undefined || frontend.source.kind === 'external') return undefined
221
- const routes = frontend.routes as Readonly<Record<string, FrontendRoute>>
222
- const route = Object.values(routes).find((candidate) => candidate.path === url.pathname)
223
- const staticAsset = url.pathname.startsWith('/assets/')
224
- if (route === undefined && !staticAsset) return undefined
225
- if (request.method !== 'GET' && request.method !== 'HEAD') return undefined
226
- const binding = environment.ASSETS
227
- if (binding === undefined) return undefined
228
- const dev = environment.FRONTEND_DEV_URL?.replace(/\/+$/u, '')
229
- if (dev !== undefined && dev.length > 0) {
230
- return fetch(new Request(`${dev}${url.pathname}${url.search}`, request))
231
- }
232
- const path =
233
- route === undefined ? `${url.pathname}${url.search}` : `/${route.document}${url.search}`
234
- return binding.fetch(new Request(new URL(path, url.origin), request))
235
- }
236
-
237
- function requestOrigin(url: URL, request: Request): string {
238
- if (url.protocol !== 'http:') return url.origin
239
- const forwarded = request.headers.get('x-forwarded-proto')?.split(',')[0]?.trim().toLowerCase()
240
- return forwarded === 'https' ? `https://${url.host}` : url.origin
241
- }
242
-
243
- function canonicalServingUrl(input: string): string {
244
- let url: URL
245
- try {
246
- url = new URL(input)
247
- } catch (cause) {
248
- throw new TypeError('Cloudflare Worker serving URL is invalid.', { cause })
249
- }
250
- if (
251
- (url.protocol !== 'https:' && url.protocol !== 'http:') ||
252
- url.username !== '' ||
253
- url.password !== '' ||
254
- url.pathname !== '/' ||
255
- url.search !== '' ||
256
- url.hash !== ''
257
- ) {
258
- throw new TypeError('Cloudflare Worker serving URL must be one HTTP(S) origin.')
259
- }
260
- return url.origin
261
- }
262
-
263
- function deploymentVersion(environment: CloudflareAdapterBindings): string {
264
- const input = environment.ASTRALE_VERSION_METADATA
265
- if (
266
- input !== null &&
267
- typeof input === 'object' &&
268
- typeof input.id === 'string' &&
269
- input.id.length > 0
270
- ) {
271
- return input.id
272
- }
273
- const fallback = environment.ASTRALE_DEPLOYMENT_VERSION
274
- if (typeof fallback === 'string' && fallback.length > 0) return fallback
275
- throw new TypeError('Cloudflare Worker deployment version is unavailable.')
276
- }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "noEmit": true,
5
- "types": [],
6
- "lib": ["ES2023", "DOM", "DOM.Iterable"],
7
- "skipLibCheck": true
8
- },
9
- "include": ["index.ts"]
10
- }