@wtfalch/foundry 0.59.0 → 0.61.0
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/apps/cloudflare.d.ts +173 -0
- package/dist/apps/cloudflare.js +17 -0
- package/dist/apps/deploy.d.ts +116 -0
- package/dist/apps/deploy.js +791 -0
- package/dist/apps/github.d.ts +47 -0
- package/dist/apps/github.js +201 -0
- package/dist/apps/index.d.ts +9 -0
- package/dist/apps/index.js +9 -0
- package/dist/apps/migrations.d.ts +110 -0
- package/dist/apps/migrations.js +458 -0
- package/dist/audit/connector.d.ts +43 -0
- package/dist/audit/connector.js +22 -0
- package/dist/audit/evidence.d.ts +49 -0
- package/dist/audit/evidence.js +56 -0
- package/dist/audit/index.d.ts +9 -0
- package/dist/audit/index.js +9 -0
- package/dist/audit/migrations.d.ts +5 -0
- package/dist/audit/migrations.js +5 -0
- package/dist/audit/registry.d.ts +49 -0
- package/dist/audit/registry.js +44 -0
- package/dist/audit/seal.d.ts +49 -0
- package/dist/audit/seal.js +131 -0
- package/dist/audit/sealer-role.d.ts +33 -0
- package/dist/audit/sealer-role.js +56 -0
- package/dist/audit/signer.d.ts +40 -0
- package/dist/audit/signer.js +78 -0
- package/dist/audit/tick.d.ts +36 -0
- package/dist/audit/tick.js +87 -0
- package/dist/catalogue/copy-en.d.ts +1 -0
- package/dist/catalogue/copy-en.js +129 -0
- package/dist/catalogue/index.d.ts +9 -0
- package/dist/catalogue/index.js +21 -0
- package/dist/catalogue/integrations.d.ts +4 -0
- package/dist/catalogue/integrations.js +104 -0
- package/dist/catalogue/systems.d.ts +7 -0
- package/dist/catalogue/systems.js +31 -0
- package/dist/catalogue/types.d.ts +110 -0
- package/dist/catalogue/types.js +1 -0
- package/dist/entry-rules.d.ts +53 -0
- package/dist/entry-rules.js +71 -0
- package/dist/hosted-label.d.ts +13 -0
- package/dist/hosted-label.js +81 -0
- package/dist/index.d.ts +10 -5
- package/dist/index.js +5 -3
- package/dist/migrations/0062_hosting_github.sql +6 -0
- package/dist/migrations/0063_boule_apps.sql +7 -0
- package/dist/migrations/0064_requested_changes_boule_apps.sql +6 -0
- package/dist/modules/ai.d.ts +3 -0
- package/dist/modules/ai.js +64 -0
- package/dist/modules/assistants.d.ts +3 -0
- package/dist/modules/assistants.js +72 -0
- package/dist/modules/audit-ledger.d.ts +29 -0
- package/dist/modules/audit-ledger.js +49 -0
- package/dist/modules/audit.d.ts +7 -0
- package/dist/modules/audit.js +44 -0
- package/dist/modules/database.d.ts +23 -0
- package/dist/modules/database.js +189 -0
- package/dist/modules/email.d.ts +3 -0
- package/dist/modules/email.js +18 -0
- package/dist/modules/entry-parts.d.ts +23 -0
- package/dist/modules/entry-parts.js +63 -0
- package/dist/modules/files.d.ts +3 -0
- package/dist/modules/files.js +64 -0
- package/dist/modules/index.d.ts +74 -0
- package/dist/modules/index.js +774 -0
- package/dist/modules/jobs.d.ts +3 -0
- package/dist/modules/jobs.js +23 -0
- package/dist/modules/organisations.d.ts +3 -0
- package/dist/modules/organisations.js +20 -0
- package/dist/modules/outputs.d.ts +9 -0
- package/dist/modules/outputs.js +38 -0
- package/dist/modules/sign-in.d.ts +3 -0
- package/dist/modules/sign-in.js +197 -0
- package/dist/modules/signed-records.d.ts +3 -0
- package/dist/modules/signed-records.js +32 -0
- package/dist/modules/types.d.ts +194 -0
- package/dist/modules/types.js +1 -0
- package/dist/modules/workspaces.d.ts +3 -0
- package/dist/modules/workspaces.js +75 -0
- package/dist/org.d.ts +163 -22
- package/dist/org.js +419 -47
- package/dist/plan/apps-db-route.d.ts +90 -0
- package/dist/plan/apps-db-route.js +168 -0
- package/dist/plan/apps-zones.d.ts +56 -0
- package/dist/plan/apps-zones.js +118 -0
- package/dist/plan/archon-blue-green.js +11 -1
- package/dist/plan/blue-green-apply.d.ts +37 -1
- package/dist/plan/blue-green-apply.js +55 -15
- package/dist/plan/blue-green.d.ts +41 -1
- package/dist/plan/blue-green.js +80 -2
- package/dist/plan/boule.d.ts +23 -1
- package/dist/plan/boule.js +35 -2
- package/dist/plan/broker-entries.d.ts +28 -3
- package/dist/plan/broker-entries.js +74 -3
- package/dist/plan/broker-registrar.d.ts +168 -0
- package/dist/plan/broker-registrar.js +440 -0
- package/dist/plan/broker-workspaces-database.d.ts +48 -0
- package/dist/plan/broker-workspaces-database.js +87 -0
- package/dist/plan/desktop-callback.js +12 -7
- package/dist/plan/diff.d.ts +8 -2
- package/dist/plan/diff.js +373 -42
- package/dist/plan/edge-access.d.ts +6 -0
- package/dist/plan/edge-dns.d.ts +6 -0
- package/dist/plan/edge-dns.js +9 -7
- package/dist/plan/entry-ai-key.d.ts +1 -1
- package/dist/plan/entry-ai-key.js +2 -2
- package/dist/plan/entry-audit.d.ts +58 -0
- package/dist/plan/entry-audit.js +122 -0
- package/dist/plan/entry-customer-grants.d.ts +75 -0
- package/dist/plan/entry-customer-grants.js +154 -0
- package/dist/plan/entry-edge-key.d.ts +26 -1
- package/dist/plan/entry-edge-key.js +42 -3
- package/dist/plan/entry-jobs.d.ts +64 -0
- package/dist/plan/entry-jobs.js +92 -0
- package/dist/plan/entry-repo-key-apply.d.ts +55 -0
- package/dist/plan/entry-repo-key-apply.js +185 -0
- package/dist/plan/entry-repo-key.d.ts +92 -0
- package/dist/plan/entry-repo-key.js +235 -0
- package/dist/plan/entry-workspaces.d.ts +55 -0
- package/dist/plan/entry-workspaces.js +90 -0
- package/dist/plan/executor.d.ts +191 -1
- package/dist/plan/executor.js +1505 -90
- package/dist/plan/gates.d.ts +1 -1
- package/dist/plan/gates.js +62 -9
- package/dist/plan/github-deploy-keys.d.ts +78 -0
- package/dist/plan/github-deploy-keys.js +305 -0
- package/dist/plan/github-source.d.ts +53 -0
- package/dist/plan/github-source.js +140 -0
- package/dist/plan/held-key-refs.d.ts +12 -0
- package/dist/plan/held-key-refs.js +12 -0
- package/dist/plan/hostnames.d.ts +9 -0
- package/dist/plan/hostnames.js +28 -1
- package/dist/plan/index.d.ts +9 -0
- package/dist/plan/index.js +6 -0
- package/dist/plan/jobs-scheduler-http.js +3 -0
- package/dist/plan/keys-registrar-http.d.ts +32 -0
- package/dist/plan/keys-registrar-http.js +105 -0
- package/dist/plan/keys-registrar.d.ts +67 -0
- package/dist/plan/keys-registrar.js +103 -0
- package/dist/plan/live.d.ts +1 -1
- package/dist/plan/locked-hostnames.d.ts +2 -2
- package/dist/plan/locked-hostnames.js +2 -2
- package/dist/plan/login-host.js +11 -5
- package/dist/plan/machine-paths.d.ts +36 -0
- package/dist/plan/machine-paths.js +137 -0
- package/dist/plan/mcp.d.ts +1 -1
- package/dist/plan/mcp.js +49 -17
- package/dist/plan/org-apps-admin.d.ts +108 -0
- package/dist/plan/org-apps-admin.js +434 -0
- package/dist/plan/org-names.js +12 -1
- package/dist/plan/own-diff.d.ts +23 -0
- package/dist/plan/own-diff.js +53 -0
- package/dist/plan/own-hostnames.d.ts +2 -0
- package/dist/plan/own-hostnames.js +1 -0
- package/dist/plan/own-jobs-route.d.ts +47 -0
- package/dist/plan/own-jobs-route.js +91 -0
- package/dist/plan/own-services.d.ts +4 -0
- package/dist/plan/own-services.js +4 -0
- package/dist/plan/plan-apply.d.ts +39 -0
- package/dist/plan/plan-apply.js +163 -9
- package/dist/plan/platform-diff.d.ts +2 -2
- package/dist/plan/platform-diff.js +7 -2
- package/dist/plan/platform-org.d.ts +30 -0
- package/dist/plan/platform-org.js +12 -1
- package/dist/plan/platform-providers.d.ts +2 -0
- package/dist/plan/platform-providers.js +7 -2
- package/dist/plan/post-logout-uris.js +7 -1
- package/dist/plan/private-pages.d.ts +26 -0
- package/dist/plan/private-pages.js +39 -0
- package/dist/plan/proxy-labels.d.ts +12 -1
- package/dist/plan/proxy-labels.js +32 -2
- package/dist/plan/release-host.d.ts +6 -2
- package/dist/plan/release-host.js +36 -8
- package/dist/plan/stage-env.d.ts +19 -1
- package/dist/plan/stage-env.js +25 -1
- package/dist/plan/stage-guards.d.ts +20 -0
- package/dist/plan/stage-guards.js +24 -0
- package/dist/plan/stage-isolation.d.ts +24 -0
- package/dist/plan/stage-isolation.js +29 -1
- package/dist/plan/worker-app-database.d.ts +47 -40
- package/dist/plan/worker-app-database.js +150 -152
- package/dist/plan/worker-app.d.ts +6 -65
- package/dist/plan/worker-app.js +29 -105
- package/dist/plan/worker.d.ts +2 -2
- package/dist/plan/worker.js +2 -1
- package/dist/plan/workspace-client.d.ts +25 -0
- package/dist/plan/workspace-client.js +48 -0
- package/dist/plan/workspace-sign-in.d.ts +55 -0
- package/dist/plan/workspace-sign-in.js +273 -0
- package/dist/plan/workspace-worker.d.ts +4 -0
- package/dist/plan/workspace-worker.js +13 -0
- package/dist/plan/workspace.d.ts +101 -0
- package/dist/plan/workspace.js +305 -0
- package/dist/release/decide.d.ts +30 -1
- package/dist/release/decide.js +73 -11
- package/dist/release/health.d.ts +8 -3
- package/dist/release/health.js +58 -5
- package/dist/release/run.d.ts +2 -5
- package/dist/release/run.js +4 -0
- package/dist/release/state.d.ts +3 -1
- package/dist/release/state.js +6 -0
- package/dist/release/types.d.ts +20 -0
- package/dist/requested-changes.d.ts +11 -2
- package/dist/requested-changes.js +11 -0
- package/dist/service-labels.d.ts +4 -0
- package/dist/service-labels.js +11 -0
- package/dist/slug.d.ts +2 -0
- package/dist/slug.js +10 -6
- package/dist/store/postgres.js +12 -2
- package/dist/store/schema.d.ts +37 -1
- package/dist/store/schema.js +4 -0
- package/dist/worker-app-config.d.ts +6 -10
- package/dist/worker-app-config.js +22 -17
- package/dist/worker-app-migrations.d.ts +105 -0
- package/dist/worker-app-migrations.js +166 -0
- package/dist/worker-app-upload.d.ts +103 -0
- package/dist/worker-app-upload.js +128 -0
- package/package.json +18 -1
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type DeclaredOrg, type HostingEntry } from '../org.js';
|
|
2
2
|
import { type WorkerAppConfig } from '../worker-app-config.js';
|
|
3
|
+
import { WORKER_APP_DNS_RECORD, type WorkerAppContainer, type WorkerAppUpload, withCallTimeout, workerAppRegistryPrefix } from '../worker-app-upload.js';
|
|
3
4
|
import type { DraftStep, StepAction } from './diff.js';
|
|
4
5
|
import type { DnsPort } from './edge-dns.js';
|
|
5
6
|
import type { ZonePort } from './edge-zones.js';
|
|
6
7
|
import type { StepExecutionContext } from './plan-apply.js';
|
|
7
8
|
import { type WorkerAppDatabaseDeps } from './worker-app-database.js';
|
|
9
|
+
export type { WorkerAppBinding, WorkerAppContainer, WorkerAppMigrations, WorkerAppUpload, } from '../worker-app-upload.js';
|
|
10
|
+
export { WORKER_APP_DNS_RECORD, withCallTimeout, workerAppRegistryPrefix };
|
|
8
11
|
/**
|
|
9
12
|
* ADR 0002: a hosting entry that declares `workerApp` runs as one Cloudflare
|
|
10
13
|
* Worker with Durable Objects and Containers on the platform account. One step
|
|
@@ -46,11 +49,6 @@ export declare const WORKER_APP_ORG_HASH_LENGTH = 8;
|
|
|
46
49
|
export declare const workerAppName: (org: DeclaredOrg, entry: HostingEntry) => string;
|
|
47
50
|
/** The zone that holds the routes of an org with no domain of its own. */
|
|
48
51
|
export declare const WORKER_APP_OIKOS_ZONE = "oikos.host";
|
|
49
|
-
/** The record a Worker route needs on the org's own zone: Cloudflare's documented dummy, proxied. */
|
|
50
|
-
export declare const WORKER_APP_DNS_RECORD: {
|
|
51
|
-
readonly type: "AAAA";
|
|
52
|
-
readonly content: "100::";
|
|
53
|
-
};
|
|
54
52
|
/** True once the Worker app holds the broker key (`authEnv` delivered it). */
|
|
55
53
|
export declare const brokerKeyDelivered: (recorded: Readonly<Record<string, string>>, entryKey: string) => boolean;
|
|
56
54
|
/**
|
|
@@ -69,57 +67,6 @@ export declare const WORKER_APP_AUTH_KEY_SECRET = "AUTH_APP_KEY";
|
|
|
69
67
|
* and name are hashed: a held ref is at most 128 characters.
|
|
70
68
|
*/
|
|
71
69
|
export declare const workerAppSecretRef: (orgId: string, entryKey: string, name: string) => string;
|
|
72
|
-
export type WorkerAppBinding = {
|
|
73
|
-
type: 'durable_object_namespace';
|
|
74
|
-
name: string;
|
|
75
|
-
class_name: string;
|
|
76
|
-
} | {
|
|
77
|
-
type: 'plain_text';
|
|
78
|
-
name: string;
|
|
79
|
-
text: string;
|
|
80
|
-
} | {
|
|
81
|
-
type: 'hyperdrive';
|
|
82
|
-
name: string;
|
|
83
|
-
id: string;
|
|
84
|
-
};
|
|
85
|
-
export interface WorkerAppMigrations {
|
|
86
|
-
/** The tag the script holds now. Absent for a script with none. */
|
|
87
|
-
oldTag?: string;
|
|
88
|
-
newTag: string;
|
|
89
|
-
steps: {
|
|
90
|
-
newSqliteClasses?: string[];
|
|
91
|
-
deletedClasses?: string[];
|
|
92
|
-
renamedClasses?: {
|
|
93
|
-
from: string;
|
|
94
|
-
to: string;
|
|
95
|
-
}[];
|
|
96
|
-
}[];
|
|
97
|
-
}
|
|
98
|
-
export interface WorkerAppUpload {
|
|
99
|
-
name: string;
|
|
100
|
-
artifactUrl: string;
|
|
101
|
-
artifactSha256: string;
|
|
102
|
-
compatibilityDate: string;
|
|
103
|
-
compatibilityFlags: string[];
|
|
104
|
-
/** All of them: a redeploy replaces the list. Secrets survive a redeploy. */
|
|
105
|
-
bindings: WorkerAppBinding[];
|
|
106
|
-
/** Only the migrations not yet applied; absent when there are none. */
|
|
107
|
-
migrations?: WorkerAppMigrations;
|
|
108
|
-
/** Durable Object classes the upload marks container-enabled; absent when the entry declares no container. */
|
|
109
|
-
containers?: string[];
|
|
110
|
-
/** Workers Logs: always on, every request sampled. The adapter sends it as the upload metadata's `observability`. */
|
|
111
|
-
observability: {
|
|
112
|
-
enabled: true;
|
|
113
|
-
head_sampling_rate: 1;
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
export interface WorkerAppContainer {
|
|
117
|
-
className: string;
|
|
118
|
-
/** `<repository>@sha256:<digest>`. */
|
|
119
|
-
image: string;
|
|
120
|
-
instanceType: string;
|
|
121
|
-
maxInstances: number;
|
|
122
|
-
}
|
|
123
70
|
/**
|
|
124
71
|
* The Cloudflare calls a Worker app needs. Edge 0.13.0 has none of the Durable
|
|
125
72
|
* Object, migration or container calls, so no adapter ships with this port yet
|
|
@@ -208,6 +155,7 @@ export interface WorkerAppWant {
|
|
|
208
155
|
/** `workerApp.database`: the Hyperdrive binding and the tables the runtime role may only append to, sorted. */
|
|
209
156
|
database?: {
|
|
210
157
|
appendOnly: string[];
|
|
158
|
+
grants?: string[];
|
|
211
159
|
migrationsSha256?: string;
|
|
212
160
|
};
|
|
213
161
|
/** Secret names and where each comes from, sorted. */
|
|
@@ -218,7 +166,7 @@ export interface WorkerAppWant {
|
|
|
218
166
|
}[];
|
|
219
167
|
}
|
|
220
168
|
/** The zone apex the entry's route lives in. */
|
|
221
|
-
export declare const workerAppApex: (org: DeclaredOrg) => string;
|
|
169
|
+
export declare const workerAppApex: (org: DeclaredOrg, entry?: HostingEntry) => string;
|
|
222
170
|
/**
|
|
223
171
|
* What the Worker must look like, or the inputs still missing (names only).
|
|
224
172
|
* `issuer` is the sign-in issuer the plan read; `keysUrl` the custody Worker.
|
|
@@ -268,8 +216,6 @@ export interface WorkerAppPlanInput {
|
|
|
268
216
|
accessReady: boolean;
|
|
269
217
|
};
|
|
270
218
|
}
|
|
271
|
-
/** The registry prefix a container image of this platform must start with. */
|
|
272
|
-
export declare const workerAppRegistryPrefix: (accountId: string) => string;
|
|
273
219
|
/** Every entry key with a deployed or half-deployed Worker app recorded. */
|
|
274
220
|
export declare function liveWorkerApps(orgId: string, recordedIds: Readonly<Record<string, string>>): Map<string, string>;
|
|
275
221
|
/**
|
|
@@ -305,10 +251,6 @@ export interface WorkerAppDeps {
|
|
|
305
251
|
/** An outside call that has not answered in this long fails the step; the next apply repairs it. */
|
|
306
252
|
export declare const WORKER_APP_CALL_TIMEOUT_MS = 120000;
|
|
307
253
|
export declare const WORKER_APP_KEYS_DELIVERY_ATTEMPTS = 3;
|
|
308
|
-
/**
|
|
309
|
-
* Races `task` against a timer. An error names the call and edge's error code, never the cause.
|
|
310
|
-
*/
|
|
311
|
-
export declare function withCallTimeout<T>(ms: number, label: string, task: () => Promise<T>): Promise<T>;
|
|
312
254
|
/**
|
|
313
255
|
* Makes the Worker app true. Throws a fixed message on a failure, naming the
|
|
314
256
|
* step and never a value or a provider's cause for anything that carried a
|
|
@@ -324,4 +266,3 @@ export declare function applyWorkerApp(deps: WorkerAppDeps, org: DeclaredOrg, ac
|
|
|
324
266
|
* by org destroy.
|
|
325
267
|
*/
|
|
326
268
|
export declare function teardownWorkerApp(deps: WorkerAppDeps, entryKey: string, scriptName: string | undefined, recorded: Readonly<Record<string, string>>, call: <T>(label: string, task: () => Promise<T>) => Promise<T>, orgId: string, context?: Pick<StepExecutionContext, 'recordScopeId' | 'persistScopeIds'> | null): Promise<void>;
|
|
327
|
-
export {};
|
package/dist/plan/worker-app.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { APPS_DOMAIN } from '../org.js';
|
|
3
|
+
import { WORKER_APP_KEYS_SECRET, WORKER_APP_KEYS_URL_VAR, workerAppHasBuild, workerAppHostId, } from '../worker-app-config.js';
|
|
4
|
+
import { WORKER_APP_DNS_RECORD, pendingWorkerAppMigrations, strayContainerImage, withCallTimeout, workerAppRegistryPrefix, workerAppShape, workerAppUpload, } from '../worker-app-upload.js';
|
|
3
5
|
import { entryAuthEnv } from './broker-entries.js';
|
|
4
6
|
import { workerAppHostname } from './hostnames.js';
|
|
7
|
+
import { workspaceWorkerParent } from './workspace-worker.js';
|
|
5
8
|
import { appStepKey } from './platform-diff.js';
|
|
6
9
|
import { remoteDatabaseKeys } from './remote-database.js';
|
|
7
10
|
import { applyWorkerAppDatabase, deleteWorkerAppDatabaseRoute, deleteWorkerAppHyperdrive, workerAppDbRouteMarkerScopeKey, workerAppHyperdriveScopeKey, workerAppMigrationsScopeKey, } from './worker-app-database.js';
|
|
8
|
-
|
|
11
|
+
export { WORKER_APP_DNS_RECORD, withCallTimeout, workerAppRegistryPrefix };
|
|
9
12
|
/**
|
|
10
13
|
* ADR 0002: a hosting entry that declares `workerApp` runs as one Cloudflare
|
|
11
14
|
* Worker with Durable Objects and Containers on the platform account. One step
|
|
@@ -60,14 +63,11 @@ export const workerAppTraceScopeKeys = (entryKey) => [
|
|
|
60
63
|
export const workerAppResourceKey = (stepKey) => `cloudflare:worker-app:${stepKey}`;
|
|
61
64
|
export const WORKER_APP_ORG_HASH_LENGTH = 8;
|
|
62
65
|
/** `${8 hex of the org id's sha256}-${entry key}-app`. */
|
|
63
|
-
export const workerAppName = (org, entry) =>
|
|
66
|
+
export const workerAppName = (org, entry) => workspaceWorkerParent(entry) !== null
|
|
67
|
+
? `${org.slug}-${workspaceWorkerParent(entry)}-work`
|
|
68
|
+
: `${createHash('sha256').update(org.id).digest('hex').slice(0, WORKER_APP_ORG_HASH_LENGTH)}-${entry.key}-app`;
|
|
64
69
|
/** The zone that holds the routes of an org with no domain of its own. */
|
|
65
70
|
export const WORKER_APP_OIKOS_ZONE = 'oikos.host';
|
|
66
|
-
/** The record a Worker route needs on the org's own zone: Cloudflare's documented dummy, proxied. */
|
|
67
|
-
export const WORKER_APP_DNS_RECORD = {
|
|
68
|
-
type: 'AAAA',
|
|
69
|
-
content: '100::',
|
|
70
|
-
};
|
|
71
71
|
/** True once the Worker app holds the broker key (`authEnv` delivered it). */
|
|
72
72
|
export const brokerKeyDelivered = (recorded, entryKey) => Boolean(parseJson(recorded[workerAppSecretsScopeKey(entryKey)], {})
|
|
73
73
|
.AUTH_APP_KEY);
|
|
@@ -107,7 +107,11 @@ export function custodyRenewalDue(recorded, entryKey, now) {
|
|
|
107
107
|
}
|
|
108
108
|
const sortedEntries = (record) => Object.entries(record ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
109
109
|
/** The zone apex the entry's route lives in. */
|
|
110
|
-
export const workerAppApex = (org) =>
|
|
110
|
+
export const workerAppApex = (org, entry) => entry && workspaceWorkerParent(entry) !== null
|
|
111
|
+
? APPS_DOMAIN
|
|
112
|
+
: org.hosting.usesDomain && org.domain
|
|
113
|
+
? org.domain
|
|
114
|
+
: WORKER_APP_OIKOS_ZONE;
|
|
111
115
|
/** True when the want has a secret that must be held (a generated one, the broker key, a custody credential). */
|
|
112
116
|
const holdsGenerated = (want) => want.secrets.length > 0;
|
|
113
117
|
/**
|
|
@@ -160,32 +164,25 @@ export function wantedWorkerApp(org, entry, recorded, input) {
|
|
|
160
164
|
}
|
|
161
165
|
if (missing.length > 0)
|
|
162
166
|
return { missing };
|
|
167
|
+
const shape = workerAppShape(app);
|
|
163
168
|
return {
|
|
164
169
|
want: {
|
|
165
170
|
name: workerAppName(org, entry),
|
|
166
|
-
artifactUrl:
|
|
167
|
-
artifactSha256:
|
|
168
|
-
compatibilityDate:
|
|
169
|
-
compatibilityFlags:
|
|
171
|
+
artifactUrl: shape.artifactUrl,
|
|
172
|
+
artifactSha256: shape.artifactSha256,
|
|
173
|
+
compatibilityDate: shape.compatibilityDate,
|
|
174
|
+
compatibilityFlags: shape.compatibilityFlags,
|
|
170
175
|
host: workerAppHostname(org, entry),
|
|
171
|
-
apex: workerAppApex(org),
|
|
172
|
-
classes:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
migrations: app.durableObjects.migrations,
|
|
176
|
-
containers: (app.containers ?? [])
|
|
177
|
-
.map((c) => ({
|
|
178
|
-
className: c.className,
|
|
179
|
-
image: `${c.image.repository}@${c.image.digest}`,
|
|
180
|
-
instanceType: c.instanceType,
|
|
181
|
-
maxInstances: c.maxInstances,
|
|
182
|
-
}))
|
|
183
|
-
.sort((a, b) => a.className.localeCompare(b.className)),
|
|
176
|
+
apex: workerAppApex(org, entry),
|
|
177
|
+
classes: shape.classes,
|
|
178
|
+
migrations: shape.migrations,
|
|
179
|
+
containers: shape.containers,
|
|
184
180
|
vars: sortedEntries(vars),
|
|
185
181
|
...(app.database
|
|
186
182
|
? {
|
|
187
183
|
database: {
|
|
188
184
|
appendOnly: [...(app.database.appendOnly ?? [])].sort(),
|
|
185
|
+
...(app.database.grants?.length ? { grants: [...app.database.grants].sort() } : {}),
|
|
189
186
|
...(app.database.migrationsSha256
|
|
190
187
|
? { migrationsSha256: app.database.migrationsSha256 }
|
|
191
188
|
: {}),
|
|
@@ -222,8 +219,6 @@ export function migrationPrefixError(applied, declared) {
|
|
|
222
219
|
}
|
|
223
220
|
/** The unapplied migrations after the last tag in `applied`. */
|
|
224
221
|
export const unappliedMigrations = (declared, applied) => declared.slice(applied.length);
|
|
225
|
-
/** The registry prefix a container image of this platform must start with. */
|
|
226
|
-
export const workerAppRegistryPrefix = (accountId) => `registry.cloudflare.com/${accountId}/`;
|
|
227
222
|
/** Every entry key with a deployed or half-deployed Worker app recorded. */
|
|
228
223
|
export function liveWorkerApps(orgId, recordedIds) {
|
|
229
224
|
const mainPrefix = `cloudflare:worker-app:${orgId}:`;
|
|
@@ -327,9 +322,7 @@ export function planWorkerApps(org, recordedIds, input) {
|
|
|
327
322
|
continue;
|
|
328
323
|
}
|
|
329
324
|
const { want } = wanted;
|
|
330
|
-
const stray = (app
|
|
331
|
-
!c.image.repository.startsWith(workerAppRegistryPrefix(input.registryAccountId)) ||
|
|
332
|
-
c.image.repository.split('/').some((part) => /^\.+$/.test(part)));
|
|
325
|
+
const stray = strayContainerImage(app, input.registryAccountId);
|
|
333
326
|
if (stray) {
|
|
334
327
|
block(input.registryAccountId === undefined
|
|
335
328
|
? 'the host supplies no Cloudflare account id for the container registry'
|
|
@@ -399,31 +392,6 @@ export function planWorkerApps(org, recordedIds, input) {
|
|
|
399
392
|
/** An outside call that has not answered in this long fails the step; the next apply repairs it. */
|
|
400
393
|
export const WORKER_APP_CALL_TIMEOUT_MS = 120_000;
|
|
401
394
|
export const WORKER_APP_KEYS_DELIVERY_ATTEMPTS = 3;
|
|
402
|
-
/**
|
|
403
|
-
* Races `task` against a timer. An error names the call and edge's error code, never the cause.
|
|
404
|
-
*/
|
|
405
|
-
export function withCallTimeout(ms, label, task) {
|
|
406
|
-
return new Promise((resolve, reject) => {
|
|
407
|
-
const timer = setTimeout(() => reject(new Error(`${label}: no answer within ${ms} ms`)), ms);
|
|
408
|
-
task().then((value) => {
|
|
409
|
-
clearTimeout(timer);
|
|
410
|
-
resolve(value);
|
|
411
|
-
}, (error) => {
|
|
412
|
-
clearTimeout(timer);
|
|
413
|
-
// The cause is dropped: a provider error can echo a request body. The
|
|
414
|
-
// HTTP status stays, because callers tell "already gone" by it. So does
|
|
415
|
-
// edge's error code (a fixed lower-case enum, never a secret), to say why.
|
|
416
|
-
const { status, code } = (error ?? {});
|
|
417
|
-
const known = typeof code === 'string' && /^[a-z_]{1,40}$/.test(code) ? code : undefined;
|
|
418
|
-
const failure = new Error(known ? `${label} failed (${known})` : `${label} failed`);
|
|
419
|
-
if (typeof status === 'number')
|
|
420
|
-
failure.status = status;
|
|
421
|
-
if (known)
|
|
422
|
-
failure.code = known;
|
|
423
|
-
reject(failure);
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
395
|
const isGone = (error) => {
|
|
428
396
|
const status = error?.status;
|
|
429
397
|
return typeof status === 'number' && status === 404;
|
|
@@ -480,58 +448,14 @@ export async function applyWorkerApp(deps, org, action, context) {
|
|
|
480
448
|
const recordedTags = parseJson(recorded0[workerAppTagsScopeKey(entryKey)], []);
|
|
481
449
|
// The live tag is the fact; the record can be behind it after a crash between upload and record.
|
|
482
450
|
const liveTag = await call('read the migration tag', () => workers.liveMigrationTag(want.name));
|
|
483
|
-
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
else {
|
|
487
|
-
const index = declaredTags.indexOf(liveTag);
|
|
488
|
-
if (index < 0) {
|
|
489
|
-
throw new Error(`${at}: the live Worker holds migration '${liveTag}', which is not in the declared list; migrations are append-only and foundry will not guess`);
|
|
490
|
-
}
|
|
491
|
-
appliedCount = index + 1;
|
|
492
|
-
}
|
|
451
|
+
const pending = pendingWorkerAppMigrations(want.migrations, liveTag);
|
|
452
|
+
if (!pending.ok)
|
|
453
|
+
throw new Error(`${at}: ${pending.reason}`);
|
|
493
454
|
const recordedError = migrationPrefixError(recordedTags, declaredTags);
|
|
494
455
|
if (recordedError)
|
|
495
456
|
throw new Error(`${at}: ${recordedError}`);
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
? {
|
|
499
|
-
...(liveTag !== undefined ? { oldTag: liveTag } : {}),
|
|
500
|
-
newTag: pending[pending.length - 1]?.tag,
|
|
501
|
-
steps: pending.map((m) => ({
|
|
502
|
-
...(m.newSqliteClasses ? { newSqliteClasses: m.newSqliteClasses } : {}),
|
|
503
|
-
...(m.deletedClasses ? { deletedClasses: m.deletedClasses } : {}),
|
|
504
|
-
...(m.renamedClasses ? { renamedClasses: m.renamedClasses } : {}),
|
|
505
|
-
})),
|
|
506
|
-
}
|
|
507
|
-
: undefined;
|
|
508
|
-
await call('upload the script', () => workers.uploadWorkerApp({
|
|
509
|
-
name: want.name,
|
|
510
|
-
artifactUrl: want.artifactUrl,
|
|
511
|
-
artifactSha256: want.artifactSha256,
|
|
512
|
-
compatibilityDate: want.compatibilityDate,
|
|
513
|
-
compatibilityFlags: want.compatibilityFlags,
|
|
514
|
-
bindings: [
|
|
515
|
-
...want.classes.map((c) => ({
|
|
516
|
-
type: 'durable_object_namespace',
|
|
517
|
-
name: c.binding,
|
|
518
|
-
class_name: c.className,
|
|
519
|
-
})),
|
|
520
|
-
...want.vars.map(([name, text]) => ({
|
|
521
|
-
type: 'plain_text',
|
|
522
|
-
name,
|
|
523
|
-
text,
|
|
524
|
-
})),
|
|
525
|
-
...(hyperdriveId
|
|
526
|
-
? [{ type: 'hyperdrive', name: WORKER_APP_HYPERDRIVE_BINDING, id: hyperdriveId }]
|
|
527
|
-
: []),
|
|
528
|
-
],
|
|
529
|
-
observability: { enabled: true, head_sampling_rate: 1 },
|
|
530
|
-
...(migrations ? { migrations } : {}),
|
|
531
|
-
...(want.containers.length > 0
|
|
532
|
-
? { containers: want.containers.map((c) => c.className) }
|
|
533
|
-
: {}),
|
|
534
|
-
}));
|
|
457
|
+
const migrations = pending.migrations;
|
|
458
|
+
await call('upload the script', () => workers.uploadWorkerApp(workerAppUpload(want.name, want, want.vars, migrations, hyperdriveId)));
|
|
535
459
|
context.recordScopeId(workerAppTagsScopeKey(entryKey), JSON.stringify(declaredTags));
|
|
536
460
|
await context.persistScopeIds();
|
|
537
461
|
// A Worker that no longer declares a database no longer binds its config: delete it now.
|
package/dist/plan/worker.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DeclaredOrg, HostingEntry, WorkerConfig } from '../org.js';
|
|
2
|
+
import { WORKER_COMPATIBILITY_DATE } from '../worker-app-upload.js';
|
|
2
3
|
import type { DraftStep } from './diff.js';
|
|
3
4
|
/**
|
|
4
5
|
* F8 (#318): one plain Cloudflare Worker per hosting entry that declares
|
|
@@ -91,7 +92,7 @@ export declare const workerName: (org: DeclaredOrg, entry: HostingEntry) => stri
|
|
|
91
92
|
* like {@link RATE_LIMIT_FREE_PLAN_PERIOD_SECONDS} is a fixed Cloudflare
|
|
92
93
|
* Free plan value rather than something an org declares.
|
|
93
94
|
*/
|
|
94
|
-
export
|
|
95
|
+
export { WORKER_COMPATIBILITY_DATE };
|
|
95
96
|
export type WorkerMode = 'apply' | 'retire';
|
|
96
97
|
export interface WorkerWant {
|
|
97
98
|
name: string;
|
|
@@ -131,4 +132,3 @@ export declare function planWorkers(org: DeclaredOrg, recordedIds: Readonly<Reco
|
|
|
131
132
|
steps: DraftStep[];
|
|
132
133
|
blocked: Blocked;
|
|
133
134
|
};
|
|
134
|
-
export {};
|
package/dist/plan/worker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
+
import { WORKER_COMPATIBILITY_DATE } from '../worker-app-upload.js';
|
|
2
3
|
import { entryHostnames } from './hostnames.js';
|
|
3
4
|
import { lockedHostnames } from './locked-hostnames.js';
|
|
4
5
|
import { appStepKey } from './platform-diff.js';
|
|
@@ -123,7 +124,7 @@ export const workerName = (org, entry) =>
|
|
|
123
124
|
* like {@link RATE_LIMIT_FREE_PLAN_PERIOD_SECONDS} is a fixed Cloudflare
|
|
124
125
|
* Free plan value rather than something an org declares.
|
|
125
126
|
*/
|
|
126
|
-
export
|
|
127
|
+
export { WORKER_COMPATIBILITY_DATE };
|
|
127
128
|
/**
|
|
128
129
|
* With a wildcard route (`*.<domain>/*`) the Worker would also capture every
|
|
129
130
|
* hostname foundry provisions on the domain, so each gets a no-Worker route
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DeclaredOrg, HostingEntry } from '../org.js';
|
|
2
|
+
/** The name of an entry's workspace client and of the key its workspace broker entry is held under. */
|
|
3
|
+
export declare const workspaceClientKey: (entryKey: string) => string;
|
|
4
|
+
/** The company's ZITADEL organisation for the customers of its workspaces. The space keeps it apart from every slug. */
|
|
5
|
+
export declare const testCustomersOrgName: (slug: string) => string;
|
|
6
|
+
/** The recorded id of that organisation (the step's `system:kind:key`). */
|
|
7
|
+
export declare const testCustomersOrgKey: (orgId: string) => string;
|
|
8
|
+
/** `'1'` or `'0'`: whether the test customers organisation was last left open to self-registration. */
|
|
9
|
+
export declare const TEST_CUSTOMERS_SIGN_UP_SCOPE_KEY = "test_customers_sign_up";
|
|
10
|
+
/** `'email-code'` once the emailed-code metadata was written to the test customers organisation. */
|
|
11
|
+
export declare const TEST_CUSTOMERS_SIGN_IN_SCOPE_KEY = "test_customers_sign_in";
|
|
12
|
+
/** The server entries whose workspace has its own sign-in: a workspace and `uses.auth`. */
|
|
13
|
+
export declare function workspaceAuthEntries(org: DeclaredOrg): HostingEntry[];
|
|
14
|
+
/** The entry whose workspace client is named `clientKey`, or `undefined`. */
|
|
15
|
+
export declare const workspaceAuthEntryOf: (org: DeclaredOrg, clientKey: string) => HostingEntry | undefined;
|
|
16
|
+
/** The entries whose workspace signs in the test customers organisation. */
|
|
17
|
+
export declare const testCustomersEntries: (org: DeclaredOrg) => HostingEntry[];
|
|
18
|
+
/** The addresses a workspace client signs in at: the workspace host. */
|
|
19
|
+
export declare const workspaceClientHosts: (org: DeclaredOrg, entry: HostingEntry) => string[];
|
|
20
|
+
/**
|
|
21
|
+
* `org` (planned without its dark entries) plus the sign-in-only shape of the dark entries whose workspace has
|
|
22
|
+
* sign-in, so the login host's lists of customers clients match what the executor, which reads the org as
|
|
23
|
+
* declared, writes. The copies have no address and no Worker.
|
|
24
|
+
*/
|
|
25
|
+
export declare function withDarkWorkspaceSignIn(org: DeclaredOrg, declared: DeclaredOrg): DeclaredOrg;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { workspaceHost } from './hostnames.js';
|
|
2
|
+
/**
|
|
3
|
+
* What the sign-in of a server workspace is called, shared by the planners that must agree on it. A workspace of
|
|
4
|
+
* an entry with `uses.auth` signs in with its own ZITADEL client named `<entry key>-work`, and its customers
|
|
5
|
+
* sign in a separate "<slug> test customers" organisation, never the live customers one.
|
|
6
|
+
*/
|
|
7
|
+
/** The suffix of the client, broker entry and held key that belong to an entry's workspace. */
|
|
8
|
+
const WORKSPACE_CLIENT_SUFFIX = '-work';
|
|
9
|
+
/** The name of an entry's workspace client and of the key its workspace broker entry is held under. */
|
|
10
|
+
export const workspaceClientKey = (entryKey) => `${entryKey}${WORKSPACE_CLIENT_SUFFIX}`;
|
|
11
|
+
/** The company's ZITADEL organisation for the customers of its workspaces. The space keeps it apart from every slug. */
|
|
12
|
+
export const testCustomersOrgName = (slug) => `${slug} test customers`;
|
|
13
|
+
/** The recorded id of that organisation (the step's `system:kind:key`). */
|
|
14
|
+
export const testCustomersOrgKey = (orgId) => `zitadel:customers-test:${orgId}`;
|
|
15
|
+
/** `'1'` or `'0'`: whether the test customers organisation was last left open to self-registration. */
|
|
16
|
+
export const TEST_CUSTOMERS_SIGN_UP_SCOPE_KEY = 'test_customers_sign_up';
|
|
17
|
+
/** `'email-code'` once the emailed-code metadata was written to the test customers organisation. */
|
|
18
|
+
export const TEST_CUSTOMERS_SIGN_IN_SCOPE_KEY = 'test_customers_sign_in';
|
|
19
|
+
/** The server entries whose workspace has its own sign-in: a workspace and `uses.auth`. */
|
|
20
|
+
export function workspaceAuthEntries(org) {
|
|
21
|
+
if (!org.hosting.enabled)
|
|
22
|
+
return [];
|
|
23
|
+
return org.hosting.entries.filter((entry) => entry.workspace !== undefined &&
|
|
24
|
+
entry.workerApp === undefined &&
|
|
25
|
+
entry.uses?.auth !== undefined);
|
|
26
|
+
}
|
|
27
|
+
/** The entry whose workspace client is named `clientKey`, or `undefined`. */
|
|
28
|
+
export const workspaceAuthEntryOf = (org, clientKey) => workspaceAuthEntries(org).find((entry) => workspaceClientKey(entry.key) === clientKey);
|
|
29
|
+
/** The entries whose workspace signs in the test customers organisation. */
|
|
30
|
+
export const testCustomersEntries = (org) => workspaceAuthEntries(org).filter((entry) => entry.uses?.auth?.users === 'customers');
|
|
31
|
+
/** The addresses a workspace client signs in at: the workspace host. */
|
|
32
|
+
export const workspaceClientHosts = (org, entry) => [
|
|
33
|
+
workspaceHost(org.slug, entry.key),
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* `org` (planned without its dark entries) plus the sign-in-only shape of the dark entries whose workspace has
|
|
37
|
+
* sign-in, so the login host's lists of customers clients match what the executor, which reads the org as
|
|
38
|
+
* declared, writes. The copies have no address and no Worker.
|
|
39
|
+
*/
|
|
40
|
+
export function withDarkWorkspaceSignIn(org, declared) {
|
|
41
|
+
const present = new Set(org.hosting.entries.map((entry) => entry.key));
|
|
42
|
+
const dark = workspaceAuthEntries(declared)
|
|
43
|
+
.filter((entry) => !present.has(entry.key))
|
|
44
|
+
.map((entry) => ({ ...entry, hostnames: [], gates: [] }));
|
|
45
|
+
if (dark.length === 0)
|
|
46
|
+
return org;
|
|
47
|
+
return { ...org, hosting: { ...org.hosting, entries: [...org.hosting.entries, ...dark] } };
|
|
48
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { DeclaredOrg, HostingEntry } from '../org.js';
|
|
2
|
+
import type { UnmanagedItem } from '../plan.js';
|
|
3
|
+
import { type BrokerEntryInput, type BrokerEntryWant } from './broker-entries.js';
|
|
4
|
+
import type { DraftStep } from './diff.js';
|
|
5
|
+
import type { LiveResource } from './live.js';
|
|
6
|
+
/** The step `coolify:workspace-broker-entry` and its action key, for the planner and the executor to share. */
|
|
7
|
+
export declare const WORKSPACE_BROKER_ENTRY_KIND = "workspace-broker-entry";
|
|
8
|
+
/**
|
|
9
|
+
* The MCP resource of a workspace with `uses.auth.mcp`: its own endpoint on the workspace host, its own audience
|
|
10
|
+
* project in the test customers organisation (recorded by `zitadel:mcp-project` under the client's name), and the
|
|
11
|
+
* onboarding page on the workspace host. `null` without `mcp` or until the project is recorded.
|
|
12
|
+
*/
|
|
13
|
+
export declare function workspaceMcp(org: DeclaredOrg, entry: HostingEntry, recorded: Readonly<Record<string, string>>): {
|
|
14
|
+
resource: string;
|
|
15
|
+
projectId: string;
|
|
16
|
+
loginHost: string;
|
|
17
|
+
onboardingUrl?: string;
|
|
18
|
+
} | null;
|
|
19
|
+
/**
|
|
20
|
+
* What the app's env says about who signs in at the workspace, as recorded so far, or `null` until the
|
|
21
|
+
* organisation, client and issuer are all known. Non-secret values only.
|
|
22
|
+
*/
|
|
23
|
+
export declare function workspaceAuthEnv(org: DeclaredOrg, entry: HostingEntry, recorded: Readonly<Record<string, string>>, issuer: string | null): Record<string, string> | null;
|
|
24
|
+
/**
|
|
25
|
+
* The client ids of the company's live apps: every recorded client id except the workspaces' own. A stage that
|
|
26
|
+
* holds one of them could sign in as the live app.
|
|
27
|
+
*/
|
|
28
|
+
export declare function liveClientIds(org: DeclaredOrg, recorded: Readonly<Record<string, string>>): string[];
|
|
29
|
+
/** What `ws.<key>.signin` records: the sign-in env above as JSON, or `null` while it is not all known. */
|
|
30
|
+
export declare function workspaceSignInMarker(org: DeclaredOrg, entry: HostingEntry, recorded: Readonly<Record<string, string>>, issuer: string | null): string | null;
|
|
31
|
+
/**
|
|
32
|
+
* The test customers organisation, planned once an entry's workspace signs in customers. Sign-up and the emailed
|
|
33
|
+
* code follow what the company declares for its live customers (`customers.signUp`, the entries' `signIn`), so
|
|
34
|
+
* testers meet the same sign-in the customers will. Idempotent on the recorded id and the two markers.
|
|
35
|
+
*/
|
|
36
|
+
export declare function planTestCustomers(org: DeclaredOrg, live: readonly LiveResource[], claimed: Set<string>, recordedIds: Readonly<Record<string, string>>, metadataWired: boolean): {
|
|
37
|
+
step: DraftStep | null;
|
|
38
|
+
blocked: UnmanagedItem[];
|
|
39
|
+
};
|
|
40
|
+
/** One ZITADEL web client `<key>-work` per workspace with sign-in, planned once; its redirect addresses name the workspace host. */
|
|
41
|
+
export declare function planWorkspaceClients(org: DeclaredOrg, byKey: ReadonlyMap<string, LiveResource>, claimed: Set<string>): DraftStep[];
|
|
42
|
+
/** What a workspace broker entry must say, without its keys, or what is still missing. */
|
|
43
|
+
export declare function wantedWorkspaceBrokerEntry(org: DeclaredOrg, entry: HostingEntry, recorded: Readonly<Record<string, string>>): {
|
|
44
|
+
want: BrokerEntryWant;
|
|
45
|
+
} | {
|
|
46
|
+
missing: BrokerEntryInput[];
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* One broker `AUTH_APPS` entry per workspace with sign-in, written after the workspace step has put the stage's
|
|
50
|
+
* `AUTH_APP_KEY` in its env. Idempotent on the recorded marker of what the last write said.
|
|
51
|
+
*/
|
|
52
|
+
export declare function planWorkspaceBrokerEntries(org: DeclaredOrg, recordedIds: Readonly<Record<string, string>>, plannedSteps: readonly DraftStep[], brokerWired: boolean, brokerWorkspacesDatabase: boolean, stageReady: (entry: HostingEntry) => boolean): {
|
|
53
|
+
steps: DraftStep[];
|
|
54
|
+
blocked: UnmanagedItem[];
|
|
55
|
+
};
|