@every-app/sdk 0.3.0-rc.3 → 0.3.0-rc.5

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.
Files changed (100) hide show
  1. package/README.md +1 -1
  2. package/dist/cloudflare/catalogSettings.generated.d.ts +95 -0
  3. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
  4. package/dist/cloudflare/catalogSettings.generated.js +221 -0
  5. package/dist/cloudflare/index.d.ts +2 -1
  6. package/dist/cloudflare/index.d.ts.map +1 -1
  7. package/dist/cloudflare/index.js +3 -0
  8. package/dist/cloudflare/sentinel.d.ts +21 -4
  9. package/dist/cloudflare/sentinel.d.ts.map +1 -1
  10. package/dist/cloudflare/sentinel.js +8 -4
  11. package/dist/cloudflare/worker.d.ts +195 -103
  12. package/dist/cloudflare/worker.d.ts.map +1 -1
  13. package/dist/cloudflare/worker.js +220 -137
  14. package/dist/dev/index.d.ts +1 -1
  15. package/dist/dev/index.d.ts.map +1 -1
  16. package/dist/dev/index.js +1 -1
  17. package/dist/dev/keypair.d.ts +2 -0
  18. package/dist/dev/keypair.d.ts.map +1 -1
  19. package/dist/dev/keypair.js +2 -0
  20. package/dist/index.d.ts +6 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +5 -0
  23. package/dist/internal/capabilities.d.ts +1 -1
  24. package/dist/internal/catalog/index.d.ts +1 -1
  25. package/dist/internal/catalog/inference.d.ts +1 -1
  26. package/dist/internal/catalog/inference.js +3 -3
  27. package/dist/internal/events.d.ts +1 -1
  28. package/dist/internal/events.d.ts.map +1 -1
  29. package/dist/internal/events.js +23 -1
  30. package/dist/internal/gatewaySettings.d.ts +7 -1
  31. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  32. package/dist/internal/gatewaySettings.js +12 -1
  33. package/dist/internal/identity.d.ts +29 -3
  34. package/dist/internal/identity.d.ts.map +1 -1
  35. package/dist/internal/identity.js +37 -9
  36. package/dist/internal/index.d.ts +1 -1
  37. package/dist/internal/index.d.ts.map +1 -1
  38. package/dist/internal/index.js +1 -1
  39. package/dist/internal/members.d.ts +3 -2
  40. package/dist/internal/members.d.ts.map +1 -1
  41. package/dist/internal/protocol.d.ts +25 -1
  42. package/dist/internal/protocol.d.ts.map +1 -1
  43. package/dist/internal/protocol.js +21 -1
  44. package/dist/manifest.d.ts +205 -0
  45. package/dist/manifest.d.ts.map +1 -0
  46. package/dist/manifest.js +30 -0
  47. package/dist/server/ai.d.ts +21 -0
  48. package/dist/server/ai.d.ts.map +1 -1
  49. package/dist/server/ai.js +37 -8
  50. package/dist/server/aiBinding.d.ts +68 -0
  51. package/dist/server/aiBinding.d.ts.map +1 -0
  52. package/dist/server/aiBinding.js +294 -0
  53. package/dist/server/attribution.d.ts +9 -5
  54. package/dist/server/attribution.d.ts.map +1 -1
  55. package/dist/server/attribution.js +12 -4
  56. package/dist/server/context.d.ts +68 -0
  57. package/dist/server/context.d.ts.map +1 -0
  58. package/dist/server/context.js +117 -0
  59. package/dist/server/emailBinding.d.ts +72 -0
  60. package/dist/server/emailBinding.d.ts.map +1 -0
  61. package/dist/server/emailBinding.js +124 -0
  62. package/dist/server/emit.d.ts +1 -1
  63. package/dist/server/emit.d.ts.map +1 -1
  64. package/dist/server/emit.js +4 -4
  65. package/dist/server/env.d.ts +41 -0
  66. package/dist/server/env.d.ts.map +1 -0
  67. package/dist/server/env.js +1 -0
  68. package/dist/server/everyApp.d.ts +27 -7
  69. package/dist/server/everyApp.d.ts.map +1 -1
  70. package/dist/server/everyApp.js +106 -5
  71. package/dist/server/fromMcpTools.d.ts +89 -0
  72. package/dist/server/fromMcpTools.d.ts.map +1 -0
  73. package/dist/server/fromMcpTools.js +192 -0
  74. package/dist/server/gatewayFetch.d.ts +24 -18
  75. package/dist/server/gatewayFetch.d.ts.map +1 -1
  76. package/dist/server/gatewayFetch.js +9 -9
  77. package/dist/server/gatewayUrl.d.ts +13 -0
  78. package/dist/server/gatewayUrl.d.ts.map +1 -0
  79. package/dist/server/gatewayUrl.js +34 -0
  80. package/dist/server/index.d.ts +11 -3
  81. package/dist/server/index.d.ts.map +1 -1
  82. package/dist/server/index.js +9 -2
  83. package/dist/server/integrationFetch.d.ts +28 -7
  84. package/dist/server/integrationFetch.d.ts.map +1 -1
  85. package/dist/server/integrationFetch.js +24 -28
  86. package/dist/server/listMembers.d.ts +1 -1
  87. package/dist/server/listMembers.d.ts.map +1 -1
  88. package/dist/server/listMembers.js +5 -8
  89. package/dist/server/sendEmail.d.ts +1 -1
  90. package/dist/server/sendEmail.d.ts.map +1 -1
  91. package/dist/server/sendEmail.js +4 -4
  92. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  93. package/dist/server/toolsEndpoint.js +11 -5
  94. package/dist/tanstack-start/index.d.ts +9 -0
  95. package/dist/tanstack-start/index.d.ts.map +1 -0
  96. package/dist/tanstack-start/index.js +29 -0
  97. package/dist/testing/index.d.ts +93 -0
  98. package/dist/testing/index.d.ts.map +1 -0
  99. package/dist/testing/index.js +156 -0
  100. package/package.json +25 -4
@@ -2,7 +2,11 @@ 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";
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";
6
10
  import { sentinelDeployTarget, } from "./sentinel.js";
7
11
  /** The names of the manifest's `secrets`, whichever form each took. */
8
12
  function manifestSecretNames(manifest) {
@@ -12,131 +16,6 @@ function manifestSecretNames(manifest) {
12
16
  function manifestProviderIds(manifest) {
13
17
  return (manifest.providers ?? []).map((entry) => typeof entry === "string" ? entry : entry.id);
14
18
  }
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, a Basic username held as a setting, a host
19
- * setting). With no gateway in development, the SDK reaches them with the
20
- * developer's own values under the same names. A copy of the catalog's
21
- * settings (`capabilitySettings()` over every entry of
22
- * ../internal/capabilities.ts), which the Worker loads and this file must
23
- * not import (see ./index.ts: the Vite plugin hides whatever
24
- * cloudflare.config.ts imports from the Worker). worker.test.ts keeps the
25
- * copy in step: a provider added to the catalog is added here.
26
- */
27
- const DEV_GATEWAY_KEYS = {
28
- providers: {
29
- openai: ["OPENAI_API_KEY"],
30
- openrouter: ["OPENROUTER_API_KEY"],
31
- "workers-ai": ["WORKERS_AI_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"],
32
- // Not Cloudflare AI Gateway (the proxy entry): dev goes to the
33
- // providers directly, so its settings are the Gateway's alone.
34
- // The AI providers of ../internal/catalog/inference.ts.
35
- anthropic: ["ANTHROPIC_API_KEY"],
36
- gemini: ["GEMINI_API_KEY"],
37
- mistral: ["MISTRAL_API_KEY"],
38
- groq: ["GROQ_API_KEY"],
39
- cohere: ["COHERE_API_KEY"],
40
- },
41
- capabilities: {
42
- dataforseo: ["DATAFORSEO_API_KEY"],
43
- context: ["CONTEXT_API_KEY"],
44
- // The sender, and the developer's own Email Sending account: set all
45
- // but the name and sendEmail() really sends; otherwise it prints.
46
- // Resend's key and EMAIL_PROVIDER pick the provider integrationStatus()
47
- // names, as the gateway would.
48
- // TODO(integration): the last two added by the email-providers lane
49
- // (this file is outside its ownership list).
50
- email: [
51
- "EMAIL_FROM",
52
- "EMAIL_FROM_NAME",
53
- "EMAIL_REST_API_TOKEN",
54
- "CLOUDFLARE_ACCOUNT_ID",
55
- "RESEND_API_KEY",
56
- "EMAIL_PROVIDER",
57
- ],
58
- // The organization's members: nothing to set; listMembers() answers
59
- // from the dev identity.
60
- members: [],
61
- // OAuth clients: the id, the secret, and the instance a provider with
62
- // no fixed host is on.
63
- linkedin: ["LINKEDIN_CLIENT_ID", "LINKEDIN_CLIENT_SECRET"],
64
- facebook: ["FACEBOOK_APP_ID", "FACEBOOK_APP_SECRET"],
65
- instagram: ["INSTAGRAM_APP_ID", "INSTAGRAM_APP_SECRET"],
66
- threads: ["THREADS_APP_ID", "THREADS_APP_SECRET"],
67
- youtube: ["YOUTUBE_CLIENT_ID", "YOUTUBE_CLIENT_SECRET"],
68
- tiktok: ["TIKTOK_CLIENT_ID", "TIKTOK_CLIENT_SECRET"],
69
- "tiktok-business": [
70
- "TIKTOK_BUSINESS_CLIENT_ID",
71
- "TIKTOK_BUSINESS_CLIENT_SECRET",
72
- ],
73
- reddit: ["REDDIT_CLIENT_ID", "REDDIT_CLIENT_SECRET"],
74
- pinterest: ["PINTEREST_CLIENT_ID", "PINTEREST_CLIENT_SECRET"],
75
- discord: ["DISCORD_CLIENT_ID", "DISCORD_CLIENT_SECRET"],
76
- slack: ["SLACK_CLIENT_ID", "SLACK_CLIENT_SECRET"],
77
- mastodon: ["MASTODON_CLIENT_ID", "MASTODON_CLIENT_SECRET", "MASTODON_URL"],
78
- kick: ["KICK_CLIENT_ID", "KICK_CLIENT_SECRET"],
79
- twitch: ["TWITCH_CLIENT_ID", "TWITCH_CLIENT_SECRET"],
80
- tumblr: ["TUMBLR_CLIENT_ID", "TUMBLR_CLIENT_SECRET"],
81
- dribbble: ["DRIBBBLE_CLIENT_ID", "DRIBBBLE_CLIENT_SECRET"],
82
- "google-workspace": [
83
- "GOOGLE_WORKSPACE_CLIENT_ID",
84
- "GOOGLE_WORKSPACE_CLIENT_SECRET",
85
- ],
86
- "microsoft-365": ["MICROSOFT_365_CLIENT_ID", "MICROSOFT_365_CLIENT_SECRET"],
87
- // The API-key providers of ../internal/catalog/*.ts, in catalog order:
88
- // the key, then a Basic username held as a setting, then the host
89
- // setting of a provider whose host is the operator's.
90
- stripe: ["STRIPE_API_KEY"],
91
- lemonsqueezy: ["LEMONSQUEEZY_API_KEY"],
92
- paddle: ["PADDLE_API_KEY"],
93
- shopify: ["SHOPIFY_API_TOKEN", "SHOPIFY_HOST"],
94
- plaid: ["PLAID_SECRET"],
95
- attio: ["ATTIO_API_KEY"],
96
- hubspot: ["HUBSPOT_API_TOKEN"],
97
- pipedrive: ["PIPEDRIVE_API_TOKEN"],
98
- airtable: ["AIRTABLE_API_TOKEN"],
99
- notion: ["NOTION_API_TOKEN"],
100
- postmark: ["POSTMARK_API_TOKEN"],
101
- sendgrid: ["SENDGRID_API_KEY"],
102
- twilio: ["TWILIO_API_SECRET", "TWILIO_API_KEY"],
103
- intercom: ["INTERCOM_API_TOKEN"],
104
- crisp: ["CRISP_API_KEY", "CRISP_API_IDENTIFIER"],
105
- linear: ["LINEAR_API_KEY"],
106
- github: ["GITHUB_API_TOKEN"],
107
- gitlab: ["GITLAB_API_TOKEN"],
108
- vercel: ["VERCEL_API_TOKEN"],
109
- cloudflare: ["CLOUDFLARE_PROVIDER_API_TOKEN"],
110
- posthog: ["POSTHOG_PERSONAL_API_KEY"],
111
- mixpanel: [
112
- "MIXPANEL_SERVICE_ACCOUNT_SECRET",
113
- "MIXPANEL_SERVICE_ACCOUNT_USERNAME",
114
- ],
115
- segment: ["SEGMENT_WRITE_KEY"],
116
- supabase: ["SUPABASE_SECRET_KEY", "SUPABASE_HOST"],
117
- turso: ["TURSO_API_TOKEN"],
118
- "turso-db": ["TURSO_DB_AUTH_TOKEN", "TURSO_DB_HOST"],
119
- neon: ["NEON_API_KEY"],
120
- pinecone: ["PINECONE_API_KEY", "PINECONE_HOST"],
121
- mapbox: ["MAPBOX_ACCESS_TOKEN"],
122
- openweather: ["OPENWEATHER_API_KEY"],
123
- voyage: ["VOYAGE_API_KEY"],
124
- perplexity: ["PERPLEXITY_API_KEY"],
125
- jina: ["JINA_API_KEY"],
126
- replicate: ["REPLICATE_API_TOKEN"],
127
- huggingface: ["HUGGINGFACE_API_TOKEN"],
128
- firecrawl: ["FIRECRAWL_API_KEY"],
129
- exa: ["EXA_API_KEY"],
130
- tavily: ["TAVILY_API_KEY"],
131
- serper: ["SERPER_API_KEY"],
132
- "brave-search": ["BRAVE_SEARCH_API_KEY"],
133
- apify: ["APIFY_API_TOKEN"],
134
- deepgram: ["DEEPGRAM_API_KEY"],
135
- elevenlabs: ["ELEVENLABS_API_KEY"],
136
- assemblyai: ["ASSEMBLYAI_API_KEY"],
137
- newsapi: ["NEWSAPI_API_KEY"],
138
- },
139
- };
140
19
  /**
141
20
  * Observability every Every App Worker ships with: logs (with invocation
142
21
  * logs), sampled traces and Workers Issues, with query strings redacted so
@@ -166,6 +45,19 @@ const BUILD_ENV = "EVERYAPP_BUILD";
166
45
  const RESERVED_SECRET_NAME = /^(GATEWAY_URL|CF_VERSION_METADATA|EVERYAPP_\w*|EVERY_APP_\w*|APP__\w*)$/i;
167
46
  const GATEWAY_WORKER_NAME = "every-app-gateway";
168
47
  const GATEWAY_ENTRYPOINT = "AppGateway";
48
+ /**
49
+ * Where `everyapp dev` serves its launcher, which it passes to the app's dev
50
+ * server in EVERYAPP_DEV_GATEWAY_URL; its default address otherwise. The
51
+ * deployed Worker gets the Gateway's URL from the deploy instead.
52
+ */
53
+ const DEV_GATEWAY_URL_ENV = "EVERYAPP_DEV_GATEWAY_URL";
54
+ const DEFAULT_DEV_GATEWAY_URL = "http://localhost:8787";
55
+ function devGatewayUrl() {
56
+ return {
57
+ type: "text",
58
+ value: process.env[DEV_GATEWAY_URL_ENV] || DEFAULT_DEV_GATEWAY_URL,
59
+ };
60
+ }
169
61
  /**
170
62
  * The manifest's `capabilities` as the SDK's local-dev twin of the gateway
171
63
  * reads them: with no gateway to hold an OAuth client's calls to the scope
@@ -177,7 +69,7 @@ function devCapabilities(manifest) {
177
69
  /**
178
70
  * The binding the dev Worker gets the specs of providers registered on a
179
71
  * gateway in (`DEV_PROVIDERS_ENV` in ../internal/devProviders.ts; a copy,
180
- * kept in step by a test, for the reason {@link DEV_GATEWAY_KEYS} is one).
72
+ * kept in step by a test, since this file may not import ../internal).
181
73
  */
182
74
  const DEV_PROVIDERS_ENV = "EVERYAPP_DEV_PROVIDERS";
183
75
  /**
@@ -256,6 +148,11 @@ function devProviderKeys(manifest, root) {
256
148
  }
257
149
  /** Cloudflare's limit on a Workflow's name. */
258
150
  const MAX_WORKFLOW_NAME_LENGTH = 64;
151
+ /**
152
+ * The store development binds Secrets Store secrets in: Miniflare keeps
153
+ * one per id under `.cloudflare/state`, and any id works locally.
154
+ */
155
+ const LOCAL_SECRETS_STORE_ID = "local";
259
156
  /** Cloudflare's limit on a Vectorize index's name. */
260
157
  const MAX_VECTORIZE_NAME_LENGTH = 64;
261
158
  /** Cloudflare's limit on a Worker's name. */
@@ -286,6 +183,12 @@ export function everyAppWorker(options) {
286
183
  const { deploy, name } = appWorkerName(options, "everyAppWorker");
287
184
  const env = {
288
185
  EVERYAPP_APP_ID: { type: "text", value: manifest.id },
186
+ // The manifest itself, for a page that shows what the app asked for:
187
+ // the Worker can't import everyapp.config.ts (see ./index.ts).
188
+ EVERYAPP_MANIFEST: {
189
+ type: "json",
190
+ value: manifest,
191
+ },
289
192
  CF_VERSION_METADATA: { type: "version-metadata" },
290
193
  };
291
194
  if (deploy) {
@@ -312,13 +215,23 @@ export function everyAppWorker(options) {
312
215
  value: JSON.stringify([DEV_PUBLIC_KEY_PEM]),
313
216
  };
314
217
  env["EVERYAPP_IDENTITY_ISSUER"] = { type: "text", value: DEV_ISSUER };
218
+ env["GATEWAY_URL"] = devGatewayUrl();
219
+ // The stub gateway's organization, as the deploy's secret would be, so
220
+ // background work's withOrganization(env.EVERY_APP_ORG_ID, …) runs
221
+ // here too. Not a pin: under the dev opt-in everyApp() enforces none,
222
+ // so `everyapp dev --mode mirror`'s real organizations still open.
223
+ env["EVERY_APP_ORG_ID"] = { type: "text", value: DEV_ORG_ID };
315
224
  addDevProviders(env, options.root);
316
225
  }
317
226
  Object.assign(env, storageBindings(manifest, name, deploy));
227
+ addQueueBindings(env, manifest, name, deploy);
318
228
  addAiBindings(env, manifest, deploy);
319
229
  addBrowserBindings(env, manifest);
320
230
  addImagesBindings(env, manifest);
321
231
  addAiGatewayBindings(env, manifest, name, deploy);
232
+ addSendEmailBindings(env, manifest);
233
+ addSecretsStoreBindings(env, manifest, name, deploy);
234
+ addAnalyticsEngineBindings(env, manifest, name);
322
235
  addDatabaseBindings(env, options, name, undefined, deploy);
323
236
  // The app binds every class, a helper Worker's in that Worker, and
324
237
  // exports only its own.
@@ -336,13 +249,19 @@ export function everyAppWorker(options) {
336
249
  };
337
250
  }
338
251
  addSecrets(env, options, deploy);
339
- const triggers = cronTriggers(options.crons);
252
+ const triggers = [
253
+ ...cronTriggers(options.crons),
254
+ ...queueTriggers(manifest, name, undefined, deploy),
255
+ ];
340
256
  const compatibilityDate = manifest.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE;
341
257
  // cf types a Durable Object namespace with its class only when the binding
342
258
  // names the defining Worker by a definition whose module it can read, so
343
259
  // the classes this Worker defines name it that way; cf resolves the
344
260
  // reference to the Worker's name. A path entrypoint gives cf no module.
261
+ // Only here: `EveryAppBinding` names Workers by name, as the Vite
262
+ // plugin's `auxiliaryWorkers` require.
345
263
  const { entrypoint } = options;
264
+ const ownClasses = {};
346
265
  if (typeof entrypoint === "object") {
347
266
  const self = {
348
267
  name,
@@ -352,7 +271,7 @@ export function everyAppWorker(options) {
352
271
  for (const durableObject of manifest.resources?.durableObjects ?? []) {
353
272
  if (durableObject.worker !== undefined)
354
273
  continue;
355
- env[durableObject.name] = {
274
+ ownClasses[durableObject.name] = {
356
275
  type: "durable-object",
357
276
  worker: self,
358
277
  exportName: durableObject.className,
@@ -369,7 +288,8 @@ export function everyAppWorker(options) {
369
288
  ...(triggers.length ? { triggers } : {}),
370
289
  ...runtimeSettings(manifest, manifest.limits, deploy),
371
290
  observability: observability(),
372
- env: env,
291
+ // Each class keeps its key's place in `env`: classBindings bound it by name.
292
+ env: { ...env, ...ownClasses },
373
293
  exports: exports,
374
294
  };
375
295
  }
@@ -377,8 +297,10 @@ export function everyAppWorker(options) {
377
297
  * Build the definition of one of the app's helper Workers (the manifest's
378
298
  * `workers`), e.g. one that runs a memory-hungry job away from the app's
379
299
  * Worker. It is named `<app worker>--<worker>`, is private like the app, and
380
- * binds the app's D1 databases, KV namespaces, R2 buckets, Workers AI, Browser Rendering and Images, the classes it defines, and
381
- * binds the app's D1 databases, KV namespaces, R2 buckets, Workers AI, Browser Rendering and Vectorize indexes, the classes it defines, and
300
+ * binds the app's and Analytics Engine datasets, the classes it defines, and
301
+ * binds the app's D1 databases, KV namespaces, R2 buckets, Workers AI,
302
+ * Browser Rendering, Images, Vectorize indexes, AI Gateways, `send_email`
303
+ * bindings and Secrets Store secrets, the classes it defines, and
382
304
  * the gateway (with the app's identity, for capabilities). Only the app's
383
305
  * Worker reaches it. Pass it to the Vite plugin:
384
306
  *
@@ -400,6 +322,8 @@ export function everyAppAuxiliaryWorker(options) {
400
322
  EVERYAPP_APP_ID: { type: "text", value: manifest.id },
401
323
  ...storageBindings(manifest, appWorker, deploy),
402
324
  };
325
+ addQueueBindings(env, manifest, appWorker, deploy);
326
+ addSendEmailBindings(env, manifest);
403
327
  if (deploy) {
404
328
  // The app's identity: the gateway treats its helper Workers as the app.
405
329
  env["EVERY_APP_GATEWAY"] = gatewayBinding(deploy, manifest.id, appWorker);
@@ -411,17 +335,24 @@ export function everyAppAuxiliaryWorker(options) {
411
335
  else {
412
336
  env["EVERYAPP_DEV"] = { type: "text", value: "1" };
413
337
  env["EVERYAPP_DEV_CAPABILITIES"] = devCapabilities(manifest);
338
+ env["GATEWAY_URL"] = devGatewayUrl();
339
+ env["EVERY_APP_ORG_ID"] = { type: "text", value: DEV_ORG_ID };
414
340
  addDevProviders(env, options.root);
415
341
  }
416
342
  addAiBindings(env, manifest, deploy);
417
343
  addBrowserBindings(env, manifest);
418
344
  addImagesBindings(env, manifest);
419
345
  addAiGatewayBindings(env, manifest, appWorker, deploy);
346
+ addSecretsStoreBindings(env, manifest, appWorker, deploy);
347
+ addAnalyticsEngineBindings(env, manifest, appWorker);
420
348
  addDatabaseBindings(env, options, appWorker, worker, deploy);
421
349
  const { bindings, exports } = classBindings(manifest, appWorker, worker, env);
422
350
  Object.assign(env, bindings);
423
351
  addSecrets(env, options, deploy);
424
- const triggers = cronTriggers(options.crons);
352
+ const triggers = [
353
+ ...cronTriggers(options.crons),
354
+ ...queueTriggers(manifest, appWorker, worker, deploy),
355
+ ];
425
356
  return {
426
357
  name,
427
358
  entrypoint: options.entrypoint,
@@ -455,7 +386,7 @@ function appWorkerName(options, builder) {
455
386
  let deploy;
456
387
  if (ctx.mode === "production") {
457
388
  if (process.env[BUILD_ENV] !== "1") {
458
- throw new Error(`Every App Workers are built for production only by \`everyapp deploy\` (\`pnpm run deploy\`), which sets ${BUILD_ENV}=1 and installs the build: ` +
389
+ 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: ` +
459
390
  "a bare `cf build` or `cf deploy` would ship a Worker whose name, identity keys, gateway binding and resources are placeholders. " +
460
391
  "For a local build, run Vite in development mode (`vite build --mode development`).");
461
392
  }
@@ -557,6 +488,23 @@ function storageBindings(manifest, appWorker, deploy) {
557
488
  }
558
489
  return env;
559
490
  }
491
+ /**
492
+ * A producer binding to each of the app's queues, which every one of its
493
+ * Workers gets. Deployed, each names the queue the deploy provisioned (a
494
+ * placeholder until installed); in development, the queue its rule gives
495
+ * (Miniflare makes it, shared by the Workers of one `cf dev`).
496
+ */
497
+ function addQueueBindings(env, manifest, appWorker, deploy) {
498
+ for (const { name: binding } of manifest.resources?.queues ?? []) {
499
+ if (env[binding]) {
500
+ throw new Error(`queues: ${binding} reuses a binding name the Worker already has.`);
501
+ }
502
+ env[binding] = {
503
+ type: "queue",
504
+ name: queueName(appWorker, binding, deploy),
505
+ };
506
+ }
507
+ }
560
508
  /**
561
509
  * The manifest's Workers AI bindings, which every one of the app's Workers
562
510
  * gets: `{ type: "ai" }` deployed (account-wide, nothing to provision), and
@@ -574,6 +522,51 @@ function addAiBindings(env, manifest, deploy) {
574
522
  : { type: "ai", dev: { remote: true } };
575
523
  }
576
524
  }
525
+ /**
526
+ * The consumer triggers of one of the app's Workers: each queue whose
527
+ * `consumer` is this Worker (`true`, or no `worker`, for the app's own;
528
+ * `forWorker` for a helper), with its settings and its dead-letter queue,
529
+ * another of the app's queues, by name.
530
+ */
531
+ function queueTriggers(manifest, appWorker, forWorker, deploy) {
532
+ const triggers = [];
533
+ for (const queue of manifest.resources?.queues ?? []) {
534
+ if (queue.consumer === undefined)
535
+ continue;
536
+ const settings = queue.consumer === true ? {} : queue.consumer;
537
+ if (settings.worker !== forWorker)
538
+ continue;
539
+ const { deadLetterQueue } = settings;
540
+ if (deadLetterQueue !== undefined &&
541
+ !manifest.resources?.queues?.some((other) => other.name === deadLetterQueue)) {
542
+ throw new Error(`queues: ${queue.name}'s deadLetterQueue "${deadLetterQueue}" isn't one of resources.queues.`);
543
+ }
544
+ triggers.push({
545
+ type: "queue",
546
+ name: queueName(appWorker, queue.name, deploy),
547
+ ...(deadLetterQueue !== undefined
548
+ ? { deadLetterQueue: queueName(appWorker, deadLetterQueue, deploy) }
549
+ : {}),
550
+ ...Object.fromEntries(QUEUE_CONSUMER_SETTINGS.flatMap((key) => settings[key] === undefined ? [] : [[key, settings[key]]])),
551
+ });
552
+ }
553
+ return triggers;
554
+ }
555
+ /** The consumer settings a queue trigger carries, in the order it lists them. */
556
+ const QUEUE_CONSUMER_SETTINGS = [
557
+ "maxBatchSize",
558
+ "maxBatchTimeout",
559
+ "maxRetries",
560
+ "maxConcurrency",
561
+ "retryDelay",
562
+ "visibilityTimeoutMs",
563
+ ];
564
+ /** A queue binding's queue: the deploy's placeholder, or in development its rule. */
565
+ function queueName(appWorker, binding, deploy) {
566
+ return deploy
567
+ ? provisioned(deploy.resources.queues, binding).name
568
+ : queueNameFor(appWorker, binding);
569
+ }
577
570
  /**
578
571
  * The manifest's Browser Rendering bindings, which every one of the app's
579
572
  * Workers gets: `{ type: "browser" }` in both modes (account-wide,
@@ -618,6 +611,91 @@ function addAiGatewayBindings(env, manifest, appWorker, deploy) {
618
611
  env[binding] = { type: "json", value };
619
612
  }
620
613
  }
614
+ /**
615
+ * The manifest's `send_email` bindings, the same on every one of the
616
+ * app's Workers in both modes: Cloudflare's binding with the addresses
617
+ * the manifest lists (the deploy's policy holds the built lists to them).
618
+ * Deployed, Cloudflare checks a message against them and the account's
619
+ * Email Sending domains when the Worker sends; in development Miniflare
620
+ * logs the message and checks the senders.
621
+ */
622
+ function addSendEmailBindings(env, manifest) {
623
+ for (const entry of manifest.resources?.sendEmail ?? []) {
624
+ if (env[entry.name]) {
625
+ throw new Error(`sendEmail: ${entry.name} reuses a binding name the Worker already has.`);
626
+ }
627
+ const senders = entry.allowedSenderAddresses
628
+ ? { allowedSenderAddresses: [...entry.allowedSenderAddresses] }
629
+ : {};
630
+ if (entry.destinationAddress !== undefined &&
631
+ entry.allowedDestinationAddresses !== undefined) {
632
+ throw new Error(`sendEmail: ${entry.name} sets both destinationAddress and allowedDestinationAddresses; set one.`);
633
+ }
634
+ env[entry.name] =
635
+ entry.destinationAddress !== undefined
636
+ ? {
637
+ type: "send-email",
638
+ ...senders,
639
+ destinationAddress: entry.destinationAddress,
640
+ }
641
+ : entry.allowedDestinationAddresses !== undefined
642
+ ? {
643
+ type: "send-email",
644
+ ...senders,
645
+ allowedDestinationAddresses: [
646
+ ...entry.allowedDestinationAddresses,
647
+ ],
648
+ }
649
+ : { type: "send-email", ...senders };
650
+ }
651
+ }
652
+ /**
653
+ * The manifest's Secrets Store secrets, which every one of the app's
654
+ * Workers binds. Deployed, the store and secret the deploy provisioned
655
+ * (placeholders until installed); in development the local store `local`,
656
+ * under the name the deploy would give the secret, so a seeded local
657
+ * secret is named like a deployed one.
658
+ */
659
+ function addSecretsStoreBindings(env, manifest, appWorker, deploy) {
660
+ for (const { name: binding } of manifest.resources?.secretsStore ?? []) {
661
+ if (env[binding]) {
662
+ throw new Error(`secretsStore: ${binding} reuses a binding name the Worker already has.`);
663
+ }
664
+ if (deploy) {
665
+ const secret = provisioned(deploy.resources.secretsStore, binding);
666
+ env[binding] = {
667
+ type: "secrets-store-secret",
668
+ storeId: secret.storeId,
669
+ secretName: secret.secretName,
670
+ };
671
+ }
672
+ else {
673
+ env[binding] = {
674
+ type: "secrets-store-secret",
675
+ storeId: LOCAL_SECRETS_STORE_ID,
676
+ secretName: suffixedName(appWorker, binding),
677
+ };
678
+ }
679
+ }
680
+ }
681
+ /**
682
+ * The manifest's Analytics Engine datasets, which every one of the app's
683
+ * Workers writes to: the `dataset` an entry names, or
684
+ * `<app worker>--<binding>`. Deployed, the app's Worker name is the build's
685
+ * placeholder, which the install rewrites like every other; a named
686
+ * dataset holds none and stays as written.
687
+ */
688
+ function addAnalyticsEngineBindings(env, manifest, appWorker) {
689
+ for (const entry of manifest.resources?.analyticsEngine ?? []) {
690
+ if (env[entry.name]) {
691
+ throw new Error(`analyticsEngine: ${entry.name} reuses a binding name the Worker already has.`);
692
+ }
693
+ env[entry.name] = {
694
+ type: "analytics-engine-dataset",
695
+ name: entry.dataset ?? suffixedName(appWorker, entry.name),
696
+ };
697
+ }
698
+ }
621
699
  /**
622
700
  * DATABASE_PROVIDER and the Hyperdrive configs one of the app's Workers
623
701
  * binds: each config every Worker shares, and those a helper (`forWorker`)
@@ -864,9 +942,10 @@ function workerNameFor(appId) {
864
942
  }
865
943
  /**
866
944
  * Copies of the gateway's naming rules (@every-app/perimeter/manifest's
867
- * resourceNameFor, bucketNameFor and vectorizeNameFor): `DB` and `KV` (`R2`
868
- * for a bucket) take the Worker's name, any other binding (and every
869
- * Vectorize index) `<worker>--<binding>`.
945
+ * resourceNameFor, bucketNameFor, vectorizeNameFor and storeSecretNameFor):
946
+ * `DB` and `KV` (`R2` for a bucket) take the Worker's name, any other
947
+ * binding (and every Vectorize index and Secrets Store secret)
948
+ * `<worker>--<binding>`.
870
949
  */
871
950
  function resourceNameFor(workerName, binding) {
872
951
  return binding === "DB" || binding === "KV"
@@ -876,6 +955,10 @@ function resourceNameFor(workerName, binding) {
876
955
  function bucketNameFor(workerName, binding) {
877
956
  return binding === "R2" ? workerName : suffixedName(workerName, binding);
878
957
  }
958
+ /** A copy of @every-app/perimeter/manifest's queueNameFor: `<worker>--<binding>`. */
959
+ function queueNameFor(workerName, binding) {
960
+ return suffixedName(workerName, binding);
961
+ }
879
962
  function suffixedName(workerName, binding) {
880
963
  return `${workerName}--${binding.toLowerCase().replaceAll("_", "-")}`;
881
964
  }
@@ -3,6 +3,6 @@
3
3
  * signs with, its issuer and its key id. For the platform's local dev tools
4
4
  * (the CLI, `@every-app/perimeter/dev`); never imported by a Worker.
5
5
  */
6
- export { DEV_ISSUER, DEV_PRIVATE_KEY_PEM, DEV_PUBLIC_KEY_PEM, } from "./keypair.js";
6
+ export { DEV_ISSUER, DEV_ORG_ID, DEV_PRIVATE_KEY_PEM, DEV_PUBLIC_KEY_PEM, } from "./keypair.js";
7
7
  export { IDENTITY_DEV_KEY_ID } from "../internal/protocol.js";
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dev/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/dev/index.js CHANGED
@@ -3,5 +3,5 @@
3
3
  * signs with, its issuer and its key id. For the platform's local dev tools
4
4
  * (the CLI, `@every-app/perimeter/dev`); never imported by a Worker.
5
5
  */
6
- export { DEV_ISSUER, DEV_PRIVATE_KEY_PEM, DEV_PUBLIC_KEY_PEM, } from "./keypair.js";
6
+ export { DEV_ISSUER, DEV_ORG_ID, DEV_PRIVATE_KEY_PEM, DEV_PUBLIC_KEY_PEM, } from "./keypair.js";
7
7
  export { IDENTITY_DEV_KEY_ID } from "../internal/protocol.js";
@@ -17,6 +17,8 @@
17
17
  */
18
18
  /** Issuer of locally minted dev identity tokens. */
19
19
  export declare const DEV_ISSUER = "https://gateway.dev.localhost";
20
+ /** The organization the dev gateway's seeded user belongs to (`everyAppWorker()` sets it as `EVERY_APP_ORG_ID` in dev). */
21
+ export declare const DEV_ORG_ID = "dev-org";
20
22
  /** SPKI PEM public half of the dev keypair. */
21
23
  export declare const DEV_PUBLIC_KEY_PEM = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh40zWf+QK3rJdMTNoy+X\nF9iydV5raWSeEt4DXyeY98c8IZHRjy2UXaaYejweebRsv9Az+bZuL3cfJ0E7pOGd\n4VCOiSG1BjJglmTC1whV4Md6qhLDVOP6iztUJSxDwnlR85UtOyNJ4G5dYY6RrAWl\nPMMXPSYdGWqAP4n5QXAw+7uTFmJMdWy1W8KLbTj//NSGn3DCm+EZJIC8q9yDhMSc\nM0ejJnBVColvp4T8k8dt2kYGu2Wd7dCMrHv/lxUoqD58avpdMOzbaZtwsQ7BAaIa\nmIeQ1AKxmnCj1np23uWvYQmoaZocvupLSALK7fWGUyVDZ+RW4HtN5KqzZypfiyZ8\nkQIDAQAB\n-----END PUBLIC KEY-----\n";
22
24
  /** PKCS#8 PEM private half of the dev keypair. Public, see above. */
@@ -1 +1 @@
1
- {"version":3,"file":"keypair.d.ts","sourceRoot":"","sources":["../../src/dev/keypair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,oDAAoD;AACpD,eAAO,MAAM,UAAU,kCAAkC,CAAC;AAE1D,+CAA+C;AAC/C,eAAO,MAAM,kBAAkB,idAS9B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,ysDA4B/B,CAAC"}
1
+ {"version":3,"file":"keypair.d.ts","sourceRoot":"","sources":["../../src/dev/keypair.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,oDAAoD;AACpD,eAAO,MAAM,UAAU,kCAAkC,CAAC;AAC1D,2HAA2H;AAC3H,eAAO,MAAM,UAAU,YAAY,CAAC;AAEpC,+CAA+C;AAC/C,eAAO,MAAM,kBAAkB,idAS9B,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,mBAAmB,ysDA4B/B,CAAC"}
@@ -17,6 +17,8 @@
17
17
  */
18
18
  /** Issuer of locally minted dev identity tokens. */
19
19
  export const DEV_ISSUER = "https://gateway.dev.localhost";
20
+ /** The organization the dev gateway's seeded user belongs to (`everyAppWorker()` sets it as `EVERY_APP_ORG_ID` in dev). */
21
+ export const DEV_ORG_ID = "dev-org";
20
22
  /** SPKI PEM public half of the dev keypair. */
21
23
  export const DEV_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
22
24
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAh40zWf+QK3rJdMTNoy+X
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @every-app/sdk — what `everyapp.config.ts` imports: `defineEveryApp` and
3
+ * the manifest's types. A leaf, like the config itself (see ./manifest.ts).
4
+ */
5
+ export { defineEveryApp, type EveryAppCapabilityEntry, type EveryAppClassResource, type EveryAppManifest, type EveryAppPublicRoute, type EveryAppQueueConsumer, type EveryAppResources, type EveryAppSecretEntry, type EveryAppSettingNeed, type EveryAppWorkerLimits, } from "./manifest.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,cAAc,EACd,KAAK,uBAAuB,EAC5B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @every-app/sdk — what `everyapp.config.ts` imports: `defineEveryApp` and
3
+ * the manifest's types. A leaf, like the config itself (see ./manifest.ts).
4
+ */
5
+ export { defineEveryApp, } from "./manifest.js";
@@ -945,7 +945,7 @@ export declare const CAPABILITIES: {
945
945
  readonly hint: "An API key from the Claude Console's API keys page; the Gateway sends it as a bearer token. Apps send the anthropic-version header themselves.";
946
946
  readonly secrets: readonly [{
947
947
  readonly name: "ANTHROPIC_API_KEY";
948
- readonly hint: "A key scoped to one workspace, so no call needs an anthropic-workspace-id header.";
948
+ readonly hint: "A key scoped to one workspace; the Gateway strips any anthropic-workspace-id header an app sends, so the key alone decides the workspace.";
949
949
  }];
950
950
  readonly smoke: {
951
951
  readonly method: "GET";
@@ -420,7 +420,7 @@ export declare const CATALOG_ENTRIES: {
420
420
  readonly hint: "An API key from the Claude Console's API keys page; the Gateway sends it as a bearer token. Apps send the anthropic-version header themselves.";
421
421
  readonly secrets: readonly [{
422
422
  readonly name: "ANTHROPIC_API_KEY";
423
- readonly hint: "A key scoped to one workspace, so no call needs an anthropic-workspace-id header.";
423
+ readonly hint: "A key scoped to one workspace; the Gateway strips any anthropic-workspace-id header an app sends, so the key alone decides the workspace.";
424
424
  }];
425
425
  readonly smoke: {
426
426
  readonly method: "GET";
@@ -23,7 +23,7 @@ export declare const INFERENCE: {
23
23
  readonly hint: "An API key from the Claude Console's API keys page; the Gateway sends it as a bearer token. Apps send the anthropic-version header themselves.";
24
24
  readonly secrets: readonly [{
25
25
  readonly name: "ANTHROPIC_API_KEY";
26
- readonly hint: "A key scoped to one workspace, so no call needs an anthropic-workspace-id header.";
26
+ readonly hint: "A key scoped to one workspace; the Gateway strips any anthropic-workspace-id header an app sends, so the key alone decides the workspace.";
27
27
  }];
28
28
  readonly smoke: {
29
29
  readonly method: "GET";
@@ -1,5 +1,5 @@
1
1
  export const INFERENCE = {
2
- // Bearer is the documented header today; x-api-key is the legacy fallback. A workspace-scoped key keeps anthropic-workspace-id harmless.
2
+ // Bearer is the documented header today; x-api-key is the legacy fallback. The Gateway strips anthropic-workspace-id (and x-api-key), so the key alone decides the workspace.
3
3
  anthropic: {
4
4
  mode: "ai",
5
5
  label: "Anthropic",
@@ -28,7 +28,7 @@ export const INFERENCE = {
28
28
  secrets: [
29
29
  {
30
30
  name: "ANTHROPIC_API_KEY",
31
- hint: "A key scoped to one workspace, so no call needs an anthropic-workspace-id header.",
31
+ hint: "A key scoped to one workspace; the Gateway strips any anthropic-workspace-id header an app sends, so the key alone decides the workspace.",
32
32
  },
33
33
  ],
34
34
  smoke: {
@@ -420,7 +420,7 @@ export const INFERENCE = {
420
420
  },
421
421
  tags: ["ai", "images", "video", "audio"],
422
422
  },
423
- // The router forwards X-HF-Bill-To as sent, so an app can bill any organization the token's user belongs to (TODO.md).
423
+ // The Gateway strips X-HF-Bill-To, so calls bill the token's own account, never another organization the token's user belongs to.
424
424
  huggingface: {
425
425
  mode: "api_key",
426
426
  label: "Hugging Face Inference",
@@ -9,7 +9,7 @@
9
9
  * apps/every-app-gateway/src/server/events/catalogue.ts; this module knows
10
10
  * the names, versions and one-line descriptions only.
11
11
  */
12
- export type EventGroup = "member" | "app" | "cap" | "tool" | "connection" | "token" | "access" | "signin_provider" | "mcp_app" | "provider" | "ai" | "ping";
12
+ export type EventGroup = "member" | "organization" | "app" | "cap" | "group" | "tool" | "connection" | "token" | "access" | "signin_provider" | "mcp_app" | "provider" | "subscription" | "ai" | "ping";
13
13
  export interface EventTypeInfo {
14
14
  /** `member.joined`, `cap.reached`, … */
15
15
  readonly type: string;