@astrale-os/adapter-cloudflare 0.5.0-beta.17 → 0.5.0-beta.170
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/README.md +230 -2
- package/dist/adapter.d.ts +24 -0
- package/dist/adapter.js +59 -0
- package/dist/build/bundle/admit.d.ts +3 -0
- package/dist/build/bundle/admit.js +46 -0
- package/dist/build/bundle/compiled-wasm-loader.d.ts +8 -0
- package/dist/build/bundle/compiled-wasm-loader.js +21 -0
- package/dist/build/bundler.d.ts +21 -0
- package/dist/build/bundler.js +96 -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 +32 -6
- package/dist/build/codegen/wrangler.js +77 -21
- 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/settings.d.ts +6 -0
- package/dist/build/configuration/settings.js +42 -0
- package/dist/build/configuration.d.ts +4 -0
- package/dist/build/configuration.js +4 -0
- package/dist/build/frontend/files.d.ts +19 -0
- package/dist/build/frontend/files.js +99 -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 +14 -0
- package/dist/build/frontend/vite.js +42 -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 +155 -0
- package/dist/build/index.d.ts +16 -14
- package/dist/build/index.js +15 -14
- package/dist/build/material.d.ts +10 -0
- package/dist/build/material.js +25 -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 +10 -0
- package/dist/build/worker/codegen/entry.js +65 -0
- package/dist/build/worker/codegen/literal.d.ts +28 -0
- package/dist/build/worker/codegen/literal.js +136 -0
- package/dist/build/worker/codegen/service.d.ts +14 -0
- package/dist/build/worker/codegen/service.js +58 -0
- package/dist/build/worker/execution.prepare.d.ts +20 -0
- package/dist/build/worker/execution.prepare.js +129 -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 +17 -0
- package/dist/build/worker/modules.js +32 -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 +5 -0
- package/dist/build/worker/specifier.js +15 -0
- package/dist/build/worker/upload.d.ts +39 -0
- package/dist/build/worker/upload.js +80 -0
- package/dist/cloudflare.d.ts +1 -45
- package/dist/cloudflare.js +1 -202
- package/dist/identity/binding.d.ts +2 -0
- package/dist/identity/binding.js +2 -0
- package/dist/identity/material.d.ts +7 -0
- package/dist/identity/material.js +42 -0
- package/dist/index.d.ts +16 -13
- package/dist/index.js +15 -12
- package/dist/legacy/direct/addressing.d.ts +2 -0
- package/dist/legacy/direct/addressing.js +17 -0
- package/dist/legacy/direct/deploy.d.ts +17 -0
- package/dist/legacy/direct/deploy.js +243 -0
- package/dist/legacy/direct/identity/index.d.ts +11 -0
- package/dist/legacy/direct/identity/index.js +11 -0
- package/dist/legacy/direct/identity/secret.d.ts +3 -0
- package/dist/legacy/direct/identity/secret.js +7 -0
- package/dist/legacy/direct/identity/source.d.ts +26 -0
- package/dist/legacy/direct/identity/source.js +127 -0
- package/dist/legacy/direct/index.d.ts +27 -0
- package/dist/legacy/direct/index.js +27 -0
- package/dist/legacy/direct/materialize.d.ts +4 -0
- package/dist/legacy/direct/materialize.js +10 -0
- package/dist/legacy/direct/placement.d.ts +4 -0
- package/dist/legacy/direct/placement.js +91 -0
- package/dist/legacy/direct/prepare.d.ts +7 -0
- package/dist/legacy/direct/prepare.js +158 -0
- package/dist/legacy/direct/readiness.d.ts +34 -0
- package/dist/legacy/direct/readiness.js +95 -0
- package/dist/legacy/direct/vars.d.ts +27 -0
- package/dist/legacy/direct/vars.js +53 -0
- package/dist/legacy/direct/worker-name.d.ts +2 -0
- package/dist/legacy/direct/worker-name.js +18 -0
- package/dist/legacy/direct/worker.d.ts +37 -0
- package/dist/legacy/direct/worker.js +58 -0
- package/dist/legacy/direct/wrangler/deploy.d.ts +24 -0
- package/dist/legacy/direct/wrangler/deploy.js +44 -0
- package/dist/legacy/direct/wrangler/output.d.ts +2 -0
- package/dist/legacy/direct/wrangler/output.js +10 -0
- package/dist/legacy/direct/wrangler/rollout.d.ts +27 -0
- package/dist/legacy/direct/wrangler/rollout.js +75 -0
- package/dist/legacy/direct/wrangler/secrets.d.ts +2 -0
- package/dist/legacy/direct/wrangler/secrets.js +24 -0
- package/dist/legacy/direct/wrangler/status.d.ts +18 -0
- package/dist/legacy/direct/wrangler/status.js +75 -0
- package/dist/legacy/direct/wrangler/triggers.d.ts +5 -0
- package/dist/legacy/direct/wrangler/triggers.js +12 -0
- package/dist/legacy/direct/wrangler/versions.d.ts +13 -0
- package/dist/legacy/direct/wrangler/versions.js +38 -0
- package/dist/namespace/api.d.ts +71 -0
- package/dist/namespace/api.js +265 -0
- package/dist/namespace/calls.d.ts +29 -0
- package/dist/namespace/calls.js +104 -0
- package/dist/namespace/configure.d.ts +26 -0
- package/dist/namespace/configure.js +199 -0
- package/dist/namespace/coordinates.d.ts +44 -0
- package/dist/namespace/coordinates.js +99 -0
- package/dist/namespace/deploy-release.d.ts +56 -0
- package/dist/namespace/deploy-release.js +403 -0
- package/dist/namespace/index.d.ts +15 -0
- package/dist/namespace/index.js +15 -0
- package/dist/namespace/list.d.ts +33 -0
- package/dist/namespace/list.js +139 -0
- package/dist/namespace/readiness.d.ts +40 -0
- package/dist/namespace/readiness.js +81 -0
- package/dist/namespace/routing.d.ts +55 -0
- package/dist/namespace/routing.js +165 -0
- package/dist/namespace/script.d.ts +106 -0
- package/dist/namespace/script.js +278 -0
- package/dist/params.d.ts +118 -40
- package/dist/params.js +0 -1
- package/dist/release/build.d.ts +35 -0
- package/dist/release/build.js +44 -0
- package/dist/release/deployment.d.ts +44 -0
- package/dist/release/deployment.js +141 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +3 -0
- package/dist/release/staged-worker-name.d.ts +3 -0
- package/dist/release/staged-worker-name.js +10 -0
- package/dist/worker/build-digest.d.ts +16 -0
- package/dist/worker/build-digest.js +34 -0
- package/dist/worker/durable.d.ts +11 -0
- package/dist/worker/durable.js +24 -0
- package/dist/worker/entry.d.ts +36 -0
- package/dist/worker/entry.js +138 -0
- package/dist/worker/fetch.d.ts +0 -1
- package/dist/worker/fetch.js +0 -1
- package/dist/worker/front.d.ts +75 -0
- package/dist/worker/front.js +64 -0
- package/dist/worker/frontend.d.ts +4 -0
- package/dist/worker/frontend.js +83 -0
- package/dist/worker/identity.d.ts +7 -0
- package/dist/worker/identity.js +23 -0
- package/dist/worker/index.d.ts +6 -61
- package/dist/worker/index.js +6 -221
- package/dist/worker/restate.d.ts +6 -0
- package/dist/worker/restate.js +6 -0
- package/dist/worker/settings.d.ts +48 -0
- package/dist/worker/settings.js +161 -0
- package/dist/wrangler/bundle.d.ts +10 -0
- package/dist/wrangler/bundle.js +27 -0
- package/dist/wrangler/index.d.ts +3 -0
- package/dist/wrangler/index.js +3 -0
- package/dist/wrangler/process.d.ts +15 -0
- package/dist/wrangler/process.js +102 -0
- package/dist/wrangler/type-generation.d.ts +6 -0
- package/dist/wrangler/type-generation.js +19 -0
- package/package.json +45 -20
- package/template/astrale.config.ts +29 -30
- 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 -335
- package/src/worker/tsconfig.json +0 -10
|
@@ -12,17 +12,27 @@
|
|
|
12
12
|
* (extra bindings: KV, R2, D1, queues, …) — see `mergeUserConfig`.
|
|
13
13
|
*/
|
|
14
14
|
import { deepMergeConfig } from './merge.js';
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The Workers runtime every generated Worker runs on, wherever it is deployed: its compatibility
|
|
17
|
+
* date, kept on the runtime baseline qualified with Wrangler 4.123.0, and its compatibility flags.
|
|
18
|
+
* Domain execution calls its Kernel through the invocation's public issuer, so Cloudflare must route
|
|
19
|
+
* same-zone callbacks through the public front door instead of bypassing Worker routes
|
|
20
|
+
* (`global_fetch_strictly_public`). An immutable deployment freezes it into its configuration.
|
|
21
|
+
*/
|
|
22
|
+
export const GENERATED_WORKER_RUNTIME = Object.freeze({
|
|
23
|
+
compatibilityDate: '2026-08-11',
|
|
24
|
+
compatibilityFlags: Object.freeze(['nodejs_compat', 'global_fetch_strictly_public']),
|
|
25
|
+
});
|
|
17
26
|
export function generateWranglerConfig(opts) {
|
|
18
27
|
const config = {
|
|
19
28
|
name: opts.workerName,
|
|
20
|
-
main: './worker.gen.ts',
|
|
21
|
-
compatibility_date:
|
|
22
|
-
compatibility_flags: [
|
|
23
|
-
workers_dev:
|
|
29
|
+
main: opts.main ?? './worker.gen.ts',
|
|
30
|
+
compatibility_date: GENERATED_WORKER_RUNTIME.compatibilityDate,
|
|
31
|
+
compatibility_flags: [...GENERATED_WORKER_RUNTIME.compatibilityFlags],
|
|
32
|
+
workers_dev: opts.route === undefined,
|
|
24
33
|
version_metadata: { binding: 'ASTRALE_VERSION_METADATA' },
|
|
25
34
|
observability: { enabled: true },
|
|
35
|
+
minify: true,
|
|
26
36
|
};
|
|
27
37
|
if (opts.route) {
|
|
28
38
|
config.routes = [{ pattern: opts.route, custom_domain: true }];
|
|
@@ -31,19 +41,10 @@ export function generateWranglerConfig(opts) {
|
|
|
31
41
|
config.assets = {
|
|
32
42
|
directory: opts.frontend.directory,
|
|
33
43
|
binding: 'ASSETS',
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
'/invoke/*',
|
|
39
|
-
'/install',
|
|
40
|
-
'/install/*',
|
|
41
|
-
'/health',
|
|
42
|
-
'/health/*',
|
|
43
|
-
'/.well-known',
|
|
44
|
-
'/.well-known/*',
|
|
45
|
-
...opts.frontend.routes,
|
|
46
|
-
],
|
|
44
|
+
html_handling: 'none',
|
|
45
|
+
// Match the portable Worker artifact: frontend routing and sandbox-safe asset
|
|
46
|
+
// response headers are owned by the Worker for both deployment adapters.
|
|
47
|
+
run_worker_first: true,
|
|
47
48
|
};
|
|
48
49
|
}
|
|
49
50
|
const services = [];
|
|
@@ -57,6 +58,18 @@ export function generateWranglerConfig(opts) {
|
|
|
57
58
|
if (opts.vars && Object.keys(opts.vars).length > 0) {
|
|
58
59
|
config.vars = opts.vars;
|
|
59
60
|
}
|
|
61
|
+
if (opts.secretBindings && opts.secretBindings.length > 0) {
|
|
62
|
+
config.secrets = { required: [...opts.secretBindings] };
|
|
63
|
+
}
|
|
64
|
+
if (opts.modules !== undefined) {
|
|
65
|
+
config.find_additional_modules = true;
|
|
66
|
+
config.base_dir = opts.modules.baseDir;
|
|
67
|
+
config.rules = opts.modules.rules.map((rule) => ({
|
|
68
|
+
type: rule.type,
|
|
69
|
+
globs: [...rule.globs],
|
|
70
|
+
fallthrough: true,
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
60
73
|
const merged = opts.wrangler ? mergeUserConfig(config, opts.wrangler) : config;
|
|
61
74
|
return JSON.stringify(merged, null, 2) + '\n';
|
|
62
75
|
}
|
|
@@ -67,11 +80,43 @@ const ADAPTER_OWNED_KEYS = [
|
|
|
67
80
|
'assets',
|
|
68
81
|
'routes',
|
|
69
82
|
'compatibility_date',
|
|
83
|
+
'workers_dev',
|
|
70
84
|
'version_metadata',
|
|
85
|
+
'secrets',
|
|
86
|
+
'find_additional_modules',
|
|
87
|
+
'base_dir',
|
|
71
88
|
];
|
|
89
|
+
/**
|
|
90
|
+
* Keys Wrangler's bundler reads. Bundling sees the adapter base configuration only, so that every
|
|
91
|
+
* Environment of one source bundles the same bytes; an Environment's overlay never sets these.
|
|
92
|
+
*/
|
|
93
|
+
const BUNDLING_KEYS = [
|
|
94
|
+
'alias',
|
|
95
|
+
'build',
|
|
96
|
+
'define',
|
|
97
|
+
'jsx_factory',
|
|
98
|
+
'jsx_fragment',
|
|
99
|
+
'keep_names',
|
|
100
|
+
'minify',
|
|
101
|
+
'no_bundle',
|
|
102
|
+
'node_compat',
|
|
103
|
+
'preserve_file_names',
|
|
104
|
+
'rules',
|
|
105
|
+
'tsconfig',
|
|
106
|
+
'upload_source_maps',
|
|
107
|
+
];
|
|
108
|
+
/** Compatibility flags Wrangler's bundler reads: Node.js polyfills, `navigator`, fetch typing, Python. */
|
|
109
|
+
const BUNDLING_FLAG = /nodejs|navigator|fetch_iterable_type_support|python_workers/u;
|
|
110
|
+
/**
|
|
111
|
+
* Whether a compatibility flag shapes the bundle (Wrangler's bundler reads it), so that no
|
|
112
|
+
* Environment may add it: bundling takes no Environment.
|
|
113
|
+
*/
|
|
114
|
+
export function isBundlingCompatibilityFlag(flag) {
|
|
115
|
+
return BUNDLING_FLAG.test(flag);
|
|
116
|
+
}
|
|
72
117
|
/**
|
|
73
118
|
* Deep-merge the dev's raw wrangler overlay over the generated config, after
|
|
74
|
-
* refusing any attempt to override an adapter-owned key. Throwing (rather than
|
|
119
|
+
* refusing any attempt to override an adapter-owned key or a bundler input. Throwing (rather than
|
|
75
120
|
* silently dropping) keeps a stray `main` from breaking the Worker unnoticed.
|
|
76
121
|
*/
|
|
77
122
|
function mergeUserConfig(base, user) {
|
|
@@ -81,6 +126,17 @@ function mergeUserConfig(base, user) {
|
|
|
81
126
|
'Use `workerName` for the worker name and `route` for routing; ' +
|
|
82
127
|
'`main` and `assets` are generated by the adapter.');
|
|
83
128
|
}
|
|
129
|
+
const baseFlags = Array.isArray(base.compatibility_flags) ? base.compatibility_flags : [];
|
|
130
|
+
const bundling = [
|
|
131
|
+
...BUNDLING_KEYS.filter((k) => k in user),
|
|
132
|
+
...(Array.isArray(user.compatibility_flags) ? user.compatibility_flags : [])
|
|
133
|
+
.filter((flag) => typeof flag === 'string' && BUNDLING_FLAG.test(flag))
|
|
134
|
+
.filter((flag) => !baseFlags.includes(flag))
|
|
135
|
+
.map((flag) => `compatibility_flags ${String(flag)}`),
|
|
136
|
+
];
|
|
137
|
+
if (bundling.length > 0) {
|
|
138
|
+
throw new Error(`wrangler: ${bundling.join(', ')} shape(s) the bundle, which takes no Environment. ` +
|
|
139
|
+
'Bundling uses the adapter base configuration only.');
|
|
140
|
+
}
|
|
84
141
|
return deepMergeConfig(base, user);
|
|
85
142
|
}
|
|
86
|
-
//# sourceMappingURL=wrangler.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MAXIMUM_INVOCATION_TIMEOUT_MS } from '../../worker/settings.js';
|
|
2
|
+
export function workerInvocationTimeout(value) {
|
|
3
|
+
if (value === undefined)
|
|
4
|
+
return undefined;
|
|
5
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > MAXIMUM_INVOCATION_TIMEOUT_MS) {
|
|
6
|
+
throw new TypeError('Cloudflare invocationTimeoutMs must be a positive integer within the platform timer range.');
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { MAXIMUM_REQUEST_BYTES } from '../../worker/settings.js';
|
|
2
|
+
export function workerRequestLimit(value) {
|
|
3
|
+
if (value === undefined)
|
|
4
|
+
return undefined;
|
|
5
|
+
if (!Number.isSafeInteger(value) || value <= 0 || value > MAXIMUM_REQUEST_BYTES) {
|
|
6
|
+
throw new TypeError('Cloudflare maximumRequestBytes must be a positive integer at most 16 MiB.');
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CloudflareParams } from '../../params.js';
|
|
2
|
+
export declare function resolveRouter(input: CloudflareParams['router']): {
|
|
3
|
+
readonly binding: string;
|
|
4
|
+
readonly service: string;
|
|
5
|
+
readonly hostSuffix: string;
|
|
6
|
+
readonly minLabels: number;
|
|
7
|
+
} | undefined;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function resolveRouter(input) {
|
|
2
|
+
if (input === false || input === undefined)
|
|
3
|
+
return undefined;
|
|
4
|
+
const value = input;
|
|
5
|
+
return Object.freeze({
|
|
6
|
+
binding: value.binding ?? 'ROUTER',
|
|
7
|
+
service: value.service ?? 'admin-router',
|
|
8
|
+
hostSuffix: value.hostSuffix ?? '.astrale.ai',
|
|
9
|
+
minLabels: value.minLabels ?? 4,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CloudflareParams } from '../../params.js';
|
|
2
|
+
import type { WorkerSettings } from '../../worker/settings.js';
|
|
3
|
+
/** Whether a binding name is one the adapter owns, so no author var or secret may take it. */
|
|
4
|
+
export declare function isAdapterVar(name: string): boolean;
|
|
5
|
+
/** Resolve one Environment's execution settings, defaults included, as its deployment binds them. */
|
|
6
|
+
export declare function workerSettings(parameters: CloudflareParams): WorkerSettings;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { maximumExchangeTtl } from '@astrale-os/sdk/execution';
|
|
2
|
+
import { BUILD_DIGEST_BINDING } from '../../worker/build-digest.js';
|
|
3
|
+
import { captureDurableOptions, DEFAULT_INVOCATION_TIMEOUT_MS, DEFAULT_MAXIMUM_REQUEST_BYTES, SETTINGS_BINDING, } from '../../worker/settings.js';
|
|
4
|
+
import { workerInvocationTimeout } from './invocation-timeout.js';
|
|
5
|
+
import { workerRequestLimit } from './request-limit.js';
|
|
6
|
+
import { resolveRouter } from './router.js';
|
|
7
|
+
/**
|
|
8
|
+
* Plain bindings the adapter owns on a deployment, bound or not; no author var or secret takes
|
|
9
|
+
* these names. `IDENTITY_ISS` and `ASTRALE_PROVIDER_ASSIGNED_WORKER` belong to legacy direct mode.
|
|
10
|
+
*/
|
|
11
|
+
const ADAPTER_VARS = new Set([
|
|
12
|
+
'WORKER_URL',
|
|
13
|
+
BUILD_DIGEST_BINDING,
|
|
14
|
+
SETTINGS_BINDING,
|
|
15
|
+
'IDENTITY_ISS',
|
|
16
|
+
'ASTRALE_PROVIDER_ASSIGNED_WORKER',
|
|
17
|
+
]);
|
|
18
|
+
/** Whether a binding name is one the adapter owns, so no author var or secret may take it. */
|
|
19
|
+
export function isAdapterVar(name) {
|
|
20
|
+
return ADAPTER_VARS.has(name);
|
|
21
|
+
}
|
|
22
|
+
/** Resolve one Environment's execution settings, defaults included, as its deployment binds them. */
|
|
23
|
+
export function workerSettings(parameters) {
|
|
24
|
+
const router = resolveRouter(parameters.router);
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
...(parameters.durable === undefined
|
|
27
|
+
? {}
|
|
28
|
+
: { durableOptions: captureDurableOptions(parameters.durable) }),
|
|
29
|
+
maximumRequestBytes: workerRequestLimit(parameters.maximumRequestBytes) ?? DEFAULT_MAXIMUM_REQUEST_BYTES,
|
|
30
|
+
invocationTimeoutMs: workerInvocationTimeout(parameters.invocationTimeoutMs) ?? DEFAULT_INVOCATION_TIMEOUT_MS,
|
|
31
|
+
tokenExchange: parameters.tokenExchange === false
|
|
32
|
+
? false
|
|
33
|
+
: Object.freeze({ maximumLifetimeSeconds: maximumExchangeTtl(parameters.tokenExchange) }),
|
|
34
|
+
router: router === undefined
|
|
35
|
+
? false
|
|
36
|
+
: Object.freeze({
|
|
37
|
+
binding: router.binding,
|
|
38
|
+
hostSuffix: router.hostSuffix,
|
|
39
|
+
minLabels: router.minLabels,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { workerInvocationTimeout } from './configuration/invocation-timeout.js';
|
|
2
|
+
export { workerRequestLimit } from './configuration/request-limit.js';
|
|
3
|
+
export { resolveRouter } from './configuration/router.js';
|
|
4
|
+
export { isAdapterVar, workerSettings } from './configuration/settings.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { workerInvocationTimeout } from './configuration/invocation-timeout.js';
|
|
2
|
+
export { workerRequestLimit } from './configuration/request-limit.js';
|
|
3
|
+
export { resolveRouter } from './configuration/router.js';
|
|
4
|
+
export { isAdapterVar, workerSettings } from './configuration/settings.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Build } from '@astrale-os/sdk/deployment/build';
|
|
2
|
+
import { frontend as frontendBuild } from '@astrale-os/sdk/deployment/build';
|
|
3
|
+
/** The served frontend, and which of its files a browser may keep without revalidating. */
|
|
4
|
+
export interface FrontendOutput {
|
|
5
|
+
readonly files: readonly frontendBuild.FrontendFile[];
|
|
6
|
+
/**
|
|
7
|
+
* Served paths the bundler content-addressed, so their bytes can never change under a given name.
|
|
8
|
+
* A frontend Astrale does not build leaves this empty, which is what keeps the absence of evidence
|
|
9
|
+
* indistinguishable from evidence of mutability.
|
|
10
|
+
*/
|
|
11
|
+
readonly addressed: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function frontendFiles(input: {
|
|
14
|
+
readonly frontend: Build['frontend'];
|
|
15
|
+
readonly projectDir: string;
|
|
16
|
+
readonly output: string;
|
|
17
|
+
readonly recordSource?: (path: string) => void;
|
|
18
|
+
readonly signal: AbortSignal;
|
|
19
|
+
}): Promise<FrontendOutput>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { frontend as frontendBuild } from '@astrale-os/sdk/deployment/build';
|
|
2
|
+
import { readFile, readdir, rm } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { buildViteFrontend } from './vite.js';
|
|
5
|
+
/**
|
|
6
|
+
* Root names that Cloudflare Workers Static Assets reads as configuration rather than serving.
|
|
7
|
+
*
|
|
8
|
+
* Wrangler withholds them from a direct upload and applies them, while the managed upload through
|
|
9
|
+
* Services serves them as ordinary public files, so the same output would behave differently per
|
|
10
|
+
* adapter. The Worker runs first on every request and owns frontend response headers and routing for
|
|
11
|
+
* both, which leaves these files nothing to configure.
|
|
12
|
+
*/
|
|
13
|
+
const RESERVED_ROOT_NAMES = new Set([
|
|
14
|
+
'_headers',
|
|
15
|
+
'_redirects',
|
|
16
|
+
'.assetsignore',
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Name of a Cloudflare Pages worker script, the server-side code of a Pages project.
|
|
20
|
+
*
|
|
21
|
+
* Wrangler refuses to upload a root asset under this name, since that would publish the code, unless
|
|
22
|
+
* an `.assetsignore` exists, and the reserved names above already exclude one. A direct deployment
|
|
23
|
+
* would therefore fail at upload, while the managed upload through Services would publish the code.
|
|
24
|
+
* Neither adapter runs a Pages worker: the Worker is the only server-side code for both.
|
|
25
|
+
*/
|
|
26
|
+
const PAGES_WORKER_NAME = '_worker.js';
|
|
27
|
+
export async function frontendFiles(input) {
|
|
28
|
+
const frontend = input.frontend;
|
|
29
|
+
if (frontend === undefined)
|
|
30
|
+
return { files: [], addressed: [] };
|
|
31
|
+
if (frontend.source.kind === 'external') {
|
|
32
|
+
return { files: frontendBuild.files(frontend, []), addressed: [] };
|
|
33
|
+
}
|
|
34
|
+
await rm(input.output, { recursive: true, force: true });
|
|
35
|
+
const built = await buildViteFrontend({
|
|
36
|
+
projectDir: input.projectDir,
|
|
37
|
+
output: input.output,
|
|
38
|
+
...(frontend.source.config === undefined ? {} : { config: frontend.source.config }),
|
|
39
|
+
recordSource: input.recordSource,
|
|
40
|
+
signal: input.signal,
|
|
41
|
+
});
|
|
42
|
+
const files = frontendBuild.files(frontend, await readFiles(input.output));
|
|
43
|
+
refuseReservedNames(files);
|
|
44
|
+
refusePagesWorker(files);
|
|
45
|
+
// A name the bundler reported but that never reached disk is not a served path; keeping only the
|
|
46
|
+
// intersection means the evidence describes exactly what this deployment will answer with.
|
|
47
|
+
const served = new Set(files.map((file) => file.path));
|
|
48
|
+
return { files, addressed: built.addressed.filter((path) => served.has(path)) };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Refuse output that Cloudflare would read as asset configuration instead of serving it.
|
|
52
|
+
*
|
|
53
|
+
* Only the root is reserved: a nested `docs/_headers` stays an ordinary file. The match ignores case
|
|
54
|
+
* and covers directories, because Wrangler's upload filter withholds any root entry under these names
|
|
55
|
+
* regardless of case, and a case-insensitive filesystem hands it `_Headers` when it reads `_headers`.
|
|
56
|
+
*/
|
|
57
|
+
function refuseReservedNames(files) {
|
|
58
|
+
const reserved = new Set(files
|
|
59
|
+
.map((file) => file.path.split('/')[0])
|
|
60
|
+
.filter((name) => RESERVED_ROOT_NAMES.has(name.toLowerCase())));
|
|
61
|
+
if (reserved.size === 0)
|
|
62
|
+
return;
|
|
63
|
+
const names = [...reserved].join(', ');
|
|
64
|
+
throw new TypeError(`Frontend output contains reserved Cloudflare asset configuration name(s) at its root: ${names}. ` +
|
|
65
|
+
'A direct cloudflare() deployment would apply them as Workers Static Assets configuration, while a managed astrale() deployment would serve them as public files. ' +
|
|
66
|
+
'The Worker owns frontend response headers and routing for both adapters: remove them from the Vite public directory, or stop the build from emitting them.');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Refuse output that Wrangler would decline to upload as a Cloudflare Pages worker script.
|
|
70
|
+
*
|
|
71
|
+
* Wrangler tests the relative path with a plain `startsWith`, not through its case-insensitive upload
|
|
72
|
+
* filter, so the match here is a case-sensitive prefix of the root name too. It covers a `_worker.js`
|
|
73
|
+
* file or directory and siblings such as `_worker.js.map`, which Wrangler refuses alike, while both
|
|
74
|
+
* adapters serve `_Worker.js` or a nested `docs/_worker.js` as an ordinary file.
|
|
75
|
+
*/
|
|
76
|
+
function refusePagesWorker(files) {
|
|
77
|
+
const workers = new Set(files
|
|
78
|
+
.map((file) => file.path.split('/')[0])
|
|
79
|
+
.filter((name) => name.startsWith(PAGES_WORKER_NAME)));
|
|
80
|
+
if (workers.size === 0)
|
|
81
|
+
return;
|
|
82
|
+
const names = [...workers].join(', ');
|
|
83
|
+
throw new TypeError(`Frontend output contains Cloudflare Pages worker name(s) at its root: ${names}. ` +
|
|
84
|
+
`A direct cloudflare() deployment would fail because Wrangler refuses to upload a root name starting with ${PAGES_WORKER_NAME} as possible server-side code, while a managed astrale() deployment would serve them as public files. ` +
|
|
85
|
+
'The Worker is the only server-side code for both adapters: remove or rename them in the Vite public directory, or stop the build from emitting them.');
|
|
86
|
+
}
|
|
87
|
+
async function readFiles(root, current = '') {
|
|
88
|
+
const entries = await readdir(join(root, current), { withFileTypes: true });
|
|
89
|
+
const files = [];
|
|
90
|
+
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
91
|
+
const path = current === '' ? entry.name : `${current}/${entry.name}`;
|
|
92
|
+
if (entry.isDirectory())
|
|
93
|
+
files.push(...(await readFiles(root, path)));
|
|
94
|
+
else if (entry.isFile()) {
|
|
95
|
+
files.push({ path, content: new Uint8Array(await readFile(join(root, path))) });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return files;
|
|
99
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { legacy } from '@astrale-os/sdk/deployment/release';
|
|
2
|
+
import type { verifyReadiness } from '@astrale-os/sdk/deployment/verify';
|
|
3
|
+
import type { WorkerArtifact } from '../worker/artifact.js';
|
|
4
|
+
type ReadinessFetch = NonNullable<NonNullable<Parameters<typeof verifyReadiness>[1]>['fetch']>;
|
|
5
|
+
/** Publication equality alone cannot prove a frontend-only deployment has propagated. */
|
|
6
|
+
export declare function verifyWorkerFrontend(release: legacy.Release, worker: Pick<WorkerArtifact, 'assets'>, options?: {
|
|
7
|
+
readonly fetch?: ReadinessFetch;
|
|
8
|
+
readonly signal?: AbortSignal;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { frontendRevision } from './revision.js';
|
|
2
|
+
/** Publication equality alone cannot prove a frontend-only deployment has propagated. */
|
|
3
|
+
export async function verifyWorkerFrontend(release, worker, options = {}) {
|
|
4
|
+
const frontend = release.build.frontend;
|
|
5
|
+
if (frontend === undefined || frontend.source.kind === 'external')
|
|
6
|
+
return;
|
|
7
|
+
if (worker.assets === undefined)
|
|
8
|
+
throw new Error('Prepared Worker frontend assets are missing.');
|
|
9
|
+
const expected = frontendRevision(worker.assets.files);
|
|
10
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
11
|
+
for (const route of frontend.routes) {
|
|
12
|
+
const response = await fetcher(new URL(route.path, release.addressing.publicUrl).href, {
|
|
13
|
+
redirect: 'error',
|
|
14
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
15
|
+
});
|
|
16
|
+
const matches = response.status === 200 && response.headers.get('X-Astrale-Frontend') === expected;
|
|
17
|
+
await response.body?.cancel();
|
|
18
|
+
if (!matches)
|
|
19
|
+
throw new Error(`Frontend ${route.path} is not serving the prepared browser build yet.`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
/** Bind the generated Worker to its exact browser files, independently of Schema revision. */
|
|
3
|
+
export function frontendRevision(files) {
|
|
4
|
+
const hash = createHash('sha256');
|
|
5
|
+
const ordered = files
|
|
6
|
+
.map((file) => ({ path: file.path.replace(/^\//u, ''), bytes: file.bytes }))
|
|
7
|
+
.sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
|
|
8
|
+
for (const file of ordered) {
|
|
9
|
+
const bytes = file.bytes();
|
|
10
|
+
hash.update(JSON.stringify([file.path, bytes.byteLength]));
|
|
11
|
+
hash.update(bytes);
|
|
12
|
+
}
|
|
13
|
+
return `sha256:${hash.digest('hex')}`;
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a Vite frontend in an isolated process and report its authored inputs, along with the
|
|
3
|
+
* outputs the bundler content-addressed. Those are the files a browser may keep forever, and only
|
|
4
|
+
* the build that named them can say which they are.
|
|
5
|
+
*/
|
|
6
|
+
export declare function buildViteFrontend(input: {
|
|
7
|
+
readonly projectDir: string;
|
|
8
|
+
readonly output: string;
|
|
9
|
+
readonly config?: string;
|
|
10
|
+
readonly recordSource?: (path: string) => void;
|
|
11
|
+
readonly signal: AbortSignal;
|
|
12
|
+
}): Promise<{
|
|
13
|
+
readonly addressed: readonly string[];
|
|
14
|
+
}>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { runViteProcess } from './vite.process.js';
|
|
6
|
+
/**
|
|
7
|
+
* Build a Vite frontend in an isolated process and report its authored inputs, along with the
|
|
8
|
+
* outputs the bundler content-addressed. Those are the files a browser may keep forever, and only
|
|
9
|
+
* the build that named them can say which they are.
|
|
10
|
+
*/
|
|
11
|
+
export async function buildViteFrontend(input) {
|
|
12
|
+
const parent = dirname(input.output);
|
|
13
|
+
await mkdir(parent, { recursive: true });
|
|
14
|
+
const scratch = await mkdtemp(join(parent, 'vite-run-'));
|
|
15
|
+
const evidence = join(scratch, 'sources.json');
|
|
16
|
+
const addressed = join(scratch, 'addressed.json');
|
|
17
|
+
try {
|
|
18
|
+
await runViteProcess({
|
|
19
|
+
cwd: input.projectDir,
|
|
20
|
+
arguments: [runnerPath(), input.output, evidence, input.config ?? '', addressed],
|
|
21
|
+
signal: input.signal,
|
|
22
|
+
});
|
|
23
|
+
const sources = JSON.parse(await readFile(evidence, 'utf8'));
|
|
24
|
+
if (!Array.isArray(sources) || sources.some((path) => typeof path !== 'string')) {
|
|
25
|
+
throw new TypeError('Vite preparation source evidence is invalid.');
|
|
26
|
+
}
|
|
27
|
+
for (const path of sources)
|
|
28
|
+
input.recordSource?.(path);
|
|
29
|
+
const named = JSON.parse(await readFile(addressed, 'utf8'));
|
|
30
|
+
if (!Array.isArray(named) || named.some((path) => typeof path !== 'string')) {
|
|
31
|
+
throw new TypeError('Vite preparation output evidence is invalid.');
|
|
32
|
+
}
|
|
33
|
+
return { addressed: Object.freeze([...named]) };
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
await rm(scratch, { recursive: true, force: true });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function runnerPath() {
|
|
40
|
+
const built = fileURLToPath(new URL('./vite.runner.js', import.meta.url));
|
|
41
|
+
return existsSync(built) ? built : fileURLToPath(new URL('./vite.runner.ts', import.meta.url));
|
|
42
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
const MAXIMUM_OUTPUT_BYTES = 1024 * 1024;
|
|
3
|
+
const TERMINATION_GRACE_MS = 1_000;
|
|
4
|
+
const VITE_TIMEOUT_MS = 300_000;
|
|
5
|
+
/** Run one bounded, cancellable Vite subprocess. */
|
|
6
|
+
export function runViteProcess(input) {
|
|
7
|
+
if (input.signal.aborted)
|
|
8
|
+
return Promise.reject(abortReason(input.signal));
|
|
9
|
+
return new Promise((resolve, reject) => {
|
|
10
|
+
const child = spawn(process.execPath, input.arguments, {
|
|
11
|
+
cwd: input.cwd,
|
|
12
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
13
|
+
windowsHide: true,
|
|
14
|
+
});
|
|
15
|
+
let output = '';
|
|
16
|
+
let outputBytes = 0;
|
|
17
|
+
let terminating;
|
|
18
|
+
let escalation;
|
|
19
|
+
const timeout = setTimeout(() => terminate(new Error('Vite process timed out.')), input.timeoutMs ?? VITE_TIMEOUT_MS);
|
|
20
|
+
const abort = () => terminate(abortReason(input.signal));
|
|
21
|
+
input.signal.addEventListener('abort', abort, { once: true });
|
|
22
|
+
const cleanup = () => {
|
|
23
|
+
clearTimeout(timeout);
|
|
24
|
+
if (escalation !== undefined)
|
|
25
|
+
clearTimeout(escalation);
|
|
26
|
+
input.signal.removeEventListener('abort', abort);
|
|
27
|
+
};
|
|
28
|
+
function terminate(cause) {
|
|
29
|
+
if (terminating !== undefined)
|
|
30
|
+
return;
|
|
31
|
+
terminating = cause;
|
|
32
|
+
child.kill('SIGTERM');
|
|
33
|
+
escalation = setTimeout(() => {
|
|
34
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
35
|
+
child.kill('SIGKILL');
|
|
36
|
+
}, TERMINATION_GRACE_MS);
|
|
37
|
+
}
|
|
38
|
+
const collect = (buffer) => {
|
|
39
|
+
outputBytes += buffer.byteLength;
|
|
40
|
+
if (outputBytes > MAXIMUM_OUTPUT_BYTES) {
|
|
41
|
+
terminate(new Error('Vite process exceeded its output limit.'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
output += buffer.toString();
|
|
45
|
+
};
|
|
46
|
+
child.stdout.on('data', collect);
|
|
47
|
+
child.stderr.on('data', collect);
|
|
48
|
+
child.once('error', (cause) => {
|
|
49
|
+
cleanup();
|
|
50
|
+
reject(terminating ?? cause);
|
|
51
|
+
});
|
|
52
|
+
child.once('close', (code) => {
|
|
53
|
+
cleanup();
|
|
54
|
+
if (terminating !== undefined)
|
|
55
|
+
reject(terminating);
|
|
56
|
+
else if (code === 0)
|
|
57
|
+
resolve();
|
|
58
|
+
else
|
|
59
|
+
reject(new Error(`Vite failed (code ${code ?? 1}):\n${output.slice(-1500)}`));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function abortReason(signal) {
|
|
64
|
+
return signal.reason instanceof Error ? signal.reason : new Error('Vite process cancelled.');
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|