@astrale-os/sdk 0.1.6 → 0.1.8
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/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 +84 -0
- package/dist/cli/run.d.ts.map +1 -0
- package/dist/cli/run.js +603 -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 +80 -5
- package/dist/server/worker-entry.d.ts.map +1 -1
- package/dist/server/worker-entry.js +105 -24
- package/dist/server/worker-entry.js.map +1 -1
- package/package.json +12 -3
- 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 +710 -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 +128 -24
|
@@ -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
|
|
@@ -29,6 +31,54 @@ import { canonicalizeServingUrl } from './serving-url'
|
|
|
29
31
|
type Fetcher = { fetch(request: Request): Response | Promise<Response> }
|
|
30
32
|
type App = { fetch(request: Request): Response | Promise<Response> }
|
|
31
33
|
|
|
34
|
+
/** A built app cached by its serving URL: `origin` for same-origin matching,
|
|
35
|
+
* `app` for in-process self-dispatch. */
|
|
36
|
+
type CachedApp = { origin: string; app: App }
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Choose where an OUTBOUND subrequest to `u` is routed — or `null` to let the
|
|
40
|
+
* real `fetch` handle it. Order matters and same-origin wins FIRST: a call to
|
|
41
|
+
* this worker's OWN serving origin is a self-dispatch, not an edge fetch — and
|
|
42
|
+
* the caller's `routeSubrequest` policy may itself match our own host (e.g. an
|
|
43
|
+
* `isInstanceHost` predicate matches every `*.svc.astrale.ai`, ours included),
|
|
44
|
+
* so it must never get first look at a same-origin call.
|
|
45
|
+
*
|
|
46
|
+
* • same-origin + SELF binding present → the SELF fetcher: a fresh same-script
|
|
47
|
+
* invocation. The normal Cloudflare path — a Worker can't fetch its own
|
|
48
|
+
* hostname over the edge, so it re-enters itself through the binding.
|
|
49
|
+
* • same-origin + NO SELF binding → the cached app, dispatched IN-PROCESS. A
|
|
50
|
+
* Workers-for-Platforms dispatch-namespace tenant can't service-bind to
|
|
51
|
+
* itself (its script name is platform-renamed), so it has no SELF binding; an
|
|
52
|
+
* in-process dispatch reaches the same script with no edge hop and no binding.
|
|
53
|
+
* `App` and the SELF `Fetcher` share the `{ fetch(request) }` shape, so the
|
|
54
|
+
* caller drives both identically.
|
|
55
|
+
* • else, the caller's `routeSubrequest` policy matched → its fetcher.
|
|
56
|
+
* • else → `null`: passthrough to the real network fetch.
|
|
57
|
+
*
|
|
58
|
+
* Pure (no closure over instance state) so the routing decision is unit-testable
|
|
59
|
+
* without standing up a real app.
|
|
60
|
+
*/
|
|
61
|
+
export function selectSubrequestTarget<TDeps>(
|
|
62
|
+
u: URL,
|
|
63
|
+
ctx: {
|
|
64
|
+
self: Fetcher | null
|
|
65
|
+
apps: Iterable<CachedApp>
|
|
66
|
+
routeEnv: TDeps | null
|
|
67
|
+
routeSubrequest?: (url: URL, env: TDeps) => Fetcher | null | undefined
|
|
68
|
+
},
|
|
69
|
+
): { fetch(request: Request): Response | Promise<Response> } | null {
|
|
70
|
+
// Same-origin self-subrequest → the same script (SELF binding, else in-process).
|
|
71
|
+
for (const cached of ctx.apps) {
|
|
72
|
+
if (cached.origin === u.origin) return ctx.self ?? cached.app
|
|
73
|
+
}
|
|
74
|
+
// Caller policy (e.g. a platform router on the same zone) → its fetcher.
|
|
75
|
+
if (ctx.routeSubrequest && ctx.routeEnv) {
|
|
76
|
+
const via = ctx.routeSubrequest(u, ctx.routeEnv)
|
|
77
|
+
if (via) return via
|
|
78
|
+
}
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
|
|
32
82
|
export interface WorkerEntryConfig<TDeps> {
|
|
33
83
|
/**
|
|
34
84
|
* Build the `createRemoteServer` config for the resolved serving `url`. Called
|
|
@@ -49,6 +99,17 @@ export interface WorkerEntryConfig<TDeps> {
|
|
|
49
99
|
resolveUrl?: (env: TDeps, requestOrigin: string) => string
|
|
50
100
|
/** Optional: the `SELF` service binding used to route same-host subrequests. */
|
|
51
101
|
selfBinding?: (env: TDeps) => Fetcher | null | undefined
|
|
102
|
+
/**
|
|
103
|
+
* Optional, vendor-neutral: route an OUTBOUND subrequest through a
|
|
104
|
+
* caller-supplied fetcher instead of the network — for hosts a Worker can't
|
|
105
|
+
* reach directly over the edge (e.g. a platform router on the SAME zone:
|
|
106
|
+
* Cloudflare 522s a same-zone Worker→Worker public `fetch`). Return a `Fetcher`
|
|
107
|
+
* to route the request through, or null/undefined to fall through to the normal
|
|
108
|
+
* fetch. The SDK names no backend or topology — the CALLER owns BOTH the
|
|
109
|
+
* predicate (which hosts) and the fetcher (the binding). This generalizes
|
|
110
|
+
* `selfBinding` (same-origin → SELF) to any caller policy; both are honored.
|
|
111
|
+
*/
|
|
112
|
+
routeSubrequest?: (url: URL, env: TDeps) => Fetcher | null | undefined
|
|
52
113
|
/**
|
|
53
114
|
* Optional: handle a request before it reaches the kernel app — e.g. serve a
|
|
54
115
|
* SPA under `/ui/*` or a same-origin `/api/*` endpoint the view calls. Return
|
|
@@ -91,6 +152,46 @@ export function clientOrigin(url: URL, request: Request): string {
|
|
|
91
152
|
return scheme === 'https' ? `https://${url.host}` : url.origin
|
|
92
153
|
}
|
|
93
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Build a `before` hook that serves a static-asset `binding` (e.g. a Workers
|
|
157
|
+
* Assets binding) mounted under `base` (default `/ui`) — the runtime half of a
|
|
158
|
+
* domain's `client` binding. Returns `undefined` for non-matching paths (and
|
|
159
|
+
* when no binding is present) so the request falls through to domain dispatch.
|
|
160
|
+
*
|
|
161
|
+
* Asset URLs are rooted at `base` (a client bundler sets `base: '<base>/'`);
|
|
162
|
+
* this hook strips that prefix before delegating to the binding, so
|
|
163
|
+
* `<base>/x.js` resolves from the binding's root. When `devProxy` yields a URL
|
|
164
|
+
* (local dev), requests are proxied there instead — the seam for a bundler's
|
|
165
|
+
* HMR dev server. Whether unknown sub-paths fall back to `index.html` is the
|
|
166
|
+
* binding's own concern (e.g. wrangler's `not_found_handling`), not baked in
|
|
167
|
+
* here.
|
|
168
|
+
*
|
|
169
|
+
* Lives here, type-checked and testable, instead of being emitted as a string
|
|
170
|
+
* by every adapter's worker codegen.
|
|
171
|
+
*/
|
|
172
|
+
export function assets<TDeps>(opts: {
|
|
173
|
+
base?: string
|
|
174
|
+
binding: (env: TDeps) => Fetcher | null | undefined
|
|
175
|
+
devProxy?: (env: TDeps) => string | null | undefined
|
|
176
|
+
}): (env: TDeps, url: URL, request: Request) => Response | Promise<Response> | undefined {
|
|
177
|
+
const base = (opts.base ?? '/ui').replace(/\/+$/, '')
|
|
178
|
+
const prefix = `${base}/`
|
|
179
|
+
return (env, url, request) => {
|
|
180
|
+
const binding = opts.binding(env)
|
|
181
|
+
if (!binding) return undefined
|
|
182
|
+
if (url.pathname !== base && !url.pathname.startsWith(prefix)) return undefined
|
|
183
|
+
const devUrl = opts.devProxy?.(env)
|
|
184
|
+
if (devUrl) {
|
|
185
|
+
const devBase = devUrl.replace(/\/+$/, '')
|
|
186
|
+
return fetch(new Request(`${devBase}${url.pathname}${url.search}`, request))
|
|
187
|
+
}
|
|
188
|
+
// `<base>` → `/`, `<base>/x` → `/x`: serve from the binding's root.
|
|
189
|
+
const stripped = url.pathname.slice(base.length) || '/'
|
|
190
|
+
const rewritten = new URL(stripped + url.search, url.origin)
|
|
191
|
+
return binding.fetch(new Request(rewritten, request))
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
94
195
|
export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): WorkerEntry<TDeps> {
|
|
95
196
|
// Cache the built app per distinct resolved URL — plural and bounded. On the
|
|
96
197
|
// request-origin fallback the URL legitimately alternates for one worker
|
|
@@ -99,8 +200,9 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
|
|
|
99
200
|
// every alternation. The bound caps abuse via attacker-minted Host /
|
|
100
201
|
// X-Forwarded-Proto values on that same fallback path.
|
|
101
202
|
const MAX_CACHED_APPS = 4
|
|
102
|
-
const apps = new Map<string,
|
|
203
|
+
const apps = new Map<string, CachedApp>()
|
|
103
204
|
let self: Fetcher | null = null
|
|
205
|
+
let routeEnv: TDeps | null = null
|
|
104
206
|
|
|
105
207
|
function getApp(url: string, env: TDeps): App {
|
|
106
208
|
const cached = apps.get(url)
|
|
@@ -114,26 +216,27 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
|
|
|
114
216
|
return app
|
|
115
217
|
}
|
|
116
218
|
|
|
117
|
-
// A Worker can't fetch its own hostname
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
|
|
219
|
+
// A Worker can't fetch its own hostname over the edge, nor — on Cloudflare — a
|
|
220
|
+
// same-zone hostname routed to another Worker (the `routeSubrequest` case).
|
|
221
|
+
// When either is configured, override `globalThis.fetch` to redirect those
|
|
222
|
+
// subrequests through the right target (see `selectSubrequestTarget`). Workers
|
|
223
|
+
// with neither selfBinding nor routeSubrequest get no global mutation. (A
|
|
224
|
+
// self-issued credential's JWKS is resolved in-memory by the verifier, not
|
|
225
|
+
// fetched — see `auth/verify.ts` — so no self-JWKS shim is needed.)
|
|
226
|
+
if (config.selfBinding || config.routeSubrequest) {
|
|
124
227
|
const originalFetch = globalThis.fetch
|
|
125
228
|
globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
229
|
+
const href = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url
|
|
230
|
+
try {
|
|
231
|
+
const target = selectSubrequestTarget(new URL(href), {
|
|
232
|
+
self,
|
|
233
|
+
apps: apps.values(),
|
|
234
|
+
routeEnv,
|
|
235
|
+
routeSubrequest: config.routeSubrequest,
|
|
236
|
+
})
|
|
237
|
+
if (target) return target.fetch(new Request(input, init))
|
|
238
|
+
} catch {
|
|
239
|
+
// non-absolute URL — fall through to the original fetch
|
|
137
240
|
}
|
|
138
241
|
return originalFetch(input, init)
|
|
139
242
|
}) as typeof fetch
|
|
@@ -142,6 +245,7 @@ export function createWorkerEntry<TDeps>(config: WorkerEntryConfig<TDeps>): Work
|
|
|
142
245
|
return {
|
|
143
246
|
async fetch(request: Request, env: TDeps): Promise<Response> {
|
|
144
247
|
if (config.selfBinding) self ??= config.selfBinding(env) ?? null
|
|
248
|
+
if (config.routeSubrequest) routeEnv = env
|
|
145
249
|
// Only parse the request URL when a hook actually needs it.
|
|
146
250
|
const requestUrl = config.before || config.resolveUrl ? new URL(request.url) : null
|
|
147
251
|
if (config.before && requestUrl) {
|