@astrale-os/adapter-cloudflare 0.5.0-beta.145 → 0.5.0-beta.147

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/adapter.js CHANGED
@@ -20,7 +20,7 @@ export function cloudflare(parameters = {}) {
20
20
  waitForReadiness: (release, signal, version, worker, workerName) => waitForReadiness(async (release, options) => {
21
21
  const checks = {
22
22
  ...options,
23
- fetch: versionReadinessFetch(release.addressing.issuer, version, globalThis.fetch, workerName),
23
+ fetch: versionReadinessFetch(release.addressing.publicUrl, version, globalThis.fetch, workerName, release.addressing.issuer),
24
24
  };
25
25
  await verifyReadiness(release, checks);
26
26
  await verifyWorkerFrontend(release, worker, checks);
@@ -155,7 +155,9 @@ async function prepare(params, context, direct) {
155
155
  ? Object.freeze({ kind: 'provider-assigned', claim: name })
156
156
  : Object.freeze({
157
157
  kind: 'configured',
158
- addressing: addressing(configuredIssuer),
158
+ addressing: addressing(params.identityIssuer ?? configuredIssuer, {
159
+ publicUrl: configuredIssuer,
160
+ }),
159
161
  }),
160
162
  });
161
163
  }
@@ -1,5 +1,11 @@
1
1
  import { addressing } from '@astrale-os/sdk/deployment/release';
2
2
  export function deploymentAddressing(parameters) {
3
+ if (parameters.identityIssuer !== undefined) {
4
+ addressing(parameters.identityIssuer);
5
+ if (parameters.route === undefined && parameters.addressing === undefined) {
6
+ throw new TypeError('A stable Cloudflare identityIssuer requires explicit public addressing.');
7
+ }
8
+ }
3
9
  const routed = parameters.route === undefined ? undefined : addressing(`https://${parameters.route}`).issuer;
4
10
  if (parameters.addressing === undefined)
5
11
  return routed;
@@ -9,7 +9,7 @@ export async function verifyWorkerFrontend(release, worker, options = {}) {
9
9
  const expected = frontendRevision(worker.assets.files);
10
10
  const fetcher = options.fetch ?? globalThis.fetch;
11
11
  for (const route of frontend.routes) {
12
- const response = await fetcher(new URL(route.path, release.addressing.issuer).href, {
12
+ const response = await fetcher(new URL(route.path, release.addressing.publicUrl).href, {
13
13
  redirect: 'error',
14
14
  ...(options.signal === undefined ? {} : { signal: options.signal }),
15
15
  });
@@ -8,6 +8,7 @@ export interface ExecutionCodegenOptions {
8
8
  readonly frontendRevision?: string;
9
9
  readonly maximumRequestBytes?: number;
10
10
  readonly invocationTimeoutMs?: number;
11
+ readonly identityIssuer?: string;
11
12
  readonly providerAssignedWorker?: string;
12
13
  readonly tokenExchange?: ExchangeOptions | false;
13
14
  readonly router?: {
@@ -45,6 +45,7 @@ ${frontend}${options.frontendRevision === undefined ? '' : ` frontendRevision:
45
45
  resolveUrl: (env, requestOrigin) => env.WORKER_URL ?? env.IDENTITY_ISS ?? requestOrigin,
46
46
  ${options.maximumRequestBytes === undefined ? '' : `maximumRequestBytes: ${options.maximumRequestBytes},`}
47
47
  ${options.invocationTimeoutMs === undefined ? '' : `invocationTimeoutMs: ${options.invocationTimeoutMs},`}
48
+ ${options.identityIssuer === undefined ? '' : `identityIssuer: ${JSON.stringify(options.identityIssuer)},`}
48
49
  ${options.providerAssignedWorker === undefined ? '' : `providerAssignedWorker: ${JSON.stringify(options.providerAssignedWorker)},`}
49
50
  ${options.tokenExchange === undefined ? '' : `tokenExchange: ${JSON.stringify(options.tokenExchange)} as const,`}
50
51
  selfBinding: (env) => env.SELF,${routeSubrequest}
@@ -18,6 +18,9 @@ export async function prepareExecution(input) {
18
18
  const invocationTimeoutMs = workerInvocationTimeout(input.params.invocationTimeoutMs);
19
19
  const serviceSource = generateExecutionService({
20
20
  origin: input.context.build.schema.compiled.root.origin,
21
+ ...(input.params.identityIssuer === undefined
22
+ ? {}
23
+ : { identityIssuer: input.params.identityIssuer }),
21
24
  runtimeSpecifier,
22
25
  workerSpecifier,
23
26
  build: input.context.build,
@@ -150,7 +150,15 @@ function partialProvider(cause, artifact) {
150
150
  let issuer;
151
151
  if (cause.applied.url !== undefined) {
152
152
  try {
153
- issuer = addressing(cause.applied.url).issuer;
153
+ const observed = addressing(cause.applied.url);
154
+ if (artifact.addressing.kind === 'configured') {
155
+ if (observed.publicUrl === artifact.addressing.addressing.publicUrl) {
156
+ issuer = artifact.addressing.addressing.issuer;
157
+ }
158
+ }
159
+ else {
160
+ issuer = observed.issuer;
161
+ }
154
162
  }
155
163
  catch {
156
164
  // The provider effect remains known when its printed URL is not Addressing.
@@ -178,10 +186,10 @@ function observeAddressing(artifact, deployedUrl, expectedWorker) {
178
186
  if (deployedUrl === undefined)
179
187
  return artifact.addressing.addressing;
180
188
  const observed = addressing(deployedUrl);
181
- if (observed.issuer !== artifact.addressing.addressing.issuer) {
189
+ if (observed.publicUrl !== artifact.addressing.addressing.publicUrl) {
182
190
  throw new TypeError('Cloudflare deployed URL differs from configured Addressing.');
183
191
  }
184
- return observed;
192
+ return artifact.addressing.addressing;
185
193
  }
186
194
  if (deployedUrl === undefined) {
187
195
  throw new TypeError('Cloudflare did not report provider-assigned Addressing.');
@@ -1,5 +1,5 @@
1
1
  import type { verifyReadiness } from '@astrale-os/sdk/deployment/verify';
2
2
  type ReadinessFetch = NonNullable<NonNullable<Parameters<typeof verifyReadiness>[1]>['fetch']>;
3
3
  /** Readiness is evidence for the provider version, not just a compatible Publication. */
4
- export declare function versionReadinessFetch(issuer: string, version: string, fetcher?: typeof fetch, workerName?: string): ReadinessFetch;
4
+ export declare function versionReadinessFetch(issuer: string, version: string, fetcher?: typeof fetch, workerName?: string, identityIssuer?: string): ReadinessFetch;
5
5
  export {};
@@ -1,7 +1,10 @@
1
1
  /** Readiness is evidence for the provider version, not just a compatible Publication. */
2
- export function versionReadinessFetch(issuer, version, fetcher = globalThis.fetch, workerName) {
2
+ export function versionReadinessFetch(issuer, version, fetcher = globalThis.fetch, workerName, identityIssuer = issuer) {
3
3
  return async (input, init) => {
4
- if (new URL(input).origin !== issuer) {
4
+ const origin = new URL(input).origin;
5
+ if (origin !== issuer && origin === identityIssuer)
6
+ return fetcher(input, init);
7
+ if (origin !== issuer) {
5
8
  throw new TypeError('Cloudflare readiness cannot leave the expected Worker origin.');
6
9
  }
7
10
  const headers = new Headers();
package/dist/params.d.ts CHANGED
@@ -18,6 +18,9 @@ export interface CloudflareParams {
18
18
  * present they must identify the same HTTPS origin.
19
19
  */
20
20
  readonly addressing?: string;
21
+ /** Stable Domain issuer when this deployment serves at a different public origin.
22
+ * Requires explicit route or addressing and the existing issuer signing key. */
23
+ readonly identityIssuer?: string;
21
24
  /** Gitignored secrets file (whole contents = secrets), e.g. `'.env.dev'`. */
22
25
  readonly secrets?: string;
23
26
  /**
@@ -25,6 +25,7 @@ export interface CloudflareWorkerConfig<RuntimeValue extends Runtime, Bindings e
25
25
  readonly privateKey: JsonWebKey | ((bindings: Bindings) => JsonWebKey);
26
26
  readonly resolveEnvironment?: (bindings: Bindings) => Environment;
27
27
  readonly resolveUrl?: (bindings: Bindings, requestOrigin: string) => string;
28
+ readonly identityIssuer?: string;
28
29
  readonly providerAssignedWorker?: string;
29
30
  readonly selfBinding?: (bindings: Bindings) => Fetcher | null | undefined;
30
31
  readonly routeSubrequest?: (url: URL, bindings: Bindings) => Fetcher | null | undefined;
@@ -14,7 +14,7 @@ export function generatedCloudflareWorkerEntry() {
14
14
  }
15
15
  function createCloudflareWorkerEntry(restore) {
16
16
  return function (config) {
17
- let currentIssuer;
17
+ let currentPublicUrl;
18
18
  let realized;
19
19
  const realize = () => {
20
20
  if (realized !== undefined)
@@ -26,23 +26,25 @@ function createCloudflareWorkerEntry(restore) {
26
26
  const application = serve({
27
27
  runtime: source.runtime,
28
28
  load(environment) {
29
- if (currentIssuer !== environment.issuer) {
29
+ if (currentPublicUrl !== environment.publicUrl) {
30
30
  throw new TypeError('Cloudflare Worker request origin differs from its current Release.');
31
31
  }
32
- return assemble(build, addressing(environment.issuer));
32
+ return assemble(build, addressing(config.identityIssuer ?? environment.publicUrl, {
33
+ publicUrl: environment.publicUrl,
34
+ }));
33
35
  },
34
36
  initialize: (environment) => config.resolveEnvironment === undefined
35
37
  ? environment.bindings
36
38
  : config.resolveEnvironment(environment.bindings),
37
39
  identity(environment) {
38
40
  return createIdentity({
39
- issuer: issuer.accept(environment.issuer),
41
+ issuer: issuer.accept(config.identityIssuer ?? environment.publicUrl),
40
42
  subject: build.schema.compiled.root.origin,
41
43
  privateKey: typeof config.privateKey === 'function'
42
44
  ? config.privateKey(environment.bindings)
43
45
  : config.privateKey,
44
46
  fetch: routedFetch(application, environment, config),
45
- ...(new URL(environment.issuer).protocol === 'http:'
47
+ ...(new URL(environment.publicUrl).protocol === 'http:'
46
48
  ? { allowInsecureHttp: true }
47
49
  : {}),
48
50
  ...(config.tokenExchange === undefined
@@ -68,22 +70,22 @@ function createCloudflareWorkerEntry(restore) {
68
70
  return Object.freeze({
69
71
  async fetch(request, bindings, context) {
70
72
  const url = new URL(request.url);
71
- const issuer = canonicalServingUrl(bindings.WORKER_URL ??
73
+ const publicUrl = canonicalServingUrl(bindings.WORKER_URL ??
72
74
  bindings.IDENTITY_ISS ??
73
75
  config.resolveUrl?.(bindings, requestOrigin(url, request)) ??
74
76
  requestOrigin(url, request));
75
77
  if (config.providerAssignedWorker !== undefined &&
76
78
  bindings.WORKER_URL === undefined &&
77
79
  bindings.IDENTITY_ISS === undefined &&
78
- !matchesWorkersDevClaim(issuer, config.providerAssignedWorker)) {
80
+ !matchesWorkersDevClaim(publicUrl, config.providerAssignedWorker)) {
79
81
  return new Response('Request origin differs from provider-assigned Addressing.', {
80
82
  status: 421,
81
83
  });
82
84
  }
83
- if (currentIssuer !== undefined && currentIssuer !== issuer) {
85
+ if (currentPublicUrl !== undefined && currentPublicUrl !== publicUrl) {
84
86
  return new Response('Request origin differs from the current Release.', { status: 421 });
85
87
  }
86
- currentIssuer = issuer;
88
+ currentPublicUrl = publicUrl;
87
89
  if (config.frontend !== undefined) {
88
90
  const frontend = await frontendAsset(config.frontend, bindings.ASSETS, url, request, config.frontendRevision);
89
91
  if (frontend !== undefined)
@@ -95,7 +97,7 @@ function createCloudflareWorkerEntry(restore) {
95
97
  if (frontend !== undefined)
96
98
  return withProviderVersion(frontend, bindings);
97
99
  }
98
- const response = await application.fetch(request, Object.freeze({ bindings, issuer }), context);
100
+ const response = await application.fetch(request, Object.freeze({ bindings, publicUrl }), context);
99
101
  return withProviderVersion(response, bindings);
100
102
  },
101
103
  });
@@ -112,7 +114,7 @@ function routedFetch(application, environment, config) {
112
114
  return async (input, init) => {
113
115
  const { request, rejectRedirect } = prepareCloudflareRequest(input, init);
114
116
  const destination = new URL(request.url);
115
- if (destination.origin === environment.issuer) {
117
+ if (destination.origin === environment.publicUrl) {
116
118
  const self = config.selfBinding?.(environment.bindings);
117
119
  return dispatchCloudflareRequest(request, rejectRedirect, (selected) => self === null || self === undefined
118
120
  ? application.fetch(selected, environment)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/adapter-cloudflare",
3
- "version": "0.5.0-beta.145",
3
+ "version": "0.5.0-beta.147",
4
4
  "description": "Deploy an Astrale application through Cloudflare Workers",
5
5
  "keywords": [
6
6
  "adapter",
@@ -53,7 +53,7 @@
53
53
  "registry": "https://registry.npmjs.org/"
54
54
  },
55
55
  "dependencies": {
56
- "@astrale-os/sdk": "0.5.0-beta.142",
56
+ "@astrale-os/sdk": "0.5.0-beta.144",
57
57
  "@noble/hashes": "2.3.0",
58
58
  "hono": "^4.6.20",
59
59
  "jose": "^6.1.3",