@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.11
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/LICENSE +21 -0
- package/README.md +62 -64
- package/dist/cloudflare/catalogSettings.generated.d.ts +84 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
- package/dist/cloudflare/catalogSettings.generated.js +175 -0
- package/dist/cloudflare/index.d.ts +3 -2
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +4 -1
- package/dist/cloudflare/sentinel.d.ts +60 -2
- package/dist/cloudflare/sentinel.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.js +37 -2
- package/dist/cloudflare/worker.d.ts +391 -63
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +394 -108
- package/dist/dev/index.d.ts +1 -1
- package/dist/dev/index.d.ts.map +1 -1
- package/dist/dev/index.js +1 -1
- package/dist/dev/keypair.d.ts +2 -0
- package/dist/dev/keypair.d.ts.map +1 -1
- package/dist/dev/keypair.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/internal/aiDefault.d.ts +205 -0
- package/dist/internal/aiDefault.d.ts.map +1 -0
- package/dist/internal/aiDefault.js +335 -0
- package/dist/internal/aiProviders.d.ts +7 -2
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +19 -6
- package/dist/internal/appSettings.d.ts +82 -4
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +204 -29
- package/dist/internal/audit.d.ts +108 -0
- package/dist/internal/audit.d.ts.map +1 -0
- package/dist/internal/audit.js +235 -0
- package/dist/internal/capabilities.d.ts +2261 -815
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +122 -1034
- package/dist/internal/catalog/commerce.d.ts +394 -0
- package/dist/internal/catalog/commerce.d.ts.map +1 -0
- package/dist/internal/catalog/commerce.js +477 -0
- package/dist/internal/catalog/comms.d.ts +375 -0
- package/dist/internal/catalog/comms.d.ts.map +1 -0
- package/dist/internal/catalog/comms.js +520 -0
- package/dist/internal/catalog/index.d.ts +2008 -0
- package/dist/internal/catalog/index.d.ts.map +1 -0
- package/dist/internal/catalog/index.js +19 -0
- package/dist/internal/catalog/inference.d.ts +393 -0
- package/dist/internal/catalog/inference.d.ts.map +1 -0
- package/dist/internal/catalog/inference.js +460 -0
- package/dist/internal/catalog/infra.d.ts +458 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -0
- package/dist/internal/catalog/infra.js +608 -0
- package/dist/internal/catalog/search.d.ts +400 -0
- package/dist/internal/catalog/search.d.ts.map +1 -0
- package/dist/internal/catalog/search.js +463 -0
- package/dist/internal/devProviders.d.ts +2 -2
- package/dist/internal/devProviders.d.ts.map +1 -1
- package/dist/internal/devProviders.js +19 -1
- package/dist/internal/events.d.ts +65 -3
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +110 -5
- package/dist/internal/gatewaySettings.d.ts +3 -2
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +12 -6
- package/dist/internal/identity.d.ts +41 -4
- package/dist/internal/identity.d.ts.map +1 -1
- package/dist/internal/identity.js +48 -10
- package/dist/internal/index.d.ts +6 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +6 -1
- package/dist/internal/members.d.ts +3 -2
- package/dist/internal/members.d.ts.map +1 -1
- package/dist/internal/oauthBuiltIns.d.ts +101 -0
- package/dist/internal/oauthBuiltIns.d.ts.map +1 -0
- package/dist/internal/oauthBuiltIns.js +122 -0
- package/dist/internal/oauthClient.d.ts +8 -50
- package/dist/internal/oauthClient.d.ts.map +1 -1
- package/dist/internal/oauthClient.js +2 -177
- package/dist/internal/oauthPresets.d.ts +32 -0
- package/dist/internal/oauthPresets.d.ts.map +1 -0
- package/dist/internal/oauthPresets.js +253 -0
- package/dist/internal/oauthScopes.d.ts +43 -100
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +83 -382
- package/dist/internal/oauthSpec.d.ts +103 -0
- package/dist/internal/oauthSpec.d.ts.map +1 -0
- package/dist/internal/oauthSpec.js +520 -0
- package/dist/internal/protocol.d.ts +97 -5
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +89 -1
- package/dist/internal/providerRegistry.d.ts +17 -6
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +71 -8
- package/dist/internal/providerSpec.d.ts +18 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +420 -34
- package/dist/internal/remoteMcp.d.ts.map +1 -1
- package/dist/internal/remoteMcp.js +1 -5
- package/dist/internal/signInProviders.d.ts +104 -0
- package/dist/internal/signInProviders.d.ts.map +1 -0
- package/dist/internal/signInProviders.js +245 -0
- package/dist/internal/tools.d.ts +5 -4
- package/dist/internal/tools.d.ts.map +1 -1
- package/dist/internal/tools.js +4 -3
- package/dist/manifest.d.ts +267 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +30 -0
- package/dist/server/ai.d.ts +111 -0
- package/dist/server/ai.d.ts.map +1 -0
- package/dist/server/ai.js +145 -0
- package/dist/server/aiBinding.d.ts +68 -0
- package/dist/server/aiBinding.d.ts.map +1 -0
- package/dist/server/aiBinding.js +294 -0
- package/dist/server/attribution.d.ts +18 -5
- package/dist/server/attribution.d.ts.map +1 -1
- package/dist/server/attribution.js +19 -5
- package/dist/server/connectUrl.d.ts +16 -0
- package/dist/server/connectUrl.d.ts.map +1 -0
- package/dist/server/connectUrl.js +40 -0
- package/dist/server/context.d.ts +68 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +117 -0
- package/dist/server/emailBinding.d.ts +72 -0
- package/dist/server/emailBinding.d.ts.map +1 -0
- package/dist/server/emailBinding.js +124 -0
- package/dist/server/emit.d.ts +1 -1
- package/dist/server/emit.d.ts.map +1 -1
- package/dist/server/emit.js +7 -7
- package/dist/server/env.d.ts +44 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +1 -0
- package/dist/server/eventsEndpoint.d.ts +48 -0
- package/dist/server/eventsEndpoint.d.ts.map +1 -0
- package/dist/server/eventsEndpoint.js +145 -0
- package/dist/server/everyApp.d.ts +40 -11
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +116 -6
- package/dist/server/fromMcpTools.d.ts +89 -0
- package/dist/server/fromMcpTools.d.ts.map +1 -0
- package/dist/server/fromMcpTools.js +192 -0
- package/dist/server/gatewayFetch.d.ts +42 -18
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +138 -19
- package/dist/server/gatewayUrl.d.ts +13 -0
- package/dist/server/gatewayUrl.d.ts.map +1 -0
- package/dist/server/gatewayUrl.js +34 -0
- package/dist/server/index.d.ts +16 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +13 -3
- package/dist/server/integrationFetch.d.ts +92 -73
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +206 -319
- package/dist/server/listMembers.d.ts +1 -1
- package/dist/server/listMembers.d.ts.map +1 -1
- package/dist/server/listMembers.js +8 -11
- package/dist/server/refusal.d.ts +55 -0
- package/dist/server/refusal.d.ts.map +1 -0
- package/dist/server/refusal.js +193 -0
- package/dist/server/sendEmail.d.ts +1 -1
- package/dist/server/sendEmail.d.ts.map +1 -1
- package/dist/server/sendEmail.js +7 -7
- package/dist/server/tools.d.ts +7 -2
- package/dist/server/tools.d.ts.map +1 -1
- package/dist/server/tools.js +2 -1
- package/dist/server/toolsEndpoint.d.ts +3 -1
- package/dist/server/toolsEndpoint.d.ts.map +1 -1
- package/dist/server/toolsEndpoint.js +19 -7
- package/dist/server/webhooks.d.ts +56 -0
- package/dist/server/webhooks.d.ts.map +1 -0
- package/dist/server/webhooks.js +205 -0
- package/dist/tanstack-start/index.d.ts +9 -0
- package/dist/tanstack-start/index.d.ts.map +1 -0
- package/dist/tanstack-start/index.js +29 -0
- package/dist/testing/index.d.ts +93 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +156 -0
- package/package.json +35 -14
- package/dist/server/authorizeUrl.d.ts +0 -43
- package/dist/server/authorizeUrl.d.ts.map +0 -1
- package/dist/server/authorizeUrl.js +0 -110
|
@@ -2,8 +2,13 @@ import { createHash, createHmac } from "node:crypto";
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseEnv } from "node:util";
|
|
5
|
-
import { DEV_ISSUER, DEV_PUBLIC_KEY_PEM } from "../dev/keypair.js";
|
|
6
|
-
|
|
5
|
+
import { DEV_ISSUER, DEV_ORG_ID, DEV_PUBLIC_KEY_PEM } from "../dev/keypair.js";
|
|
6
|
+
// The catalog's settings, generated from ../internal/capabilities.ts, which
|
|
7
|
+
// this file must not import (see ./index.ts: the Vite plugin hides whatever
|
|
8
|
+
// cloudflare.config.ts imports from the Worker).
|
|
9
|
+
import { DEV_GATEWAY_KEYS } from "./catalogSettings.generated.js";
|
|
10
|
+
import { helperWorkerName, sentinelDeployTarget, workflowNameFor, } from "./sentinel.js";
|
|
11
|
+
export { helperWorkerName, workflowNameFor } from "./sentinel.js";
|
|
7
12
|
/** The names of the manifest's `secrets`, whichever form each took. */
|
|
8
13
|
function manifestSecretNames(manifest) {
|
|
9
14
|
return (manifest.secrets ?? []).map((entry) => typeof entry === "string" ? entry : entry.name);
|
|
@@ -12,69 +17,6 @@ function manifestSecretNames(manifest) {
|
|
|
12
17
|
function manifestProviderIds(manifest) {
|
|
13
18
|
return (manifest.providers ?? []).map((entry) => typeof entry === "string" ? entry : entry.id);
|
|
14
19
|
}
|
|
15
|
-
/**
|
|
16
|
-
* The gateway secrets holding the keys of the AI providers and capabilities
|
|
17
|
-
* an app may ask for, with the settings a provider needs besides its key
|
|
18
|
-
* (Workers AI's account). With no gateway in development, the SDK reaches
|
|
19
|
-
* them with the developer's own values under the same names. A copy of the
|
|
20
|
-
* tables in ../internal, which the Worker loads: the Vite plugin hides
|
|
21
|
-
* whatever cloudflare.config.ts imports from the Worker. A test keeps them
|
|
22
|
-
* in step.
|
|
23
|
-
*/
|
|
24
|
-
const DEV_GATEWAY_KEYS = {
|
|
25
|
-
providers: {
|
|
26
|
-
openai: ["OPENAI_API_KEY"],
|
|
27
|
-
openrouter: ["OPENROUTER_API_KEY"],
|
|
28
|
-
"workers-ai": ["WORKERS_AI_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"],
|
|
29
|
-
},
|
|
30
|
-
capabilities: {
|
|
31
|
-
dataforseo: ["DATAFORSEO_API_KEY"],
|
|
32
|
-
context: ["CONTEXT_API_KEY"],
|
|
33
|
-
// The sender, and the developer's own Email Sending account: set all
|
|
34
|
-
// but the name and sendEmail() really sends; otherwise it prints.
|
|
35
|
-
// Resend's key and EMAIL_PROVIDER pick the provider integrationStatus()
|
|
36
|
-
// names, as the gateway would.
|
|
37
|
-
// TODO(integration): the last two added by the email-providers lane
|
|
38
|
-
// (this file is outside its ownership list).
|
|
39
|
-
email: [
|
|
40
|
-
"EMAIL_FROM",
|
|
41
|
-
"EMAIL_FROM_NAME",
|
|
42
|
-
"EMAIL_REST_API_TOKEN",
|
|
43
|
-
"CLOUDFLARE_ACCOUNT_ID",
|
|
44
|
-
"RESEND_API_KEY",
|
|
45
|
-
"EMAIL_PROVIDER",
|
|
46
|
-
],
|
|
47
|
-
// The organization's members: nothing to set; listMembers() answers
|
|
48
|
-
// from the dev identity.
|
|
49
|
-
members: [],
|
|
50
|
-
// OAuth clients: the id, the secret, and the instance a provider with
|
|
51
|
-
// no fixed host is on.
|
|
52
|
-
linkedin: ["LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET"],
|
|
53
|
-
facebook: ["FACEBOOK_APP_ID", "FACEBOOK_APP_SECRET"],
|
|
54
|
-
instagram: ["INSTAGRAM_APP_ID", "INSTAGRAM_APP_SECRET"],
|
|
55
|
-
threads: ["THREADS_APP_ID", "THREADS_APP_SECRET"],
|
|
56
|
-
youtube: ["YOUTUBE_CLIENT_ID", "YOUTUBE_CLIENT_SECRET"],
|
|
57
|
-
tiktok: ["TIKTOK_CLIENT_ID", "TIKTOK_CLIENT_SECRET"],
|
|
58
|
-
"tiktok-business": [
|
|
59
|
-
"TIKTOK_BUSINESS_CLIENT_ID",
|
|
60
|
-
"TIKTOK_BUSINESS_CLIENT_SECRET",
|
|
61
|
-
],
|
|
62
|
-
reddit: ["REDDIT_CLIENT_ID", "REDDIT_CLIENT_SECRET"],
|
|
63
|
-
pinterest: ["PINTEREST_CLIENT_ID", "PINTEREST_CLIENT_SECRET"],
|
|
64
|
-
discord: ["DISCORD_CLIENT_ID", "DISCORD_CLIENT_SECRET"],
|
|
65
|
-
slack: ["SLACK_CLIENT_ID", "SLACK_CLIENT_SECRET"],
|
|
66
|
-
mastodon: ["MASTODON_CLIENT_ID", "MASTODON_CLIENT_SECRET", "MASTODON_URL"],
|
|
67
|
-
kick: ["KICK_CLIENT_ID", "KICK_CLIENT_SECRET"],
|
|
68
|
-
twitch: ["TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET"],
|
|
69
|
-
tumblr: ["TUMBLR_CLIENT_ID", "TUMBLR_CLIENT_SECRET"],
|
|
70
|
-
dribbble: ["DRIBBBLE_CLIENT_ID", "DRIBBBLE_CLIENT_SECRET"],
|
|
71
|
-
"google-workspace": [
|
|
72
|
-
"GOOGLE_WORKSPACE_CLIENT_ID",
|
|
73
|
-
"GOOGLE_WORKSPACE_CLIENT_SECRET",
|
|
74
|
-
],
|
|
75
|
-
"microsoft-365": ["MICROSOFT_365_CLIENT_ID", "MICROSOFT_365_CLIENT_SECRET"],
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
20
|
/**
|
|
79
21
|
* Observability every Every App Worker ships with: logs (with invocation
|
|
80
22
|
* logs), sampled traces and Workers Issues, with query strings redacted so
|
|
@@ -104,10 +46,23 @@ const BUILD_ENV = "EVERYAPP_BUILD";
|
|
|
104
46
|
const RESERVED_SECRET_NAME = /^(GATEWAY_URL|CF_VERSION_METADATA|EVERYAPP_\w*|EVERY_APP_\w*|APP__\w*)$/i;
|
|
105
47
|
const GATEWAY_WORKER_NAME = "every-app-gateway";
|
|
106
48
|
const GATEWAY_ENTRYPOINT = "AppGateway";
|
|
49
|
+
/**
|
|
50
|
+
* Where `everyapp dev` serves its launcher, which it passes to the app's dev
|
|
51
|
+
* server in EVERYAPP_DEV_GATEWAY_URL; its default address otherwise. The
|
|
52
|
+
* deployed Worker gets the Gateway's URL from the deploy instead.
|
|
53
|
+
*/
|
|
54
|
+
const DEV_GATEWAY_URL_ENV = "EVERYAPP_DEV_GATEWAY_URL";
|
|
55
|
+
const DEFAULT_DEV_GATEWAY_URL = "http://localhost:8787";
|
|
56
|
+
function devGatewayUrl() {
|
|
57
|
+
return {
|
|
58
|
+
type: "text",
|
|
59
|
+
value: process.env[DEV_GATEWAY_URL_ENV] || DEFAULT_DEV_GATEWAY_URL,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
107
62
|
/**
|
|
108
63
|
* The manifest's `capabilities` as the SDK's local-dev twin of the gateway
|
|
109
|
-
* reads them: with no gateway
|
|
110
|
-
*
|
|
64
|
+
* reads them: with no gateway, `integrationStatus()` reports an OAuth
|
|
65
|
+
* provider's declared scopes, and whether the app lists it, from these.
|
|
111
66
|
*/
|
|
112
67
|
function devCapabilities(manifest) {
|
|
113
68
|
return { type: "text", value: JSON.stringify(manifest.capabilities ?? {}) };
|
|
@@ -115,9 +70,12 @@ function devCapabilities(manifest) {
|
|
|
115
70
|
/**
|
|
116
71
|
* The binding the dev Worker gets the specs of providers registered on a
|
|
117
72
|
* gateway in (`DEV_PROVIDERS_ENV` in ../internal/devProviders.ts; a copy,
|
|
118
|
-
* kept in step by a test,
|
|
73
|
+
* kept in step by a test, since this file may not import ../internal).
|
|
119
74
|
*/
|
|
120
75
|
const DEV_PROVIDERS_ENV = "EVERYAPP_DEV_PROVIDERS";
|
|
76
|
+
/** Linked dev's env names (`LINKED_DEV_URL_ENV`, `LINKED_DEV_NONCE_ENV` in ../internal/protocol.ts; copies, kept in step by the same test). */
|
|
77
|
+
const LINKED_DEV_URL_ENV = "EVERYAPP_LINKED_DEV_URL";
|
|
78
|
+
const LINKED_DEV_NONCE_ENV = "EVERYAPP_LINKED_DEV_NONCE";
|
|
121
79
|
/**
|
|
122
80
|
* The specs `everyapp provider add` (in an app directory) and `everyapp
|
|
123
81
|
* provider show <id> --save` wrote under the app, in name order; none
|
|
@@ -148,6 +106,22 @@ function devProviderFiles(root) {
|
|
|
148
106
|
return { file: `.everyapp/providers/${name}`, spec };
|
|
149
107
|
});
|
|
150
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Linked dev (`everyapp dev --gateway`, docs/design/linked-dev.md): the
|
|
111
|
+
* gateway-lite starts the app's dev server with the forwarder's loopback
|
|
112
|
+
* URL and the run's nonce in the process environment; the dev Worker gets
|
|
113
|
+
* both as text, and the SDK's `gatewayBinding()` builds the shim from
|
|
114
|
+
* them in place of the missing service binding. Absent in every other
|
|
115
|
+
* run, and never in a deploy.
|
|
116
|
+
*/
|
|
117
|
+
function addLinkedDev(env) {
|
|
118
|
+
const url = process.env[LINKED_DEV_URL_ENV]?.trim();
|
|
119
|
+
const nonce = process.env[LINKED_DEV_NONCE_ENV];
|
|
120
|
+
if (!url || !nonce)
|
|
121
|
+
return;
|
|
122
|
+
env[LINKED_DEV_URL_ENV] = { type: "text", value: url };
|
|
123
|
+
env[LINKED_DEV_NONCE_ENV] = { type: "text", value: nonce };
|
|
124
|
+
}
|
|
151
125
|
/** EVERYAPP_DEV_PROVIDERS, when the app has any spec files. */
|
|
152
126
|
function addDevProviders(env, root) {
|
|
153
127
|
const files = devProviderFiles(root);
|
|
@@ -158,8 +132,9 @@ function addDevProviders(env, root) {
|
|
|
158
132
|
/**
|
|
159
133
|
* The Gateway settings the spec files name for the capabilities the
|
|
160
134
|
* manifest lists outside {@link DEV_GATEWAY_KEYS} (a registered provider's
|
|
161
|
-
* key, a Basic username held as a setting, its host setting
|
|
162
|
-
* loosely: the SDK parses the spec when the
|
|
135
|
+
* key, a Basic username held as a setting, its host setting, an OAuth
|
|
136
|
+
* provider's dev token), read loosely: the SDK parses the spec when the
|
|
137
|
+
* app calls the provider.
|
|
163
138
|
*/
|
|
164
139
|
function devProviderKeys(manifest, root) {
|
|
165
140
|
const listed = Object.keys(manifest.capabilities ?? {}).filter((id) => !Object.hasOwn(DEV_GATEWAY_KEYS.capabilities, id));
|
|
@@ -184,6 +159,11 @@ function devProviderKeys(manifest, root) {
|
|
|
184
159
|
auth?.["secret"],
|
|
185
160
|
record(auth?.["user"])?.["secret"],
|
|
186
161
|
fields["hostSetting"],
|
|
162
|
+
// An OAuth provider: the token that stands in for a connection in
|
|
163
|
+
// dev (devAccessTokenName() in ../server/integrationFetch.ts).
|
|
164
|
+
auth?.["kind"] === "oauth2-user"
|
|
165
|
+
? `${id.toUpperCase().replace(/-/g, "_")}_ACCESS_TOKEN`
|
|
166
|
+
: undefined,
|
|
187
167
|
]) {
|
|
188
168
|
if (typeof name === "string" && /^[A-Z][A-Z0-9_]*$/.test(name)) {
|
|
189
169
|
names.push(name);
|
|
@@ -194,8 +174,14 @@ function devProviderKeys(manifest, root) {
|
|
|
194
174
|
}
|
|
195
175
|
/** Cloudflare's limit on a Workflow's name. */
|
|
196
176
|
const MAX_WORKFLOW_NAME_LENGTH = 64;
|
|
177
|
+
/**
|
|
178
|
+
* The store development binds Secrets Store secrets in: Miniflare keeps
|
|
179
|
+
* one per id under `.cloudflare/state`, and any id works locally.
|
|
180
|
+
*/
|
|
181
|
+
const LOCAL_SECRETS_STORE_ID = "local";
|
|
182
|
+
/** Cloudflare's limit on a Vectorize index's name. */
|
|
183
|
+
const MAX_VECTORIZE_NAME_LENGTH = 64;
|
|
197
184
|
/** Cloudflare's limit on a Worker's name. */
|
|
198
|
-
const MAX_WORKER_NAME_LENGTH = 63;
|
|
199
185
|
/**
|
|
200
186
|
* Where a developer puts a local Postgres URL for a Hyperdrive binding
|
|
201
187
|
* (`.dev.vars` or `.env`), the variable wrangler reads for the same.
|
|
@@ -222,6 +208,12 @@ export function everyAppWorker(options) {
|
|
|
222
208
|
const { deploy, name } = appWorkerName(options, "everyAppWorker");
|
|
223
209
|
const env = {
|
|
224
210
|
EVERYAPP_APP_ID: { type: "text", value: manifest.id },
|
|
211
|
+
// The manifest itself, for a page that shows what the app asked for:
|
|
212
|
+
// the Worker can't import everyapp.config.ts (see ./index.ts).
|
|
213
|
+
EVERYAPP_MANIFEST: {
|
|
214
|
+
type: "json",
|
|
215
|
+
value: manifest,
|
|
216
|
+
},
|
|
225
217
|
CF_VERSION_METADATA: { type: "version-metadata" },
|
|
226
218
|
};
|
|
227
219
|
if (deploy) {
|
|
@@ -248,9 +240,25 @@ export function everyAppWorker(options) {
|
|
|
248
240
|
value: JSON.stringify([DEV_PUBLIC_KEY_PEM]),
|
|
249
241
|
};
|
|
250
242
|
env["EVERYAPP_IDENTITY_ISSUER"] = { type: "text", value: DEV_ISSUER };
|
|
243
|
+
env["GATEWAY_URL"] = devGatewayUrl();
|
|
244
|
+
// The stub gateway's organization, as the deploy's secret would be, so
|
|
245
|
+
// background work's withOrganization(env.EVERY_APP_ORG_ID, …) runs
|
|
246
|
+
// here too. Not a pin: under the dev opt-in everyApp() enforces none,
|
|
247
|
+
// so `everyapp dev --mode mirror`'s real organizations still open.
|
|
248
|
+
env["EVERY_APP_ORG_ID"] = { type: "text", value: DEV_ORG_ID };
|
|
251
249
|
addDevProviders(env, options.root);
|
|
250
|
+
addLinkedDev(env);
|
|
252
251
|
}
|
|
253
252
|
Object.assign(env, storageBindings(manifest, name, deploy));
|
|
253
|
+
addQueueBindings(env, manifest, name, deploy);
|
|
254
|
+
addAiBindings(env, manifest, deploy);
|
|
255
|
+
addBrowserBindings(env, manifest);
|
|
256
|
+
addImagesBindings(env, manifest);
|
|
257
|
+
addAiGatewayBindings(env, manifest, name, deploy);
|
|
258
|
+
addSendEmailBindings(env, manifest);
|
|
259
|
+
addSecretsStoreBindings(env, manifest, name, deploy);
|
|
260
|
+
addAnalyticsEngineBindings(env, manifest, name);
|
|
261
|
+
addArtifactsBindings(env, manifest, name, deploy);
|
|
254
262
|
addDatabaseBindings(env, options, name, undefined, deploy);
|
|
255
263
|
// The app binds every class, a helper Worker's in that Worker, and
|
|
256
264
|
// exports only its own.
|
|
@@ -268,13 +276,19 @@ export function everyAppWorker(options) {
|
|
|
268
276
|
};
|
|
269
277
|
}
|
|
270
278
|
addSecrets(env, options, deploy);
|
|
271
|
-
const triggers =
|
|
279
|
+
const triggers = [
|
|
280
|
+
...cronTriggers(options.crons),
|
|
281
|
+
...queueTriggers(manifest, name, undefined, deploy),
|
|
282
|
+
];
|
|
272
283
|
const compatibilityDate = manifest.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE;
|
|
273
284
|
// cf types a Durable Object namespace with its class only when the binding
|
|
274
285
|
// names the defining Worker by a definition whose module it can read, so
|
|
275
286
|
// the classes this Worker defines name it that way; cf resolves the
|
|
276
287
|
// reference to the Worker's name. A path entrypoint gives cf no module.
|
|
288
|
+
// Only here: `EveryAppBinding` names Workers by name, as the Vite
|
|
289
|
+
// plugin's `auxiliaryWorkers` require.
|
|
277
290
|
const { entrypoint } = options;
|
|
291
|
+
const ownClasses = {};
|
|
278
292
|
if (typeof entrypoint === "object") {
|
|
279
293
|
const self = {
|
|
280
294
|
name,
|
|
@@ -284,7 +298,7 @@ export function everyAppWorker(options) {
|
|
|
284
298
|
for (const durableObject of manifest.resources?.durableObjects ?? []) {
|
|
285
299
|
if (durableObject.worker !== undefined)
|
|
286
300
|
continue;
|
|
287
|
-
|
|
301
|
+
ownClasses[durableObject.name] = {
|
|
288
302
|
type: "durable-object",
|
|
289
303
|
worker: self,
|
|
290
304
|
exportName: durableObject.className,
|
|
@@ -301,7 +315,8 @@ export function everyAppWorker(options) {
|
|
|
301
315
|
...(triggers.length ? { triggers } : {}),
|
|
302
316
|
...runtimeSettings(manifest, manifest.limits, deploy),
|
|
303
317
|
observability: observability(),
|
|
304
|
-
env
|
|
318
|
+
// Each class keeps its key's place in `env`: classBindings bound it by name.
|
|
319
|
+
env: { ...env, ...ownClasses },
|
|
305
320
|
exports: exports,
|
|
306
321
|
};
|
|
307
322
|
}
|
|
@@ -309,7 +324,10 @@ export function everyAppWorker(options) {
|
|
|
309
324
|
* Build the definition of one of the app's helper Workers (the manifest's
|
|
310
325
|
* `workers`), e.g. one that runs a memory-hungry job away from the app's
|
|
311
326
|
* Worker. It is named `<app worker>--<worker>`, is private like the app, and
|
|
312
|
-
* binds the app's
|
|
327
|
+
* binds the app's and Analytics Engine datasets, the classes it defines, and
|
|
328
|
+
* binds the app's D1 databases, KV namespaces, R2 buckets, Workers AI,
|
|
329
|
+
* Browser Rendering, Images, Vectorize indexes, AI Gateways, `send_email`
|
|
330
|
+
* bindings and Secrets Store secrets, the classes it defines, and
|
|
313
331
|
* the gateway (with the app's identity, for capabilities). Only the app's
|
|
314
332
|
* Worker reaches it. Pass it to the Vite plugin:
|
|
315
333
|
*
|
|
@@ -331,6 +349,8 @@ export function everyAppAuxiliaryWorker(options) {
|
|
|
331
349
|
EVERYAPP_APP_ID: { type: "text", value: manifest.id },
|
|
332
350
|
...storageBindings(manifest, appWorker, deploy),
|
|
333
351
|
};
|
|
352
|
+
addQueueBindings(env, manifest, appWorker, deploy);
|
|
353
|
+
addSendEmailBindings(env, manifest);
|
|
334
354
|
if (deploy) {
|
|
335
355
|
// The app's identity: the gateway treats its helper Workers as the app.
|
|
336
356
|
env["EVERY_APP_GATEWAY"] = gatewayBinding(deploy, manifest.id, appWorker);
|
|
@@ -342,13 +362,26 @@ export function everyAppAuxiliaryWorker(options) {
|
|
|
342
362
|
else {
|
|
343
363
|
env["EVERYAPP_DEV"] = { type: "text", value: "1" };
|
|
344
364
|
env["EVERYAPP_DEV_CAPABILITIES"] = devCapabilities(manifest);
|
|
365
|
+
env["GATEWAY_URL"] = devGatewayUrl();
|
|
366
|
+
env["EVERY_APP_ORG_ID"] = { type: "text", value: DEV_ORG_ID };
|
|
345
367
|
addDevProviders(env, options.root);
|
|
368
|
+
addLinkedDev(env);
|
|
346
369
|
}
|
|
370
|
+
addAiBindings(env, manifest, deploy);
|
|
371
|
+
addBrowserBindings(env, manifest);
|
|
372
|
+
addImagesBindings(env, manifest);
|
|
373
|
+
addAiGatewayBindings(env, manifest, appWorker, deploy);
|
|
374
|
+
addSecretsStoreBindings(env, manifest, appWorker, deploy);
|
|
375
|
+
addAnalyticsEngineBindings(env, manifest, appWorker);
|
|
376
|
+
addArtifactsBindings(env, manifest, appWorker, deploy);
|
|
347
377
|
addDatabaseBindings(env, options, appWorker, worker, deploy);
|
|
348
378
|
const { bindings, exports } = classBindings(manifest, appWorker, worker, env);
|
|
349
379
|
Object.assign(env, bindings);
|
|
350
380
|
addSecrets(env, options, deploy);
|
|
351
|
-
const triggers =
|
|
381
|
+
const triggers = [
|
|
382
|
+
...cronTriggers(options.crons),
|
|
383
|
+
...queueTriggers(manifest, appWorker, worker, deploy),
|
|
384
|
+
];
|
|
352
385
|
return {
|
|
353
386
|
name,
|
|
354
387
|
entrypoint: options.entrypoint,
|
|
@@ -363,18 +396,6 @@ export function everyAppAuxiliaryWorker(options) {
|
|
|
363
396
|
exports,
|
|
364
397
|
};
|
|
365
398
|
}
|
|
366
|
-
/**
|
|
367
|
-
* A helper Worker's name: `<app worker>--<name>`, within Cloudflare's
|
|
368
|
-
* limit. New app ids can't contain `--`, so it is never another app's
|
|
369
|
-
* Worker name.
|
|
370
|
-
*/
|
|
371
|
-
export function helperWorkerName(appWorker, helper) {
|
|
372
|
-
const name = `${appWorker}--${helper}`;
|
|
373
|
-
if (name.length > MAX_WORKER_NAME_LENGTH) {
|
|
374
|
-
throw new Error(`Helper Worker ${name} is longer than Cloudflare's ${MAX_WORKER_NAME_LENGTH} characters; shorten its name in everyapp.config.ts.`);
|
|
375
|
-
}
|
|
376
|
-
return name;
|
|
377
|
-
}
|
|
378
399
|
/** The placeholder target (in production) and the app Worker's name. */
|
|
379
400
|
function appWorkerName(options, builder) {
|
|
380
401
|
const { manifest, ctx } = options;
|
|
@@ -382,7 +403,7 @@ function appWorkerName(options, builder) {
|
|
|
382
403
|
let deploy;
|
|
383
404
|
if (ctx.mode === "production") {
|
|
384
405
|
if (process.env[BUILD_ENV] !== "1") {
|
|
385
|
-
throw new Error(`
|
|
406
|
+
throw new Error(`EveryApp Workers are built for production only by \`everyapp deploy\` (\`pnpm run deploy\`), which sets ${BUILD_ENV}=1 and installs the build: ` +
|
|
386
407
|
"a bare `cf build` or `cf deploy` would ship a Worker whose name, identity keys, gateway binding and resources are placeholders. " +
|
|
387
408
|
"For a local build, run Vite in development mode (`vite build --mode development`).");
|
|
388
409
|
}
|
|
@@ -410,8 +431,8 @@ function gatewayBinding(deploy, appId, appWorker) {
|
|
|
410
431
|
};
|
|
411
432
|
}
|
|
412
433
|
/**
|
|
413
|
-
* The app's D1 databases, KV namespaces
|
|
414
|
-
* Workers share.
|
|
434
|
+
* The app's D1 databases, KV namespaces, R2 buckets and Vectorize indexes,
|
|
435
|
+
* which all its Workers share.
|
|
415
436
|
*/
|
|
416
437
|
function storageBindings(manifest, appWorker, deploy) {
|
|
417
438
|
const env = {};
|
|
@@ -461,8 +482,256 @@ function storageBindings(manifest, appWorker, deploy) {
|
|
|
461
482
|
env[binding] = { type: "r2", name: bucketNameFor(appWorker, binding) };
|
|
462
483
|
}
|
|
463
484
|
}
|
|
485
|
+
// Deployed, the index the deploy provisioned (a placeholder until
|
|
486
|
+
// installed); in development the account's index of the dev Worker's
|
|
487
|
+
// name, reached remotely: Cloudflare has no local Vectorize, so calls
|
|
488
|
+
// throw until the Vite plugin's remoteBindings is on.
|
|
489
|
+
for (const { name: binding } of manifest.resources?.vectorize ?? []) {
|
|
490
|
+
if (env[binding]) {
|
|
491
|
+
throw new Error(`vectorize: ${binding} reuses a binding name the Worker already has.`);
|
|
492
|
+
}
|
|
493
|
+
if (deploy) {
|
|
494
|
+
env[binding] = {
|
|
495
|
+
type: "vectorize",
|
|
496
|
+
name: provisioned(deploy.resources.vectorize, binding).name,
|
|
497
|
+
};
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const index = suffixedName(appWorker, binding);
|
|
501
|
+
if (index.length > MAX_VECTORIZE_NAME_LENGTH) {
|
|
502
|
+
throw new Error(`vectorize: ${binding} would be the Vectorize index "${index}", longer than Cloudflare's ${MAX_VECTORIZE_NAME_LENGTH} characters; shorten the binding name.`);
|
|
503
|
+
}
|
|
504
|
+
env[binding] = { type: "vectorize", name: index, dev: { remote: true } };
|
|
505
|
+
}
|
|
464
506
|
return env;
|
|
465
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* A producer binding to each of the app's queues, which every one of its
|
|
510
|
+
* Workers gets. Deployed, each names the queue the deploy provisioned (a
|
|
511
|
+
* placeholder until installed); in development, the queue its rule gives
|
|
512
|
+
* (Miniflare makes it, shared by the Workers of one `cf dev`).
|
|
513
|
+
*/
|
|
514
|
+
function addQueueBindings(env, manifest, appWorker, deploy) {
|
|
515
|
+
for (const { name: binding } of manifest.resources?.queues ?? []) {
|
|
516
|
+
if (env[binding]) {
|
|
517
|
+
throw new Error(`queues: ${binding} reuses a binding name the Worker already has.`);
|
|
518
|
+
}
|
|
519
|
+
env[binding] = {
|
|
520
|
+
type: "queue",
|
|
521
|
+
name: queueName(appWorker, binding, deploy),
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* The manifest's Workers AI bindings, which every one of the app's Workers
|
|
527
|
+
* gets: `{ type: "ai" }` deployed (account-wide, nothing to provision), and
|
|
528
|
+
* marked remote in development, where Miniflare has no Workers AI. The
|
|
529
|
+
* remote mark does nothing until the app's Vite config sets
|
|
530
|
+
* `remoteBindings: true`, so turning that on is the one switch.
|
|
531
|
+
*/
|
|
532
|
+
function addAiBindings(env, manifest, deploy) {
|
|
533
|
+
for (const binding of manifest.resources?.ai ?? []) {
|
|
534
|
+
if (env[binding]) {
|
|
535
|
+
throw new Error(`ai: ${binding} reuses a binding name the Worker already has.`);
|
|
536
|
+
}
|
|
537
|
+
env[binding] = deploy
|
|
538
|
+
? { type: "ai" }
|
|
539
|
+
: { type: "ai", dev: { remote: true } };
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* The consumer triggers of one of the app's Workers: each queue whose
|
|
544
|
+
* `consumer` is this Worker (`true`, or no `worker`, for the app's own;
|
|
545
|
+
* `forWorker` for a helper), with its settings and its dead-letter queue,
|
|
546
|
+
* another of the app's queues, by name.
|
|
547
|
+
*/
|
|
548
|
+
function queueTriggers(manifest, appWorker, forWorker, deploy) {
|
|
549
|
+
const triggers = [];
|
|
550
|
+
for (const queue of manifest.resources?.queues ?? []) {
|
|
551
|
+
if (queue.consumer === undefined)
|
|
552
|
+
continue;
|
|
553
|
+
const settings = queue.consumer === true ? {} : queue.consumer;
|
|
554
|
+
if (settings.worker !== forWorker)
|
|
555
|
+
continue;
|
|
556
|
+
const { deadLetterQueue } = settings;
|
|
557
|
+
if (deadLetterQueue !== undefined &&
|
|
558
|
+
!manifest.resources?.queues?.some((other) => other.name === deadLetterQueue)) {
|
|
559
|
+
throw new Error(`queues: ${queue.name}'s deadLetterQueue "${deadLetterQueue}" isn't one of resources.queues.`);
|
|
560
|
+
}
|
|
561
|
+
triggers.push({
|
|
562
|
+
type: "queue",
|
|
563
|
+
name: queueName(appWorker, queue.name, deploy),
|
|
564
|
+
...(deadLetterQueue !== undefined
|
|
565
|
+
? { deadLetterQueue: queueName(appWorker, deadLetterQueue, deploy) }
|
|
566
|
+
: {}),
|
|
567
|
+
...Object.fromEntries(QUEUE_CONSUMER_SETTINGS.flatMap((key) => settings[key] === undefined ? [] : [[key, settings[key]]])),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return triggers;
|
|
571
|
+
}
|
|
572
|
+
/** The consumer settings a queue trigger carries, in the order it lists them. */
|
|
573
|
+
const QUEUE_CONSUMER_SETTINGS = [
|
|
574
|
+
"maxBatchSize",
|
|
575
|
+
"maxBatchTimeout",
|
|
576
|
+
"maxRetries",
|
|
577
|
+
"maxConcurrency",
|
|
578
|
+
"retryDelay",
|
|
579
|
+
"visibilityTimeoutMs",
|
|
580
|
+
];
|
|
581
|
+
/** A queue binding's queue: the deploy's placeholder, or in development its rule. */
|
|
582
|
+
function queueName(appWorker, binding, deploy) {
|
|
583
|
+
return deploy
|
|
584
|
+
? provisioned(deploy.resources.queues, binding).name
|
|
585
|
+
: queueNameFor(appWorker, binding);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* The manifest's Browser Rendering bindings, which every one of the app's
|
|
589
|
+
* Workers gets: `{ type: "browser" }` in both modes (account-wide,
|
|
590
|
+
* nothing to provision; Miniflare serves a local browser in development).
|
|
591
|
+
*/
|
|
592
|
+
function addBrowserBindings(env, manifest) {
|
|
593
|
+
for (const binding of manifest.resources?.browser ?? []) {
|
|
594
|
+
if (env[binding]) {
|
|
595
|
+
throw new Error(`browser: ${binding} reuses a binding name the Worker already has.`);
|
|
596
|
+
}
|
|
597
|
+
env[binding] = { type: "browser" };
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* The manifest's Images bindings, which every one of the app's Workers
|
|
602
|
+
* gets: `{ type: "images" }` in both modes (account-wide, nothing to
|
|
603
|
+
* provision; Miniflare answers it locally in development).
|
|
604
|
+
*/
|
|
605
|
+
function addImagesBindings(env, manifest) {
|
|
606
|
+
for (const binding of manifest.resources?.images ?? []) {
|
|
607
|
+
if (env[binding]) {
|
|
608
|
+
throw new Error(`images: ${binding} reuses a binding name the Worker already has.`);
|
|
609
|
+
}
|
|
610
|
+
env[binding] = { type: "images" };
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* The app's AI Gateways, which all its Workers share, each a `json`
|
|
615
|
+
* binding of `{ id, accountId, url }`. Deployed, the gateway the deploy
|
|
616
|
+
* provisioned (a placeholder until installed); in development the id the
|
|
617
|
+
* deploy's rule gives, with an empty account and URL: there is no gateway,
|
|
618
|
+
* so the app calls the provider directly.
|
|
619
|
+
*/
|
|
620
|
+
function addAiGatewayBindings(env, manifest, appWorker, deploy) {
|
|
621
|
+
for (const { name: binding } of manifest.resources?.aiGateway ?? []) {
|
|
622
|
+
if (env[binding]) {
|
|
623
|
+
throw new Error(`aiGateway: ${binding} reuses a binding name the Worker already has.`);
|
|
624
|
+
}
|
|
625
|
+
const value = deploy
|
|
626
|
+
? { ...provisioned(deploy.resources.aiGateway, binding) }
|
|
627
|
+
: { id: aiGatewayIdFor(appWorker, binding), accountId: "", url: "" };
|
|
628
|
+
env[binding] = { type: "json", value };
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* The manifest's `send_email` bindings, the same on every one of the
|
|
633
|
+
* app's Workers in both modes: Cloudflare's binding with the addresses
|
|
634
|
+
* the manifest lists (the deploy's policy holds the built lists to them).
|
|
635
|
+
* Deployed, Cloudflare checks a message against them and the account's
|
|
636
|
+
* Email Sending domains when the Worker sends; in development Miniflare
|
|
637
|
+
* logs the message and checks the senders.
|
|
638
|
+
*/
|
|
639
|
+
function addSendEmailBindings(env, manifest) {
|
|
640
|
+
for (const entry of manifest.resources?.sendEmail ?? []) {
|
|
641
|
+
if (env[entry.name]) {
|
|
642
|
+
throw new Error(`sendEmail: ${entry.name} reuses a binding name the Worker already has.`);
|
|
643
|
+
}
|
|
644
|
+
const senders = entry.allowedSenderAddresses
|
|
645
|
+
? { allowedSenderAddresses: [...entry.allowedSenderAddresses] }
|
|
646
|
+
: {};
|
|
647
|
+
if (entry.destinationAddress !== undefined &&
|
|
648
|
+
entry.allowedDestinationAddresses !== undefined) {
|
|
649
|
+
throw new Error(`sendEmail: ${entry.name} sets both destinationAddress and allowedDestinationAddresses; set one.`);
|
|
650
|
+
}
|
|
651
|
+
env[entry.name] =
|
|
652
|
+
entry.destinationAddress !== undefined
|
|
653
|
+
? {
|
|
654
|
+
type: "send-email",
|
|
655
|
+
...senders,
|
|
656
|
+
destinationAddress: entry.destinationAddress,
|
|
657
|
+
}
|
|
658
|
+
: entry.allowedDestinationAddresses !== undefined
|
|
659
|
+
? {
|
|
660
|
+
type: "send-email",
|
|
661
|
+
...senders,
|
|
662
|
+
allowedDestinationAddresses: [
|
|
663
|
+
...entry.allowedDestinationAddresses,
|
|
664
|
+
],
|
|
665
|
+
}
|
|
666
|
+
: { type: "send-email", ...senders };
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* The manifest's Secrets Store secrets, which every one of the app's
|
|
671
|
+
* Workers binds. Deployed, the store and secret the deploy provisioned
|
|
672
|
+
* (placeholders until installed); in development the local store `local`,
|
|
673
|
+
* under the name the deploy would give the secret, so a seeded local
|
|
674
|
+
* secret is named like a deployed one.
|
|
675
|
+
*/
|
|
676
|
+
function addSecretsStoreBindings(env, manifest, appWorker, deploy) {
|
|
677
|
+
for (const { name: binding } of manifest.resources?.secretsStore ?? []) {
|
|
678
|
+
if (env[binding]) {
|
|
679
|
+
throw new Error(`secretsStore: ${binding} reuses a binding name the Worker already has.`);
|
|
680
|
+
}
|
|
681
|
+
if (deploy) {
|
|
682
|
+
const secret = provisioned(deploy.resources.secretsStore, binding);
|
|
683
|
+
env[binding] = {
|
|
684
|
+
type: "secrets-store-secret",
|
|
685
|
+
storeId: secret.storeId,
|
|
686
|
+
secretName: secret.secretName,
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
env[binding] = {
|
|
691
|
+
type: "secrets-store-secret",
|
|
692
|
+
storeId: LOCAL_SECRETS_STORE_ID,
|
|
693
|
+
secretName: suffixedName(appWorker, binding),
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* The manifest's Analytics Engine datasets, which every one of the app's
|
|
700
|
+
* Workers writes to: the `dataset` an entry names, or
|
|
701
|
+
* `<app worker>--<binding>`. Deployed, the app's Worker name is the build's
|
|
702
|
+
* placeholder, which the install rewrites like every other; a named
|
|
703
|
+
* dataset holds none and stays as written.
|
|
704
|
+
*/
|
|
705
|
+
function addAnalyticsEngineBindings(env, manifest, appWorker) {
|
|
706
|
+
for (const entry of manifest.resources?.analyticsEngine ?? []) {
|
|
707
|
+
if (env[entry.name]) {
|
|
708
|
+
throw new Error(`analyticsEngine: ${entry.name} reuses a binding name the Worker already has.`);
|
|
709
|
+
}
|
|
710
|
+
env[entry.name] = {
|
|
711
|
+
type: "analytics-engine-dataset",
|
|
712
|
+
name: entry.dataset ?? suffixedName(appWorker, entry.name),
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* The manifest's Artifacts namespaces, which every one of the app's Workers
|
|
718
|
+
* binds: `<app worker>--<binding>`. Deployed, the app's Worker name is the
|
|
719
|
+
* build's placeholder, which the install rewrites like every other; in
|
|
720
|
+
* development the account's namespace of the dev Worker's name, reached
|
|
721
|
+
* remotely: Cloudflare has no local Artifacts, so calls throw until the
|
|
722
|
+
* Vite plugin's remoteBindings is on.
|
|
723
|
+
*/
|
|
724
|
+
function addArtifactsBindings(env, manifest, appWorker, deploy) {
|
|
725
|
+
for (const { name: binding } of manifest.resources?.artifacts ?? []) {
|
|
726
|
+
if (env[binding]) {
|
|
727
|
+
throw new Error(`artifacts: ${binding} reuses a binding name the Worker already has.`);
|
|
728
|
+
}
|
|
729
|
+
const namespace = suffixedName(appWorker, binding);
|
|
730
|
+
env[binding] = deploy
|
|
731
|
+
? { type: "artifacts", namespace }
|
|
732
|
+
: { type: "artifacts", namespace, dev: { remote: true } };
|
|
733
|
+
}
|
|
734
|
+
}
|
|
466
735
|
/**
|
|
467
736
|
* DATABASE_PROVIDER and the Hyperdrive configs one of the app's Workers
|
|
468
737
|
* binds: each config every Worker shares, and those a helper (`forWorker`)
|
|
@@ -572,18 +841,25 @@ taken) {
|
|
|
572
841
|
return { bindings, exports };
|
|
573
842
|
}
|
|
574
843
|
/**
|
|
575
|
-
* Check the names of the manifest's `secrets`, and in dev
|
|
576
|
-
* set locally
|
|
577
|
-
* and
|
|
844
|
+
* Check the names of the manifest's `secrets` and `settings`, and in dev
|
|
845
|
+
* declare the ones set locally (a setting that isn't, with its default),
|
|
846
|
+
* with the developer's own keys for the manifest's providers and
|
|
847
|
+
* capabilities.
|
|
578
848
|
*/
|
|
579
849
|
function addSecrets(env, options, deploy) {
|
|
580
|
-
const
|
|
850
|
+
const settings = options.manifest.settings ?? [];
|
|
851
|
+
const secrets = [
|
|
852
|
+
...manifestSecretNames(options.manifest),
|
|
853
|
+
...settings.map((setting) => setting.name),
|
|
854
|
+
];
|
|
855
|
+
const settingNames = new Set(settings.map((setting) => setting.name));
|
|
581
856
|
for (const secret of secrets) {
|
|
857
|
+
const field = settingNames.has(secret) ? "settings" : "secrets";
|
|
582
858
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(secret)) {
|
|
583
|
-
throw new Error(
|
|
859
|
+
throw new Error(`${field}: "${secret}" isn't a valid binding name.`);
|
|
584
860
|
}
|
|
585
861
|
if (env[secret] || RESERVED_SECRET_NAME.test(secret)) {
|
|
586
|
-
throw new Error(
|
|
862
|
+
throw new Error(`${field}: ${secret} is a name the deploy sets or a binding it builds.`);
|
|
587
863
|
}
|
|
588
864
|
}
|
|
589
865
|
if (!deploy) {
|
|
@@ -600,6 +876,12 @@ function addSecrets(env, options, deploy) {
|
|
|
600
876
|
for (const secret of locallySetSecrets(options.root ?? process.cwd(), options.ctx.mode ?? "development", [...names].filter((name) => !env[name]))) {
|
|
601
877
|
env[secret] = { type: "secret" };
|
|
602
878
|
}
|
|
879
|
+
// A setting nobody set locally reads its default, as a deployed one does.
|
|
880
|
+
for (const setting of settings) {
|
|
881
|
+
if (!env[setting.name] && setting.default !== undefined) {
|
|
882
|
+
env[setting.name] = { type: "text", value: setting.default };
|
|
883
|
+
}
|
|
884
|
+
}
|
|
603
885
|
}
|
|
604
886
|
}
|
|
605
887
|
/** The {@link DEV_GATEWAY_KEYS} of the manifest's providers and capabilities. */
|
|
@@ -630,15 +912,6 @@ function observability() {
|
|
|
630
912
|
issues: { ...EVERYAPP_OBSERVABILITY.issues },
|
|
631
913
|
};
|
|
632
914
|
}
|
|
633
|
-
/**
|
|
634
|
-
* The Workflow a binding defines, named after the app's Worker:
|
|
635
|
-
* `every-acme-seo` and `RANK_CHECK` → `every-acme-seo--rank-check`.
|
|
636
|
-
* Workflow names are unique in the account, and new app ids can't contain
|
|
637
|
-
* `--`, so one app's Workflow is never named like another app's.
|
|
638
|
-
*/
|
|
639
|
-
export function workflowNameFor(workerName, binding) {
|
|
640
|
-
return `${workerName}--${binding.toLowerCase().replaceAll("_", "-")}`;
|
|
641
|
-
}
|
|
642
915
|
/**
|
|
643
916
|
* The local D1 database id for a binding: a 32-hex string, because
|
|
644
917
|
* `cf d1 … --local` rejects names. The everyapp CLI computes the same id to
|
|
@@ -709,8 +982,10 @@ function workerNameFor(appId) {
|
|
|
709
982
|
}
|
|
710
983
|
/**
|
|
711
984
|
* Copies of the gateway's naming rules (@every-app/perimeter/manifest's
|
|
712
|
-
* resourceNameFor
|
|
713
|
-
* the Worker's name, any other
|
|
985
|
+
* resourceNameFor, bucketNameFor, vectorizeNameFor and storeSecretNameFor):
|
|
986
|
+
* `DB` and `KV` (`R2` for a bucket) take the Worker's name, any other
|
|
987
|
+
* binding (and every Vectorize index and Secrets Store secret)
|
|
988
|
+
* `<worker>--<binding>`.
|
|
714
989
|
*/
|
|
715
990
|
function resourceNameFor(workerName, binding) {
|
|
716
991
|
return binding === "DB" || binding === "KV"
|
|
@@ -720,9 +995,20 @@ function resourceNameFor(workerName, binding) {
|
|
|
720
995
|
function bucketNameFor(workerName, binding) {
|
|
721
996
|
return binding === "R2" ? workerName : suffixedName(workerName, binding);
|
|
722
997
|
}
|
|
998
|
+
/** A copy of @every-app/perimeter/manifest's queueNameFor: `<worker>--<binding>`. */
|
|
999
|
+
function queueNameFor(workerName, binding) {
|
|
1000
|
+
return suffixedName(workerName, binding);
|
|
1001
|
+
}
|
|
723
1002
|
function suffixedName(workerName, binding) {
|
|
724
1003
|
return `${workerName}--${binding.toLowerCase().replaceAll("_", "-")}`;
|
|
725
1004
|
}
|
|
1005
|
+
/**
|
|
1006
|
+
* A copy of the gateway's aiGatewayIdFor: one hyphen, since an AI
|
|
1007
|
+
* Gateway's id may not hold `--`.
|
|
1008
|
+
*/
|
|
1009
|
+
function aiGatewayIdFor(workerName, binding) {
|
|
1010
|
+
return `${workerName}-${binding.toLowerCase().replaceAll("_", "-")}`;
|
|
1011
|
+
}
|
|
726
1012
|
function assertAppId(id) {
|
|
727
1013
|
if (typeof id !== "string" || !/^[a-z]([a-z0-9-]*[a-z0-9])?$/.test(id)) {
|
|
728
1014
|
throw new Error(`everyapp.config.ts id must be kebab-case (lowercase letters, digits, hyphens); got ${JSON.stringify(id)}.`);
|