@every-app/sdk 0.3.0-rc.1 → 0.3.0-rc.3
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/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/sentinel.d.ts +39 -1
- package/dist/cloudflare/sentinel.d.ts.map +1 -1
- package/dist/cloudflare/sentinel.js +22 -1
- package/dist/cloudflare/worker.d.ts +205 -7
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +174 -11
- package/dist/internal/aiDefault.d.ts +141 -0
- package/dist/internal/aiDefault.d.ts.map +1 -0
- package/dist/internal/aiDefault.js +204 -0
- package/dist/internal/aiProviders.d.ts +4 -1
- package/dist/internal/aiProviders.d.ts.map +1 -1
- package/dist/internal/aiProviders.js +5 -1
- package/dist/internal/appSettings.d.ts +20 -2
- package/dist/internal/appSettings.d.ts.map +1 -1
- package/dist/internal/appSettings.js +53 -7
- 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 +2315 -141
- package/dist/internal/capabilities.d.ts.map +1 -1
- package/dist/internal/capabilities.js +122 -0
- 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 +338 -0
- package/dist/internal/catalog/comms.d.ts.map +1 -0
- package/dist/internal/catalog/comms.js +477 -0
- package/dist/internal/catalog/index.d.ts +1936 -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 +457 -0
- package/dist/internal/catalog/infra.d.ts +423 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -0
- package/dist/internal/catalog/infra.js +543 -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 +13 -2
- package/dist/internal/events.d.ts.map +1 -1
- package/dist/internal/events.js +21 -3
- package/dist/internal/gatewaySettings.d.ts +2 -1
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +10 -2
- package/dist/internal/index.d.ts +5 -0
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +5 -0
- package/dist/internal/oauthPresets.d.ts +29 -0
- package/dist/internal/oauthPresets.d.ts.map +1 -0
- package/dist/internal/oauthPresets.js +534 -0
- package/dist/internal/oauthScopes.d.ts +56 -1
- package/dist/internal/oauthScopes.d.ts.map +1 -1
- package/dist/internal/oauthScopes.js +98 -4
- package/dist/internal/oauthSpec.d.ts +59 -0
- package/dist/internal/oauthSpec.d.ts.map +1 -0
- package/dist/internal/oauthSpec.js +676 -0
- package/dist/internal/protocol.d.ts +11 -0
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +9 -0
- package/dist/internal/providerRegistry.d.ts +17 -6
- package/dist/internal/providerRegistry.d.ts.map +1 -1
- package/dist/internal/providerRegistry.js +51 -2
- package/dist/internal/providerSpec.d.ts +11 -7
- package/dist/internal/providerSpec.d.ts.map +1 -1
- package/dist/internal/providerSpec.js +142 -33
- 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/server/ai.d.ts +45 -0
- package/dist/server/ai.d.ts.map +1 -0
- package/dist/server/ai.js +40 -0
- package/dist/server/connectUrl.d.ts +21 -0
- package/dist/server/connectUrl.d.ts.map +1 -0
- package/dist/server/connectUrl.js +37 -0
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +72 -8
- package/dist/server/index.d.ts +4 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +3 -1
- package/dist/server/integrationFetch.d.ts +39 -2
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +177 -13
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrationFetch.d.ts","sourceRoot":"","sources":["../../src/server/integrationFetch.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"integrationFetch.d.ts","sourceRoot":"","sources":["../../src/server/integrationFetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CATALOG, ConfigurationError, GATEWAY_ERROR_HEADER, INTEGRATION_PROXY_PREFIX, OAUTH_TOKEN_BODY_LIMIT, allowedScopes, apiKeyHosts, authorizationFor, capabilitySettings, carriesToken, declaredScopeSets, describeTokenEndpoints, devProviderFile, grantedScopes, injectClientCredentials, integrationProxyPath, oauthClientCredentials, oauthClientOrigin, requestedScopes, requestedTokenScopes, resolveDevProvider, rewriteTokenRequestScope, scopeExcess, tokenEndpointFor, tokenGrantType, EMAIL_SENDERS_ENV, chooseEmailProvider, parseEmailList, parseEmailSender, } from "../internal/index.js";
|
|
1
|
+
import { ACCOUNT_HEADER, CATALOG, ConfigurationError, GATEWAY_ERROR_HEADER, INTEGRATION_PROXY_PREFIX, OAUTH_TOKEN_BODY_LIMIT, allowedScopes, apiKeyHosts, authorizationFor, capabilitySettings, carriesToken, declaredScopeSets, describeTokenEndpoints, devProviderFile, grantedScopes, heldTokenCallAllowed, injectClientCredentials, integrationProxyPath, oauthClientCredentials, oauthClientOrigin, oauthReadMethods, oauthWriteMethods, providerSettingPrefix, requestedScopes, requestedTokenScopes, resolveDevProvider, rewriteTokenRequestScope, scopeExcess, tokenEndpointFor, tokenGrantType, EMAIL_SENDERS_ENV, chooseEmailProvider, parseEmailList, parseEmailSender, } from "../internal/index.js";
|
|
2
2
|
import { applyAttribution, checkAttribution, } from "./attribution.js";
|
|
3
3
|
import { BINDING_ORIGIN, streamingRequest, } from "./gatewayFetch.js";
|
|
4
4
|
/**
|
|
@@ -23,7 +23,7 @@ import { BINDING_ORIGIN, streamingRequest, } from "./gatewayFetch.js";
|
|
|
23
23
|
* provider show <id> --save` writes it), put on the call as the gateway's
|
|
24
24
|
* spec says.
|
|
25
25
|
*/
|
|
26
|
-
export function integrationFetch({ env, provider, for: target, }) {
|
|
26
|
+
export function integrationFetch({ env, provider, for: target, account, }) {
|
|
27
27
|
const catalogEntry = fetchableCapability(provider);
|
|
28
28
|
checkAttribution(env, target, `integrationFetch("${provider}")`);
|
|
29
29
|
const values = env;
|
|
@@ -36,8 +36,23 @@ export function integrationFetch({ env, provider, for: target, }) {
|
|
|
36
36
|
const integration = catalogEntry ??
|
|
37
37
|
(binding ? undefined : devRegisteredProvider(provider, values));
|
|
38
38
|
const local = binding || !integration ? null : localCredentials(integration, values);
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
// In dev an OAuth provider with a held token in `.dev.vars` serves API
|
|
40
|
+
// calls without the client (the token stands in for a connection).
|
|
41
|
+
const devToken = !binding && integration?.mode === "oauth_client" && integration.flow
|
|
42
|
+
? stringValue(values[devAccessTokenName(provider)])
|
|
43
|
+
: undefined;
|
|
44
|
+
// An OAuth provider with a Gateway-owned flow answers an API call
|
|
45
|
+
// without the client (`integration_not_connected`, below); a token
|
|
46
|
+
// endpoint call without the client throws when it is made.
|
|
47
|
+
const decidesLater = integration?.mode === "oauth_client" && integration.flow !== undefined;
|
|
48
|
+
if (!binding && !local && !devToken && integration && !decidesLater) {
|
|
49
|
+
const names = [
|
|
50
|
+
...capabilitySettings(integration),
|
|
51
|
+
...(integration.mode === "oauth_client" && integration.flow
|
|
52
|
+
? [devAccessTokenName(provider)]
|
|
53
|
+
: []),
|
|
54
|
+
];
|
|
55
|
+
throw new ConfigurationError(`${integration.label} is unavailable: deploy with the EVERY_APP_GATEWAY binding, or set ${names.join(", ")} in .dev.vars for local development.`);
|
|
41
56
|
}
|
|
42
57
|
return async (input, init) => {
|
|
43
58
|
const outbound = new Request(input, init);
|
|
@@ -55,6 +70,30 @@ export function integrationFetch({ env, provider, for: target, }) {
|
|
|
55
70
|
return binding.fetch(streamingRequest(`${BINDING_ORIGIN}${integrationProxyPath(provider, upstream)}`, outbound, headers));
|
|
56
71
|
}
|
|
57
72
|
if (integration.mode === "oauth_client") {
|
|
73
|
+
// A call to the provider's API with the token the gateway holds
|
|
74
|
+
// (docs/design/2026-10-oauth-providers.md section 7), for an entry
|
|
75
|
+
// that supports the Gateway-owned flow; a token endpoint stays the
|
|
76
|
+
// app-driven call it always was.
|
|
77
|
+
const toApi = integration.flow !== undefined &&
|
|
78
|
+
upstream.protocol === "https:" &&
|
|
79
|
+
integration.api.includes(upstream.host) &&
|
|
80
|
+
!tokenEndpointFor(integration, upstream, binding && integration.hostSetting ? undefined : values);
|
|
81
|
+
if (toApi) {
|
|
82
|
+
if (binding) {
|
|
83
|
+
if (account)
|
|
84
|
+
headers.set(ACCOUNT_HEADER, account);
|
|
85
|
+
applyAttribution(headers, target);
|
|
86
|
+
return binding.fetch(streamingRequest(`${BINDING_ORIGIN}${integrationProxyPath(provider, upstream)}`, outbound, headers));
|
|
87
|
+
}
|
|
88
|
+
return devHeldTokenCall({
|
|
89
|
+
provider,
|
|
90
|
+
capability: integration,
|
|
91
|
+
outbound,
|
|
92
|
+
upstream,
|
|
93
|
+
headers,
|
|
94
|
+
values,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
58
97
|
return oauthClientCall({
|
|
59
98
|
provider: provider,
|
|
60
99
|
capability: integration,
|
|
@@ -92,6 +131,93 @@ export function integrationFetch({ env, provider, for: target, }) {
|
|
|
92
131
|
return fetch(streamingRequest(upstream.toString(), outbound, headers));
|
|
93
132
|
};
|
|
94
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* The `.dev.vars` name of a held token for `provider` in local dev
|
|
136
|
+
* (`GOOGLE_WORKSPACE_ACCESS_TOKEN`, `GITHUB_ACCESS_TOKEN`): a token the
|
|
137
|
+
* developer obtained themselves, standing in for the connection the
|
|
138
|
+
* gateway would hold. Dev-only: the gateway never reads it.
|
|
139
|
+
*/
|
|
140
|
+
export function devAccessTokenName(provider) {
|
|
141
|
+
return `${providerSettingPrefix(provider)}ACCESS_TOKEN`;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* An app's call to an OAuth provider's API in local dev, with no
|
|
145
|
+
* gateway: the gateway's held-token checks in its order (the app's
|
|
146
|
+
* declared sets, the method, the route), then the developer's own token
|
|
147
|
+
* from `.dev.vars` as the bearer, straight to the provider.
|
|
148
|
+
*/
|
|
149
|
+
async function devHeldTokenCall({ provider, capability, outbound, upstream, headers, values, }) {
|
|
150
|
+
const token = stringValue(values[devAccessTokenName(provider)]);
|
|
151
|
+
if (!token) {
|
|
152
|
+
return devRefusal(409, "integration_not_connected", `${capability.label} isn't connected here. In local dev, set ${devAccessTokenName(provider)} in .dev.vars to a token you obtained yourself; deployed, the person connects at /connect/${provider}.`, { connectPath: `/connect/${provider}`, reconnect: false });
|
|
153
|
+
}
|
|
154
|
+
const policy = capability.scopePolicy;
|
|
155
|
+
const grant = policy
|
|
156
|
+
? devScopeSets(policy, provider, capability.label, values, "any")
|
|
157
|
+
: null;
|
|
158
|
+
if (grant && "problem" in grant) {
|
|
159
|
+
return devRefusal(403, grant.code, grant.problem);
|
|
160
|
+
}
|
|
161
|
+
const method = outbound.method.toUpperCase();
|
|
162
|
+
const reads = oauthReadMethods(capability).includes(method);
|
|
163
|
+
const writes = !reads &&
|
|
164
|
+
capability.access.includes("write") &&
|
|
165
|
+
oauthWriteMethods(capability).includes(method) &&
|
|
166
|
+
devDeclaredAccess(provider, values) === "write";
|
|
167
|
+
if (policy && grant) {
|
|
168
|
+
const allowed = heldTokenCallAllowed({
|
|
169
|
+
policy,
|
|
170
|
+
declaredSets: grant.sets,
|
|
171
|
+
// The developer's token stands for a connection granted every set.
|
|
172
|
+
connectionSets: grant.sets,
|
|
173
|
+
hosts: capability.api,
|
|
174
|
+
method,
|
|
175
|
+
upstream,
|
|
176
|
+
readMethods: oauthReadMethods(capability),
|
|
177
|
+
writes,
|
|
178
|
+
});
|
|
179
|
+
if (!allowed.ok) {
|
|
180
|
+
const status = {
|
|
181
|
+
integration_not_connected: 409,
|
|
182
|
+
integration_method_not_allowed: 405,
|
|
183
|
+
integration_scope_not_allowed: 403,
|
|
184
|
+
}[allowed.code];
|
|
185
|
+
return devRefusal(status, allowed.code, allowed.code === "integration_not_connected"
|
|
186
|
+
? `${capability.label} is connected without ${allowed.missingSets.join(", ")}.`
|
|
187
|
+
: allowed.message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else if (!reads && !writes) {
|
|
191
|
+
return devRefusal(405, "integration_method_not_allowed", `${capability.label} calls may use ${oauthReadMethods(capability).join(" or ")} with access: "read"; ${method} needs access: "write" in the app's capabilities entry, on a provider that offers it.`);
|
|
192
|
+
}
|
|
193
|
+
applyAttribution(headers, undefined);
|
|
194
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
195
|
+
for (const name of ["access_token", "token"]) {
|
|
196
|
+
upstream.searchParams.delete(name);
|
|
197
|
+
}
|
|
198
|
+
headers.delete("x-http-method-override");
|
|
199
|
+
return fetch(new Request(upstream.toString(), {
|
|
200
|
+
method,
|
|
201
|
+
headers,
|
|
202
|
+
body: method === "GET" || method === "HEAD" ? null : outbound.body,
|
|
203
|
+
signal: outbound.signal,
|
|
204
|
+
// As the gateway: the token never follows a redirect.
|
|
205
|
+
redirect: "manual",
|
|
206
|
+
...(method === "GET" || method === "HEAD" ? {} : { duplex: "half" }),
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
/** The `access` the manifest entry for `provider` declares in dev (EVERYAPP_DEV_CAPABILITIES), or null. */
|
|
210
|
+
function devDeclaredAccess(provider, values) {
|
|
211
|
+
const raw = values["EVERYAPP_DEV_CAPABILITIES"];
|
|
212
|
+
try {
|
|
213
|
+
const capabilities = typeof raw === "string" ? jsonRecord(JSON.parse(raw)) : undefined;
|
|
214
|
+
const entry = jsonRecord(capabilities?.[provider]);
|
|
215
|
+
return typeof entry?.["access"] === "string" ? entry["access"] : null;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
95
221
|
/**
|
|
96
222
|
* The developer's own key or client from `.dev.vars` (with EVERYAPP_DEV
|
|
97
223
|
* set), under the gateway's names; null until every setting it needs is.
|
|
@@ -125,6 +251,9 @@ async function oauthClientCall({ provider, capability, outbound, upstream, heade
|
|
|
125
251
|
return binding.fetch(streamingRequest(`${BINDING_ORIGIN}${integrationProxyPath(provider, upstream)}`, outbound, headers));
|
|
126
252
|
}
|
|
127
253
|
applyAttribution(headers, undefined);
|
|
254
|
+
if (!client) {
|
|
255
|
+
throw new ConfigurationError(`${capability.label}'s token endpoints need the client in .dev.vars for local development: set ${capabilitySettings(capability).join(", ")}.`);
|
|
256
|
+
}
|
|
128
257
|
// The gateway's scope checks, in its order, answering its refusals: an
|
|
129
258
|
// app sees in `everyapp dev` what the gateway will say.
|
|
130
259
|
const policy = capability.scopePolicy;
|
|
@@ -173,13 +302,15 @@ async function oauthClientCall({ provider, capability, outbound, upstream, heade
|
|
|
173
302
|
* `x-everyapp-error`) from the local-dev twin, so {@link gatewayRefusal}
|
|
174
303
|
* reads it the same way.
|
|
175
304
|
*/
|
|
176
|
-
function devRefusal(status, code, message) {
|
|
305
|
+
function devRefusal(status, code, message, extra = {}) {
|
|
177
306
|
const labels = {
|
|
178
307
|
400: "Bad Request",
|
|
179
308
|
403: "Forbidden",
|
|
309
|
+
405: "Method Not Allowed",
|
|
310
|
+
409: "Conflict",
|
|
180
311
|
502: "Bad Gateway",
|
|
181
312
|
};
|
|
182
|
-
return Response.json({ error: labels[status] ?? "Error", code, message }, { status, headers: { [GATEWAY_ERROR_HEADER]: code } });
|
|
313
|
+
return Response.json({ error: labels[status] ?? "Error", code, message, ...extra }, { status, headers: { [GATEWAY_ERROR_HEADER]: code } });
|
|
183
314
|
}
|
|
184
315
|
/**
|
|
185
316
|
* The scope sets the manifest declares for `id`, read from
|
|
@@ -191,7 +322,9 @@ function devRefusal(status, code, message) {
|
|
|
191
322
|
* doesn't offer). Fails closed like the gateway: a hand-rolled dev harness
|
|
192
323
|
* that passes no capabilities gets nothing.
|
|
193
324
|
*/
|
|
194
|
-
function devScopeSets(policy, id, label, values
|
|
325
|
+
function devScopeSets(policy, id, label, values,
|
|
326
|
+
/** The grant the entry needs: `write` for a token-endpoint call (the app-driven flow), `any` for a held-token call. */
|
|
327
|
+
grant = "write") {
|
|
195
328
|
const offered = Object.keys(policy.sets).join(", ");
|
|
196
329
|
const raw = values["EVERYAPP_DEV_CAPABILITIES"];
|
|
197
330
|
let capabilities;
|
|
@@ -211,11 +344,16 @@ function devScopeSets(policy, id, label, values) {
|
|
|
211
344
|
const entry = Object.hasOwn(capabilities, id)
|
|
212
345
|
? jsonRecord(capabilities[id])
|
|
213
346
|
: undefined;
|
|
214
|
-
// The gateway's check 2: listing the client for writing is the grant
|
|
215
|
-
|
|
347
|
+
// The gateway's check 2: listing the client for writing is the grant
|
|
348
|
+
// of the app-driven flow; a held-token call takes any listing.
|
|
349
|
+
const access = entry?.["access"];
|
|
350
|
+
if (!entry ||
|
|
351
|
+
(grant === "write"
|
|
352
|
+
? access !== "write"
|
|
353
|
+
: access !== "read" && access !== "write")) {
|
|
216
354
|
return {
|
|
217
355
|
code: "capability_not_requested",
|
|
218
|
-
problem: `This app doesn't list ${label} in its capabilities. Add capabilities: { ${JSON.stringify(id)}: { access: "write" } } to everyapp.config.ts and start everyapp dev again.`,
|
|
356
|
+
problem: `This app doesn't list ${label} in its capabilities. Add capabilities: { ${JSON.stringify(id)}: { access: "${grant === "write" ? "write" : "read"}" } } to everyapp.config.ts and start everyapp dev again.`,
|
|
219
357
|
};
|
|
220
358
|
}
|
|
221
359
|
const sets = declaredScopeSets(policy, entry);
|
|
@@ -315,6 +453,8 @@ async function checkTokenResponse(policy, capability, sets, response) {
|
|
|
315
453
|
google: "Ask for only the scopes integrationStatus() returns, without include_granted_scopes, and connect again; if the app needs more, add the scope set to everyapp.config.ts and deploy.",
|
|
316
454
|
microsoft: "On a shared Microsoft client a token carries everything the person consented to this client: have them remove this app's consent at https://myapplications.microsoft.com and connect again, or add the scope set to everyapp.config.ts and deploy.",
|
|
317
455
|
slack: "Slack scopes add up per workspace installation: reinstall the app in the workspace with only the status's scopes, or add the scope set to everyapp.config.ts and deploy.",
|
|
456
|
+
space: "Ask for only the scopes integrationStatus() returns and connect again, or add the scope set to everyapp.config.ts and deploy.",
|
|
457
|
+
comma: "Ask for only the scopes integrationStatus() returns and connect again, or add the scope set to everyapp.config.ts and deploy.",
|
|
318
458
|
}[policy.dialect];
|
|
319
459
|
return devRefusal(403, "integration_scope_exceeded", `${capability.label} granted scopes this app doesn't declare: ${excess.join(", ")}. It declares ${sets.join(", ")} (${requestedScopes(policy, sets).scopes.join(", ")}). Nothing was passed on. ${remedy}`);
|
|
320
460
|
}
|
|
@@ -431,9 +571,31 @@ function devOAuthClientStatus(id, capability, values) {
|
|
|
431
571
|
: null;
|
|
432
572
|
const origin = oauthClientOrigin(capability, values);
|
|
433
573
|
const policy = capability.scopePolicy;
|
|
574
|
+
// An entry with a Gateway-owned flow is listed for reading or writing;
|
|
575
|
+
// the app-driven flow alone needs the write grant.
|
|
434
576
|
const grant = policy
|
|
435
|
-
? devScopeSets(policy, id, capability.label, values)
|
|
577
|
+
? devScopeSets(policy, id, capability.label, values, capability.flow ? "any" : "write")
|
|
436
578
|
: null;
|
|
579
|
+
// The Gateway-owned flow's fields, in dev: a token in `.dev.vars`
|
|
580
|
+
// stands for a connection granted every declared set.
|
|
581
|
+
const flow = capability.flow ? "both" : "app";
|
|
582
|
+
const devToken = capability.flow
|
|
583
|
+
? stringValue(values[devAccessTokenName(id)])
|
|
584
|
+
: undefined;
|
|
585
|
+
const gateway = (sets) => capability.flow
|
|
586
|
+
? {
|
|
587
|
+
flow,
|
|
588
|
+
connectPath: `/connect/${id}`,
|
|
589
|
+
connection: devToken
|
|
590
|
+
? {
|
|
591
|
+
account: "user",
|
|
592
|
+
label: devAccessTokenName(id),
|
|
593
|
+
scopeSets: sets,
|
|
594
|
+
ready: true,
|
|
595
|
+
}
|
|
596
|
+
: null,
|
|
597
|
+
}
|
|
598
|
+
: { flow };
|
|
437
599
|
if (grant && "problem" in grant) {
|
|
438
600
|
if (grant.code === "capability_not_requested") {
|
|
439
601
|
return { requested: false, configured: false, ready: false };
|
|
@@ -443,16 +605,17 @@ function devOAuthClientStatus(id, capability, values) {
|
|
|
443
605
|
configured: Boolean(client),
|
|
444
606
|
ready: false,
|
|
445
607
|
scopeSets: [],
|
|
608
|
+
...gateway([]),
|
|
446
609
|
};
|
|
447
610
|
}
|
|
448
611
|
const asked = policy && grant ? requestedScopes(policy, grant.sets) : null;
|
|
449
612
|
return {
|
|
450
613
|
requested: true,
|
|
451
614
|
configured: Boolean(client),
|
|
452
|
-
ready: Boolean(client),
|
|
615
|
+
ready: Boolean(client) || Boolean(devToken),
|
|
453
616
|
...(client ? { clientId: client.clientId } : {}),
|
|
454
617
|
...(client && origin ? { origin } : {}),
|
|
455
|
-
...(client && grant && asked
|
|
618
|
+
...((client || devToken) && grant && asked
|
|
456
619
|
? {
|
|
457
620
|
scopeSets: grant.sets,
|
|
458
621
|
scopes: asked.scopes,
|
|
@@ -461,6 +624,7 @@ function devOAuthClientStatus(id, capability, values) {
|
|
|
461
624
|
: {}),
|
|
462
625
|
}
|
|
463
626
|
: {}),
|
|
627
|
+
...gateway(grant?.sets ?? []),
|
|
464
628
|
};
|
|
465
629
|
}
|
|
466
630
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@every-app/sdk",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -30,16 +30,6 @@
|
|
|
30
30
|
"dist",
|
|
31
31
|
"README.md"
|
|
32
32
|
],
|
|
33
|
-
"scripts": {
|
|
34
|
-
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
35
|
-
"build": "tsc -p tsconfig.build.json",
|
|
36
|
-
"types:check": "tsc --noEmit",
|
|
37
|
-
"format:check": "prettier --check .",
|
|
38
|
-
"format:write": "prettier . --write",
|
|
39
|
-
"test": "vitest run --silent",
|
|
40
|
-
"test:watch": "vitest",
|
|
41
|
-
"check:esm-relative-imports": "node ./scripts/check-esm-relative-imports.mjs"
|
|
42
|
-
},
|
|
43
33
|
"peerDependencies": {
|
|
44
34
|
"jose": "^6.2.5"
|
|
45
35
|
},
|
|
@@ -53,5 +43,15 @@
|
|
|
53
43
|
"vite-tsconfig-paths": "^5.1.4",
|
|
54
44
|
"vitest": "^3.2.6",
|
|
55
45
|
"zod": "^4.2.0"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
49
|
+
"build": "tsc -p tsconfig.build.json",
|
|
50
|
+
"types:check": "tsc --noEmit",
|
|
51
|
+
"format:check": "prettier --check .",
|
|
52
|
+
"format:write": "prettier . --write",
|
|
53
|
+
"test": "vitest run --silent",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"check:esm-relative-imports": "node ./scripts/check-esm-relative-imports.mjs"
|
|
56
56
|
}
|
|
57
|
-
}
|
|
57
|
+
}
|