@every-app/sdk 0.1.14 → 0.3.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +608 -2
- package/dist/cloudflare/index.d.ts +21 -1
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +21 -1
- package/dist/cloudflare/sentinel.d.ts +69 -0
- package/dist/cloudflare/sentinel.d.ts.map +1 -0
- package/dist/cloudflare/sentinel.js +50 -0
- package/dist/cloudflare/worker.d.ts +511 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +738 -0
- package/dist/dev/index.d.ts +8 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +7 -0
- package/dist/dev/keypair.d.ts +24 -0
- package/dist/dev/keypair.d.ts.map +1 -0
- package/dist/dev/keypair.js +60 -0
- package/dist/internal/aiProviders.d.ts +67 -0
- package/dist/internal/aiProviders.d.ts.map +1 -0
- package/dist/internal/aiProviders.js +220 -0
- package/dist/internal/appSettings.d.ts +237 -0
- package/dist/internal/appSettings.d.ts.map +1 -0
- package/dist/internal/appSettings.js +374 -0
- package/dist/internal/capabilities.d.ts +1344 -0
- package/dist/internal/capabilities.d.ts.map +1 -0
- package/dist/internal/capabilities.js +1521 -0
- package/dist/internal/devProviders.d.ts +49 -0
- package/dist/internal/devProviders.d.ts.map +1 -0
- package/dist/internal/devProviders.js +62 -0
- package/dist/internal/email.d.ts +280 -0
- package/dist/internal/email.d.ts.map +1 -0
- package/dist/internal/email.js +613 -0
- package/dist/internal/events.d.ts +182 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +324 -0
- package/dist/internal/gatewaySettings.d.ts +82 -0
- package/dist/internal/gatewaySettings.d.ts.map +1 -0
- package/dist/internal/gatewaySettings.js +209 -0
- package/dist/internal/identity.d.ts +88 -0
- package/dist/internal/identity.d.ts.map +1 -0
- package/dist/internal/identity.js +231 -0
- package/dist/internal/index.d.ts +19 -0
- package/dist/internal/index.d.ts.map +1 -0
- package/dist/internal/index.js +18 -0
- package/dist/internal/members.d.ts +35 -0
- package/dist/internal/members.d.ts.map +1 -0
- package/dist/internal/members.js +7 -0
- package/dist/internal/oauthClient.d.ts +63 -0
- package/dist/internal/oauthClient.d.ts.map +1 -0
- package/dist/internal/oauthClient.js +194 -0
- package/dist/internal/oauthScopes.d.ts +117 -0
- package/dist/internal/oauthScopes.d.ts.map +1 -0
- package/dist/internal/oauthScopes.js +416 -0
- package/dist/internal/organizationName.d.ts +18 -0
- package/dist/internal/organizationName.d.ts.map +1 -0
- package/dist/internal/organizationName.js +24 -0
- package/dist/internal/protocol.d.ts +195 -0
- package/dist/internal/protocol.d.ts.map +1 -0
- package/dist/internal/protocol.js +250 -0
- package/dist/internal/providerApps.d.ts +75 -0
- package/dist/internal/providerApps.d.ts.map +1 -0
- package/dist/internal/providerApps.js +59 -0
- package/dist/internal/providerRegistry.d.ts +147 -0
- package/dist/internal/providerRegistry.d.ts.map +1 -0
- package/dist/internal/providerRegistry.js +262 -0
- package/dist/internal/providerSpec.d.ts +55 -0
- package/dist/internal/providerSpec.d.ts.map +1 -0
- package/dist/internal/providerSpec.js +746 -0
- package/dist/internal/remoteMcp.d.ts +188 -0
- package/dist/internal/remoteMcp.d.ts.map +1 -0
- package/dist/internal/remoteMcp.js +543 -0
- package/dist/internal/tools.d.ts +115 -0
- package/dist/internal/tools.d.ts.map +1 -0
- package/dist/internal/tools.js +80 -0
- package/dist/server/attribution.d.ts +39 -0
- package/dist/server/attribution.d.ts.map +1 -0
- package/dist/server/attribution.js +67 -0
- package/dist/server/authorizeUrl.d.ts +43 -0
- package/dist/server/authorizeUrl.d.ts.map +1 -0
- package/dist/server/authorizeUrl.js +110 -0
- package/dist/server/emit.d.ts +63 -0
- package/dist/server/emit.d.ts.map +1 -0
- package/dist/server/emit.js +101 -0
- package/dist/server/everyApp.d.ts +81 -0
- package/dist/server/everyApp.d.ts.map +1 -0
- package/dist/server/everyApp.js +185 -0
- package/dist/server/gatewayFetch.d.ts +37 -0
- package/dist/server/gatewayFetch.d.ts.map +1 -0
- package/dist/server/gatewayFetch.js +115 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +15 -0
- package/dist/server/integrationFetch.d.ts +131 -0
- package/dist/server/integrationFetch.d.ts.map +1 -0
- package/dist/server/integrationFetch.js +526 -0
- package/dist/server/listMembers.d.ts +32 -0
- package/dist/server/listMembers.d.ts.map +1 -0
- package/dist/server/listMembers.js +74 -0
- package/dist/server/nodeServer.d.ts +48 -0
- package/dist/server/nodeServer.d.ts.map +1 -0
- package/dist/server/nodeServer.js +45 -0
- package/dist/server/sendEmail.d.ts +39 -0
- package/dist/server/sendEmail.d.ts.map +1 -0
- package/dist/server/sendEmail.js +114 -0
- package/dist/server/tools.d.ts +184 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +347 -0
- package/dist/server/toolsEndpoint.d.ts +32 -0
- package/dist/server/toolsEndpoint.d.ts.map +1 -0
- package/dist/server/toolsEndpoint.js +231 -0
- package/package.json +21 -31
- package/dist/cloudflare/getLocalD1Url.d.ts +0 -2
- package/dist/cloudflare/getLocalD1Url.d.ts.map +0 -1
- package/dist/cloudflare/getLocalD1Url.js +0 -40
- package/dist/cloudflare/lazyInit.d.ts +0 -22
- package/dist/cloudflare/lazyInit.d.ts.map +0 -1
- package/dist/cloudflare/lazyInit.js +0 -65
- package/dist/cloudflare/server/gateway.d.ts +0 -29
- package/dist/cloudflare/server/gateway.d.ts.map +0 -1
- package/dist/cloudflare/server/gateway.js +0 -69
- package/dist/cloudflare/server/index.d.ts +0 -3
- package/dist/cloudflare/server/index.d.ts.map +0 -1
- package/dist/cloudflare/server/index.js +0 -2
- package/dist/core/authenticatedFetch.d.ts +0 -9
- package/dist/core/authenticatedFetch.d.ts.map +0 -1
- package/dist/core/authenticatedFetch.js +0 -31
- package/dist/core/index.d.ts +0 -4
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -2
- package/dist/core/sessionManager.d.ts +0 -58
- package/dist/core/sessionManager.d.ts.map +0 -1
- package/dist/core/sessionManager.js +0 -357
- package/dist/shared/bypassGatewayLocalOnly.d.ts +0 -15
- package/dist/shared/bypassGatewayLocalOnly.d.ts.map +0 -1
- package/dist/shared/bypassGatewayLocalOnly.js +0 -45
- package/dist/shared/parseMessagePayload.d.ts +0 -3
- package/dist/shared/parseMessagePayload.d.ts.map +0 -1
- package/dist/shared/parseMessagePayload.js +0 -18
- package/dist/tanstack/EmbeddedAppProvider.d.ts +0 -30
- package/dist/tanstack/EmbeddedAppProvider.d.ts.map +0 -1
- package/dist/tanstack/EmbeddedAppProvider.js +0 -57
- package/dist/tanstack/GatewayRequiredError.d.ts +0 -20
- package/dist/tanstack/GatewayRequiredError.d.ts.map +0 -1
- package/dist/tanstack/GatewayRequiredError.js +0 -91
- package/dist/tanstack/_internal/useEveryAppSession.d.ts +0 -18
- package/dist/tanstack/_internal/useEveryAppSession.d.ts.map +0 -1
- package/dist/tanstack/_internal/useEveryAppSession.js +0 -47
- package/dist/tanstack/index.d.ts +0 -4
- package/dist/tanstack/index.d.ts.map +0 -1
- package/dist/tanstack/index.js +0 -3
- package/dist/tanstack/server/authConfig.d.ts +0 -3
- package/dist/tanstack/server/authConfig.d.ts.map +0 -1
- package/dist/tanstack/server/authConfig.js +0 -13
- package/dist/tanstack/server/authenticateRequest.d.ts +0 -31
- package/dist/tanstack/server/authenticateRequest.d.ts.map +0 -1
- package/dist/tanstack/server/authenticateRequest.js +0 -154
- package/dist/tanstack/server/index.d.ts +0 -4
- package/dist/tanstack/server/index.d.ts.map +0 -1
- package/dist/tanstack/server/index.js +0 -2
- package/dist/tanstack/server/types.d.ts +0 -5
- package/dist/tanstack/server/types.d.ts.map +0 -1
- package/dist/tanstack/server/types.js +0 -1
- package/dist/tanstack/useEveryAppRouter.d.ts +0 -7
- package/dist/tanstack/useEveryAppRouter.d.ts.map +0 -1
- package/dist/tanstack/useEveryAppRouter.js +0 -66
- package/dist/tanstack/useSessionTokenClientMiddleware.d.ts +0 -2
- package/dist/tanstack/useSessionTokenClientMiddleware.d.ts.map +0 -1
- package/dist/tanstack/useSessionTokenClientMiddleware.js +0 -29
package/README.md
CHANGED
|
@@ -2,8 +2,614 @@
|
|
|
2
2
|
|
|
3
3
|
The embedded SDK for building apps on the Every App platform.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It verifies the identity the Gateway injects, defines the app's Worker and its agent tools, and reaches the Gateway's AI, email and integrations. There is no client-side export.
|
|
6
6
|
|
|
7
7
|
## Documentation
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
The complete references are the agent docs at https://everyapp.com/llms.txt (`identity.md`, `manifest.md`, `mcp.md`, `email.md`, `secrets.md`).
|
|
10
|
+
|
|
11
|
+
## Entry points
|
|
12
|
+
|
|
13
|
+
- `@every-app/sdk/server`: runs in the Worker. `everyApp()` verifies the identity the gateway injects. Tokens carry the app's Worker name as their audience, which `everyAppWorker()` declares as `env.EVERYAPP_AUDIENCE` and the deploy fills in (in development, where `everyapp dev` mints for the app id, `env.EVERYAPP_APP_ID` stands in); the app id comes from `env.EVERYAPP_APP_ID`. The handler gets the runtime's `ExecutionContext` as the Worker received it, so it goes on to Hono's `app.fetch(request, env, ctx)` or React Router's request handler without a cast.
|
|
14
|
+
- `@every-app/sdk/cloudflare`: runs in Node while `cf` loads `cloudflare.config.ts`. `everyAppWorker()` builds the Worker definition from `everyapp.config.ts`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { defineConfig } from "cf/config";
|
|
18
|
+
import * as entrypoint from "./src/entry.worker.ts" with { type: "cf-worker" };
|
|
19
|
+
import manifest from "./everyapp.config.ts";
|
|
20
|
+
import { everyAppWorker } from "@every-app/sdk/cloudflare";
|
|
21
|
+
|
|
22
|
+
export default defineConfig((ctx) => ({
|
|
23
|
+
worker: everyAppWorker({ manifest, entrypoint, ctx }),
|
|
24
|
+
}));
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Production builds are tenant-neutral: the same Build Output whoever builds the app, with placeholders (`sentinelDeployTarget()`, `BUILD_SENTINEL` in `@every-app/sdk/cloudflare/sentinel`) wherever an install's Worker name, organization, gateway URL, identity keys and resource names or ids go. `everyapp deploy` builds with `EVERYAPP_BUILD=1` and then installs the output (rewrites every placeholder, refusing one that survives); without that variable a production build throws, so deploy with `everyapp deploy` rather than `cf deploy`. A deploy that deletes Durable Object classes adds their tombstones.
|
|
28
|
+
|
|
29
|
+
`localD1Path(root, id)` is the sqlite file `cf dev` keeps the local D1 database `id` in (`env.DB.id` of a development `everyAppWorker()`, or `localD1Id(workerName, binding)`), for drizzle-kit studio.
|
|
30
|
+
|
|
31
|
+
- `@every-app/sdk/dev`: the well-known keypair and issuer `everyapp dev` signs local identities with. Public by design: the SDK accepts it only in development (`EVERYAPP_DEV`, which `everyAppWorker()` sets there and a deploy never does) and only from the dev issuer.
|
|
32
|
+
|
|
33
|
+
## Scheduled work and secrets
|
|
34
|
+
|
|
35
|
+
`everyAppWorker()` also takes cron schedules, and the manifest names the
|
|
36
|
+
app's own secrets:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
// everyapp.config.ts
|
|
40
|
+
export default {
|
|
41
|
+
id: "repolens",
|
|
42
|
+
// The app's own secrets, set with `everyapp secret set GITHUB_TOKEN`
|
|
43
|
+
// in this directory; in dev, declared when .dev.vars sets them.
|
|
44
|
+
secrets: ["GITHUB_TOKEN"],
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
// cloudflare.config.ts
|
|
48
|
+
export default defineConfig((ctx) => ({
|
|
49
|
+
worker: everyAppWorker({
|
|
50
|
+
manifest,
|
|
51
|
+
entrypoint,
|
|
52
|
+
ctx,
|
|
53
|
+
// Runs the Worker's scheduled handler every six hours (UTC).
|
|
54
|
+
crons: ["17 */6 * * *"],
|
|
55
|
+
}),
|
|
56
|
+
}));
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- **Crons.** Export a `scheduled` handler next to `everyApp()`'s `fetch`:
|
|
60
|
+
`export default { ...everyApp(handler, { tools }), scheduled }`. A
|
|
61
|
+
scheduled run has no user (identity is per request). Deploys keep the
|
|
62
|
+
triggers; an app can't declare routes or domains.
|
|
63
|
+
- **Secrets.** The manifest's `secrets` lists the app's own by name; a
|
|
64
|
+
name that is one of the Gateway's settings (`OPENAI_API_KEY`, say) is
|
|
65
|
+
refused there, since the app reaches those through `providers` and
|
|
66
|
+
`capabilities`. `everyapp secret set GITHUB_TOKEN` in the app's directory
|
|
67
|
+
puts it on the live Worker (and each helper) in a new version, and later
|
|
68
|
+
deploys keep it, so production builds don't declare it. In dev, put it
|
|
69
|
+
in `.dev.vars`: `everyAppWorker()` declares it only when it is set there,
|
|
70
|
+
since the Vite plugin passes a `.dev.vars` value through only for a
|
|
71
|
+
declared secret. Type it as optional in `src/env.d.ts`
|
|
72
|
+
(`GITHUB_TOKEN?: string` in `Cloudflare.Env`). An entry can carry its
|
|
73
|
+
need: `{ name: "GITHUB_TOKEN", why, docs, optional: true }`, or
|
|
74
|
+
`anyOf: "<group>"` when one of a group must be set, and
|
|
75
|
+
`setup: "setup/<name>.md"` names its guide in the everyapp skill;
|
|
76
|
+
`providers` and `capabilities` entries take `why`, `docs`, `optional` and
|
|
77
|
+
`anyOf` too. Until every required setting is set the app shows "Needs
|
|
78
|
+
setup" and its tools stay out of `/mcp`; `pnpm everyapp secret list` says
|
|
79
|
+
what is missing (`docs/manifest-settings.md`).
|
|
80
|
+
|
|
81
|
+
## Compatibility flags
|
|
82
|
+
|
|
83
|
+
Every Worker `everyAppWorker()` and `everyAppAuxiliaryWorker()` build has
|
|
84
|
+
`nodejs_compat` and the compatibility date `2026-06-01` (or the manifest's
|
|
85
|
+
`compatibilityDate`). `compatibilityFlags` adds flags after `nodejs_compat`,
|
|
86
|
+
each once:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
everyAppWorker({
|
|
90
|
+
manifest,
|
|
91
|
+
entrypoint,
|
|
92
|
+
ctx,
|
|
93
|
+
// fetch() reaches only the public internet: no private or local addresses.
|
|
94
|
+
compatibilityFlags: ["global_fetch_strictly_public"],
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Durable Objects
|
|
99
|
+
|
|
100
|
+
Declare a Durable Object by binding and class,
|
|
101
|
+
`resources: { durableObjects: [{ name: "MAILBOX", className: "MailboxDO" }] }`,
|
|
102
|
+
and export the class from the Worker entry. When `entrypoint` is the entry
|
|
103
|
+
imported as a `cf-worker` module (as above), `cf workers types` types the
|
|
104
|
+
binding with the class, `env.MAILBOX: DurableObjectNamespace<MailboxDO>`, so
|
|
105
|
+
RPC calls on a stub type-check: the binding names the Worker by its
|
|
106
|
+
definition (`worker: { name, compatibilityDate, entrypoint }`), which cf
|
|
107
|
+
resolves to the Worker's name. With a path entrypoint, and for a class a
|
|
108
|
+
helper Worker defines, the binding names the Worker by name and the
|
|
109
|
+
namespace is untyped. Retype it in `src/env.d.ts` (a plain cast is refused):
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import type { Scratchpad } from "./audit.worker";
|
|
113
|
+
|
|
114
|
+
export type AppEnv = Omit<Env, "SCRATCHPAD"> & {
|
|
115
|
+
SCRATCHPAD: DurableObjectNamespace<Scratchpad>;
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Workflows
|
|
120
|
+
|
|
121
|
+
Declare a [Workflow](https://developers.cloudflare.com/workflows/) in
|
|
122
|
+
`everyapp.config.ts` by binding and class,
|
|
123
|
+
`resources: { workflows: [{ name: "RANK_CHECK", className: "RankCheckWorkflow" }] }`,
|
|
124
|
+
and export the `WorkflowEntrypoint` class from the Worker entry.
|
|
125
|
+
`everyAppWorker()` defines and binds it under a name after the Worker
|
|
126
|
+
(`every-acme-seo--rank-check`, from `workflowNameFor()`), because Workflow
|
|
127
|
+
names are shared by the whole Cloudflare account. Start one with
|
|
128
|
+
`env.RANK_CHECK.create({ params })`; a run has no user.
|
|
129
|
+
|
|
130
|
+
## Helper Workers
|
|
131
|
+
|
|
132
|
+
An app can run part of its work on Workers of its own, e.g. a memory-hungry
|
|
133
|
+
job that would crowd the app's isolate. Declare each one, and the Durable
|
|
134
|
+
Objects and Workflows it defines:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
resources: {
|
|
138
|
+
durableObjects: [{ name: "SCRATCHPAD", className: "Scratchpad", worker: "audit" }],
|
|
139
|
+
workflows: [{ name: "SITE_AUDIT", className: "SiteAuditWorkflow", worker: "audit" }],
|
|
140
|
+
},
|
|
141
|
+
workers: [{ name: "audit", binding: "AUDIT" }],
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Build it as an auxiliary Worker of the Vite plugin:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
// vite.config.ts: defineConfig(({ mode }) => ({ plugins: [ … ] }))
|
|
148
|
+
cloudflare({
|
|
149
|
+
viteEnvironment: { name: "ssr" },
|
|
150
|
+
auxiliaryWorkers: [
|
|
151
|
+
{
|
|
152
|
+
config: everyAppAuxiliaryWorker({
|
|
153
|
+
manifest,
|
|
154
|
+
worker: "audit",
|
|
155
|
+
entrypoint: "./src/audit.worker.ts",
|
|
156
|
+
ctx: { mode },
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
}),
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
It deploys as `every-acme-seo--audit`, private like the app: only the app's
|
|
164
|
+
Worker reaches it, through `env.AUDIT` and the bindings of the classes it
|
|
165
|
+
defines. It shares the app's D1 databases, KV namespaces and R2 buckets, and
|
|
166
|
+
calls the gateway as the app, with the app's `GATEWAY_URL` and
|
|
167
|
+
`EVERY_APP_ORG_ID`, so its background calls (a Workflow's, say) are the
|
|
168
|
+
organization's like the app's. `everyapp deploy` checks and uploads helpers before the
|
|
169
|
+
app's Worker, and `everyapp delete --purge` deletes them, and the Workflows
|
|
170
|
+
they run, with it. A class stays in the Worker that first defined it.
|
|
171
|
+
|
|
172
|
+
## Postgres through Hyperdrive
|
|
173
|
+
|
|
174
|
+
An app whose data lives in Postgres declares the database and a
|
|
175
|
+
[Hyperdrive](https://developers.cloudflare.com/hyperdrive/) config, and
|
|
176
|
+
migrates it with drizzle:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
resources: { hyperdrive: [{ name: "HYPERDRIVE" }] },
|
|
180
|
+
database: { provider: "postgres", region: "aws:us-east-1" },
|
|
181
|
+
migrations: { engine: "drizzle", dialect: "postgresql", dir: "drizzle/pg", config: "drizzle/pg.config.ts" },
|
|
182
|
+
workers: [{ name: "audit", limits: { cpuMs: 300_000 } }],
|
|
183
|
+
limits: { cpuMs: 30_000 },
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Every one of the app's Workers binds `env.HYPERDRIVE` (a config with
|
|
187
|
+
`worker: "<helper>"` binds only that helper) and reads `DATABASE_PROVIDER`
|
|
188
|
+
(`"postgres"`); deployed, they run near `database.region`. `limits.cpuMs`
|
|
189
|
+
raises a Worker's CPU time per invocation, up to 5 minutes. `everyapp
|
|
190
|
+
deploy` reads the database's URL from `.everyapp/secrets.env`, which it
|
|
191
|
+
never uploads or prints:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
POSTGRES_DATABASE_URL=postgres://<user>:<password>@<host>:<port>/<database>
|
|
195
|
+
# optional: the origin a binding's config connects to, when it isn't that one
|
|
196
|
+
HYPERDRIVE_ORIGIN_URL=postgres://<app user>:<password>@<host>:<port>/<database>
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
It creates the config `every-acme-seo--hyperdrive` from it, or adopts one of
|
|
200
|
+
that name that connects to the same database, runs `drizzle-kit migrate`
|
|
201
|
+
(your config reads `process.env.POSTGRES_DATABASE_URL`) before it uploads, and
|
|
202
|
+
`everyapp delete --purge` deletes the config, never the database. In
|
|
203
|
+
development, set `CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE`
|
|
204
|
+
in `.dev.vars` to a local Postgres; without it the binding is absent.
|
|
205
|
+
|
|
206
|
+
The configs `everyapp deploy` creates cache no queries: Hyperdrive never
|
|
207
|
+
invalidates a cached read when the app writes, and app code reads its own
|
|
208
|
+
writes. A deploy turns caching back off on a config that caches. For a
|
|
209
|
+
read-heavy config that tolerates reads up to a minute old,
|
|
210
|
+
`hyperdrive: [{ name: "HYPERDRIVE", caching: "default" }]` keeps Cloudflare's
|
|
211
|
+
own caching (60 s, served stale for 15 s more).
|
|
212
|
+
|
|
213
|
+
An app on Prisma migrates with `prisma migrate deploy` instead:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
migrations: { engine: "prisma", dialect: "postgresql", config: "prisma.config.ts" },
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The deploy runs the app's own `prisma` (a devDependency) with
|
|
220
|
+
`POSTGRES_DATABASE_URL` in its environment, so `prisma.config.ts` reads it:
|
|
221
|
+
`datasource: { url: process.env.POSTGRES_DATABASE_URL }`. Prisma finds its
|
|
222
|
+
migrations through its config; a `dir` in the manifest only makes the deploy
|
|
223
|
+
skip the migration when that directory is missing. The deploy can't check the
|
|
224
|
+
schema against the migrations (Prisma needs a shadow database for that) and
|
|
225
|
+
says so; run `prisma migrate diff --from-migrations <dir> --to-schema
|
|
226
|
+
<schema> --exit-code` yourself. Prisma on D1 is no engine of its own: generate
|
|
227
|
+
the SQL (`prisma migrate diff --from-empty --to-schema schema.prisma
|
|
228
|
+
--script`), put `--> statement-breakpoint` between its statements, and ship it
|
|
229
|
+
with `migrations: { engine: "d1-sql", dir: "migrations", binding: "DB" }`.
|
|
230
|
+
|
|
231
|
+
## R2 buckets
|
|
232
|
+
|
|
233
|
+
Declare the app's buckets by binding, and every one of its Workers (helpers
|
|
234
|
+
included) binds them:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
resources: { r2: ["R2", "MEDIA"] },
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`R2` is the bucket named after the app's Worker (`every-acme-postiz`), any
|
|
241
|
+
other binding `<worker>--<binding>` (`every-acme-postiz--media`); the
|
|
242
|
+
gateway assigns the names when it registers the app, and the deploy's policy
|
|
243
|
+
refuses any other bucket. The deploy doesn't create, record or purge buckets
|
|
244
|
+
yet: `cf deploy` should create a bound bucket that doesn't exist (not yet
|
|
245
|
+
confirmed on a live account), and the dashboard can create one before the
|
|
246
|
+
first deploy. A bucket bound by hand in
|
|
247
|
+
`cloudflare.config.ts`, `bindings.r2({ name: worker.name })`, keeps working
|
|
248
|
+
while the manifest declares none.
|
|
249
|
+
|
|
250
|
+
## Node frameworks (Express, NestJS)
|
|
251
|
+
|
|
252
|
+
A Node HTTP framework runs in the Worker on workerd's `node:http` bridge
|
|
253
|
+
(`cloudflare:node`), which `nodejs_compat` and the compatibility date turn
|
|
254
|
+
on. `nodeServerHandler()` starts the server on the isolate's first request
|
|
255
|
+
and hands it every request `everyApp()` lets through:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
// src/entry.worker.ts
|
|
259
|
+
import "reflect-metadata";
|
|
260
|
+
import { NestFactory } from "@nestjs/core";
|
|
261
|
+
import { everyApp, nodeServerHandler } from "@every-app/sdk/server";
|
|
262
|
+
import { AppModule } from "./app.module";
|
|
263
|
+
|
|
264
|
+
const handler = nodeServerHandler({
|
|
265
|
+
boot: async () => {
|
|
266
|
+
const app = await NestFactory.create(AppModule, { rawBody: true });
|
|
267
|
+
await app.listen(0);
|
|
268
|
+
return { port: app.getHttpServer().address().port };
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
export default { ...everyApp(handler), scheduled };
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
- **`listen(0)`.** workerd picks the port. A fixed port is still held by the
|
|
276
|
+
server of the module graph a Vite reload replaced, so the next boot fails
|
|
277
|
+
with `EADDRINUSE`.
|
|
278
|
+
- **Identity.** `everyApp()` has verified the identity before the Node app
|
|
279
|
+
sees the request, which still carries it as `x-everyapp-identity`. To know
|
|
280
|
+
the user in Node code, verify it again from the request's headers with the
|
|
281
|
+
Worker's text bindings, which `nodejs_compat` puts on `process.env`:
|
|
282
|
+
`await getEveryAppUser(new Request(url, { headers }), process.env)`.
|
|
283
|
+
- **What comes back.** `Set-Cookie` is dropped: the gateway owns the
|
|
284
|
+
session and strips cookies from what reaches the app. Drop `compression()`
|
|
285
|
+
too (Cloudflare compresses at the edge); a body Node encodes anyway is
|
|
286
|
+
passed through as is, since the bridge alone would encode it a second
|
|
287
|
+
time.
|
|
288
|
+
- **No identity outside `fetch`.** `scheduled` (and `email`) handlers sit
|
|
289
|
+
beside `fetch` and run with no user; they reach the Node app only if they
|
|
290
|
+
call it themselves.
|
|
291
|
+
|
|
292
|
+
Three rules decide whether such a Worker runs at all:
|
|
293
|
+
|
|
294
|
+
1. **Minify the Worker.** A Node framework's dependency graph is large, and
|
|
295
|
+
the isolate's heap grows with the source it loads (about 2.5 MB per MiB
|
|
296
|
+
locally), against a 128 MB limit. `unplugin-swc` (for Nest's decorator
|
|
297
|
+
metadata) sets `esbuild: false`, which also silently skips Vite's esbuild
|
|
298
|
+
minifier: `build.minify: true` changes nothing. Use
|
|
299
|
+
`build.minify: "terser"` (with `terser` installed), or minify the Worker's
|
|
300
|
+
chunks yourself:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
// vite.config.ts
|
|
304
|
+
import { transform } from "esbuild";
|
|
305
|
+
|
|
306
|
+
const minifyWorker = {
|
|
307
|
+
name: "minify-worker",
|
|
308
|
+
apply: "build" as const,
|
|
309
|
+
enforce: "post" as const,
|
|
310
|
+
applyToEnvironment: (environment: { name: string }) =>
|
|
311
|
+
environment.name !== "client",
|
|
312
|
+
async renderChunk(code: string) {
|
|
313
|
+
const result = await transform(code, {
|
|
314
|
+
minify: true,
|
|
315
|
+
format: "esm",
|
|
316
|
+
target: "es2022",
|
|
317
|
+
legalComments: "none",
|
|
318
|
+
});
|
|
319
|
+
return { code: result.code, map: null };
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Check that the bundle's size actually changed. On Vite 8 (oxc instead of
|
|
325
|
+
SWC) measure it again before and after.
|
|
326
|
+
|
|
327
|
+
2. **No work at module scope.** workerd refuses random values
|
|
328
|
+
(`crypto.randomUUID`, `getRandomValues`), timers and I/O (`fetch`,
|
|
329
|
+
sockets) outside a request ("Disallowed operation called within global
|
|
330
|
+
scope"), and `import.meta.url` is undefined, so
|
|
331
|
+
`createRequire(import.meta.url)` and `fileURLToPath(import.meta.url)`
|
|
332
|
+
throw. Nothing reachable from a module's top level may do these: make
|
|
333
|
+
every client (`new Stripe()`, `new OpenAI()`, SDK singletons) a lazy
|
|
334
|
+
factory, and create the app in `boot`.
|
|
335
|
+
|
|
336
|
+
3. **Stay inside the heap budget.** After `boot` and a few requests, the
|
|
337
|
+
isolate's used heap should be at most about 64 MB locally, half the
|
|
338
|
+
limit, leaving room for request bodies and caches. Read it from
|
|
339
|
+
miniflare's inspector (`inspectorPort`, then `Runtime.getHeapUsage` over
|
|
340
|
+
the websocket); `v8.getHeapStatistics()` reads zero in workerd.
|
|
341
|
+
`wrangler check startup` is only a smoke test for module-scope work.
|
|
342
|
+
|
|
343
|
+
## Agent tools
|
|
344
|
+
|
|
345
|
+
`defineTools()` describes the actions an app offers to AI clients (Claude,
|
|
346
|
+
Cursor, any MCP client). The app never speaks MCP itself: the gateway
|
|
347
|
+
snapshots the catalog when you deploy, serves MCP clients from it, and calls a
|
|
348
|
+
tool over a private, internal contract with a verified identity.
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { defineTools, everyApp, ToolError } from "@every-app/sdk/server";
|
|
352
|
+
import { z } from "zod"; // or any Standard Schema + Standard JSON Schema validator
|
|
353
|
+
|
|
354
|
+
export const tools = defineTools(
|
|
355
|
+
{
|
|
356
|
+
list_todos: {
|
|
357
|
+
description: "List the user's todos.",
|
|
358
|
+
risk: "read",
|
|
359
|
+
input: z.object({}).strict(),
|
|
360
|
+
handler: async (_input, { user }) => listTodos(user.id),
|
|
361
|
+
},
|
|
362
|
+
delete_todo: {
|
|
363
|
+
description: "Permanently delete one todo.",
|
|
364
|
+
risk: "destructive", // hidden from AI clients until approvals exist
|
|
365
|
+
input: z.object({ id: z.string() }).strict(),
|
|
366
|
+
handler: async ({ id }, { user }) => {
|
|
367
|
+
if (!(await deleteTodo(user.id, id)))
|
|
368
|
+
throw new ToolError("Todo not found");
|
|
369
|
+
},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
{ instructions: "Never guess todo ids; call list_todos first." },
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
export default everyApp(handler, { tools });
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
- `risk` is `read`, `write` or `destructive`. AI clients never see
|
|
379
|
+
destructive tools yet.
|
|
380
|
+
- `input` (and the optional `output`) take zod >= 4.2 or any validator that
|
|
381
|
+
implements Standard Schema and Standard JSON Schema; a plain
|
|
382
|
+
`{ jsonSchema, validate }` pair works too. Both describe objects: a
|
|
383
|
+
`z.object`, or a union of objects. A tool whose schema can't be described
|
|
384
|
+
that way is left out of the catalog (the other tools keep working), and
|
|
385
|
+
`everyapp deploy` prints why.
|
|
386
|
+
- Throw `ToolError` for messages the model may read. Other errors are logged
|
|
387
|
+
in the app and reported as "Internal error in <app> (ref <call id>)".
|
|
388
|
+
- Return `toolResult({ text, structured })` to give the model a written
|
|
389
|
+
summary while clients get the data: `structured` is checked against the
|
|
390
|
+
tool's `output` schema. A plain value is sent as JSON text.
|
|
391
|
+
- A handler's second argument carries `user`, `env` (typed as
|
|
392
|
+
`Cloudflare.Env`, which `cf` generates from `cloudflare.config.ts`), `ctx`,
|
|
393
|
+
`request` and `callId`.
|
|
394
|
+
|
|
395
|
+
## Capabilities
|
|
396
|
+
|
|
397
|
+
Third-party APIs whose key the gateway holds (`docs/capabilities.md`). List
|
|
398
|
+
one in `everyapp.config.ts` (`capabilities: { dataforseo: { access: "read",
|
|
399
|
+
why: "Keyword research" } }`) and call the provider's real URLs:
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
import { gatewayRefusal, integrationFetch } from "@every-app/sdk/server";
|
|
403
|
+
|
|
404
|
+
const dataforseo = integrationFetch({
|
|
405
|
+
env,
|
|
406
|
+
provider: "dataforseo",
|
|
407
|
+
for: request,
|
|
408
|
+
});
|
|
409
|
+
const response = await dataforseo(
|
|
410
|
+
"https://api.dataforseo.com/v3/dataforseo_labs/google/keyword_ideas/live",
|
|
411
|
+
{ method: "POST", body: JSON.stringify([{ keywords: ["seo"] }]) },
|
|
412
|
+
);
|
|
413
|
+
const refused = await gatewayRefusal(response); // null: DataForSEO answered
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
The call goes over the gateway binding, which puts the key on it.
|
|
417
|
+
`gatewayRefusal(response)`
|
|
418
|
+
tells the gateway's own refusals (`x-everyapp-error`: the manifest doesn't
|
|
419
|
+
list the capability, the gateway holds no key) from the provider's answers.
|
|
420
|
+
`integrationStatus({ env, provider })` says whether calls would go through,
|
|
421
|
+
without making one. In local dev, with no binding, the key comes from
|
|
422
|
+
`.dev.vars` under the gateway secret's name (`DATAFORSEO_API_KEY`), and so do
|
|
423
|
+
the AI providers' (`OPENAI_API_KEY`, `OPENROUTER_API_KEY`, or
|
|
424
|
+
`WORKERS_AI_API_TOKEN` with `CLOUDFLARE_ACCOUNT_ID` for Workers AI, which is
|
|
425
|
+
billed to that account) for `createGatewayFetch`: `everyAppWorker()` declares
|
|
426
|
+
the settings of each capability and provider the manifest lists when they
|
|
427
|
+
are set there, so the dev server passes them through.
|
|
428
|
+
|
|
429
|
+
### Email
|
|
430
|
+
|
|
431
|
+
The gateway sends mail for apps (`docs/capabilities.md`, "Gateway services:
|
|
432
|
+
email"). List `capabilities: { email: { access: "write" } }`; to send as
|
|
433
|
+
your own addresses, set them on the installation with
|
|
434
|
+
`pnpm everyapp email set --sender "*@mail.example.com"` in the app's
|
|
435
|
+
directory after its first deploy (each domain onboarded to Email Sending in
|
|
436
|
+
the gateway's account), and send:
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
import { GatewayRefusalError, sendEmail } from "@every-app/sdk/server";
|
|
440
|
+
|
|
441
|
+
const { messageId, status } = await sendEmail({
|
|
442
|
+
env,
|
|
443
|
+
for: request,
|
|
444
|
+
message: {
|
|
445
|
+
to: ["customer@example.net"],
|
|
446
|
+
subject: "Re: your question",
|
|
447
|
+
text: "Thanks for writing.",
|
|
448
|
+
from: { address: "support@mail.example.com", name: "Acme Support" },
|
|
449
|
+
headers: { "In-Reply-To": "<original@example.net>" },
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Leave `from` out to send as the gateway's sender. A refusal throws
|
|
455
|
+
`GatewayRefusalError` with its `code` (`email_sender_not_allowed`,
|
|
456
|
+
`email_budget_exhausted`, `integration_not_configured`, ...) and a message to
|
|
457
|
+
show. `integrationStatus({ env, provider: "email" })` adds the gateway's
|
|
458
|
+
`sender` and the `senders` the installation allows. In local dev, with no
|
|
459
|
+
binding, the message is checked as the gateway would, then printed and
|
|
460
|
+
answered `{ status: "logged" }`; set `EVERYAPP_EMAIL_SENDERS` in `.dev.vars`
|
|
461
|
+
to allow `from` addresses locally, and `EMAIL_REST_API_TOKEN`,
|
|
462
|
+
`CLOUDFLARE_ACCOUNT_ID` and `EMAIL_FROM` to send for real from your own
|
|
463
|
+
account. An `email()` handler (inbound mail, declared with
|
|
464
|
+
`email: { inbound: true }`; `everyapp email set --inbound` routes addresses
|
|
465
|
+
to it) sits beside `everyApp()`'s `fetch` and passes
|
|
466
|
+
`for: { background: true, organizationId }`.
|
|
467
|
+
|
|
468
|
+
### Members
|
|
469
|
+
|
|
470
|
+
The gateway lists the organization's members who can open the app now
|
|
471
|
+
(`docs/capabilities.md`, "Gateway services: members"): read it when acting
|
|
472
|
+
for people outside a request, such as choosing whom a scheduled report goes
|
|
473
|
+
to, rather than a copy the app kept. List
|
|
474
|
+
`capabilities: { members: { access: "read" } }`, and:
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
import { listMembers } from "@every-app/sdk/server";
|
|
478
|
+
|
|
479
|
+
const { members } = await listMembers({ env, for: { background: true } });
|
|
480
|
+
// [{ id, email, name, role: "owner" | "admin" | "member" }, ...]
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
People who were deactivated, left the organization or (for an
|
|
484
|
+
organization's own app) weren't granted the app are left out. An agent's
|
|
485
|
+
tool call is refused (`integration_agent_not_allowed`). In local dev, with
|
|
486
|
+
no binding, it answers the request's signed-in user alone, and no one for
|
|
487
|
+
background work.
|
|
488
|
+
|
|
489
|
+
### Events
|
|
490
|
+
|
|
491
|
+
An app puts its own events on the gateway's event bus, for the
|
|
492
|
+
organization's webhook and email subscribers
|
|
493
|
+
(`docs/design/2026-10-events-out.md`). No manifest entry is needed: the
|
|
494
|
+
gateway names the event `app.<app id>.<event>`, and owners and admins
|
|
495
|
+
subscribe to `app.<app id>.*` (or one name) in Manage workspace, Events.
|
|
496
|
+
|
|
497
|
+
```ts
|
|
498
|
+
import { emit } from "@every-app/sdk/server";
|
|
499
|
+
import { waitUntil } from "cloudflare:workers";
|
|
500
|
+
|
|
501
|
+
waitUntil(
|
|
502
|
+
emit({ env, for: request, event: "invoice.paid", data: { invoiceId } }),
|
|
503
|
+
);
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
`event` is lowercase dotted segments (`invoice.paid`), at most 64
|
|
507
|
+
characters. `data` is a JSON object, at most 8 KiB and 8 levels deep, and
|
|
508
|
+
goes as written to every URL a subscriber chose: never a credential, and a
|
|
509
|
+
key named like one at any depth (`token`, `secret`, `password`, `apiKey`,
|
|
510
|
+
`session`, `cookie`, ...) is refused. It answers `{ id, type }`, the
|
|
511
|
+
event's id and its delivered type. A refusal throws `GatewayRefusalError`
|
|
512
|
+
with its `code` (`event_name_invalid`, `event_data_invalid`,
|
|
513
|
+
`event_data_refused`, `event_data_too_large`, `event_rate_limited` past 600
|
|
514
|
+
an hour, ...) and a message to show; an emit that must never fail the work
|
|
515
|
+
around it goes under `waitUntil` or a `.catch()`. In local dev, with no
|
|
516
|
+
binding, the event is checked as the gateway would, printed as
|
|
517
|
+
`[everyapp dev] event app.<id>.<event>` (never its data) and answered
|
|
518
|
+
`{ id: "dev-<uuid>", type }`.
|
|
519
|
+
|
|
520
|
+
### Social networks' OAuth clients
|
|
521
|
+
|
|
522
|
+
The gateway holds a client for each social network its operator set up
|
|
523
|
+
(`docs/capabilities.md`, "OAuth clients": `linkedin`, `facebook`,
|
|
524
|
+
`instagram`, `threads`, `youtube`, `tiktok`, `tiktok-business`, `reddit`,
|
|
525
|
+
`pinterest`, `discord`, `slack`, `mastodon`, `kick`, `twitch`, `tumblr`,
|
|
526
|
+
`dribbble`; and for mail and calendar, `google-workspace` and
|
|
527
|
+
`microsoft-365`). List one for writing, send people to the provider's
|
|
528
|
+
authorize URL (for a client with scope sets, the one `authorizeUrl()`
|
|
529
|
+
builds from its status; for the others, one you build from the status's
|
|
530
|
+
`clientId` and the network's documented scopes), and exchange or refresh
|
|
531
|
+
tokens through the gateway without any client:
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
// everyapp.config.ts
|
|
535
|
+
capabilities: {
|
|
536
|
+
linkedin: { access: "write" },
|
|
537
|
+
// A client several apps share lists the scope sets it uses.
|
|
538
|
+
"google-workspace": {
|
|
539
|
+
access: "write",
|
|
540
|
+
scopes: ["identity", "search-console.read"],
|
|
541
|
+
why: "Reads Search Console for the sites you connect",
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
```ts
|
|
547
|
+
import {
|
|
548
|
+
authorizeUrl,
|
|
549
|
+
integrationFetch,
|
|
550
|
+
integrationStatus,
|
|
551
|
+
} from "@every-app/sdk/server";
|
|
552
|
+
|
|
553
|
+
const status = await integrationStatus({ env, provider: "google-workspace" });
|
|
554
|
+
if (!status.ready) return notAvailable(); // the operator hasn't set it up
|
|
555
|
+
// ...redirect to
|
|
556
|
+
const url = authorizeUrl({
|
|
557
|
+
provider: "google-workspace",
|
|
558
|
+
status,
|
|
559
|
+
redirectUri: `${origin}/integrations/google/callback`,
|
|
560
|
+
state,
|
|
561
|
+
codeChallenge, // optional PKCE (S256)
|
|
562
|
+
});
|
|
563
|
+
// ...and on the callback, exchange the code:
|
|
564
|
+
const tokens = await integrationFetch({
|
|
565
|
+
env,
|
|
566
|
+
provider: "google-workspace",
|
|
567
|
+
for: request,
|
|
568
|
+
})("https://oauth2.googleapis.com/token", {
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
571
|
+
body: new URLSearchParams({
|
|
572
|
+
grant_type: "authorization_code",
|
|
573
|
+
code,
|
|
574
|
+
redirect_uri,
|
|
575
|
+
code_verifier,
|
|
576
|
+
}),
|
|
577
|
+
});
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
The gateway completes the network's token endpoints only, putting its client
|
|
581
|
+
where each one takes it (form body, HTTP Basic, query string or JSON body);
|
|
582
|
+
anything else throws a `TypeError` before it is sent. Call the network's API
|
|
583
|
+
with the user's token directly. Mastodon's status also gives the instance as
|
|
584
|
+
`origin`.
|
|
585
|
+
|
|
586
|
+
For a client with scope sets (`google-workspace`, `microsoft-365`, `slack`),
|
|
587
|
+
the status carries the sets the manifest declares as `scopeSets` and the
|
|
588
|
+
provider's scope strings they ask for as `scopes`, which `authorizeUrl()`
|
|
589
|
+
puts on the URL (with Google's `access_type=offline`, Microsoft's
|
|
590
|
+
`response_mode=query`, Slack's comma-joined `scope` and `user_scope`, and
|
|
591
|
+
never `include_granted_scopes`); the gateway
|
|
592
|
+
refuses an exchange or refresh whose granted scopes exceed the sets
|
|
593
|
+
(`integration_scope_exceeded`), so a person's wider consent on another app
|
|
594
|
+
never reaches this one. A manifest entry without usable `scopes` is not
|
|
595
|
+
`ready`. The manifest's `scopes` field needs a gateway and CLI from the
|
|
596
|
+
same release as this SDK: an older `everyapp dev` refuses a manifest that
|
|
597
|
+
has it at startup. In local dev, with no binding, `.dev.vars` holds your
|
|
598
|
+
own client under the gateway's names (`LINKEDIN_CLIENT_ID`,
|
|
599
|
+
`LINKEDIN_CLIENT_SECRET`, `MASTODON_URL`, ...), the same rule puts it on
|
|
600
|
+
the call, and the same scope checks apply, from the manifest `everyapp dev`
|
|
601
|
+
passes the Worker.
|
|
602
|
+
|
|
603
|
+
### Whom a call is for
|
|
604
|
+
|
|
605
|
+
`createGatewayFetch`, `integrationFetch`, `sendEmail`, `listMembers` and
|
|
606
|
+
`emit` take `for`, which decides the organization the call is attributed to:
|
|
607
|
+
|
|
608
|
+
- `for: request`, the inbound request: the identity the gateway put on it
|
|
609
|
+
is forwarded, so the call is that person's, in their organization.
|
|
610
|
+
- `for: { background: true, organizationId }`, for work no request started
|
|
611
|
+
(a cron, a Workflow step, a queue): sent as `x-everyapp-org` with no
|
|
612
|
+
identity.
|
|
613
|
+
- Omitted: allowed only in an organization's own app, whose background
|
|
614
|
+
calls are the app's, in its own organization (the same as
|
|
615
|
+
`{ background: true }`). Naming another organization is refused.
|
|
@@ -1,2 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @every-app/sdk/cloudflare — the Worker definition for an app's
|
|
3
|
+
* `cloudflare.config.ts`:
|
|
4
|
+
*
|
|
5
|
+
* import { defineConfig } from "cf/config";
|
|
6
|
+
* import * as entrypoint from "./src/entry.worker.ts" with { type: "cf-worker" };
|
|
7
|
+
* import manifest from "./everyapp.config.ts";
|
|
8
|
+
* import { everyAppWorker } from "@every-app/sdk/cloudflare";
|
|
9
|
+
*
|
|
10
|
+
* export default defineConfig((ctx) => ({
|
|
11
|
+
* worker: everyAppWorker({ manifest, entrypoint, ctx }),
|
|
12
|
+
* }));
|
|
13
|
+
*
|
|
14
|
+
* This runs in Node while cf and the Vite plugin load the config, never in the
|
|
15
|
+
* Worker. Keep it self-contained: the plugin hides every file the config
|
|
16
|
+
* imports from the Worker in dev, so sharing a module with `/server` or
|
|
17
|
+
* `/internal` would break `cf dev` wherever the SDK is linked from source.
|
|
18
|
+
* (The dev keypair it hands the dev Worker is a leaf the Worker never loads.)
|
|
19
|
+
*/
|
|
20
|
+
export { EVERYAPP_OBSERVABILITY, everyAppAuxiliaryWorker, everyAppWorker, helperWorkerName, localD1Id, localD1Path, workflowNameFor, type D1Binding, type DurableObjectBinding, type DurableObjectExport, type EveryAppAuxiliaryWorkerConfig, type EveryAppAuxiliaryWorkerOptions, type EveryAppBinding, type EveryAppConfigContext, type EveryAppDeployEnv, type EveryAppDevEnv, type EveryAppExports, type EveryAppObservability, type EveryAppPlacement, type EveryAppWorkerConfig, type EveryAppWorkerManifest, type EveryAppWorkerOptions, type EveryAppWorkerReference, type GatewayBinding, type HyperdriveBinding, type KvBinding, type SecretBinding, type ServiceBinding, type TextBinding, type VersionMetadataBinding, type WorkflowBinding, type WorkflowExport, } from "./worker.js";
|
|
21
|
+
export { BUILD_SENTINEL, resourceSentinel, sentinelDeployTarget, type EveryAppDeployTarget, } from "./sentinel.js";
|
|
2
22
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cloudflare/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cloudflare/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,WAAW,EACX,eAAe,EACf,KAAK,SAAS,EACd,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EAClC,KAAK,8BAA8B,EACnC,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAC5B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,oBAAoB,GAC1B,MAAM,eAAe,CAAC"}
|
package/dist/cloudflare/index.js
CHANGED
|
@@ -1 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @every-app/sdk/cloudflare — the Worker definition for an app's
|
|
3
|
+
* `cloudflare.config.ts`:
|
|
4
|
+
*
|
|
5
|
+
* import { defineConfig } from "cf/config";
|
|
6
|
+
* import * as entrypoint from "./src/entry.worker.ts" with { type: "cf-worker" };
|
|
7
|
+
* import manifest from "./everyapp.config.ts";
|
|
8
|
+
* import { everyAppWorker } from "@every-app/sdk/cloudflare";
|
|
9
|
+
*
|
|
10
|
+
* export default defineConfig((ctx) => ({
|
|
11
|
+
* worker: everyAppWorker({ manifest, entrypoint, ctx }),
|
|
12
|
+
* }));
|
|
13
|
+
*
|
|
14
|
+
* This runs in Node while cf and the Vite plugin load the config, never in the
|
|
15
|
+
* Worker. Keep it self-contained: the plugin hides every file the config
|
|
16
|
+
* imports from the Worker in dev, so sharing a module with `/server` or
|
|
17
|
+
* `/internal` would break `cf dev` wherever the SDK is linked from source.
|
|
18
|
+
* (The dev keypair it hands the dev Worker is a leaf the Worker never loads.)
|
|
19
|
+
*/
|
|
20
|
+
export { EVERYAPP_OBSERVABILITY, everyAppAuxiliaryWorker, everyAppWorker, helperWorkerName, localD1Id, localD1Path, workflowNameFor, } from "./worker.js";
|
|
21
|
+
export { BUILD_SENTINEL, resourceSentinel, sentinelDeployTarget, } from "./sentinel.js";
|