@astrale-os/adapter-cloudflare 0.1.1 → 0.1.3

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 (52) hide show
  1. package/dist/astrale.d.ts +27 -0
  2. package/dist/astrale.d.ts.map +1 -0
  3. package/dist/astrale.js +163 -0
  4. package/dist/astrale.js.map +1 -0
  5. package/dist/client.d.ts +7 -0
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/client.js +43 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/cloudflare.d.ts +22 -0
  10. package/dist/cloudflare.d.ts.map +1 -0
  11. package/dist/cloudflare.js +186 -0
  12. package/dist/cloudflare.js.map +1 -0
  13. package/dist/codegen/identity.d.ts +10 -0
  14. package/dist/codegen/identity.d.ts.map +1 -0
  15. package/dist/codegen/identity.js +32 -0
  16. package/dist/codegen/identity.js.map +1 -0
  17. package/dist/codegen/merge.d.ts +20 -0
  18. package/dist/codegen/merge.d.ts.map +1 -0
  19. package/dist/codegen/merge.js +91 -0
  20. package/dist/codegen/merge.js.map +1 -0
  21. package/dist/codegen/worker.d.ts +31 -0
  22. package/dist/codegen/worker.d.ts.map +1 -0
  23. package/dist/codegen/worker.js +93 -0
  24. package/dist/codegen/worker.js.map +1 -0
  25. package/dist/codegen/wrangler.d.ts +34 -0
  26. package/dist/codegen/wrangler.d.ts.map +1 -0
  27. package/dist/codegen/wrangler.js +61 -0
  28. package/dist/codegen/wrangler.js.map +1 -0
  29. package/dist/index.d.ts +18 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +17 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/params.d.ts +69 -0
  34. package/dist/params.d.ts.map +1 -0
  35. package/dist/params.js +2 -0
  36. package/dist/params.js.map +1 -0
  37. package/dist/parse-output.d.ts +25 -0
  38. package/dist/parse-output.d.ts.map +1 -0
  39. package/dist/parse-output.js +53 -0
  40. package/dist/parse-output.js.map +1 -0
  41. package/dist/wrangler-cli.d.ts +74 -0
  42. package/dist/wrangler-cli.d.ts.map +1 -0
  43. package/dist/wrangler-cli.js +201 -0
  44. package/dist/wrangler-cli.js.map +1 -0
  45. package/package.json +2 -2
  46. package/src/astrale.ts +194 -0
  47. package/src/cloudflare.ts +2 -2
  48. package/src/index.ts +2 -1
  49. package/src/params.ts +20 -0
  50. package/src/wrangler-cli.ts +26 -1
  51. package/template/astrale.config.ts +9 -0
  52. package/template/package.json +10 -1
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `astrale(envs)` — the Astrale-managed deployment adapter.
3
+ *
4
+ * Ships the domain THROUGH the platform instead of to the author's own cloud
5
+ * account: `deploy` builds the same single-module workerd bundle the Cloudflare
6
+ * adapter ships (shared codegen + `wrangler deploy --dry-run`), then publishes
7
+ * it via the admin domain's catalog — `DomainPublished.register` →
8
+ * `::release { bundleBase64 }` (the worker stages the bytes in the platform
9
+ * registry) → `::install { instanceId, source: { kind: 'package' } }`, which
10
+ * deploys it as a host-local Service next to the target instance and installs
11
+ * the domain on it. No Cloudflare account, no wrangler auth — the only
12
+ * credential is the author's Astrale identity, supplied by the `astrale` CLI
13
+ * session this adapter shells out to (the same trust source as
14
+ * `astrale instance install`).
15
+ *
16
+ * `watch` is identical to the Cloudflare adapter's local dev (wrangler dev on
17
+ * localhost) — managed deploys change WHERE the worker runs, not how you
18
+ * iterate locally.
19
+ *
20
+ * Current limits (v1): client/ SPA assets are not shipped (managed packages
21
+ * serve no `/ui` yet) and no runtime secrets reach the service — the bundle
22
+ * must be self-contained (its signing identity is baked in by the codegen).
23
+ */
24
+ import type { DomainAdapter } from '@astrale-os/devkit';
25
+ import type { AstraleParams } from './params';
26
+ export declare function astrale(envs: Record<string, AstraleParams>): DomainAdapter<AstraleParams>;
27
+ //# sourceMappingURL=astrale.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astrale.d.ts","sourceRoot":"","sources":["../src/astrale.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAQvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAU7C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,aAAa,CAAC,aAAa,CAAC,CA2FzF"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * `astrale(envs)` — the Astrale-managed deployment adapter.
3
+ *
4
+ * Ships the domain THROUGH the platform instead of to the author's own cloud
5
+ * account: `deploy` builds the same single-module workerd bundle the Cloudflare
6
+ * adapter ships (shared codegen + `wrangler deploy --dry-run`), then publishes
7
+ * it via the admin domain's catalog — `DomainPublished.register` →
8
+ * `::release { bundleBase64 }` (the worker stages the bytes in the platform
9
+ * registry) → `::install { instanceId, source: { kind: 'package' } }`, which
10
+ * deploys it as a host-local Service next to the target instance and installs
11
+ * the domain on it. No Cloudflare account, no wrangler auth — the only
12
+ * credential is the author's Astrale identity, supplied by the `astrale` CLI
13
+ * session this adapter shells out to (the same trust source as
14
+ * `astrale instance install`).
15
+ *
16
+ * `watch` is identical to the Cloudflare adapter's local dev (wrangler dev on
17
+ * localhost) — managed deploys change WHERE the worker runs, not how you
18
+ * iterate locally.
19
+ *
20
+ * Current limits (v1): client/ SPA assets are not shipped (managed packages
21
+ * serve no `/ui` yet) and no runtime secrets reach the service — the bundle
22
+ * must be self-contained (its signing identity is baked in by the codegen).
23
+ */
24
+ import { defineAdapter } from '@astrale-os/devkit';
25
+ import { spawn } from 'node:child_process';
26
+ import { existsSync } from 'node:fs';
27
+ import { readFile } from 'node:fs/promises';
28
+ import { join } from 'node:path';
29
+ import { logTo, prepare } from './cloudflare';
30
+ import { runWranglerBundle, runWranglerDev } from './wrangler-cli';
31
+ const DEFAULT_PORT = 8787;
32
+ const DEFAULT_ADMIN_URL = 'https://admin.eu.astrale.ai/api';
33
+ const DEFAULT_REGION = 'eu';
34
+ const ADMIN_ORIGIN = 'admin.astrale.ai';
35
+ export function astrale(envs) {
36
+ return defineAdapter({
37
+ name: 'astrale',
38
+ envs,
39
+ // Local dev is unchanged: wrangler dev on localhost. Managed deploys change
40
+ // WHERE the worker ships, not the inner loop.
41
+ async watch(params, ctx) {
42
+ const { configPath } = await prepare({ ...(params.vars ? { vars: params.vars } : {}), port: params.port ?? DEFAULT_PORT }, ctx, 'dev');
43
+ const handle = await runWranglerDev({
44
+ projectDir: ctx.projectDir,
45
+ configPath,
46
+ port: params.port ?? DEFAULT_PORT,
47
+ remote: false,
48
+ onReload: ctx.onReload,
49
+ onLog: logTo(),
50
+ });
51
+ return { url: handle.url, stop: handle.stop };
52
+ },
53
+ async deploy(params, ctx) {
54
+ const log = logTo();
55
+ if (ctx.clientDir) {
56
+ log(`client/ detected — managed deploys do not ship SPA assets yet; ` +
57
+ `/ui will not serve on the managed service (functions/methods are unaffected).`);
58
+ }
59
+ // 1. Codegen + bundle: the exact worker module a Cloudflare deploy would
60
+ // ship, produced locally with `wrangler deploy --dry-run` (no auth).
61
+ // `clientDir` is stripped: managed packages don't ship SPA assets yet,
62
+ // and leaving it in would put a dangling `assets.directory` (the
63
+ // unbuilt dist-client) into the generated config.
64
+ const { clientDir: _omitted, ...bundleCtx } = ctx;
65
+ const { configPath } = await prepare({}, bundleCtx, 'dev');
66
+ const outDir = join(ctx.projectDir, '.astrale', 'dist-managed');
67
+ const bundlePath = await runWranglerBundle({
68
+ projectDir: ctx.projectDir,
69
+ configPath,
70
+ outDir,
71
+ onLog: log,
72
+ });
73
+ const bundle = await readFile(bundlePath);
74
+ // 2. Publish through the platform, authenticated by the author's astrale
75
+ // CLI session. The release payload rides STDIN — a bundle is megabytes
76
+ // of base64, far past argv limits.
77
+ const name = params.name ?? packageNameFor(ctx.domain.origin);
78
+ const adminUrl = params.adminUrl ?? DEFAULT_ADMIN_URL;
79
+ const version = `0.0.0-managed.${Date.now()}`;
80
+ log(`registering "${name}" (${ctx.domain.origin}) in the platform catalog…`);
81
+ await astraleCall(ctx.projectDir, params, adminUrl, {
82
+ path: `/${ADMIN_ORIGIN}/class.DomainPublished/register`,
83
+ data: { origin: ctx.domain.origin, name },
84
+ });
85
+ log(`publishing ${name}@${version} (${bundle.length} bytes)…`);
86
+ await astraleCall(ctx.projectDir, params, adminUrl, {
87
+ path: `/admin/domains/${name}::release`,
88
+ data: { version, bundleBase64: bundle.toString('base64'), makeCurrent: true },
89
+ viaStdin: true,
90
+ });
91
+ log(`installing on instance "${params.instance}"…`);
92
+ const installed = (await astraleCall(ctx.projectDir, params, adminUrl, {
93
+ path: `/admin/domains/${name}::install`,
94
+ data: { instanceId: params.instance, source: { kind: 'package' } },
95
+ }));
96
+ if (installed.error || installed.state !== 'installed') {
97
+ throw new Error(`managed install failed: state=${installed.state ?? '?'} error=${installed.error ?? '?'}`);
98
+ }
99
+ if (!installed.serviceSlug) {
100
+ throw new Error('managed install returned no serviceSlug — cannot derive the service URL');
101
+ }
102
+ const region = params.region ?? DEFAULT_REGION;
103
+ return { url: `https://${installed.serviceSlug}.svc.${region}.astrale.ai` };
104
+ },
105
+ secretsFile(params) {
106
+ return params.secrets;
107
+ },
108
+ });
109
+ }
110
+ /** Catalog package name from the origin: first DNS label (e.g. `my-notes.example.dev` → `my-notes`). */
111
+ function packageNameFor(origin) {
112
+ const label = origin.split('.')[0] ?? origin;
113
+ return label.toLowerCase().replace(/[^a-z0-9-]+/g, '-');
114
+ }
115
+ /**
116
+ * Invoke the user's `astrale` CLI for one platform call and parse its JSON
117
+ * output. The CLI owns identity (IdP session, audience scoping, delegation
118
+ * minting) — reimplementing that here would fork the trust path.
119
+ */
120
+ async function astraleCall(projectDir, params, adminUrl, call) {
121
+ const args = ['call', call.path, '--url', adminUrl, '--json'];
122
+ if (params.identity)
123
+ args.push('--as', params.identity);
124
+ const payload = JSON.stringify(call.data);
125
+ if (!call.viaStdin)
126
+ args.push('--data', payload);
127
+ const { code, out } = await new Promise((resolve, reject) => {
128
+ const child = spawn(astraleBin(projectDir), args, {
129
+ cwd: projectDir,
130
+ stdio: [call.viaStdin ? 'pipe' : 'ignore', 'pipe', 'pipe'],
131
+ });
132
+ let out = '';
133
+ child.stdout?.on('data', (b) => (out += b.toString()));
134
+ child.stderr?.on('data', (b) => (out += b.toString()));
135
+ child.on('exit', (c) => resolve({ code: c ?? 1, out }));
136
+ child.on('error', reject);
137
+ if (call.viaStdin) {
138
+ child.stdin?.write(payload);
139
+ child.stdin?.end();
140
+ }
141
+ });
142
+ if (code !== 0) {
143
+ throw new Error(`astrale call ${call.path} failed (exit ${code}): ${out.trim().slice(0, 500)}\n` +
144
+ `Is the astrale CLI installed and signed in? (astrale auth login)`);
145
+ }
146
+ try {
147
+ return JSON.parse(out);
148
+ }
149
+ catch {
150
+ throw new Error(`astrale call ${call.path}: could not parse CLI output: ${out.slice(0, 300)}`);
151
+ }
152
+ }
153
+ /** The user's astrale CLI: ~/.astrale/bin/astrale when present, else PATH. */
154
+ function astraleBin(_projectDir) {
155
+ const home = process.env.HOME;
156
+ if (home) {
157
+ const installed = join(home, '.astrale', 'bin', 'astrale');
158
+ if (existsSync(installed))
159
+ return installed;
160
+ }
161
+ return 'astrale';
162
+ }
163
+ //# sourceMappingURL=astrale.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"astrale.js","sourceRoot":"","sources":["../src/astrale.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Build the optional client SPA (the Views' UI). Best-effort: if the project
3
+ * has no `client/` or no resolvable Vite, we skip — the RPC surface and inline
4
+ * Views still work without a built SPA.
5
+ */
6
+ export declare function buildClient(clientDir: string, projectDir: string, onLog?: (line: string) => void): Promise<void>;
7
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAC7B,OAAO,CAAC,IAAI,CAAC,CAkBf"}
package/dist/client.js ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Build the optional client SPA (the Views' UI). Best-effort: if the project
3
+ * has no `client/` or no resolvable Vite, we skip — the RPC surface and inline
4
+ * Views still work without a built SPA.
5
+ */
6
+ import { spawn } from 'node:child_process';
7
+ import { existsSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ export async function buildClient(clientDir, projectDir, onLog) {
10
+ const viteBin = [
11
+ join(clientDir, 'node_modules', '.bin', 'vite'),
12
+ join(projectDir, 'node_modules', '.bin', 'vite'),
13
+ ].find((p) => existsSync(p));
14
+ if (!viteBin) {
15
+ // The project has a client/ but its deps aren't installed — wrangler would
16
+ // otherwise fail cryptically on the missing dist-client assets dir. Fail
17
+ // loud with the fix (the client is a workspace package — one install covers it).
18
+ throw new Error(`client build: vite not found in ${clientDir}. Run \`pnpm install\` at the project root ` +
19
+ `first (the client/ SPA is a workspace package).`);
20
+ }
21
+ const { code, out } = await runCapture(viteBin, ['build'], clientDir, onLog);
22
+ if (code !== 0) {
23
+ throw new Error(`client build failed (code ${code}):\n${out.slice(-1500)}`);
24
+ }
25
+ }
26
+ function runCapture(cmd, args, cwd, onLog) {
27
+ return new Promise((resolve, reject) => {
28
+ const child = spawn(cmd, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
29
+ let out = '';
30
+ const onData = (buf) => {
31
+ const text = buf.toString();
32
+ out += text;
33
+ for (const line of text.split('\n'))
34
+ if (line.trim())
35
+ onLog?.(line);
36
+ };
37
+ child.stdout?.on('data', onData);
38
+ child.stderr?.on('data', onData);
39
+ child.on('exit', (code) => resolve({ code: code ?? 1, out }));
40
+ child.on('error', reject);
41
+ });
42
+ }
43
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAiB,EACjB,UAAkB,EAClB,KAA8B;IAE9B,MAAM,OAAO,GAAG;QACd,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC;QAC/C,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC;KACjD,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,2EAA2E;QAC3E,yEAAyE;QACzE,iFAAiF;QACjF,MAAM,IAAI,KAAK,CACb,mCAAmC,SAAS,6CAA6C;YACvF,iDAAiD,CACpD,CAAA;IACH,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,CAAA;IAC5E,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAC7E,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CACjB,GAAW,EACX,IAAc,EACd,GAAW,EACX,KAA8B;IAE9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,CAAA;QAC1E,IAAI,GAAG,GAAG,EAAE,CAAA;QACZ,MAAM,MAAM,GAAG,CAAC,GAAW,EAAE,EAAE;YAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAA;YAC3B,GAAG,IAAI,IAAI,CAAA;YACX,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,IAAI,IAAI,CAAC,IAAI,EAAE;oBAAE,KAAK,EAAE,CAAC,IAAI,CAAC,CAAA;QACrE,CAAC,CAAA;QACD,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAChC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAA;QAC7D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC3B,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * `cloudflare(envs)` — the Cloudflare deployment adapter.
3
+ *
4
+ * Owns everything target-specific: it codegens the Worker entry + wrangler
5
+ * config (the dev never sees a `wrangler.jsonc`), builds the optional client
6
+ * SPA, and shells out to `wrangler`. `watch` → `wrangler dev` (local URL);
7
+ * `deploy` → `wrangler deploy` (workers.dev or a custom-domain URL); secrets →
8
+ * `wrangler secret bulk`. Both `watch` and `deploy` return the URL the devkit
9
+ * prints and `astrale instance install <url>` consumes.
10
+ */
11
+ import type { DeployCtx, DomainAdapter, WatchCtx } from '@astrale-os/devkit';
12
+ import type { CloudflareParams } from './params';
13
+ export declare function cloudflare(envs: Record<string, CloudflareParams>): DomainAdapter<CloudflareParams>;
14
+ export declare function prepare(params: CloudflareParams, ctx: WatchCtx | DeployCtx, mode: 'dev' | 'deploy'): Promise<{
15
+ configPath: string;
16
+ fallbackConfigPath?: string;
17
+ port: number;
18
+ workerName: string;
19
+ }>;
20
+ export declare function workerName(params: CloudflareParams, origin: string): string;
21
+ export declare function logTo(): (line: string) => void;
22
+ //# sourceMappingURL=cloudflare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../src/cloudflare.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAM5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAUhD,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,GACrC,aAAa,CAAC,gBAAgB,CAAC,CAsDjC;AAID,wBAAsB,OAAO,CAC3B,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,QAAQ,GAAG,SAAS,EACzB,IAAI,EAAE,KAAK,GAAG,QAAQ,GACrB,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA0EhG;AAoCD,wBAAgB,UAAU,CAAC,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAsB3E;AAED,wBAAgB,KAAK,IAAI,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAE9C"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * `cloudflare(envs)` — the Cloudflare deployment adapter.
3
+ *
4
+ * Owns everything target-specific: it codegens the Worker entry + wrangler
5
+ * config (the dev never sees a `wrangler.jsonc`), builds the optional client
6
+ * SPA, and shells out to `wrangler`. `watch` → `wrangler dev` (local URL);
7
+ * `deploy` → `wrangler deploy` (workers.dev or a custom-domain URL); secrets →
8
+ * `wrangler secret bulk`. Both `watch` and `deploy` return the URL the devkit
9
+ * prints and `astrale instance install <url>` consumes.
10
+ */
11
+ import { defineAdapter, hasStarModule } from '@astrale-os/devkit';
12
+ import { mkdir, writeFile } from 'node:fs/promises';
13
+ import { join } from 'node:path';
14
+ import { buildClient } from './client';
15
+ import { ensureIdentity } from './codegen/identity';
16
+ import { generateWorkerEntry } from './codegen/worker';
17
+ import { generateWranglerConfig } from './codegen/wrangler';
18
+ import { bulkPutSecrets, runWranglerDeploy, runWranglerDev } from './wrangler-cli';
19
+ const DEFAULT_PORT = 8787;
20
+ export function cloudflare(envs) {
21
+ return defineAdapter({
22
+ name: 'cloudflare',
23
+ envs,
24
+ async watch(params, ctx) {
25
+ const { configPath, port } = await prepare(params, ctx, 'dev');
26
+ if (ctx.clientDir)
27
+ await buildClient(ctx.clientDir, ctx.projectDir, logTo());
28
+ const handle = await runWranglerDev({
29
+ projectDir: ctx.projectDir,
30
+ configPath,
31
+ port,
32
+ remote: Boolean(params.remote),
33
+ onReload: ctx.onReload,
34
+ onLog: logTo(),
35
+ });
36
+ return { url: handle.url, stop: handle.stop };
37
+ },
38
+ async deploy(params, ctx) {
39
+ const { configPath, fallbackConfigPath, workerName: name, } = await prepare(params, ctx, 'deploy');
40
+ if (ctx.clientDir)
41
+ await buildClient(ctx.clientDir, ctx.projectDir, logTo());
42
+ const { url } = await runWranglerDeploy({
43
+ projectDir: ctx.projectDir,
44
+ configPath,
45
+ workerName: name,
46
+ ...(fallbackConfigPath ? { fallbackConfigPath } : {}),
47
+ ...(params.route ? { route: params.route } : {}),
48
+ onLog: logTo(),
49
+ });
50
+ if (Object.keys(ctx.secrets).length > 0) {
51
+ await bulkPutSecrets({
52
+ projectDir: ctx.projectDir,
53
+ configPath,
54
+ secrets: ctx.secrets,
55
+ onLog: logTo(),
56
+ });
57
+ }
58
+ // A first deploy on a fresh `*.workers.dev` host can take ~30-60s to
59
+ // propagate; an `astrale instance install <url>` issued right away would
60
+ // 404 (Cloudflare 1042). Block until the URL actually serves so the
61
+ // install line we print is immediately actionable.
62
+ await waitUntilLive(url, logTo());
63
+ return { url };
64
+ },
65
+ secretsFile(params) {
66
+ return params.secrets;
67
+ },
68
+ });
69
+ }
70
+ // ── codegen orchestration ──────────────────────────────────────────────────
71
+ export async function prepare(params, ctx, mode) {
72
+ const astraleDir = join(ctx.projectDir, '.astrale');
73
+ await mkdir(astraleDir, { recursive: true });
74
+ await ensureIdentity(astraleDir, ctx.domain.origin);
75
+ const name = workerName(params, ctx.domain.origin);
76
+ // Shared ★-files probe: must agree with the codegen's `import { views } from
77
+ // '../views'` resolution (folder index OR sibling file) — a folder-only
78
+ // existsSync would deploy a worker whose graph silently lacks a sibling-file
79
+ // module the diagnostic spec includes.
80
+ const hasViews = hasStarModule(ctx.projectDir, 'views');
81
+ const hasFunctions = hasStarModule(ctx.projectDir, 'functions');
82
+ const hasClient = Boolean(ctx.clientDir);
83
+ await writeFile(join(astraleDir, 'worker.gen.ts'), generateWorkerEntry({
84
+ origin: ctx.domain.origin,
85
+ ...(ctx.domain.postInstall ? { postInstall: ctx.domain.postInstall } : {}),
86
+ requires: ctx.domain.requires,
87
+ hasViews,
88
+ hasFunctions,
89
+ hasClient,
90
+ }));
91
+ // Pin the worker's canonical serving URL (its `iss` identity) for routed
92
+ // deploys: a routed worker may ALSO be reachable via `*.workers.dev`, so
93
+ // resolving the URL from the per-request Host would let `iss` drift between
94
+ // hostnames. Dev + workers.dev-only deploys are single-host, so the worker
95
+ // falls back to the per-request Host (always the canonical URL there).
96
+ // An explicit `vars.WORKER_URL` (e.g. a tunnel/proxy front) wins.
97
+ const workerUrl = mode === 'deploy' && params.route ? `https://${params.route}` : undefined;
98
+ // Dev injects secrets as local vars (the gitignored .astrale config never
99
+ // ships); deploy keeps secrets out of the config and pushes them encrypted.
100
+ const vars = {
101
+ ...(workerUrl ? { WORKER_URL: workerUrl } : {}),
102
+ ...(mode === 'dev' ? { ...params.vars, ...ctx.secrets } : { ...params.vars }),
103
+ };
104
+ const baseConfig = {
105
+ workerName: name,
106
+ ...(mode === 'deploy' && params.route ? { route: params.route } : {}),
107
+ hasClient,
108
+ vars,
109
+ // Escape hatch: extra bindings (KV/R2/D1/queues/…) deep-merged on top. Same
110
+ // overlay in dev and deploy — it's infra, not env-specific plumbing — and it
111
+ // flows into both the SELF and the SELF-less fallback config below.
112
+ ...(params.wrangler ? { wrangler: params.wrangler } : {}),
113
+ };
114
+ // Always self-bind: it enables autobinding (a handler calling its own domain).
115
+ // Locally the dev registry resolves it; on deploy the first deploy of a fresh
116
+ // worker can't (the script doesn't exist yet), so we also emit a SELF-less
117
+ // fallback config that `runWranglerDeploy` uses for a one-time two-pass deploy.
118
+ const configPath = join(astraleDir, 'wrangler.gen.jsonc');
119
+ await writeFile(configPath, generateWranglerConfig({ ...baseConfig, selfBinding: true }));
120
+ let fallbackConfigPath;
121
+ if (mode === 'deploy') {
122
+ fallbackConfigPath = join(astraleDir, 'wrangler.no-self.gen.jsonc');
123
+ await writeFile(fallbackConfigPath, generateWranglerConfig({ ...baseConfig, selfBinding: false }));
124
+ }
125
+ return {
126
+ configPath,
127
+ ...(fallbackConfigPath ? { fallbackConfigPath } : {}),
128
+ port: params.port ?? DEFAULT_PORT,
129
+ workerName: name,
130
+ };
131
+ }
132
+ /**
133
+ * Poll the deployed URL until the worker answers (anything but the Cloudflare
134
+ * 1042 "no script on this host" 404), or give up after `timeoutMs`. Resolves
135
+ * silently on the fast path; logs only when propagation actually makes us wait.
136
+ */
137
+ async function waitUntilLive(url, onLog, timeoutMs = 90_000) {
138
+ const probe = new URL('/health', url);
139
+ const deadline = Date.now() + timeoutMs;
140
+ let waited = false;
141
+ for (;;) {
142
+ try {
143
+ const res = await fetch(probe, { redirect: 'manual' });
144
+ if (res.status !== 404)
145
+ return;
146
+ }
147
+ catch {
148
+ // Network hiccup — treat like "not live yet" and keep polling.
149
+ }
150
+ if (Date.now() >= deadline) {
151
+ onLog(`URL not reachable yet after ${Math.round(timeoutMs / 1000)}s — it may need a moment.`);
152
+ return;
153
+ }
154
+ if (!waited) {
155
+ waited = true;
156
+ onLog('waiting for the URL to go live (first deploys can take ~30-60s)…');
157
+ }
158
+ await new Promise((r) => setTimeout(r, 3000));
159
+ }
160
+ }
161
+ const WORKER_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
162
+ export function workerName(params, origin) {
163
+ // An explicit name is the deployed worker's identity (and the SELF service
164
+ // target) — validate and reject an invalid one rather than silently rewriting
165
+ // it, so the dev fixes it instead of deploying under a surprising name.
166
+ if (params.workerName !== undefined) {
167
+ if (!WORKER_NAME_RE.test(params.workerName) || params.workerName.length > 63) {
168
+ throw new Error(`cloudflare adapter: invalid \`workerName\` "${params.workerName}". A Cloudflare worker ` +
169
+ 'name must be lowercase, start with a letter or digit, contain only letters, digits and ' +
170
+ 'dashes, and be at most 63 chars.');
171
+ }
172
+ return params.workerName;
173
+ }
174
+ // Derive from the origin: lowercase, collapse non-alphanumerics to a dash, then
175
+ // SLICE before trimming dashes so a cut landing mid-dash can't leave a trailing
176
+ // "-" (which wrangler rejects).
177
+ return origin
178
+ .toLowerCase()
179
+ .replace(/[^a-z0-9]+/g, '-')
180
+ .slice(0, 54)
181
+ .replace(/^-+|-+$/g, '');
182
+ }
183
+ export function logTo() {
184
+ return (line) => process.stderr.write(`\x1b[2m ${line}\x1b[0m\n`);
185
+ }
186
+ //# sourceMappingURL=cloudflare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../src/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Ensure the domain has a stable Ed25519 signing identity at
3
+ * `.astrale/identity.ts`. Generated ONCE (first `dev`/`deploy` or by the
4
+ * scaffolder) and never regenerated — the private key IS the domain's identity,
5
+ * so rotating it would orphan every instance that installed the old key. The
6
+ * file is gitignored; for real production this key should graduate to a managed
7
+ * secret.
8
+ */
9
+ export declare function ensureIdentity(astraleDir: string, origin: string): Promise<void>;
10
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/codegen/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtF"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Ensure the domain has a stable Ed25519 signing identity at
3
+ * `.astrale/identity.ts`. Generated ONCE (first `dev`/`deploy` or by the
4
+ * scaffolder) and never regenerated — the private key IS the domain's identity,
5
+ * so rotating it would orphan every instance that installed the old key. The
6
+ * file is gitignored; for real production this key should graduate to a managed
7
+ * secret.
8
+ */
9
+ import { exportJWK, generateKeyPair } from 'jose';
10
+ import { existsSync } from 'node:fs';
11
+ import { mkdir, writeFile } from 'node:fs/promises';
12
+ import { join } from 'node:path';
13
+ export async function ensureIdentity(astraleDir, origin) {
14
+ const file = join(astraleDir, 'identity.ts');
15
+ if (existsSync(file))
16
+ return;
17
+ const { privateKey } = await generateKeyPair('EdDSA', { extractable: true });
18
+ const jwk = (await exportJWK(privateKey));
19
+ jwk.alg = 'EdDSA';
20
+ jwk.kid = `${origin}-key`;
21
+ await mkdir(astraleDir, { recursive: true });
22
+ await writeFile(file, `// AUTO-GENERATED stable signing identity for "${origin}" — do not edit, do not commit.\n` +
23
+ `export const PRIVATE_JWK = ${JSON.stringify(jwk, null, 2)} as const\n`);
24
+ // Loud on purpose: the file is gitignored, so a fresh clone / CI runner
25
+ // silently mints a NEW key — and a redeploy with a new key breaks JWKS
26
+ // verification on every instance that installed the old one, with no hint
27
+ // why. Make the lifecycle visible the one time it happens.
28
+ process.stderr.write(`\x1b[33m!\x1b[0m new signing identity generated at .astrale/identity.ts (kid ${String(jwk.kid)}).\n` +
29
+ ` This key IS "${origin}"'s identity: back it up (or move it to a managed secret) —\n` +
30
+ ` deploying from another machine without it re-keys the domain and breaks existing installs.\n`);
31
+ }
32
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/codegen/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAAkB,EAAE,MAAc;IACrE,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAA;IAC5C,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAM;IAE5B,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5E,MAAM,GAAG,GAAG,CAAC,MAAM,SAAS,CAAC,UAAU,CAAC,CAA4B,CAAA;IACpE,GAAG,CAAC,GAAG,GAAG,OAAO,CAAA;IACjB,GAAG,CAAC,GAAG,GAAG,GAAG,MAAM,MAAM,CAAA;IAEzB,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC5C,MAAM,SAAS,CACb,IAAI,EACJ,kDAAkD,MAAM,mCAAmC;QACzF,8BAA8B,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,aAAa,CAC1E,CAAA;IAED,wEAAwE;IACxE,uEAAuE;IACvE,0EAA0E;IAC1E,2DAA2D;IAC3D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,gFAAgF,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM;QACnG,kBAAkB,MAAM,+DAA+D;QACvF,gGAAgG,CACnG,CAAA;AACH,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Deep-merge for the generated wrangler config. `base` is what the adapter
3
+ * generates; `overlay` is the dev's `params.wrangler` escape hatch. The merge
4
+ * is additive so a dev can declare extra bindings without losing the adapter's
5
+ * invariants:
6
+ * - plain objects → recurse
7
+ * - arrays → concat, de-duplicating BOTH primitive members and
8
+ * object bindings by an identity key (`binding` /
9
+ * `pattern` / `queue` / `name`, else structural value),
10
+ * with the overlay entry overriding a base entry of the
11
+ * same identity. So a dev overlay that re-declares the
12
+ * adapter's `SELF` service (or any binding) replaces it
13
+ * instead of emitting a duplicate wrangler rejects.
14
+ * - scalars / new keys → overlay wins
15
+ *
16
+ * Protecting the load-bearing keys (`main`, `assets`, …) is policy enforced by
17
+ * the caller (`generateWranglerConfig`), not here — this stays a generic merge.
18
+ */
19
+ export declare function deepMergeConfig(base: Record<string, unknown>, overlay: Record<string, unknown>): Record<string, unknown>;
20
+ //# sourceMappingURL=merge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../src/codegen/merge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB"}