@astrale-os/adapter-cloudflare 0.1.7 → 0.1.9
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/assets-pack.d.ts +1 -1
- package/dist/assets-pack.js +1 -1
- package/dist/build.d.ts +15 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +15 -0
- package/dist/build.js.map +1 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +10 -1
- package/dist/client.js.map +1 -1
- package/dist/cloudflare.d.ts +15 -3
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +73 -21
- package/dist/cloudflare.js.map +1 -1
- package/dist/codegen/worker.d.ts +26 -6
- package/dist/codegen/worker.d.ts.map +1 -1
- package/dist/codegen/worker.js +70 -54
- package/dist/codegen/worker.js.map +1 -1
- package/dist/codegen/wrangler.d.ts +11 -2
- package/dist/codegen/wrangler.d.ts.map +1 -1
- package/dist/codegen/wrangler.js +11 -5
- package/dist/codegen/wrangler.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/params.d.ts +30 -30
- package/dist/params.d.ts.map +1 -1
- package/dist/parse-output.d.ts +1 -1
- package/dist/parse-output.js +1 -1
- package/package.json +6 -2
- package/src/assets-pack.ts +1 -1
- package/src/build.ts +15 -0
- package/src/client.ts +11 -1
- package/src/cloudflare.ts +77 -23
- package/src/codegen/worker.ts +79 -59
- package/src/codegen/wrangler.ts +15 -5
- package/src/index.ts +6 -3
- package/src/params.ts +32 -31
- package/src/parse-output.ts +1 -1
- package/template/.agents/skills/astrale-cli/SKILL.md +25 -11
- package/template/.agents/skills/astrale-domain/SKILL.md +60 -32
- package/template/.env.example +8 -0
- package/template/README.md +26 -10
- package/template/astrale.config.ts +22 -29
- package/template/client/README.md +2 -2
- package/template/client/src/styles.css +4 -1
- package/template/client/tsconfig.json +1 -1
- package/template/client/vite.config.ts +3 -3
- package/template/client/vitest.config.ts +1 -1
- package/template/core/keys.ts +28 -0
- package/template/{methods → core}/note.ts +42 -25
- package/template/deps.ts +25 -0
- package/template/domain.ts +33 -0
- package/template/env.ts +11 -0
- package/template/integrations/summary/heuristic.ts +25 -0
- package/template/integrations/summary/http.ts +69 -0
- package/template/integrations/summary/port.ts +21 -0
- package/template/integrations/summary/registry.ts +52 -0
- package/template/package.json +2 -3
- package/template/runtime/index.ts +62 -0
- package/template/schema/index.ts +2 -0
- package/template/schema/note.ts +5 -2
- package/template/tsconfig.json +13 -2
- package/template/views/note.ts +1 -1
- package/dist/astrale.d.ts +0 -27
- package/dist/astrale.d.ts.map +0 -1
- package/dist/astrale.js +0 -212
- package/dist/astrale.js.map +0 -1
- package/src/astrale.ts +0 -244
- package/template/methods/index.ts +0 -66
- package/template/schema/compiled.ts +0 -14
package/dist/codegen/wrangler.js
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
* `wrangler.jsonc` that used to be hand-copied into every domain — now an
|
|
4
4
|
* internal adapter detail the dev never sees.
|
|
5
5
|
*
|
|
6
|
-
* `main` is `./worker.gen.ts` (same dir). `assets.directory`
|
|
7
|
-
*
|
|
6
|
+
* `main` is `./worker.gen.ts` (same dir). `assets.directory` points at the
|
|
7
|
+
* project's Vite build (`CLIENT_DIST_DIR`). A `SELF` service binding enables autobinding
|
|
8
8
|
* (a handler calling back into its own domain). Custom-domain deploys attach a
|
|
9
9
|
* `custom_domain` route; otherwise the Worker ships to `*.workers.dev`.
|
|
10
10
|
*
|
|
11
11
|
* A dev's `params.wrangler` escape hatch is deep-merged on top of this base
|
|
12
12
|
* (extra bindings: KV, R2, D1, queues, …) — see `mergeUserConfig`.
|
|
13
13
|
*/
|
|
14
|
+
import { CLIENT_DIST_DIR } from '../client';
|
|
14
15
|
import { deepMergeConfig } from './merge';
|
|
15
16
|
const COMPATIBILITY_DATE = '2025-03-01';
|
|
16
17
|
export function generateWranglerConfig(opts) {
|
|
@@ -27,15 +28,20 @@ export function generateWranglerConfig(opts) {
|
|
|
27
28
|
}
|
|
28
29
|
if (opts.hasClient) {
|
|
29
30
|
config.assets = {
|
|
30
|
-
directory:
|
|
31
|
+
directory: `../${CLIENT_DIST_DIR}`,
|
|
31
32
|
binding: 'ASSETS',
|
|
32
33
|
not_found_handling: 'single-page-application',
|
|
33
34
|
run_worker_first: true,
|
|
34
35
|
};
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
const services = [];
|
|
38
|
+
if (opts.selfBinding)
|
|
39
|
+
services.push({ binding: 'SELF', service: opts.workerName });
|
|
40
|
+
if (opts.router) {
|
|
41
|
+
services.push({ binding: opts.router.binding, service: opts.router.service });
|
|
38
42
|
}
|
|
43
|
+
if (services.length > 0)
|
|
44
|
+
config.services = services;
|
|
39
45
|
if (opts.vars && Object.keys(opts.vars).length > 0) {
|
|
40
46
|
config.vars = opts.vars;
|
|
41
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrangler.js","sourceRoot":"","sources":["../../src/codegen/wrangler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"wrangler.js","sourceRoot":"","sources":["../../src/codegen/wrangler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AA4BzC,MAAM,kBAAkB,GAAG,YAAY,CAAA;AAEvC,MAAM,UAAU,sBAAsB,CAAC,IAA4B;IACjE,MAAM,MAAM,GAA4B;QACtC,IAAI,EAAE,IAAI,CAAC,UAAU;QACrB,IAAI,EAAE,iBAAiB;QACvB,kBAAkB,EAAE,kBAAkB;QACtC,mBAAmB,EAAE,CAAC,eAAe,EAAE,oCAAoC,CAAC;QAC5E,WAAW,EAAE,IAAI;QACjB,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;KACjC,CAAA;IAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAChE,CAAC;IAED,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,CAAC,MAAM,GAAG;YACd,SAAS,EAAE,MAAM,eAAe,EAAE;YAClC,OAAO,EAAE,QAAQ;YACjB,kBAAkB,EAAE,yBAAyB;YAC7C,gBAAgB,EAAE,IAAI;SACvB,CAAA;IACH,CAAC;IAED,MAAM,QAAQ,GAAgD,EAAE,CAAA;IAChE,IAAI,IAAI,CAAC,WAAW;QAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAA;IAClF,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;IAC/E,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAEnD,IAAI,IAAI,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;IACzB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;IAC9E,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAA;AAC/C,CAAC;AAED,qEAAqE;AACrE,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;AAE/D;;;;GAIG;AACH,SAAS,eAAe,CACtB,IAA6B,EAC7B,IAA6B;IAE7B,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAA;IAC9D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,qDAAqD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC5E,gEAAgE;YAChE,mDAAmD,CACtD,CAAA;IACH,CAAC;IACD,OAAO,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AACpC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@astrale-os/adapter-cloudflare` — deploy an Astrale domain as a standalone
|
|
3
|
-
* Cloudflare Worker.
|
|
3
|
+
* Cloudflare Worker on YOUR OWN Cloudflare account.
|
|
4
4
|
*
|
|
5
5
|
* import { cloudflare } from '@astrale-os/adapter-cloudflare'
|
|
6
6
|
*
|
|
@@ -11,8 +11,11 @@
|
|
|
11
11
|
*
|
|
12
12
|
* `dev` runs `wrangler dev` locally; an env with no `route` ships to
|
|
13
13
|
* `*.workers.dev`; an env with a `route` ships to that custom domain.
|
|
14
|
+
*
|
|
15
|
+
* Prefer a managed deploy (no Cloudflare account)? Use
|
|
16
|
+
* `@astrale-os/adapter-astrale`, which builds the same bundle via this package's
|
|
17
|
+
* `/build` toolkit and ships it through the Astrale platform.
|
|
14
18
|
*/
|
|
15
|
-
export { astrale } from './astrale';
|
|
16
19
|
export { cloudflare } from './cloudflare';
|
|
17
|
-
export type {
|
|
20
|
+
export type { CloudflareParams } from './params';
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@astrale-os/adapter-cloudflare` — deploy an Astrale domain as a standalone
|
|
3
|
-
* Cloudflare Worker.
|
|
3
|
+
* Cloudflare Worker on YOUR OWN Cloudflare account.
|
|
4
4
|
*
|
|
5
5
|
* import { cloudflare } from '@astrale-os/adapter-cloudflare'
|
|
6
6
|
*
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
*
|
|
12
12
|
* `dev` runs `wrangler dev` locally; an env with no `route` ships to
|
|
13
13
|
* `*.workers.dev`; an env with a `route` ships to that custom domain.
|
|
14
|
+
*
|
|
15
|
+
* Prefer a managed deploy (no Cloudflare account)? Use
|
|
16
|
+
* `@astrale-os/adapter-astrale`, which builds the same bundle via this package's
|
|
17
|
+
* `/build` toolkit and ships it through the Astrale platform.
|
|
14
18
|
*/
|
|
15
|
-
export { astrale } from './astrale';
|
|
16
19
|
export { cloudflare } from './cloudflare';
|
|
17
20
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA"}
|
package/dist/params.d.ts
CHANGED
|
@@ -55,37 +55,37 @@ export interface CloudflareParams {
|
|
|
55
55
|
* worker-entry codegen and aren't supported through this field yet.
|
|
56
56
|
*/
|
|
57
57
|
wrangler?: Record<string, unknown>;
|
|
58
|
-
}
|
|
59
|
-
/** Params for the Astrale-managed adapter (`astrale(envs)`). */
|
|
60
|
-
export interface AstraleParams {
|
|
61
58
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* off-localhost dev needs: wrangler binds 0.0.0.0 (reachable from outside)
|
|
83
|
-
* and `WORKER_URL` is pinned to this URL so the worker's per-request-Host
|
|
84
|
-
* identity (`iss`) doesn't drift to the proxy's internal hostname.
|
|
85
|
-
* Usually injected by `pnpm dev --host <url>` rather than written here.
|
|
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
|
|
86
79
|
*/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
router?: false | {
|
|
81
|
+
/** Router Worker the binding targets. Default `'admin-router'`. */
|
|
82
|
+
service?: string;
|
|
83
|
+
/** Service-binding name the worker reads. Default `'ROUTER'`. */
|
|
84
|
+
binding?: string;
|
|
85
|
+
/** Host suffix that marks an instance host. Default `'.astrale.ai'`. */
|
|
86
|
+
hostSuffix?: string;
|
|
87
|
+
/** Min dot-separated label count for an instance host. Default `4` (`<slug>.<region>.astrale.ai`). */
|
|
88
|
+
minLabels?: number;
|
|
89
|
+
};
|
|
90
90
|
}
|
|
91
91
|
//# sourceMappingURL=params.d.ts.map
|
package/dist/params.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,MAAM,CAAC,EACH,KAAK,GACL;QACE,mEAAmE;QACnE,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,iEAAiE;QACjE,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,wEAAwE;QACxE,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,sGAAsG;QACtG,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACN"}
|
package/dist/parse-output.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* The fallback (when no explicit "Ready on" line) only accepts a localhost URL
|
|
11
11
|
* on the EXPECTED port — wrangler may print unrelated localhost URLs (e.g. an
|
|
12
12
|
* inspector/devtools endpoint on a different port) before readiness, and taking
|
|
13
|
-
* one of those would point `astrale
|
|
13
|
+
* one of those would point `astrale domain install <url>` at a dead endpoint.
|
|
14
14
|
*/
|
|
15
15
|
export declare function parseDevReadyUrl(text: string, port: number): string | undefined;
|
|
16
16
|
/**
|
package/dist/parse-output.js
CHANGED
|
@@ -12,7 +12,7 @@ const WORKERS_DEV_RE = /(https:\/\/[^\s]+\.workers\.dev)/i;
|
|
|
12
12
|
* The fallback (when no explicit "Ready on" line) only accepts a localhost URL
|
|
13
13
|
* on the EXPECTED port — wrangler may print unrelated localhost URLs (e.g. an
|
|
14
14
|
* inspector/devtools endpoint on a different port) before readiness, and taking
|
|
15
|
-
* one of those would point `astrale
|
|
15
|
+
* one of those would point `astrale domain install <url>` at a dead endpoint.
|
|
16
16
|
*/
|
|
17
17
|
export function parseDevReadyUrl(text, port) {
|
|
18
18
|
const ready = READY_RE.exec(text);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/adapter-cloudflare",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Deploy an Astrale domain as a standalone Cloudflare Worker",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"adapter",
|
|
@@ -24,11 +24,15 @@
|
|
|
24
24
|
"types": "./dist/index.d.ts",
|
|
25
25
|
"import": "./dist/index.js"
|
|
26
26
|
},
|
|
27
|
+
"./build": {
|
|
28
|
+
"types": "./dist/build.d.ts",
|
|
29
|
+
"import": "./dist/build.js"
|
|
30
|
+
},
|
|
27
31
|
"./package.json": "./package.json"
|
|
28
32
|
},
|
|
29
33
|
"dependencies": {
|
|
30
34
|
"jose": "^6.1.3",
|
|
31
|
-
"@astrale-os/
|
|
35
|
+
"@astrale-os/sdk": "^0.1.7"
|
|
32
36
|
},
|
|
33
37
|
"devDependencies": {
|
|
34
38
|
"@astrale-os/ox": ">=0.1.0 <1.0.0",
|
package/src/assets-pack.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pack a built client SPA (`
|
|
2
|
+
* Pack a built client SPA (the `CLIENT_DIST_DIR` build) into the host box's asset-archive
|
|
3
3
|
* format: `gzip(JSON [{ path, contentBase64 }])` — the exact shape
|
|
4
4
|
* `downloadAssets` on the box consumes (kernel/host workerd service layout).
|
|
5
5
|
*/
|
package/src/build.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@astrale-os/adapter-cloudflare/build` — the shared Cloudflare-Workers build
|
|
3
|
+
* toolkit, exposed for sibling deploy adapters (e.g.
|
|
4
|
+
* `@astrale-os/adapter-astrale`).
|
|
5
|
+
*
|
|
6
|
+
* This is the "compile a `defineDomain` into a Cloudflare Workers bundle + run
|
|
7
|
+
* local dev" surface that every Cloudflare-Workers deploy strategy shares,
|
|
8
|
+
* regardless of WHERE the bundle ultimately ships. It is NOT part of the public
|
|
9
|
+
* adapter API (`@astrale-os/adapter-cloudflare`) — it's the seam two adapters
|
|
10
|
+
* build on so the codegen/bundling/dev logic lives in exactly one place.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export { logTo, prepare } from './cloudflare'
|
|
14
|
+
export { buildClient, CLIENT_DIST_DIR } from './client'
|
|
15
|
+
export { runWranglerBundle, runWranglerDev } from './wrangler-cli'
|
package/src/client.ts
CHANGED
|
@@ -8,6 +8,16 @@ import { spawn } from 'node:child_process'
|
|
|
8
8
|
import { existsSync } from 'node:fs'
|
|
9
9
|
import { join } from 'node:path'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Where the adapter expects the built client SPA to land, relative to the
|
|
13
|
+
* project root. This is a contract: the domain's Vite config MUST emit here
|
|
14
|
+
* (`build.outDir`), and the wrangler `assets.directory` binding + the managed
|
|
15
|
+
* deploy's asset reader both look here. Dot-prefixed so the build output sorts
|
|
16
|
+
* out of the way with the other generated dirs (`.astrale`) instead of
|
|
17
|
+
* polluting the middle of the project listing.
|
|
18
|
+
*/
|
|
19
|
+
export const CLIENT_DIST_DIR = '.dist'
|
|
20
|
+
|
|
11
21
|
export async function buildClient(
|
|
12
22
|
clientDir: string,
|
|
13
23
|
projectDir: string,
|
|
@@ -19,7 +29,7 @@ export async function buildClient(
|
|
|
19
29
|
].find((p) => existsSync(p))
|
|
20
30
|
if (!viteBin) {
|
|
21
31
|
// The project has a client/ but its deps aren't installed — wrangler would
|
|
22
|
-
// otherwise fail cryptically on the missing
|
|
32
|
+
// otherwise fail cryptically on the missing built-assets dir. Fail
|
|
23
33
|
// loud with the fix (the client is a workspace package — one install covers it).
|
|
24
34
|
throw new Error(
|
|
25
35
|
`client build: vite not found in ${clientDir}. Run \`pnpm install\` at the project root ` +
|
package/src/cloudflare.ts
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* config (the dev never sees a `wrangler.jsonc`), builds the optional client
|
|
6
6
|
* SPA, and shells out to `wrangler`. `watch` → `wrangler dev` (local URL);
|
|
7
7
|
* `deploy` → `wrangler deploy` (workers.dev or a custom-domain URL); secrets →
|
|
8
|
-
* `wrangler secret bulk`. Both `watch` and `deploy` return the URL the
|
|
9
|
-
* prints and `astrale
|
|
8
|
+
* `wrangler secret bulk`. Both `watch` and `deploy` return the URL the CLI
|
|
9
|
+
* prints and `astrale domain install <url>` consumes.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { DeployCtx, DomainAdapter, WatchCtx } from '@astrale-os/
|
|
12
|
+
import type { DeployCtx, DomainAdapter, WatchCtx } from '@astrale-os/sdk'
|
|
13
13
|
|
|
14
|
-
import { defineAdapter
|
|
15
|
-
import { mkdir, writeFile } from 'node:fs/promises'
|
|
14
|
+
import { defineAdapter } from '@astrale-os/sdk'
|
|
15
|
+
import { mkdir, rename, writeFile } from 'node:fs/promises'
|
|
16
16
|
import { join } from 'node:path'
|
|
17
17
|
|
|
18
18
|
import type { CloudflareParams } from './params'
|
|
@@ -50,6 +50,15 @@ export function cloudflare(
|
|
|
50
50
|
return { url: params.host ?? handle.url, stop: handle.stop }
|
|
51
51
|
},
|
|
52
52
|
|
|
53
|
+
// Config hot-regen: rewrite the generated files only — the running
|
|
54
|
+
// `wrangler dev` watches its `--config` (and the worker entry) and reloads
|
|
55
|
+
// them itself, so a `astrale.config.ts` edit lands without a restart.
|
|
56
|
+
// Mirrors `watch`'s prep exactly (codegen + client build), minus the spawn.
|
|
57
|
+
async regenerate(params, ctx) {
|
|
58
|
+
await prepare(params, ctx, 'dev')
|
|
59
|
+
if (ctx.clientDir) await buildClient(ctx.clientDir, ctx.projectDir, logTo())
|
|
60
|
+
},
|
|
61
|
+
|
|
53
62
|
async deploy(params, ctx) {
|
|
54
63
|
const {
|
|
55
64
|
configPath,
|
|
@@ -74,7 +83,7 @@ export function cloudflare(
|
|
|
74
83
|
})
|
|
75
84
|
}
|
|
76
85
|
// A first deploy on a fresh `*.workers.dev` host can take ~30-60s to
|
|
77
|
-
// propagate; an `astrale
|
|
86
|
+
// propagate; an `astrale domain install <url>` issued right away would
|
|
78
87
|
// 404 (Cloudflare 1042). Block until the URL actually serves so the
|
|
79
88
|
// install line we print is immediately actionable.
|
|
80
89
|
await waitUntilLive(url, logTo())
|
|
@@ -99,23 +108,35 @@ export async function prepare(
|
|
|
99
108
|
await ensureIdentity(astraleDir, ctx.domain.origin)
|
|
100
109
|
|
|
101
110
|
const name = workerName(params, ctx.domain.origin)
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
// Instance-router service binding (on by default → admin-router): lets the
|
|
112
|
+
// worker reach other instances' hosts internally, so a cross-instance JWKS
|
|
113
|
+
// fetch doesn't 522. Resolved once, fed to BOTH codegens below.
|
|
114
|
+
const router = resolveRouter(params.router)
|
|
115
|
+
// Two distinct client signals: the WORKER's `/ui` hook is present whenever the
|
|
116
|
+
// domain declares a client (`ctx.domain.hasClient`) — including managed
|
|
117
|
+
// deploys, which ship the built assets separately and pass no `clientDir`; the
|
|
118
|
+
// wrangler `assets.directory` binding is wired only when there's a local dir to
|
|
119
|
+
// point at (`ctx.clientDir` — direct Cloudflare deploys + local dev).
|
|
120
|
+
const servesClient = ctx.domain.hasClient
|
|
121
|
+
const bundleAssets = Boolean(ctx.clientDir)
|
|
122
|
+
|
|
123
|
+
await writeFileAtomic(
|
|
111
124
|
join(astraleDir, 'worker.gen.ts'),
|
|
125
|
+
// Everything else — schema/methods/deps/views/functions/requires/postInstall —
|
|
126
|
+
// rides the `...domain` spread the generated entry imports from `../domain`;
|
|
127
|
+
// the only remaining build-time signal is whether to wire the /ui asset hook.
|
|
112
128
|
generateWorkerEntry({
|
|
113
129
|
origin: ctx.domain.origin,
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
130
|
+
hasClient: servesClient,
|
|
131
|
+
...(router
|
|
132
|
+
? {
|
|
133
|
+
router: {
|
|
134
|
+
binding: router.binding,
|
|
135
|
+
hostSuffix: router.hostSuffix,
|
|
136
|
+
minLabels: router.minLabels,
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
: {}),
|
|
119
140
|
}),
|
|
120
141
|
)
|
|
121
142
|
|
|
@@ -145,8 +166,12 @@ export async function prepare(
|
|
|
145
166
|
const baseConfig = {
|
|
146
167
|
workerName: name,
|
|
147
168
|
...(mode === 'deploy' && params.route ? { route: params.route } : {}),
|
|
148
|
-
hasClient,
|
|
169
|
+
hasClient: bundleAssets,
|
|
149
170
|
vars,
|
|
171
|
+
// Router service binding flows into BOTH the SELF and SELF-less fallback
|
|
172
|
+
// configs — the first-deploy two-pass must keep it. `admin-router` already
|
|
173
|
+
// exists, so unlike SELF it always resolves (no first-deploy chicken-egg).
|
|
174
|
+
...(router ? { router: { binding: router.binding, service: router.service } } : {}),
|
|
150
175
|
// Escape hatch: extra bindings (KV/R2/D1/queues/…) deep-merged on top. Same
|
|
151
176
|
// overlay in dev and deploy — it's infra, not env-specific plumbing — and it
|
|
152
177
|
// flows into both the SELF and the SELF-less fallback config below.
|
|
@@ -158,12 +183,12 @@ export async function prepare(
|
|
|
158
183
|
// worker can't (the script doesn't exist yet), so we also emit a SELF-less
|
|
159
184
|
// fallback config that `runWranglerDeploy` uses for a one-time two-pass deploy.
|
|
160
185
|
const configPath = join(astraleDir, 'wrangler.gen.jsonc')
|
|
161
|
-
await
|
|
186
|
+
await writeFileAtomic(configPath, generateWranglerConfig({ ...baseConfig, selfBinding: true }))
|
|
162
187
|
|
|
163
188
|
let fallbackConfigPath: string | undefined
|
|
164
189
|
if (mode === 'deploy') {
|
|
165
190
|
fallbackConfigPath = join(astraleDir, 'wrangler.no-self.gen.jsonc')
|
|
166
|
-
await
|
|
191
|
+
await writeFileAtomic(
|
|
167
192
|
fallbackConfigPath,
|
|
168
193
|
generateWranglerConfig({ ...baseConfig, selfBinding: false }),
|
|
169
194
|
)
|
|
@@ -177,6 +202,16 @@ export async function prepare(
|
|
|
177
202
|
}
|
|
178
203
|
}
|
|
179
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Write-then-rename: hot-regen rewrites these files while a live `wrangler dev`
|
|
207
|
+
* watches them — a plain `writeFile` can be read truncated mid-write.
|
|
208
|
+
*/
|
|
209
|
+
async function writeFileAtomic(path: string, content: string): Promise<void> {
|
|
210
|
+
const tmp = `${path}.tmp`
|
|
211
|
+
await writeFile(tmp, content)
|
|
212
|
+
await rename(tmp, path)
|
|
213
|
+
}
|
|
214
|
+
|
|
180
215
|
/**
|
|
181
216
|
* Poll the deployed URL until the worker answers (anything but the Cloudflare
|
|
182
217
|
* 1042 "no script on this host" 404), or give up after `timeoutMs`. Resolves
|
|
@@ -235,6 +270,25 @@ export function workerName(params: CloudflareParams, origin: string): string {
|
|
|
235
270
|
.replace(/^-+|-+$/g, '')
|
|
236
271
|
}
|
|
237
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Resolve the instance-router config. ON by default (targets `admin-router`) so
|
|
275
|
+
* every adapter-deployed worker can reach other instances' hosts internally and
|
|
276
|
+
* verify cross-instance credentials without a 522; `false` disables it. The
|
|
277
|
+
* returned shape carries every value the worker- and wrangler-codegen need.
|
|
278
|
+
*/
|
|
279
|
+
export function resolveRouter(
|
|
280
|
+
ir: CloudflareParams['router'],
|
|
281
|
+
): { binding: string; service: string; hostSuffix: string; minLabels: number } | undefined {
|
|
282
|
+
if (ir === false) return undefined
|
|
283
|
+
const cfg = ir ?? {}
|
|
284
|
+
return {
|
|
285
|
+
binding: cfg.binding ?? 'ROUTER',
|
|
286
|
+
service: cfg.service ?? 'admin-router',
|
|
287
|
+
hostSuffix: cfg.hostSuffix ?? '.astrale.ai',
|
|
288
|
+
minLabels: cfg.minLabels ?? 4,
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
238
292
|
export function logTo(): (line: string) => void {
|
|
239
293
|
return (line: string) => process.stderr.write(`\x1b[2m ${line}\x1b[0m\n`)
|
|
240
294
|
}
|
package/src/codegen/worker.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Codegen for the Cloudflare Worker entry (`.astrale/worker.gen.ts`).
|
|
3
3
|
*
|
|
4
|
-
* The dev
|
|
5
|
-
* `export
|
|
4
|
+
* The dev wires the domain ONCE in a worker-safe `domain.ts`
|
|
5
|
+
* (`export const domain = defineDomain({ schema, methods, deps, views,
|
|
6
|
+
* functions, client })`) and binds it to an adapter in `astrale.config.ts`
|
|
7
|
+
* (`deploy(domain, cloudflare({ … }))`). This generates the
|
|
8
|
+
* `export default { fetch }` plumbing the adapter owns by importing that single
|
|
9
|
+
* `domain` and SPREADING it into `domainWorkerEntry` — so the author's folder
|
|
10
|
+
* layout is irrelevant (any internal organization works as long as `domain.ts`
|
|
11
|
+
* re-exports the wired definition) and the node-only deploy adapter, which lives
|
|
12
|
+
* in `astrale.config.ts`, never enters this worker bundle.
|
|
13
|
+
*
|
|
14
|
+
* Everything the entry needs — schema/methods/deps/views/functions/requires/
|
|
15
|
+
* postInstall — rides on the spread; the only build-time signal left is whether
|
|
16
|
+
* the domain serves a client SPA (`hasClient`), which gates the `/ui` asset hook
|
|
17
|
+
* and its import. Key properties:
|
|
6
18
|
*
|
|
7
19
|
* • Per-request `url` = the live serving origin (`scheme://host`). It is passed
|
|
8
20
|
* only to `createRemoteServer({ url })`; the SDK stamps every
|
|
@@ -22,84 +34,92 @@
|
|
|
22
34
|
|
|
23
35
|
export interface WorkerCodegenOptions {
|
|
24
36
|
origin: string
|
|
25
|
-
postInstall?: string
|
|
26
|
-
requires: readonly string[]
|
|
27
|
-
hasViews: boolean
|
|
28
|
-
hasFunctions: boolean
|
|
29
37
|
hasClient: boolean
|
|
38
|
+
/**
|
|
39
|
+
* When set, the worker routes subrequests to platform INSTANCE hostnames
|
|
40
|
+
* through `<binding>` (a service binding to a router Worker) instead of the
|
|
41
|
+
* public edge — a same-zone Worker→Worker public fetch 522s, which otherwise
|
|
42
|
+
* blocks fetching an instance kernel's JWKS to verify a cross-instance
|
|
43
|
+
* credential.
|
|
44
|
+
*/
|
|
45
|
+
router?: { binding: string; hostSuffix: string; minLabels: number }
|
|
30
46
|
}
|
|
31
47
|
|
|
32
48
|
export function generateWorkerEntry(opts: WorkerCodegenOptions): string {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
// The asset-serving hook is a library helper (`assets`), not inlined string
|
|
50
|
+
// logic — imported only when the domain declares a `client` binding.
|
|
51
|
+
const serverImport = opts.hasClient
|
|
52
|
+
? `import { assets, domainWorkerEntry } from '@astrale-os/sdk/server'`
|
|
53
|
+
: `import { domainWorkerEntry } from '@astrale-os/sdk/server'`
|
|
54
|
+
|
|
55
|
+
const clientHook = opts.hasClient
|
|
56
|
+
? `
|
|
57
|
+
// Client assets under /ui/* — served from the ASSETS binding (or proxied to
|
|
58
|
+
// Vite in dev via VIEW_DEV_URL). The matching/stripping logic lives in the SDK helper.
|
|
59
|
+
before: assets({ binding: (env) => env.ASSETS, devProxy: (env) => env.VIEW_DEV_URL }),`
|
|
60
|
+
: ''
|
|
37
61
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
62
|
+
const router = opts.router
|
|
63
|
+
// Service binding for instance-host subrequest routing (Env field + helper +
|
|
64
|
+
// routeSubrequest), wired only when the adapter's `router` is set.
|
|
65
|
+
const routerEnvField = router
|
|
66
|
+
? `
|
|
67
|
+
${router.binding}?: { fetch(request: Request): Promise<Response> }`
|
|
68
|
+
: ''
|
|
69
|
+
const routerHelper = router
|
|
70
|
+
? `
|
|
71
|
+
// A platform INSTANCE hostname (e.g. <slug>.<region>${router.hostSuffix}) is served
|
|
72
|
+
// by a router Worker on the SAME zone — a direct Worker→Worker public fetch 522s.
|
|
73
|
+
// Route those subrequests (e.g. fetching an instance kernel's JWKS to verify a
|
|
74
|
+
// cross-instance credential) through the ${router.binding} service binding instead.
|
|
75
|
+
function isInstanceHost(host) {
|
|
76
|
+
return host.endsWith('${router.hostSuffix}') && host.split('.').length >= ${router.minLabels}
|
|
77
|
+
}
|
|
78
|
+
`
|
|
79
|
+
: ''
|
|
80
|
+
const routerHook = router
|
|
81
|
+
? `
|
|
82
|
+
// Divert instance-host subrequests through ${router.binding} (CF 522s a same-zone
|
|
83
|
+
// Worker→Worker public fetch). No-op when the binding is absent.
|
|
84
|
+
routeSubrequest: (url, env) => (env.${router.binding} && isInstanceHost(url.hostname) ? env.${router.binding} : null),`
|
|
85
|
+
: ''
|
|
42
86
|
|
|
43
87
|
return `// AUTO-GENERATED by @astrale-os/adapter-cloudflare — do not edit.
|
|
44
|
-
// Regenerated on every \`astrale-domain dev|deploy\`. Edit
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
88
|
+
// Regenerated on every \`astrale-domain dev|deploy\`. Edit your domain.ts (the
|
|
89
|
+
// wired \`defineDomain\`) and astrale.config.ts (the \`deploy(domain, …)\`).
|
|
90
|
+
// (origin: ${opts.origin})
|
|
91
|
+
${serverImport}
|
|
48
92
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
93
|
+
// The worker-safe domain definition — schema/methods/deps/views/functions plus
|
|
94
|
+
// origin/requires/postInstall — wired by the author in domain.ts. Spreading it
|
|
95
|
+
// keeps this entry layout-agnostic; the deploy adapter stays in astrale.config.ts.
|
|
96
|
+
import { domain } from '../domain'
|
|
52
97
|
import { PRIVATE_JWK } from './identity'
|
|
53
98
|
|
|
54
|
-
const REQUIRES = ${JSON.stringify(opts.requires)}
|
|
55
|
-
${postInstallLine}
|
|
56
|
-
|
|
57
99
|
interface Env {
|
|
58
100
|
WORKER_URL?: string
|
|
59
101
|
ASSETS?: { fetch(request: Request): Promise<Response> }
|
|
60
102
|
SELF?: { fetch(request: Request): Promise<Response> }
|
|
61
|
-
VIEW_DEV_URL?: string
|
|
103
|
+
VIEW_DEV_URL?: string${routerEnvField}
|
|
62
104
|
[key: string]: unknown
|
|
63
105
|
}
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
|
|
106
|
+
${routerHelper}
|
|
107
|
+
// \`domainWorkerEntry\` folds the runtime-domain compile + server build + the
|
|
108
|
+
// shared worker plumbing (JWKS self-resolution, SELF-binding routing, per-URL
|
|
109
|
+
// app cache, canonical iss resolution) into one declaration. \`deps\` (when the
|
|
110
|
+
// domain declares one) rides the spread and runs per cold isolate / serving URL.
|
|
111
|
+
export default domainWorkerEntry<Env>()({
|
|
112
|
+
...domain,
|
|
113
|
+
privateKey: PRIVATE_JWK,
|
|
68
114
|
// The worker's serving URL = its identity (iss). Prefer the adapter-injected
|
|
69
115
|
// WORKER_URL (pins one canonical host for routed deploys, so iss stays stable
|
|
70
116
|
// even when also reachable via *.workers.dev); fall back to the per-request
|
|
71
117
|
// host for dev / workers.dev-only (single-host → it IS the canonical URL).
|
|
118
|
+
// Behind a TLS-terminating proxy (cloudflared tunnel → wrangler dev) the SDK
|
|
119
|
+
// upgrades the fallback origin via X-Forwarded-Proto, so iss = the public
|
|
120
|
+
// https URL, not the raw http one workerd sees.
|
|
72
121
|
resolveUrl: (env, requestOrigin) => env.WORKER_URL ?? requestOrigin,
|
|
73
|
-
selfBinding: (env) => env.SELF
|
|
74
|
-
build: (url, env) => {
|
|
75
|
-
const domain = defineRemoteDomain()({
|
|
76
|
-
schema,
|
|
77
|
-
methods,
|
|
78
|
-
...(views ? { views } : {}),
|
|
79
|
-
...(functions ? { remoteFunctions: functions } : {}),
|
|
80
|
-
})
|
|
81
|
-
return {
|
|
82
|
-
domain,
|
|
83
|
-
deps: env,
|
|
84
|
-
url,
|
|
85
|
-
privateKey: PRIVATE_JWK,
|
|
86
|
-
...(POST_INSTALL ? { postInstall: POST_INSTALL } : {}),
|
|
87
|
-
...(REQUIRES.length ? { requires: REQUIRES } : {}),
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
// SPA under /ui/* (views with a client renderer).
|
|
91
|
-
before: (env, url, request) => {
|
|
92
|
-
if (env.ASSETS && (url.pathname === '/ui' || url.pathname.startsWith('/ui/'))) {
|
|
93
|
-
if (env.VIEW_DEV_URL) {
|
|
94
|
-
const devBase = env.VIEW_DEV_URL.replace(/\\/$/, '')
|
|
95
|
-
return fetch(new Request(\`\${devBase}\${url.pathname}\${url.search}\`, request))
|
|
96
|
-
}
|
|
97
|
-
const stripped = url.pathname.replace(/^\\/ui\\/?/, '/')
|
|
98
|
-
const rewritten = new URL(stripped + url.search, url.origin)
|
|
99
|
-
return env.ASSETS.fetch(new Request(rewritten, request))
|
|
100
|
-
}
|
|
101
|
-
return undefined
|
|
102
|
-
},
|
|
122
|
+
selfBinding: (env) => env.SELF,${routerHook}${clientHook}
|
|
103
123
|
})
|
|
104
124
|
`
|
|
105
125
|
}
|