@astrale-os/adapter-cloudflare 0.5.0-beta.13 → 0.5.0-beta.131
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/CHANGELOG.md +1515 -0
- package/README.md +46 -2
- package/dist/adapter.d.ts +14 -0
- package/dist/adapter.js +82 -0
- package/dist/build/artifact.d.ts +7 -0
- package/dist/build/artifact.js +165 -0
- package/dist/build/bundle/admit.d.ts +1 -0
- package/dist/build/bundle/admit.js +9 -0
- package/dist/build/closure.d.ts +2 -0
- package/dist/build/closure.js +22 -0
- package/dist/build/codegen/execution.d.ts +3 -0
- package/dist/build/codegen/execution.js +2 -0
- package/dist/build/codegen/merge.d.ts +0 -1
- package/dist/build/codegen/merge.js +0 -1
- package/dist/build/codegen/wrangler.d.ts +12 -3
- package/dist/build/codegen/wrangler.js +26 -18
- package/dist/build/configuration/addressing.d.ts +2 -0
- package/dist/build/configuration/addressing.js +11 -0
- package/dist/build/configuration/invocation-timeout.d.ts +2 -0
- package/dist/build/configuration/invocation-timeout.js +9 -0
- package/dist/build/configuration/request-limit.d.ts +2 -0
- package/dist/build/configuration/request-limit.js +9 -0
- package/dist/build/configuration/router.d.ts +7 -0
- package/dist/build/configuration/router.js +11 -0
- package/dist/build/configuration/worker-name.d.ts +2 -0
- package/dist/build/configuration/worker-name.js +18 -0
- package/dist/build/configuration.d.ts +5 -0
- package/dist/build/configuration.js +5 -0
- package/dist/build/direct.d.ts +4 -0
- package/dist/build/direct.js +90 -0
- package/dist/build/frontend/files.d.ts +9 -0
- package/dist/build/frontend/files.js +33 -0
- package/dist/build/frontend/readiness.d.ts +10 -0
- package/dist/build/frontend/readiness.js +21 -0
- package/dist/build/frontend/revision.d.ts +5 -0
- package/dist/build/frontend/revision.js +14 -0
- package/dist/build/frontend/vite.d.ts +8 -0
- package/dist/build/frontend/vite.js +32 -0
- package/dist/build/frontend/vite.process.d.ts +7 -0
- package/dist/build/frontend/vite.process.js +65 -0
- package/dist/build/frontend/vite.runner.d.ts +1 -0
- package/dist/build/frontend/vite.runner.js +120 -0
- package/dist/build/index.d.ts +9 -16
- package/dist/build/index.js +8 -16
- package/dist/build/materialize.d.ts +4 -0
- package/dist/build/materialize.js +10 -0
- package/dist/build/metafile.d.ts +5 -0
- package/dist/build/metafile.js +14 -0
- package/dist/build/qualification.d.ts +5 -0
- package/dist/build/qualification.js +7 -0
- package/dist/build/roots.d.ts +2 -0
- package/dist/build/roots.js +34 -0
- package/dist/build/worker/artifact.d.ts +40 -0
- package/dist/build/worker/artifact.js +1 -0
- package/dist/build/worker/artifact.load.d.ts +3 -0
- package/dist/build/worker/artifact.load.js +308 -0
- package/dist/build/worker/artifact.manifest.d.ts +48 -0
- package/dist/build/worker/artifact.manifest.js +87 -0
- package/dist/build/worker/artifact.paths.d.ts +4 -0
- package/dist/build/worker/artifact.paths.js +25 -0
- package/dist/build/worker/codegen/entry.d.ts +2 -0
- package/dist/build/worker/codegen/entry.js +28 -0
- package/dist/build/worker/codegen/service.d.ts +19 -0
- package/dist/build/worker/codegen/service.js +53 -0
- package/dist/build/worker/execution.prepare.d.ts +19 -0
- package/dist/build/worker/execution.prepare.js +115 -0
- package/dist/build/worker/index.d.ts +2 -0
- package/dist/build/worker/index.js +1 -0
- package/dist/build/worker/modules.d.ts +11 -0
- package/dist/build/worker/modules.js +26 -0
- package/dist/build/worker/runtime.d.ts +10 -0
- package/dist/build/worker/runtime.js +35 -0
- package/dist/build/worker/specifier.d.ts +1 -0
- package/dist/build/worker/specifier.js +5 -0
- package/dist/cloudflare.d.ts +1 -45
- package/dist/cloudflare.js +1 -202
- package/dist/deploy/deploy.d.ts +17 -0
- package/dist/deploy/deploy.js +221 -0
- package/dist/deploy/index.d.ts +1 -0
- package/dist/deploy/index.js +1 -0
- package/dist/deploy/output.d.ts +2 -0
- package/dist/deploy/output.js +10 -0
- package/dist/deploy/readiness.d.ts +5 -0
- package/dist/deploy/readiness.js +18 -0
- package/dist/identity/binding.d.ts +2 -0
- package/dist/identity/binding.js +2 -0
- package/dist/identity/index.d.ts +3 -0
- package/dist/identity/index.js +3 -0
- package/dist/identity/material.d.ts +7 -0
- package/dist/identity/material.js +42 -0
- package/dist/identity/secret.d.ts +3 -0
- package/dist/identity/secret.js +7 -0
- package/dist/identity/source.d.ts +26 -0
- package/dist/identity/source.js +127 -0
- package/dist/index.d.ts +10 -9
- package/dist/index.js +10 -9
- package/dist/params.d.ts +40 -36
- package/dist/params.js +0 -1
- package/dist/release/deployment.d.ts +44 -0
- package/dist/release/deployment.js +141 -0
- package/dist/release/index.d.ts +2 -0
- package/dist/release/index.js +2 -0
- package/dist/release/staged-worker-name.d.ts +3 -0
- package/dist/release/staged-worker-name.js +10 -0
- package/dist/worker/entry.d.ts +48 -0
- package/dist/worker/entry.js +157 -0
- package/dist/worker/fetch.d.ts +0 -1
- package/dist/worker/fetch.js +0 -1
- package/dist/worker/frontend.d.ts +4 -0
- package/dist/worker/frontend.js +46 -0
- package/dist/worker/identity.d.ts +7 -0
- package/dist/worker/identity.js +23 -0
- package/dist/worker/index.d.ts +2 -61
- package/dist/worker/index.js +2 -218
- package/dist/wrangler/bundle.d.ts +10 -0
- package/dist/wrangler/bundle.js +28 -0
- package/dist/wrangler/deploy.d.ts +24 -0
- package/dist/wrangler/deploy.js +44 -0
- package/dist/wrangler/index.d.ts +5 -0
- package/dist/wrangler/index.js +5 -0
- package/dist/wrangler/process.d.ts +15 -0
- package/dist/wrangler/process.js +102 -0
- package/dist/wrangler/rollout.d.ts +27 -0
- package/dist/wrangler/rollout.js +75 -0
- package/dist/wrangler/secrets.d.ts +2 -0
- package/dist/wrangler/secrets.js +24 -0
- package/dist/wrangler/status.d.ts +18 -0
- package/dist/wrangler/status.js +75 -0
- package/dist/wrangler/triggers.d.ts +5 -0
- package/dist/wrangler/triggers.js +12 -0
- package/dist/wrangler/type-generation.d.ts +6 -0
- package/dist/wrangler/type-generation.js +19 -0
- package/dist/wrangler/versions.d.ts +13 -0
- package/dist/wrangler/versions.js +38 -0
- package/package.json +34 -19
- package/template/astrale.config.ts +30 -31
- package/dist/build/codegen/identity.d.ts +0 -10
- package/dist/build/codegen/identity.d.ts.map +0 -1
- package/dist/build/codegen/identity.js +0 -32
- package/dist/build/codegen/identity.js.map +0 -1
- package/dist/build/codegen/merge.d.ts.map +0 -1
- package/dist/build/codegen/merge.js.map +0 -1
- package/dist/build/codegen/worker.d.ts +0 -12
- package/dist/build/codegen/worker.d.ts.map +0 -1
- package/dist/build/codegen/worker.js +0 -40
- package/dist/build/codegen/worker.js.map +0 -1
- package/dist/build/codegen/wrangler.d.ts.map +0 -1
- package/dist/build/codegen/wrangler.js.map +0 -1
- package/dist/build/frontend.d.ts +0 -27
- package/dist/build/frontend.d.ts.map +0 -1
- package/dist/build/frontend.js +0 -252
- package/dist/build/frontend.js.map +0 -1
- package/dist/build/index.d.ts.map +0 -1
- package/dist/build/index.js.map +0 -1
- package/dist/build/parse-output.d.ts +0 -27
- package/dist/build/parse-output.d.ts.map +0 -1
- package/dist/build/parse-output.js +0 -58
- package/dist/build/parse-output.js.map +0 -1
- package/dist/build/prepare.d.ts +0 -18
- package/dist/build/prepare.d.ts.map +0 -1
- package/dist/build/prepare.js +0 -138
- package/dist/build/prepare.js.map +0 -1
- package/dist/build/worker.d.ts +0 -15
- package/dist/build/worker.d.ts.map +0 -1
- package/dist/build/worker.js +0 -21
- package/dist/build/worker.js.map +0 -1
- package/dist/build/wrangler-cli.d.ts +0 -88
- package/dist/build/wrangler-cli.d.ts.map +0 -1
- package/dist/build/wrangler-cli.js +0 -283
- package/dist/build/wrangler-cli.js.map +0 -1
- package/dist/cloudflare.d.ts.map +0 -1
- package/dist/cloudflare.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/params.d.ts.map +0 -1
- package/dist/params.js.map +0 -1
- package/dist/worker/fetch.d.ts.map +0 -1
- package/dist/worker/fetch.js.map +0 -1
- package/dist/worker/index.d.ts.map +0 -1
- package/dist/worker/index.js.map +0 -1
- package/src/build/codegen/identity.ts +0 -40
- package/src/build/codegen/merge.ts +0 -92
- package/src/build/codegen/worker.ts +0 -53
- package/src/build/codegen/wrangler.ts +0 -128
- package/src/build/frontend.ts +0 -298
- package/src/build/index.ts +0 -29
- package/src/build/parse-output.ts +0 -60
- package/src/build/prepare.ts +0 -189
- package/src/build/worker.ts +0 -37
- package/src/build/wrangler-cli.ts +0 -372
- package/src/cloudflare.ts +0 -275
- package/src/index.ts +0 -21
- package/src/params.ts +0 -92
- package/src/worker/fetch.ts +0 -46
- package/src/worker/index.ts +0 -332
- package/src/worker/tsconfig.json +0 -10
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/** Codegen for the Cloudflare module entry owned by the adapter. */
|
|
2
|
-
|
|
3
|
-
export interface WorkerCodegenOptions {
|
|
4
|
-
readonly origin: string
|
|
5
|
-
/** Optional same-zone routing service selected by the adapter environment. */
|
|
6
|
-
readonly router?: {
|
|
7
|
-
readonly binding: string
|
|
8
|
-
readonly hostSuffix: string
|
|
9
|
-
readonly minLabels: number
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function generateWorkerEntry(options: WorkerCodegenOptions): string {
|
|
14
|
-
const router = options.router
|
|
15
|
-
const routerPredicate =
|
|
16
|
-
router === undefined
|
|
17
|
-
? ''
|
|
18
|
-
: `
|
|
19
|
-
function isInstanceHost(host: string): boolean {
|
|
20
|
-
return host.endsWith('${router.hostSuffix}') && host.split('.').length >= ${router.minLabels}
|
|
21
|
-
}
|
|
22
|
-
`
|
|
23
|
-
const routeSubrequest =
|
|
24
|
-
router === undefined
|
|
25
|
-
? ''
|
|
26
|
-
: `
|
|
27
|
-
routeSubrequest: (url, env) => {
|
|
28
|
-
const labels = url.hostname.split('.')
|
|
29
|
-
if (env.DISPATCHER && labels[1] === 'svc') return env.DISPATCHER.get(labels[0]!)
|
|
30
|
-
if (env.${router.binding} && isInstanceHost(url.hostname)) return env.${router.binding}
|
|
31
|
-
return null
|
|
32
|
-
},`
|
|
33
|
-
|
|
34
|
-
return `// AUTO-GENERATED by @astrale-os/adapter-cloudflare — do not edit.
|
|
35
|
-
// Regenerated by every Astrale Domain build, dev, and deploy operation.
|
|
36
|
-
// Domain origin: ${options.origin}
|
|
37
|
-
import type { CloudflareAdapterBindings } from '@astrale-os/adapter-cloudflare/worker'
|
|
38
|
-
import type { ApplicationEnvironment } from '../implementation.js'
|
|
39
|
-
|
|
40
|
-
import { cloudflareWorkerEntry } from '@astrale-os/adapter-cloudflare/worker'
|
|
41
|
-
import { domain } from '../implementation.js'
|
|
42
|
-
import { PRIVATE_JWK } from './identity.js'
|
|
43
|
-
|
|
44
|
-
type WorkerEnv = CloudflareEnv & ApplicationEnvironment & CloudflareAdapterBindings
|
|
45
|
-
${routerPredicate}
|
|
46
|
-
export default cloudflareWorkerEntry<WorkerEnv>()({
|
|
47
|
-
definition: domain,
|
|
48
|
-
privateKey: PRIVATE_JWK,
|
|
49
|
-
resolveUrl: (env, requestOrigin) => env.WORKER_URL ?? requestOrigin,
|
|
50
|
-
selfBinding: (env) => env.SELF,${routeSubrequest}
|
|
51
|
-
})
|
|
52
|
-
`
|
|
53
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Codegen for the Worker config (`.astrale/wrangler.gen.jsonc`). This is the
|
|
3
|
-
* `wrangler.jsonc` that used to be hand-copied into every domain — now an
|
|
4
|
-
* internal adapter detail the dev never sees.
|
|
5
|
-
*
|
|
6
|
-
* `main` is `./worker.gen.ts` (same dir). `assets.directory` points at the
|
|
7
|
-
* adapter's materialized frontend stage. A `SELF` service binding enables autobinding
|
|
8
|
-
* (a handler calling back into its own domain). Custom-domain deploys attach a
|
|
9
|
-
* `custom_domain` route; otherwise the Worker ships to `*.workers.dev`.
|
|
10
|
-
*
|
|
11
|
-
* A dev's `params.wrangler` escape hatch is deep-merged on top of this base
|
|
12
|
-
* (extra bindings: KV, R2, D1, queues, …) — see `mergeUserConfig`.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { deepMergeConfig } from './merge.js'
|
|
16
|
-
|
|
17
|
-
export interface WranglerCodegenOptions {
|
|
18
|
-
workerName: string
|
|
19
|
-
/** Full custom hostname for a routed deploy (e.g. 'crm.acme.dev'). */
|
|
20
|
-
route?: string
|
|
21
|
-
/** Exact materialized local frontend; absence means no ASSETS binding. */
|
|
22
|
-
frontend?: {
|
|
23
|
-
readonly directory: string
|
|
24
|
-
readonly routes: readonly string[]
|
|
25
|
-
readonly dev: boolean
|
|
26
|
-
}
|
|
27
|
-
/** Plain (non-secret) vars to bake in. */
|
|
28
|
-
vars?: Record<string, string>
|
|
29
|
-
/** Add the SELF service binding (autobinding). */
|
|
30
|
-
selfBinding: boolean
|
|
31
|
-
/**
|
|
32
|
-
* Add a service binding to a router Worker for instance-host subrequest
|
|
33
|
-
* routing (see `CloudflareParams.router`). Pairs with the worker
|
|
34
|
-
* entry's `routeSubrequest`.
|
|
35
|
-
*/
|
|
36
|
-
router?: { binding: string; service: string }
|
|
37
|
-
/**
|
|
38
|
-
* Raw wrangler config to deep-merge over the generated base — the escape
|
|
39
|
-
* hatch for extra bindings (KV, R2, D1, queues, service bindings, extra
|
|
40
|
-
* `compatibility_flags`, cron `triggers`, …). Arrays concat; new keys are
|
|
41
|
-
* added as-is. The adapter-owned keys `name`, `main`, `assets`, `routes` are
|
|
42
|
-
* rejected (throws) — use `workerName` / `route` instead.
|
|
43
|
-
*/
|
|
44
|
-
wrangler?: Record<string, unknown>
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Wrangler 4.121.0's pinned workerd accepts dates through 2026-08-11.
|
|
48
|
-
const COMPATIBILITY_DATE = '2026-08-11'
|
|
49
|
-
|
|
50
|
-
export function generateWranglerConfig(opts: WranglerCodegenOptions): string {
|
|
51
|
-
const config: Record<string, unknown> = {
|
|
52
|
-
name: opts.workerName,
|
|
53
|
-
main: './worker.gen.ts',
|
|
54
|
-
compatibility_date: COMPATIBILITY_DATE,
|
|
55
|
-
compatibility_flags: ['nodejs_compat'],
|
|
56
|
-
workers_dev: true,
|
|
57
|
-
version_metadata: { binding: 'ASTRALE_VERSION_METADATA' },
|
|
58
|
-
observability: { enabled: true },
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (opts.route) {
|
|
62
|
-
config.routes = [{ pattern: opts.route, custom_domain: true }]
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (opts.frontend !== undefined) {
|
|
66
|
-
config.assets = {
|
|
67
|
-
directory: opts.frontend.directory,
|
|
68
|
-
binding: 'ASSETS',
|
|
69
|
-
run_worker_first: opts.frontend.dev
|
|
70
|
-
? true
|
|
71
|
-
: [
|
|
72
|
-
'/invoke',
|
|
73
|
-
'/invoke/*',
|
|
74
|
-
'/install',
|
|
75
|
-
'/install/*',
|
|
76
|
-
'/health',
|
|
77
|
-
'/health/*',
|
|
78
|
-
'/.well-known',
|
|
79
|
-
'/.well-known/*',
|
|
80
|
-
...opts.frontend.routes,
|
|
81
|
-
],
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const services: Array<{ binding: string; service: string }> = []
|
|
86
|
-
if (opts.selfBinding) services.push({ binding: 'SELF', service: opts.workerName })
|
|
87
|
-
if (opts.router) {
|
|
88
|
-
services.push({ binding: opts.router.binding, service: opts.router.service })
|
|
89
|
-
}
|
|
90
|
-
if (services.length > 0) config.services = services
|
|
91
|
-
|
|
92
|
-
if (opts.vars && Object.keys(opts.vars).length > 0) {
|
|
93
|
-
config.vars = opts.vars
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const merged = opts.wrangler ? mergeUserConfig(config, opts.wrangler) : config
|
|
97
|
-
return JSON.stringify(merged, null, 2) + '\n'
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Keys the adapter owns and won't let `params.wrangler` clobber. */
|
|
101
|
-
const ADAPTER_OWNED_KEYS = [
|
|
102
|
-
'name',
|
|
103
|
-
'main',
|
|
104
|
-
'assets',
|
|
105
|
-
'routes',
|
|
106
|
-
'compatibility_date',
|
|
107
|
-
'version_metadata',
|
|
108
|
-
]
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Deep-merge the dev's raw wrangler overlay over the generated config, after
|
|
112
|
-
* refusing any attempt to override an adapter-owned key. Throwing (rather than
|
|
113
|
-
* silently dropping) keeps a stray `main` from breaking the Worker unnoticed.
|
|
114
|
-
*/
|
|
115
|
-
function mergeUserConfig(
|
|
116
|
-
base: Record<string, unknown>,
|
|
117
|
-
user: Record<string, unknown>,
|
|
118
|
-
): Record<string, unknown> {
|
|
119
|
-
const collisions = ADAPTER_OWNED_KEYS.filter((k) => k in user)
|
|
120
|
-
if (collisions.length > 0) {
|
|
121
|
-
throw new Error(
|
|
122
|
-
`wrangler: cannot override adapter-managed key(s): ${collisions.join(', ')}. ` +
|
|
123
|
-
'Use `workerName` for the worker name and `route` for routing; ' +
|
|
124
|
-
'`main` and `assets` are generated by the adapter.',
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
return deepMergeConfig(base, user)
|
|
128
|
-
}
|
package/src/build/frontend.ts
DELETED
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import type { CompiledFrontendArtifact, DomainDefinition, FrontendFile } from '@astrale-os/sdk'
|
|
2
|
-
import type { ChildProcess } from 'node:child_process'
|
|
3
|
-
|
|
4
|
-
import { compileFrontend } from '@astrale-os/sdk'
|
|
5
|
-
import { spawn } from 'node:child_process'
|
|
6
|
-
import { existsSync } from 'node:fs'
|
|
7
|
-
import { mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises'
|
|
8
|
-
import { createServer } from 'node:net'
|
|
9
|
-
import { dirname, join, resolve, sep } from 'node:path'
|
|
10
|
-
|
|
11
|
-
export const FRONTEND_STAGE_DIR = '.astrale/frontend'
|
|
12
|
-
const FRONTEND_BUILD_DIR = '.astrale/frontend-build'
|
|
13
|
-
let stageGeneration = 0
|
|
14
|
-
const buildQueues = new Map<string, Promise<void>>()
|
|
15
|
-
|
|
16
|
-
export interface ViteBuildInput {
|
|
17
|
-
readonly projectDir: string
|
|
18
|
-
readonly output: string
|
|
19
|
-
readonly config?: string
|
|
20
|
-
readonly onLog?: (line: string) => void
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type ViteBuild = (input: ViteBuildInput) => Promise<void>
|
|
24
|
-
|
|
25
|
-
/** Materialize one Domain-owned frontend recipe into an immutable adapter artifact. */
|
|
26
|
-
export async function buildFrontend(input: {
|
|
27
|
-
readonly definition: DomainDefinition
|
|
28
|
-
readonly projectDir: string
|
|
29
|
-
readonly onLog?: (line: string) => void
|
|
30
|
-
readonly buildVite?: ViteBuild
|
|
31
|
-
}): Promise<CompiledFrontendArtifact | undefined> {
|
|
32
|
-
const key = resolve(input.projectDir)
|
|
33
|
-
const previous = buildQueues.get(key) ?? Promise.resolve()
|
|
34
|
-
let release = (): void => {}
|
|
35
|
-
const current = new Promise<void>((resolveBuild) => {
|
|
36
|
-
release = resolveBuild
|
|
37
|
-
})
|
|
38
|
-
const tail = previous.then(() => current)
|
|
39
|
-
buildQueues.set(key, tail)
|
|
40
|
-
await previous
|
|
41
|
-
try {
|
|
42
|
-
return await buildFrontendUnlocked(input)
|
|
43
|
-
} finally {
|
|
44
|
-
release()
|
|
45
|
-
if (buildQueues.get(key) === tail) buildQueues.delete(key)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function buildFrontendUnlocked(input: {
|
|
50
|
-
readonly definition: DomainDefinition
|
|
51
|
-
readonly projectDir: string
|
|
52
|
-
readonly onLog?: (line: string) => void
|
|
53
|
-
readonly buildVite?: ViteBuild
|
|
54
|
-
}): Promise<CompiledFrontendArtifact | undefined> {
|
|
55
|
-
const artifact = input.definition.frontend
|
|
56
|
-
if (artifact === undefined) return undefined
|
|
57
|
-
if (artifact.source.kind === 'external') {
|
|
58
|
-
return compileFrontend({
|
|
59
|
-
artifact,
|
|
60
|
-
files: [],
|
|
61
|
-
provenance: { adapter: 'cloudflare', source: 'external' },
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
const output = contained(input.projectDir, FRONTEND_BUILD_DIR, 'Vite frontend staging output')
|
|
65
|
-
await rm(output, { recursive: true, force: true })
|
|
66
|
-
await (input.buildVite ?? runViteBuild)({
|
|
67
|
-
projectDir: input.projectDir,
|
|
68
|
-
output,
|
|
69
|
-
...(artifact.source.config === undefined ? {} : { config: artifact.source.config }),
|
|
70
|
-
...(input.onLog === undefined ? {} : { onLog: input.onLog }),
|
|
71
|
-
})
|
|
72
|
-
const files = await readFiles(output)
|
|
73
|
-
const compiled = await compileFrontend({
|
|
74
|
-
artifact,
|
|
75
|
-
baseHref: 'https://frontend.invalid/',
|
|
76
|
-
files,
|
|
77
|
-
provenance: { adapter: 'cloudflare', source: artifact.source.kind },
|
|
78
|
-
})
|
|
79
|
-
await replaceStage(input.projectDir, compiled.files)
|
|
80
|
-
return compiled
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface FrontendDevHandle {
|
|
84
|
-
readonly url: string
|
|
85
|
-
stop(): Promise<void>
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Start the Domain project's own Vite server; the Worker remains the browser-facing origin. */
|
|
89
|
-
export async function startFrontendDev(input: {
|
|
90
|
-
readonly definition: DomainDefinition
|
|
91
|
-
readonly projectDir: string
|
|
92
|
-
readonly onLog?: (line: string) => void
|
|
93
|
-
}): Promise<FrontendDevHandle | undefined> {
|
|
94
|
-
const artifact = input.definition.frontend
|
|
95
|
-
if (artifact === undefined || artifact.source.kind !== 'vite') return undefined
|
|
96
|
-
const command = viteCommand(input.projectDir)
|
|
97
|
-
const port = await ephemeralPort('127.0.0.1')
|
|
98
|
-
const args = [
|
|
99
|
-
...command.prefix,
|
|
100
|
-
'--host',
|
|
101
|
-
'127.0.0.1',
|
|
102
|
-
'--port',
|
|
103
|
-
String(port),
|
|
104
|
-
'--strictPort',
|
|
105
|
-
...(artifact.source.config === undefined ? [] : ['--config', artifact.source.config]),
|
|
106
|
-
]
|
|
107
|
-
const child = spawn(command.command, args, {
|
|
108
|
-
cwd: input.projectDir,
|
|
109
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
110
|
-
})
|
|
111
|
-
const output: string[] = []
|
|
112
|
-
const collect = (buffer: Buffer) => {
|
|
113
|
-
for (const line of buffer.toString().split('\n')) {
|
|
114
|
-
if (line.trim() === '') continue
|
|
115
|
-
output.push(line)
|
|
116
|
-
if (output.length > 40) output.shift()
|
|
117
|
-
input.onLog?.(line)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
child.stdout?.on('data', collect)
|
|
121
|
-
child.stderr?.on('data', collect)
|
|
122
|
-
const url = `http://127.0.0.1:${port}`
|
|
123
|
-
try {
|
|
124
|
-
await waitUntilReady(url, child, output)
|
|
125
|
-
} catch (error) {
|
|
126
|
-
await stopChild(child)
|
|
127
|
-
throw error
|
|
128
|
-
}
|
|
129
|
-
return Object.freeze({ url, stop: () => stopChild(child) })
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
async function readFiles(root: string, current = ''): Promise<readonly FrontendFile[]> {
|
|
133
|
-
const directory = join(root, current)
|
|
134
|
-
const entries = await readdir(directory, { withFileTypes: true })
|
|
135
|
-
const files: FrontendFile[] = []
|
|
136
|
-
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
137
|
-
const path = current === '' ? entry.name : `${current}/${entry.name}`
|
|
138
|
-
if (entry.isDirectory()) files.push(...(await readFiles(root, path)))
|
|
139
|
-
else if (entry.isFile())
|
|
140
|
-
files.push({ path, content: new Uint8Array(await readFile(join(root, path))) })
|
|
141
|
-
}
|
|
142
|
-
return files
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
async function replaceStage(projectDir: string, files: readonly FrontendFile[]): Promise<void> {
|
|
146
|
-
const stage = contained(projectDir, FRONTEND_STAGE_DIR, 'frontend stage')
|
|
147
|
-
const generation = `${process.pid}-${++stageGeneration}`
|
|
148
|
-
const next = `${stage}.next-${generation}`
|
|
149
|
-
const previous = `${stage}.previous-${generation}`
|
|
150
|
-
await rm(next, { recursive: true, force: true })
|
|
151
|
-
await mkdir(next, { recursive: true })
|
|
152
|
-
try {
|
|
153
|
-
for (const file of files) {
|
|
154
|
-
const path = contained(next, file.path, 'staged frontend file')
|
|
155
|
-
await mkdir(dirname(path), { recursive: true })
|
|
156
|
-
await writeFile(path, file.content)
|
|
157
|
-
}
|
|
158
|
-
await rm(previous, { recursive: true, force: true })
|
|
159
|
-
let retainedPrevious = false
|
|
160
|
-
try {
|
|
161
|
-
await rename(stage, previous)
|
|
162
|
-
retainedPrevious = true
|
|
163
|
-
} catch (error) {
|
|
164
|
-
if (!isMissing(error)) throw error
|
|
165
|
-
}
|
|
166
|
-
try {
|
|
167
|
-
await rename(next, stage)
|
|
168
|
-
} catch (error) {
|
|
169
|
-
if (retainedPrevious) await rename(previous, stage)
|
|
170
|
-
throw error
|
|
171
|
-
}
|
|
172
|
-
if (retainedPrevious) await rm(previous, { recursive: true, force: true })
|
|
173
|
-
} finally {
|
|
174
|
-
await rm(next, { recursive: true, force: true })
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function isMissing(error: unknown): boolean {
|
|
179
|
-
return error instanceof Error && 'code' in error && error.code === 'ENOENT'
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
function contained(root: string, input: string, label: string): string {
|
|
183
|
-
const base = resolve(root)
|
|
184
|
-
const target = resolve(base, input)
|
|
185
|
-
if (target !== base && !target.startsWith(`${base}${sep}`)) {
|
|
186
|
-
throw new TypeError(`${label} must remain within the Domain project root.`)
|
|
187
|
-
}
|
|
188
|
-
return target
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function viteCommand(projectDir: string): { command: string; prefix: readonly string[] } {
|
|
192
|
-
const command = join(projectDir, 'node_modules', '.bin', 'vite')
|
|
193
|
-
if (!existsSync(command)) {
|
|
194
|
-
throw new TypeError(
|
|
195
|
-
'Vite frontend requires the Domain project to install vite as a development dependency.',
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
|
-
return { command, prefix: [] }
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
async function runViteBuild(input: ViteBuildInput): Promise<void> {
|
|
202
|
-
const command = viteCommand(input.projectDir)
|
|
203
|
-
await runProcess(
|
|
204
|
-
command.command,
|
|
205
|
-
[
|
|
206
|
-
...command.prefix,
|
|
207
|
-
'build',
|
|
208
|
-
'--outDir',
|
|
209
|
-
input.output,
|
|
210
|
-
'--emptyOutDir',
|
|
211
|
-
...(input.config === undefined ? [] : ['--config', input.config]),
|
|
212
|
-
],
|
|
213
|
-
input.projectDir,
|
|
214
|
-
input.onLog,
|
|
215
|
-
)
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function runProcess(
|
|
219
|
-
command: string,
|
|
220
|
-
args: readonly string[],
|
|
221
|
-
cwd: string,
|
|
222
|
-
onLog?: (line: string) => void,
|
|
223
|
-
): Promise<void> {
|
|
224
|
-
return new Promise((resolveBuild, reject) => {
|
|
225
|
-
const child = spawn(command, args, { cwd, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
226
|
-
let output = ''
|
|
227
|
-
const collect = (buffer: Buffer) => {
|
|
228
|
-
const value = buffer.toString()
|
|
229
|
-
output += value
|
|
230
|
-
for (const line of value.split('\n')) if (line.trim() !== '') onLog?.(line)
|
|
231
|
-
}
|
|
232
|
-
child.stdout?.on('data', collect)
|
|
233
|
-
child.stderr?.on('data', collect)
|
|
234
|
-
child.on('error', reject)
|
|
235
|
-
child.on('exit', (code) =>
|
|
236
|
-
code === 0
|
|
237
|
-
? resolveBuild()
|
|
238
|
-
: reject(
|
|
239
|
-
new Error(`Vite frontend build failed (code ${code ?? 1}):\n${output.slice(-1500)}`),
|
|
240
|
-
),
|
|
241
|
-
)
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function ephemeralPort(host: string): Promise<number> {
|
|
246
|
-
return new Promise((resolvePort, reject) => {
|
|
247
|
-
const server = createServer()
|
|
248
|
-
server.once('error', reject)
|
|
249
|
-
server.listen(0, host, () => {
|
|
250
|
-
const address = server.address()
|
|
251
|
-
const port = typeof address === 'object' && address !== null ? address.port : 0
|
|
252
|
-
server.close((error) => (error === undefined ? resolvePort(port) : reject(error)))
|
|
253
|
-
})
|
|
254
|
-
})
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
async function waitUntilReady(
|
|
258
|
-
url: string,
|
|
259
|
-
child: ChildProcess,
|
|
260
|
-
output: readonly string[],
|
|
261
|
-
): Promise<void> {
|
|
262
|
-
const deadline = Date.now() + 15_000
|
|
263
|
-
while (Date.now() < deadline) {
|
|
264
|
-
if (child.exitCode !== null) {
|
|
265
|
-
throw new Error(
|
|
266
|
-
`Vite frontend dev server exited before becoming ready (code ${child.exitCode}).\n${output.join('\n')}`,
|
|
267
|
-
)
|
|
268
|
-
}
|
|
269
|
-
try {
|
|
270
|
-
const response = await fetch(url, { signal: AbortSignal.timeout(500) })
|
|
271
|
-
await response.body?.cancel()
|
|
272
|
-
return
|
|
273
|
-
} catch {
|
|
274
|
-
await new Promise((resolveDelay) => setTimeout(resolveDelay, 50))
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
throw new Error(`Vite frontend dev server did not become ready at ${url}.\n${output.join('\n')}`)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function stopChild(child: ChildProcess): Promise<void> {
|
|
281
|
-
if (child.exitCode !== null || child.killed) return Promise.resolve()
|
|
282
|
-
return new Promise((resolveStop) => {
|
|
283
|
-
let settled = false
|
|
284
|
-
const finish = () => {
|
|
285
|
-
if (settled) return
|
|
286
|
-
settled = true
|
|
287
|
-
clearTimeout(timer)
|
|
288
|
-
resolveStop()
|
|
289
|
-
}
|
|
290
|
-
const timer = setTimeout(() => {
|
|
291
|
-
if (child.exitCode === null) child.kill('SIGKILL')
|
|
292
|
-
finish()
|
|
293
|
-
}, 2_000)
|
|
294
|
-
timer.unref?.()
|
|
295
|
-
child.once('exit', finish)
|
|
296
|
-
child.kill('SIGTERM')
|
|
297
|
-
})
|
|
298
|
-
}
|
package/src/build/index.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
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 {
|
|
14
|
-
buildFrontend,
|
|
15
|
-
FRONTEND_STAGE_DIR,
|
|
16
|
-
startFrontendDev,
|
|
17
|
-
type FrontendDevHandle,
|
|
18
|
-
type ViteBuild,
|
|
19
|
-
type ViteBuildInput,
|
|
20
|
-
} from './frontend.js'
|
|
21
|
-
export { logTo, prepare } from './prepare.js'
|
|
22
|
-
export { buildCloudflareWorker, type CloudflareWorkerBuild } from './worker.js'
|
|
23
|
-
export {
|
|
24
|
-
bulkPutSecrets,
|
|
25
|
-
runWranglerBundle,
|
|
26
|
-
runWranglerDeploy,
|
|
27
|
-
runWranglerDev,
|
|
28
|
-
runWranglerTypes,
|
|
29
|
-
} from './wrangler-cli.js'
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pure parsers for `wrangler` stdout. Kept separate from the process-spawning in
|
|
3
|
-
* `wrangler-cli.ts` so the brittle bit — scraping wrangler's human output — is
|
|
4
|
-
* unit-testable against captured fixtures without shelling out.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const READY_RE = /(?:Ready on|Listening on|⎔.*?at)\s+(https?:\/\/[^\s]+)/i
|
|
8
|
-
const WORKERS_DEV_RE = /(https:\/\/[^\s]+\.workers\.dev)/i
|
|
9
|
-
const VERSION_ID_RE = /(?:Current\s+)?Version ID:\s*([a-z0-9_-]+)/i
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Local dev URL from `wrangler dev` output, normalized to an origin on the given
|
|
13
|
-
* port. Returns `undefined` when the output hasn't reported readiness yet.
|
|
14
|
-
*
|
|
15
|
-
* The fallback (when no explicit "Ready on" line) only accepts a localhost URL
|
|
16
|
-
* on the EXPECTED port — wrangler may print unrelated localhost URLs (e.g. an
|
|
17
|
-
* inspector/devtools endpoint on a different port) before readiness, and taking
|
|
18
|
-
* one of those would point `astrale domain install <url>` at a dead endpoint.
|
|
19
|
-
*/
|
|
20
|
-
export function parseDevReadyUrl(text: string, port: number): string | undefined {
|
|
21
|
-
const ready = READY_RE.exec(text)
|
|
22
|
-
if (ready) return normalizeLocal(ready[1]!, port)
|
|
23
|
-
// `port` is a number, so it's safe to interpolate into the pattern. The
|
|
24
|
-
// negative lookahead stops `:8787` from matching inside `:87870`.
|
|
25
|
-
const localhostOnPort = new RegExp(
|
|
26
|
-
`(https?://(?:localhost|127\\.0\\.0\\.1):${port})(?![0-9])`,
|
|
27
|
-
'i',
|
|
28
|
-
)
|
|
29
|
-
const m = localhostOnPort.exec(text)
|
|
30
|
-
return m ? normalizeLocal(m[1]!, port) : undefined
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Authoritative public URL from `wrangler deploy` output. A custom-domain
|
|
35
|
-
* `route` wins (the deploy attaches it); otherwise the printed `*.workers.dev`
|
|
36
|
-
* URL. Returns `undefined` when neither is available (caller surfaces the raw
|
|
37
|
-
* output).
|
|
38
|
-
*/
|
|
39
|
-
export function parseDeployUrl(text: string, route?: string): string | undefined {
|
|
40
|
-
if (route) return `https://${route.replace(/\/.*$/, '')}`
|
|
41
|
-
const m = WORKERS_DEV_RE.exec(text)
|
|
42
|
-
return m ? m[1] : undefined
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Cloudflare version published by the final Wrangler deploy pass. */
|
|
46
|
-
export function parseDeployVersionId(text: string): string | undefined {
|
|
47
|
-
return VERSION_ID_RE.exec(text)?.[1]
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Normalize a local dev URL to an origin: `0.0.0.0` → `localhost`, fill the port. */
|
|
51
|
-
export function normalizeLocal(url: string, port: number): string {
|
|
52
|
-
try {
|
|
53
|
-
const u = new URL(url)
|
|
54
|
-
if (u.hostname === '0.0.0.0') u.hostname = 'localhost'
|
|
55
|
-
if (!u.port) u.port = String(port)
|
|
56
|
-
return u.origin
|
|
57
|
-
} catch {
|
|
58
|
-
return `http://localhost:${port}`
|
|
59
|
-
}
|
|
60
|
-
}
|