@astrale-os/sdk 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/verify.d.ts +2 -0
- package/dist/auth/verify.d.ts.map +1 -1
- package/dist/auth/verify.js +81 -26
- package/dist/auth/verify.js.map +1 -1
- package/dist/cli/bin.d.ts +7 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +15 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/dotenv.d.ts +13 -0
- package/dist/cli/dotenv.d.ts.map +1 -0
- package/dist/cli/dotenv.js +46 -0
- package/dist/cli/dotenv.js.map +1 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/run.d.ts +79 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +569 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spec.d.ts +19 -0
- package/dist/cli/spec.d.ts.map +1 -0
- package/dist/cli/spec.js +31 -0
- package/dist/cli/spec.js.map +1 -0
- package/dist/config/adapter.d.ts +140 -0
- package/dist/config/adapter.d.ts.map +1 -0
- package/dist/config/adapter.js +40 -0
- package/dist/config/adapter.js.map +1 -0
- package/dist/config/define-domain.d.ts +112 -0
- package/dist/config/define-domain.d.ts.map +1 -0
- package/dist/config/define-domain.js +98 -0
- package/dist/config/define-domain.js.map +1 -0
- package/dist/config/deploy.d.ts +28 -0
- package/dist/config/deploy.d.ts.map +1 -0
- package/dist/config/deploy.js +24 -0
- package/dist/config/deploy.js.map +1 -0
- package/dist/config/index.d.ts +21 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +18 -0
- package/dist/config/index.js.map +1 -0
- package/dist/define/remote-function.d.ts +19 -11
- package/dist/define/remote-function.d.ts.map +1 -1
- package/dist/define/remote-function.js.map +1 -1
- package/dist/dispatch/call-remote.d.ts +7 -3
- package/dist/dispatch/call-remote.d.ts.map +1 -1
- package/dist/dispatch/call-remote.js.map +1 -1
- package/dist/dispatch/dispatcher.d.ts.map +1 -1
- package/dist/dispatch/dispatcher.js +8 -4
- package/dist/dispatch/dispatcher.js.map +1 -1
- package/dist/dispatch/index.d.ts +1 -1
- package/dist/dispatch/index.d.ts.map +1 -1
- package/dist/dispatch/index.js.map +1 -1
- package/dist/dispatch/self.d.ts +46 -10
- package/dist/dispatch/self.d.ts.map +1 -1
- package/dist/dispatch/self.js +65 -8
- package/dist/dispatch/self.js.map +1 -1
- package/dist/domain/define.d.ts +3 -3
- package/dist/domain/define.js +3 -3
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/method/class.d.ts.map +1 -1
- package/dist/method/class.js.map +1 -1
- package/dist/method/context.d.ts +32 -7
- package/dist/method/context.d.ts.map +1 -1
- package/dist/method/index.d.ts +1 -1
- package/dist/method/index.d.ts.map +1 -1
- package/dist/method/single.d.ts +16 -11
- package/dist/method/single.d.ts.map +1 -1
- package/dist/method/single.js.map +1 -1
- package/dist/server/domain-entry.d.ts +67 -0
- package/dist/server/domain-entry.d.ts.map +1 -0
- package/dist/server/domain-entry.js +58 -0
- package/dist/server/domain-entry.js.map +1 -0
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/worker-entry.d.ts +57 -5
- package/dist/server/worker-entry.d.ts.map +1 -1
- package/dist/server/worker-entry.js +108 -24
- package/dist/server/worker-entry.js.map +1 -1
- package/package.json +12 -3
- package/src/auth/verify.ts +89 -28
- package/src/cli/bin.ts +15 -0
- package/src/cli/dotenv.ts +45 -0
- package/src/cli/index.ts +15 -0
- package/src/cli/run.ts +675 -0
- package/src/cli/spec.ts +42 -0
- package/src/config/adapter.ts +172 -0
- package/src/config/define-domain.ts +218 -0
- package/src/config/deploy.ts +35 -0
- package/src/config/index.ts +31 -0
- package/src/define/remote-function.ts +42 -13
- package/src/dispatch/call-remote.ts +7 -2
- package/src/dispatch/dispatcher.ts +8 -4
- package/src/dispatch/index.ts +1 -1
- package/src/dispatch/self.ts +96 -10
- package/src/domain/define.ts +3 -3
- package/src/index.ts +25 -4
- package/src/method/class.ts +4 -3
- package/src/method/context.ts +38 -7
- package/src/method/index.ts +1 -1
- package/src/method/single.ts +30 -11
- package/src/server/domain-entry.ts +113 -0
- package/src/server/index.ts +3 -1
- package/src/server/worker-entry.ts +122 -23
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
* • resolve the serving URL (== `iss`), canonicalize it (so the value matches
|
|
7
7
|
* what `createRemoteServer` signs with and the kernel pins), and cache the
|
|
8
8
|
* built app per distinct URL;
|
|
9
|
-
* • optional
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* `
|
|
9
|
+
* • optional subrequest routing: a `globalThis.fetch` override (installed ONLY
|
|
10
|
+
* when `selfBinding` and/or `routeSubrequest` is configured) redirects
|
|
11
|
+
* certain outbound fetches through a caller-supplied binding — `selfBinding`
|
|
12
|
+
* for same-host fetches (a Worker can't fetch its own hostname), and the
|
|
13
|
+
* vendor-neutral `routeSubrequest` for any caller policy (e.g. instance
|
|
14
|
+
* hostnames a same-zone Worker→Worker fetch would 522 on). The SDK names no
|
|
15
|
+
* backend or topology; the caller owns the predicate + the fetcher;
|
|
14
16
|
* • optional SPA hook (e.g. `/ui/*` served from an `ASSETS` binding).
|
|
15
17
|
*
|
|
16
18
|
* The worker's OWN JWKS (`<url>/.well-known/jwks.json`) is served as a normal
|
|
@@ -40,10 +42,26 @@ export interface WorkerEntryConfig<TDeps> {
|
|
|
40
42
|
* Resolve the raw serving URL from `env` (+ the per-request origin, for workers
|
|
41
43
|
* that fall back to the request host). Defaults to the `WORKER_URL` env var.
|
|
42
44
|
* The result is always canonicalized before use.
|
|
45
|
+
*
|
|
46
|
+
* The `requestOrigin` honors an `X-Forwarded-Proto: https` upgrade (see
|
|
47
|
+
* `clientOrigin`), so a dev worker behind a TLS-terminating proxy (cloudflared
|
|
48
|
+
* tunnel, reverse proxy) resolves its public `https://` origin, not the raw
|
|
49
|
+
* `http://` one workerd sees.
|
|
43
50
|
*/
|
|
44
51
|
resolveUrl?: (env: TDeps, requestOrigin: string) => string
|
|
45
52
|
/** Optional: the `SELF` service binding used to route same-host subrequests. */
|
|
46
53
|
selfBinding?: (env: TDeps) => Fetcher | null | undefined
|
|
54
|
+
/**
|
|
55
|
+
* Optional, vendor-neutral: route an OUTBOUND subrequest through a
|
|
56
|
+
* caller-supplied fetcher instead of the network — for hosts a Worker can't
|
|
57
|
+
* reach directly over the edge (e.g. a platform router on the SAME zone:
|
|
58
|
+
* Cloudflare 522s a same-zone Worker→Worker public `fetch`). Return a `Fetcher`
|
|
59
|
+
* to route the request through, or null/undefined to fall through to the normal
|
|
60
|
+
* fetch. The SDK names no backend or topology — the CALLER owns BOTH the
|
|
61
|
+
* predicate (which hosts) and the fetcher (the binding). This generalizes
|
|
62
|
+
* `selfBinding` (same-origin → SELF) to any caller policy; both are honored.
|
|
63
|
+
*/
|
|
64
|
+
routeSubrequest?: (url: URL, env: TDeps) => Fetcher | null | undefined
|
|
47
65
|
/**
|
|
48
66
|
* Optional: handle a request before it reaches the kernel app — e.g. serve a
|
|
49
67
|
* SPA under `/ui/*` or a same-origin `/api/*` endpoint the view calls. Return
|
|
@@ -67,34 +85,114 @@ export interface WorkerEntry<TDeps> {
|
|
|
67
85
|
fetch(request: Request, env: TDeps): Response | Promise<Response>
|
|
68
86
|
}
|
|
69
87
|
|
|
88
|
+
/**
|
|
89
|
+
* The request origin as the CLIENT reached it — i.e. the origin a fallback
|
|
90
|
+
* serving URL (and therefore the `iss`) may be derived from. Behind a
|
|
91
|
+
* TLS-terminating proxy (a cloudflared tunnel in front of `wrangler dev`, any
|
|
92
|
+
* reverse proxy) the worker sees plain HTTP, so `request.url` says `http://…`
|
|
93
|
+
* while the public URL is `https://…`; the proxy advertises the original
|
|
94
|
+
* scheme via `X-Forwarded-Proto`. Honoring it is restricted to the http→https
|
|
95
|
+
* UPGRADE of the SAME host (never a downgrade, never a host change), so a
|
|
96
|
+
* spoofed header can at worst derive an `iss` the kernel's JWKS check then
|
|
97
|
+
* fails — it can never make this worker speak for another origin.
|
|
98
|
+
*/
|
|
99
|
+
export function clientOrigin(url: URL, request: Request): string {
|
|
100
|
+
if (url.protocol !== 'http:') return url.origin
|
|
101
|
+
const forwarded = request.headers.get('x-forwarded-proto')
|
|
102
|
+
// Multiple proxies append: "https, http" — the first hop is the client-facing one.
|
|
103
|
+
const scheme = forwarded?.split(',')[0]?.trim().toLowerCase()
|
|
104
|
+
return scheme === 'https' ? `https://${url.host}` : url.origin
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Build a `before` hook that serves a static-asset `binding` (e.g. a Workers
|
|
109
|
+
* Assets binding) mounted under `base` (default `/ui`) — the runtime half of a
|
|
110
|
+
* domain's `client` binding. Returns `undefined` for non-matching paths (and
|
|
111
|
+
* when no binding is present) so the request falls through to domain dispatch.
|
|
112
|
+
*
|
|
113
|
+
* Asset URLs are rooted at `base` (a client bundler sets `base: '<base>/'`);
|
|
114
|
+
* this hook strips that prefix before delegating to the binding, so
|
|
115
|
+
* `<base>/x.js` resolves from the binding's root. When `devProxy` yields a URL
|
|
116
|
+
* (local dev), requests are proxied there instead — the seam for a bundler's
|
|
117
|
+
* HMR dev server. Whether unknown sub-paths fall back to `index.html` is the
|
|
118
|
+
* binding's own concern (e.g. wrangler's `not_found_handling`), not baked in
|
|
119
|
+
* here.
|
|
120
|
+
*
|
|
121
|
+
* Lives here, type-checked and testable, instead of being emitted as a string
|
|
122
|
+
* by every adapter's worker codegen.
|
|
123
|
+
*/
|
|
124
|
+
export function assets<TDeps>(opts: {
|
|
125
|
+
base?: string
|
|
126
|
+
binding: (env: TDeps) => Fetcher | null | undefined
|
|
127
|
+
devProxy?: (env: TDeps) => string | null | undefined
|
|
128
|
+
}): (env: TDeps, url: URL, request: Request) => Response | Promise<Response> | undefined {
|
|
129
|
+
const base = (opts.base ?? '/ui').replace(/\/+$/, '')
|
|
130
|
+
const prefix = `${base}/`
|
|
131
|
+
return (env, url, request) => {
|
|
132
|
+
const binding = opts.binding(env)
|
|
133
|
+
if (!binding) return undefined
|
|
134
|
+
if (url.pathname !== base && !url.pathname.startsWith(prefix)) return undefined
|
|
135
|
+
const devUrl = opts.devProxy?.(env)
|
|
136
|
+
if (devUrl) {
|
|
137
|
+
const devBase = devUrl.replace(/\/+$/, '')
|
|
138
|
+
return fetch(new Request(`${devBase}${url.pathname}${url.search}`, request))
|
|
139
|
+
}
|
|
140
|
+
// `<base>` → `/`, `<base>/x` → `/x`: serve from the binding's root.
|
|
141
|
+
const stripped = url.pathname.slice(base.length) || '/'
|
|
142
|
+
const rewritten = new URL(stripped + url.search, url.origin)
|
|
143
|
+
return binding.fetch(new Request(rewritten, request))
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
70
147
|
export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): WorkerEntry<TDeps> {
|
|
71
|
-
|
|
148
|
+
// Cache the built app per distinct resolved URL — plural and bounded. On the
|
|
149
|
+
// request-origin fallback the URL legitimately alternates for one worker
|
|
150
|
+
// (direct http hits vs https-upgraded tunnel hits, workers.dev + custom
|
|
151
|
+
// domain), and a single slot would tear down and rebuild the whole app on
|
|
152
|
+
// every alternation. The bound caps abuse via attacker-minted Host /
|
|
153
|
+
// X-Forwarded-Proto values on that same fallback path.
|
|
154
|
+
const MAX_CACHED_APPS = 4
|
|
155
|
+
const apps = new Map<string, { origin: string; app: App }>()
|
|
72
156
|
let self: Fetcher | null = null
|
|
157
|
+
let routeEnv: TDeps | null = null
|
|
73
158
|
|
|
74
159
|
function getApp(url: string, env: TDeps): App {
|
|
75
|
-
|
|
160
|
+
const cached = apps.get(url)
|
|
161
|
+
if (cached) return cached.app
|
|
76
162
|
const { app } = createRemoteServer<TDeps>(config.build(url, env))
|
|
77
|
-
|
|
163
|
+
if (apps.size >= MAX_CACHED_APPS) {
|
|
164
|
+
const oldest = apps.keys().next().value
|
|
165
|
+
if (oldest !== undefined) apps.delete(oldest)
|
|
166
|
+
}
|
|
167
|
+
apps.set(url, { origin: new URL(url).origin, app })
|
|
78
168
|
return app
|
|
79
169
|
}
|
|
80
170
|
|
|
81
|
-
// A Worker can't fetch its own hostname
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
// (A self-issued credential's JWKS is resolved in-memory by the
|
|
86
|
-
// fetched — see `auth/verify.ts` — so no self-JWKS
|
|
87
|
-
if (config.selfBinding) {
|
|
171
|
+
// A Worker can't fetch its own hostname (SELF), nor — on Cloudflare — a
|
|
172
|
+
// same-zone hostname routed to another Worker (the `routeSubrequest` case).
|
|
173
|
+
// When either is configured, override `globalThis.fetch` to redirect those
|
|
174
|
+
// subrequests through the caller's fetcher. Workers with neither get no global
|
|
175
|
+
// mutation. (A self-issued credential's JWKS is resolved in-memory by the
|
|
176
|
+
// verifier, not fetched — see `auth/verify.ts` — so no self-JWKS shim is needed.)
|
|
177
|
+
if (config.selfBinding || config.routeSubrequest) {
|
|
88
178
|
const originalFetch = globalThis.fetch
|
|
89
179
|
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
180
|
+
const href = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url
|
|
181
|
+
try {
|
|
182
|
+
const u = new URL(href)
|
|
183
|
+
// same-origin → SELF (a Worker can't fetch its own hostname)
|
|
184
|
+
if (self && apps.size > 0) {
|
|
185
|
+
for (const cached of apps.values()) {
|
|
186
|
+
if (cached.origin === u.origin) return self.fetch(new Request(input, init))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// caller policy → its fetcher (e.g. a platform router on the same zone)
|
|
190
|
+
if (config.routeSubrequest && routeEnv) {
|
|
191
|
+
const via = config.routeSubrequest(u, routeEnv)
|
|
192
|
+
if (via) return via.fetch(new Request(input, init))
|
|
97
193
|
}
|
|
194
|
+
} catch {
|
|
195
|
+
// non-absolute URL — fall through to the original fetch
|
|
98
196
|
}
|
|
99
197
|
return originalFetch(input, init)
|
|
100
198
|
}) as typeof fetch
|
|
@@ -103,6 +201,7 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
|
|
|
103
201
|
return {
|
|
104
202
|
async fetch(request: Request, env: TDeps): Promise<Response> {
|
|
105
203
|
if (config.selfBinding) self ??= config.selfBinding(env) ?? null
|
|
204
|
+
if (config.routeSubrequest) routeEnv = env
|
|
106
205
|
// Only parse the request URL when a hook actually needs it.
|
|
107
206
|
const requestUrl = config.before || config.resolveUrl ? new URL(request.url) : null
|
|
108
207
|
if (config.before && requestUrl) {
|
|
@@ -112,7 +211,7 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
|
|
|
112
211
|
if (handled !== undefined) return handled
|
|
113
212
|
}
|
|
114
213
|
const raw = config.resolveUrl
|
|
115
|
-
? config.resolveUrl(env, requestUrl
|
|
214
|
+
? config.resolveUrl(env, clientOrigin(requestUrl!, request))
|
|
116
215
|
: requireEnv(env, 'WORKER_URL', "the worker's public serving URL (its iss identity)")
|
|
117
216
|
const url = canonicalizeServingUrl(raw)
|
|
118
217
|
const dispatched = config.rewriteRequest ? config.rewriteRequest(env, request) : request
|