@every-app/sdk 0.3.0-rc.4 → 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.
- package/LICENSE +21 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts +95 -0
- package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -0
- package/dist/cloudflare/catalogSettings.generated.js +221 -0
- package/dist/cloudflare/index.d.ts +1 -0
- package/dist/cloudflare/index.d.ts.map +1 -1
- package/dist/cloudflare/index.js +3 -0
- package/dist/cloudflare/worker.d.ts +15 -98
- package/dist/cloudflare/worker.d.ts.map +1 -1
- package/dist/cloudflare/worker.js +26 -126
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/internal/gatewaySettings.d.ts +7 -1
- package/dist/internal/gatewaySettings.d.ts.map +1 -1
- package/dist/internal/gatewaySettings.js +12 -1
- package/dist/manifest.d.ts +205 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +30 -0
- package/dist/server/ai.d.ts +21 -0
- package/dist/server/ai.d.ts.map +1 -1
- package/dist/server/ai.js +37 -8
- package/dist/server/aiBinding.d.ts +68 -0
- package/dist/server/aiBinding.d.ts.map +1 -0
- package/dist/server/aiBinding.js +294 -0
- package/dist/server/emailBinding.d.ts +72 -0
- package/dist/server/emailBinding.d.ts.map +1 -0
- package/dist/server/emailBinding.js +124 -0
- package/dist/server/env.d.ts +41 -0
- package/dist/server/env.d.ts.map +1 -0
- package/dist/server/env.js +1 -0
- package/dist/server/gatewayFetch.d.ts +17 -13
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +4 -7
- package/dist/server/gatewayUrl.d.ts +13 -0
- package/dist/server/gatewayUrl.d.ts.map +1 -0
- package/dist/server/gatewayUrl.js +34 -0
- package/dist/server/index.d.ts +8 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +6 -1
- package/dist/server/integrationFetch.d.ts +12 -1
- package/dist/server/integrationFetch.d.ts.map +1 -1
- package/dist/server/integrationFetch.js +3 -23
- package/dist/tanstack-start/index.d.ts +9 -0
- package/dist/tanstack-start/index.d.ts.map +1 -0
- package/dist/tanstack-start/index.js +29 -0
- package/dist/testing/index.d.ts +93 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +156 -0
- package/package.json +34 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gatewayFetch.d.ts","sourceRoot":"","sources":["../../src/server/gatewayFetch.ts"],"names":[],"mappings":"AAaA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,eAAO,MAAM,cAAc,sCAAsC,CAAC;AAGlE,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxE;
|
|
1
|
+
{"version":3,"file":"gatewayFetch.d.ts","sourceRoot":"","sources":["../../src/server/gatewayFetch.ts"],"names":[],"mappings":"AAaA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAE1B,eAAO,MAAM,cAAc,sCAAsC,CAAC;AAGlE,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxE;AAeD,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,yBAAyB,GAAG;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GACtD,OAAO,KAAK,GAAG,IAAI,CAAC;AACvB,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,yBAAyB,GACjC,OAAO,KAAK,CAAC;AAsHhB,sFAAsF;AACtF,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,OAAO,GACf,OAAO,CAUT"}
|
|
@@ -2,13 +2,8 @@ import { AI_DEFAULT_MODEL, AI_DEFAULT_PROVIDER, AI_DEFAULT_ROUTES, aiProviderSet
|
|
|
2
2
|
import { applyAttribution, attributionFor, } from "./attribution.js";
|
|
3
3
|
export const BINDING_ORIGIN = "http://every-app-gateway.internal";
|
|
4
4
|
const AI_PROXY_PREFIX = "/api/ai/";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* backed by the private gateway service binding. In local dev only, the
|
|
8
|
-
* developer's own provider key from `.dev.vars` is a direct-provider
|
|
9
|
-
* fallback when no binding exists.
|
|
10
|
-
*/
|
|
11
|
-
export function createGatewayFetch({ env, for: explicitTarget, }) {
|
|
5
|
+
export function createGatewayFetch({ env: workerEnv, for: explicitTarget, optional = false, }) {
|
|
6
|
+
const env = workerEnv;
|
|
12
7
|
// Checked now, so a call nobody's fails where the fetch is made; and
|
|
13
8
|
// again per call, where an identity may since have neared its expiry.
|
|
14
9
|
attributionFor(env, explicitTarget, "createGatewayFetch");
|
|
@@ -18,6 +13,8 @@ export function createGatewayFetch({ env, for: explicitTarget, }) {
|
|
|
18
13
|
throw new ConfigurationError("EVERY_APP_GATEWAY is configured but is not a valid service binding.");
|
|
19
14
|
}
|
|
20
15
|
if (!binding && Object.keys(devProviders).length === 0) {
|
|
16
|
+
if (optional)
|
|
17
|
+
return null;
|
|
21
18
|
throw new ConfigurationError(`AI gateway unavailable: deploy with the EVERY_APP_GATEWAY binding, or set the provider's key (${listOf(Object.values(DEV_AI_PROVIDERS).map((spec) => settingsNames(spec, "with")), "or")}) in .dev.vars for direct local development.`);
|
|
22
19
|
}
|
|
23
20
|
return async (input, init) => {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Gateway's URL, with `path` on it: `gatewayUrl(env)` for the launcher,
|
|
3
|
+
* `gatewayUrl(env, "/admin/users")` for the page where owners and admins
|
|
4
|
+
* invite people.
|
|
5
|
+
*
|
|
6
|
+
* Read from `env.GATEWAY_URL`, which the deploy sets on every Worker of the
|
|
7
|
+
* app and `everyapp dev` sets to its local launcher, so the same link works
|
|
8
|
+
* in both. `env` is the Worker's env (any object; only `GATEWAY_URL` is
|
|
9
|
+
* read). Throws {@link ConfigurationError} when it is unset or not an http(s)
|
|
10
|
+
* URL: a Worker no deploy or `everyapp dev` built.
|
|
11
|
+
*/
|
|
12
|
+
export declare function gatewayUrl(env: object, path?: string): string;
|
|
13
|
+
//# sourceMappingURL=gatewayUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gatewayUrl.d.ts","sourceRoot":"","sources":["../../src/server/gatewayUrl.ts"],"names":[],"mappings":"AASA;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,MAAM,CAgB1D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gatewayUrl()`: the one rule for a link from an app back to its Gateway
|
|
3
|
+
* ("‹ Every App", "Invite people", "Sign out").
|
|
4
|
+
*/
|
|
5
|
+
import { ConfigurationError } from "../internal/index.js";
|
|
6
|
+
/** Where the Gateway is: the deploy sets it, and so does `everyapp dev`. */
|
|
7
|
+
const GATEWAY_URL_ENV = "GATEWAY_URL";
|
|
8
|
+
/**
|
|
9
|
+
* The Gateway's URL, with `path` on it: `gatewayUrl(env)` for the launcher,
|
|
10
|
+
* `gatewayUrl(env, "/admin/users")` for the page where owners and admins
|
|
11
|
+
* invite people.
|
|
12
|
+
*
|
|
13
|
+
* Read from `env.GATEWAY_URL`, which the deploy sets on every Worker of the
|
|
14
|
+
* app and `everyapp dev` sets to its local launcher, so the same link works
|
|
15
|
+
* in both. `env` is the Worker's env (any object; only `GATEWAY_URL` is
|
|
16
|
+
* read). Throws {@link ConfigurationError} when it is unset or not an http(s)
|
|
17
|
+
* URL: a Worker no deploy or `everyapp dev` built.
|
|
18
|
+
*/
|
|
19
|
+
export function gatewayUrl(env, path = "/") {
|
|
20
|
+
const value = env[GATEWAY_URL_ENV];
|
|
21
|
+
let base;
|
|
22
|
+
if (typeof value === "string" && value) {
|
|
23
|
+
try {
|
|
24
|
+
base = new URL(value);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
base = undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!base || (base.protocol !== "https:" && base.protocol !== "http:")) {
|
|
31
|
+
throw new ConfigurationError(`${GATEWAY_URL_ENV} is ${value ? "not an http(s) URL" : "unset"}: everyapp deploy and everyapp dev set it on the app's Worker.`);
|
|
32
|
+
}
|
|
33
|
+
return new URL(path, base.origin).toString();
|
|
34
|
+
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -6,13 +6,19 @@
|
|
|
6
6
|
export { ConfigurationError, IdentityError, everyApp, getEveryAppUser, hasScope, isAdmin, requireCurrentUser, requireEveryAppUser, type EveryAppOptions, type EveryAppManifestLike, type EveryAppHandler, type EveryAppUser, type IdentityResult, type OrgRole, } from "./everyApp.js";
|
|
7
7
|
export { OrganizationMismatchError, RequestContextError, currentUser, withOrganization, } from "./context.js";
|
|
8
8
|
export { AttributionError, type GatewayCallFor } from "./attribution.js";
|
|
9
|
+
export { gatewayUrl } from "./gatewayUrl.js";
|
|
10
|
+
export type { EveryAppEnv } from "./env.js";
|
|
11
|
+
/** The header a Gateway refusal carries its code in; `gatewayRefusal()` reads the rest. */
|
|
12
|
+
export { GATEWAY_ERROR_HEADER } from "../internal/protocol.js";
|
|
9
13
|
export { authorizeUrl, type AuthorizeUrlOptions } from "./authorizeUrl.js";
|
|
10
14
|
export { connectPath, connectUrl, type ConnectUrlOptions, } from "./connectUrl.js";
|
|
11
|
-
export { createGatewayFetch, type CreateGatewayFetchOptions,
|
|
12
|
-
export { ai, AI_DEFAULT_BASE_URL, type AiClient } from "./ai.js";
|
|
15
|
+
export { createGatewayFetch, type CreateGatewayFetchOptions, } from "./gatewayFetch.js";
|
|
16
|
+
export { ai, aiBaseUrl, providerBaseUrl, AI_DEFAULT_BASE_URL, type AiClient, } from "./ai.js";
|
|
13
17
|
export { GatewayRefusalError, gatewayRefusal, devAccessTokenName, integrationFetch, integrationStatus, type EmailIntegrationStatus, type GatewayRefusal, type IntegrationFetchOptions, type IntegrationStatus, type IntegrationStatusOf, type OAuthClientIntegrationStatus, } from "./integrationFetch.js";
|
|
14
18
|
export { nodeServerHandler, type NodeServer, type NodeServerHandlerOptions, } from "./nodeServer.js";
|
|
15
19
|
export { sendEmail, type SendEmailOptions } from "./sendEmail.js";
|
|
20
|
+
export { gatewayAiBinding, type GatewayAiBindingOptions } from "./aiBinding.js";
|
|
21
|
+
export { gatewayEmailBinding, type GatewayEmailBindingMessage, type GatewayEmailBindingOptions, } from "./emailBinding.js";
|
|
16
22
|
export { listMembers, type ListMembersOptions } from "./listMembers.js";
|
|
17
23
|
export { emit, type EmitOptions } from "./emit.js";
|
|
18
24
|
export type { ApiKeyCapabilityId, CapabilityId, OAuthClientCapabilityId, ConnectionAccount, EmailAddressInput, EmailAttachmentInput, SendEmailMessage, SendEmailResult, ListMembersResult, OrganizationMember, OrganizationMemberRole, EmitEventResult, } from "../internal/index.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,OAAO,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,WAAW,EACX,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,WAAW,EACX,UAAU,EACV,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,QAAQ,EACR,eAAe,EACf,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,OAAO,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,yBAAyB,EACzB,mBAAmB,EACnB,WAAW,EACX,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,2FAA2F;AAC3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,WAAW,EACX,UAAU,EACV,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,EAAE,EACF,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,KAAK,QAAQ,GACd,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,mBAAmB,EACnB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,4BAA4B,GAClC,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,wBAAwB,GAC9B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,EACL,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,0BAA0B,GAChC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,YAAY,EACV,kBAAkB,EAClB,YAAY,EACZ,uBAAuB,EACvB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,eAAe,GAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,SAAS,EACT,WAAW,EACX,UAAU,EACV,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,OAAO,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EACL,YAAY,EACZ,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,mBAAmB,CAAC"}
|
package/dist/server/index.js
CHANGED
|
@@ -6,13 +6,18 @@
|
|
|
6
6
|
export { ConfigurationError, IdentityError, everyApp, getEveryAppUser, hasScope, isAdmin, requireCurrentUser, requireEveryAppUser, } from "./everyApp.js";
|
|
7
7
|
export { OrganizationMismatchError, RequestContextError, currentUser, withOrganization, } from "./context.js";
|
|
8
8
|
export { AttributionError } from "./attribution.js";
|
|
9
|
+
export { gatewayUrl } from "./gatewayUrl.js";
|
|
10
|
+
/** The header a Gateway refusal carries its code in; `gatewayRefusal()` reads the rest. */
|
|
11
|
+
export { GATEWAY_ERROR_HEADER } from "../internal/protocol.js";
|
|
9
12
|
export { authorizeUrl } from "./authorizeUrl.js";
|
|
10
13
|
export { connectPath, connectUrl, } from "./connectUrl.js";
|
|
11
14
|
export { createGatewayFetch, } from "./gatewayFetch.js";
|
|
12
|
-
export { ai, AI_DEFAULT_BASE_URL } from "./ai.js";
|
|
15
|
+
export { ai, aiBaseUrl, providerBaseUrl, AI_DEFAULT_BASE_URL, } from "./ai.js";
|
|
13
16
|
export { GatewayRefusalError, gatewayRefusal, devAccessTokenName, integrationFetch, integrationStatus, } from "./integrationFetch.js";
|
|
14
17
|
export { nodeServerHandler, } from "./nodeServer.js";
|
|
15
18
|
export { sendEmail } from "./sendEmail.js";
|
|
19
|
+
export { gatewayAiBinding } from "./aiBinding.js";
|
|
20
|
+
export { gatewayEmailBinding, } from "./emailBinding.js";
|
|
16
21
|
export { listMembers } from "./listMembers.js";
|
|
17
22
|
export { emit } from "./emit.js";
|
|
18
23
|
export { ToolError, defineTools, toolResult, } from "./tools.js";
|
|
@@ -33,6 +33,14 @@ export interface IntegrationFetchOptions {
|
|
|
33
33
|
* the default for background work). Sent as `x-everyapp-account`.
|
|
34
34
|
*/
|
|
35
35
|
account?: ConnectionAccount;
|
|
36
|
+
/**
|
|
37
|
+
* `true`: answer null instead of throwing `ConfigurationError` when the
|
|
38
|
+
* provider is unavailable (no binding, and in local dev not its
|
|
39
|
+
* settings in `.dev.vars`), so a feature that can do without it checks
|
|
40
|
+
* for null. A wrong provider id, a binding that isn't one, and an
|
|
41
|
+
* attribution mistake still throw.
|
|
42
|
+
*/
|
|
43
|
+
optional?: boolean;
|
|
36
44
|
}
|
|
37
45
|
/**
|
|
38
46
|
* A `fetch` for one of the gateway's capabilities. Call the provider's real
|
|
@@ -56,7 +64,10 @@ export interface IntegrationFetchOptions {
|
|
|
56
64
|
* provider show <id> --save` writes it), put on the call as the gateway's
|
|
57
65
|
* spec says.
|
|
58
66
|
*/
|
|
59
|
-
export declare function integrationFetch(
|
|
67
|
+
export declare function integrationFetch(options: IntegrationFetchOptions & {
|
|
68
|
+
optional: true;
|
|
69
|
+
}): typeof fetch | null;
|
|
70
|
+
export declare function integrationFetch(options: IntegrationFetchOptions): typeof fetch;
|
|
60
71
|
/**
|
|
61
72
|
* The `.dev.vars` name of a held token for `provider` in local dev
|
|
62
73
|
* (`GOOGLE_WORKSPACE_ACCESS_TOKEN`, `GITHUB_ACCESS_TOKEN`): a token the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integrationFetch.d.ts","sourceRoot":"","sources":["../../src/server/integrationFetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAgCL,KAAK,kBAAkB,EACvB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAE1B,KAAK,uBAAuB,EAQ7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAO1B,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;OAOG;IACH,QAAQ,EAAE,kBAAkB,GAAG,uBAAuB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IACvE;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"integrationFetch.d.ts","sourceRoot":"","sources":["../../src/server/integrationFetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,29 +1,7 @@
|
|
|
1
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, attributionFor, } from "./attribution.js";
|
|
3
3
|
import { BINDING_ORIGIN, streamingRequest, } from "./gatewayFetch.js";
|
|
4
|
-
|
|
5
|
-
* A `fetch` for one of the gateway's capabilities. Call the provider's real
|
|
6
|
-
* URLs (`https://api.dataforseo.com/v3/…`); this sends them over the
|
|
7
|
-
* AppGateway binding, which checks the app's manifest lists the capability,
|
|
8
|
-
* injects the key the gateway holds and forwards the call. The app never
|
|
9
|
-
* sees the key. A refusal the gateway makes itself comes back as a response
|
|
10
|
-
* {@link gatewayRefusal} recognizes; anything else is the provider's answer.
|
|
11
|
-
*
|
|
12
|
-
* For an OAuth client (`linkedin`, `mastodon`, ...) it reaches the
|
|
13
|
-
* provider's token endpoints only, and the gateway puts its client on the
|
|
14
|
-
* request: send the code (or refresh token) without `client_id` or
|
|
15
|
-
* `client_secret`. Build the authorize URL with {@link authorizeUrl} from
|
|
16
|
-
* {@link integrationStatus}, and call the provider's API with the user's
|
|
17
|
-
* token directly.
|
|
18
|
-
*
|
|
19
|
-
* In local dev only, with no binding, the developer's own key or client
|
|
20
|
-
* from `.dev.vars` reaches the provider directly, under the gateway's names:
|
|
21
|
-
* a catalog provider's from the catalog, a provider registered on the
|
|
22
|
-
* gateway from its spec in `.everyapp/providers/<id>.json` (`everyapp
|
|
23
|
-
* provider show <id> --save` writes it), put on the call as the gateway's
|
|
24
|
-
* spec says.
|
|
25
|
-
*/
|
|
26
|
-
export function integrationFetch({ env, provider, for: explicitTarget, account, }) {
|
|
4
|
+
export function integrationFetch({ env, provider, for: explicitTarget, account, optional = false, }) {
|
|
27
5
|
const catalogEntry = fetchableCapability(provider);
|
|
28
6
|
const what = `integrationFetch("${provider}")`;
|
|
29
7
|
// Checked now, so a call nobody's fails where the fetch is made; and
|
|
@@ -49,6 +27,8 @@ export function integrationFetch({ env, provider, for: explicitTarget, account,
|
|
|
49
27
|
// endpoint call without the client throws when it is made.
|
|
50
28
|
const decidesLater = integration?.mode === "oauth_client" && integration.flow !== undefined;
|
|
51
29
|
if (!binding && !local && !devToken && integration && !decidesLater) {
|
|
30
|
+
if (optional)
|
|
31
|
+
return null;
|
|
52
32
|
const names = [
|
|
53
33
|
...capabilitySettings(integration),
|
|
54
34
|
...(integration.mode === "oauth_client" && integration.flow
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Puts the person the Gateway signed in on `context.user` (an
|
|
3
|
+
* `EveryAppUser`), verified from the identity the Gateway sent; throws the
|
|
4
|
+
* 401 Response when there is none (a public route, anonymous).
|
|
5
|
+
*/
|
|
6
|
+
export declare const ensureUserMiddleware: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, {
|
|
7
|
+
user: import("../internal/protocol.js").EveryAppUser;
|
|
8
|
+
}, undefined, undefined, undefined>;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tanstack-start/index.ts"],"names":[],"mappings":"AAmBA;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;mCAK/B,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @every-app/sdk/tanstack-start — identity for a TanStack Start app's server
|
|
3
|
+
* functions:
|
|
4
|
+
*
|
|
5
|
+
* import { ensureUserMiddleware } from "@every-app/sdk/tanstack-start";
|
|
6
|
+
*
|
|
7
|
+
* export const getTodos = createServerFn()
|
|
8
|
+
* .middleware([ensureUserMiddleware])
|
|
9
|
+
* .handler(({ context: { user } }) => listTodos(user.id));
|
|
10
|
+
*
|
|
11
|
+
* The SDK lists `@tanstack/react-start` as a peer dependency, so the Start
|
|
12
|
+
* plugin compiles this module like the app's own: the `.server()` part never
|
|
13
|
+
* reaches the browser.
|
|
14
|
+
*/
|
|
15
|
+
import { createMiddleware } from "@tanstack/react-start";
|
|
16
|
+
import { getRequest } from "@tanstack/react-start/server";
|
|
17
|
+
import { env } from "cloudflare:workers";
|
|
18
|
+
import { requireEveryAppUser } from "../server/everyApp.js";
|
|
19
|
+
/**
|
|
20
|
+
* Puts the person the Gateway signed in on `context.user` (an
|
|
21
|
+
* `EveryAppUser`), verified from the identity the Gateway sent; throws the
|
|
22
|
+
* 401 Response when there is none (a public route, anonymous).
|
|
23
|
+
*/
|
|
24
|
+
export const ensureUserMiddleware = createMiddleware({
|
|
25
|
+
type: "function",
|
|
26
|
+
}).server(async ({ next }) => {
|
|
27
|
+
const user = await requireEveryAppUser(getRequest(), env);
|
|
28
|
+
return next({ context: { user } });
|
|
29
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { IDENTITY_HEADER, type IdentityChannel, type ToolCallResultV1, type ToolsCallResponseV1, type ToolsListResponseV1 } from "../internal/index.js";
|
|
2
|
+
import type { ToolSet } from "../server/tools.js";
|
|
3
|
+
export { IDENTITY_HEADER };
|
|
4
|
+
export type { ToolCallResultV1 as ToolCallResult, ToolsCallResponseV1 as ToolCallResponse, ToolsListResponseV1 as ToolsListResponse, };
|
|
5
|
+
/** The person a test signs in; each field left out takes {@link TEST_USER}'s. */
|
|
6
|
+
export interface TestUser {
|
|
7
|
+
id?: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
orgId?: string;
|
|
10
|
+
/** `owner`, `admin` or `member`, as the Gateway signs it. */
|
|
11
|
+
orgRole?: string;
|
|
12
|
+
}
|
|
13
|
+
/** The person a test signs in by default: the owner of `org_test`. */
|
|
14
|
+
export declare const TEST_USER: Readonly<Required<TestUser>>;
|
|
15
|
+
/** The app id {@link testEnv} uses when none is given. */
|
|
16
|
+
export declare const TEST_APP_ID = "test-app";
|
|
17
|
+
/** What an identity is minted for, beside the person. */
|
|
18
|
+
export interface TestIdentityOptions {
|
|
19
|
+
user?: TestUser;
|
|
20
|
+
/** The app the identity is for (the token's audience). Default: {@link TEST_APP_ID}. */
|
|
21
|
+
appId?: string;
|
|
22
|
+
/** Default `["*"]`: a browser session, which holds every scope. */
|
|
23
|
+
scopes?: string[];
|
|
24
|
+
/** Default `"web"`. */
|
|
25
|
+
channel?: IdentityChannel;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The Worker env `everyApp()` needs to trust test identities: the app id,
|
|
29
|
+
* the dev issuer and key, `EVERYAPP_DEV`, and a `GATEWAY_URL`. `overrides`
|
|
30
|
+
* adds the app's own bindings (a fake `DB`, a secret) or replaces any of
|
|
31
|
+
* these.
|
|
32
|
+
*/
|
|
33
|
+
export declare function testEnv<T extends object = Record<never, never>>(overrides?: {
|
|
34
|
+
appId?: string;
|
|
35
|
+
} & T): {
|
|
36
|
+
EVERYAPP_APP_ID: string;
|
|
37
|
+
EVERYAPP_DEV: string;
|
|
38
|
+
EVERYAPP_IDENTITY_ISSUER: string;
|
|
39
|
+
EVERYAPP_IDENTITY_PUBLIC_KEYS: string;
|
|
40
|
+
GATEWAY_URL: string;
|
|
41
|
+
} & Omit<T, "appId">;
|
|
42
|
+
/**
|
|
43
|
+
* An `ExecutionContext` for calling a Worker's `fetch` in a test:
|
|
44
|
+
* `waitUntil` keeps each promise in `waited`, so a test can await them.
|
|
45
|
+
*/
|
|
46
|
+
export declare function testContext(): ExecutionContext & {
|
|
47
|
+
waited: Promise<unknown>[];
|
|
48
|
+
};
|
|
49
|
+
/** An identity token for `user`, as the Gateway sends in the identity header. */
|
|
50
|
+
export declare function mintTestIdentity(options?: TestIdentityOptions & {
|
|
51
|
+
toolCall?: {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
};
|
|
55
|
+
}): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* A request as the Gateway forwards it: `user` signed in (the default
|
|
58
|
+
* person when left out), or nobody with `user: null`, as on a public route.
|
|
59
|
+
*/
|
|
60
|
+
export declare function testRequest(input: string | URL, init?: RequestInit & Omit<TestIdentityOptions, "user"> & {
|
|
61
|
+
user?: TestUser | null;
|
|
62
|
+
}): Promise<Request>;
|
|
63
|
+
/** A Worker as `everyApp()` returns it. */
|
|
64
|
+
export interface TestWorker {
|
|
65
|
+
fetch(request: Request, env: never, ctx: ExecutionContext): Response | Promise<Response>;
|
|
66
|
+
}
|
|
67
|
+
export interface CallToolOptions {
|
|
68
|
+
user?: TestUser;
|
|
69
|
+
/**
|
|
70
|
+
* The caller's scopes. Default: `mcp:read`, `mcp:write` and
|
|
71
|
+
* `mcp:destructive`, so every tool may be called; pass fewer to test a
|
|
72
|
+
* refusal.
|
|
73
|
+
*/
|
|
74
|
+
scopes?: string[];
|
|
75
|
+
/** Default: {@link testEnv}() for the worker's app. */
|
|
76
|
+
env?: object;
|
|
77
|
+
ctx?: ExecutionContext;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Call one tool as the Gateway does for an AI client: a `chan: "mcp"`
|
|
81
|
+
* identity minted for this call, through the app's tools endpoint, so the
|
|
82
|
+
* SDK checks the scope, validates input and output and maps errors as it
|
|
83
|
+
* does deployed. `app` is the Worker `everyApp()` returned, or the
|
|
84
|
+
* `defineTools()` set alone. The answer is `{ result }`, or `{ error }`
|
|
85
|
+
* when the SDK refused the call.
|
|
86
|
+
*/
|
|
87
|
+
export declare function callTool(app: TestWorker | ToolSet, name: string, args?: Record<string, unknown>, options?: CallToolOptions): Promise<ToolsCallResponseV1>;
|
|
88
|
+
/** The tool catalog, read as the Gateway reads it when the app deploys. */
|
|
89
|
+
export declare function listTools(app: TestWorker | ToolSet, options?: {
|
|
90
|
+
env?: object;
|
|
91
|
+
ctx?: ExecutionContext;
|
|
92
|
+
}): Promise<ToolsListResponseV1>;
|
|
93
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @every-app/sdk/testing — an app's tests, without a Gateway: a signed-in
|
|
3
|
+
* person for a request, and the tools called the way the Gateway calls them.
|
|
4
|
+
*
|
|
5
|
+
* import { callTool, testContext, testEnv, testRequest } from "@every-app/sdk/testing";
|
|
6
|
+
*
|
|
7
|
+
* const worker = everyApp(handler, { tools });
|
|
8
|
+
* const response = await worker.fetch(
|
|
9
|
+
* await testRequest("https://todo.test/api/todos", { user: { id: "ana" } }),
|
|
10
|
+
* testEnv(),
|
|
11
|
+
* testContext(),
|
|
12
|
+
* );
|
|
13
|
+
* const result = await callTool(worker, "list_todos", {}, { user: { id: "ana" } });
|
|
14
|
+
*
|
|
15
|
+
* Identities are signed with the well-known dev keypair, which `everyApp()`
|
|
16
|
+
* trusts only with `EVERYAPP_DEV` set, as {@link testEnv} sets it: never in a
|
|
17
|
+
* deployed Worker. For tests only; a Worker never imports this module.
|
|
18
|
+
*/
|
|
19
|
+
import { DEV_ISSUER, DEV_PRIVATE_KEY_PEM, DEV_PUBLIC_KEY_PEM, } from "../dev/keypair.js";
|
|
20
|
+
import { APP_ID_ENV, AUDIENCE_ENV, IDENTITY_DEV_KEY_ID, IDENTITY_HEADER, MCP_DESTRUCTIVE_SCOPE, MCP_READ_SCOPE, MCP_WRITE_SCOPE, SYSTEM_HEADER, TOOLS_CALL_PATH, TOOLS_LIST_PATH, mintIdentityJwt, mintSystemJwt, } from "../internal/index.js";
|
|
21
|
+
import { everyApp } from "../server/everyApp.js";
|
|
22
|
+
export { IDENTITY_HEADER };
|
|
23
|
+
/** The person a test signs in by default: the owner of `org_test`. */
|
|
24
|
+
export const TEST_USER = Object.freeze({
|
|
25
|
+
id: "user_test",
|
|
26
|
+
email: "test@example.com",
|
|
27
|
+
orgId: "org_test",
|
|
28
|
+
orgRole: "owner",
|
|
29
|
+
});
|
|
30
|
+
/** The app id {@link testEnv} uses when none is given. */
|
|
31
|
+
export const TEST_APP_ID = "test-app";
|
|
32
|
+
/**
|
|
33
|
+
* The Worker env `everyApp()` needs to trust test identities: the app id,
|
|
34
|
+
* the dev issuer and key, `EVERYAPP_DEV`, and a `GATEWAY_URL`. `overrides`
|
|
35
|
+
* adds the app's own bindings (a fake `DB`, a secret) or replaces any of
|
|
36
|
+
* these.
|
|
37
|
+
*/
|
|
38
|
+
export function testEnv(overrides = {}) {
|
|
39
|
+
const { appId = TEST_APP_ID, ...rest } = overrides;
|
|
40
|
+
return {
|
|
41
|
+
EVERYAPP_APP_ID: appId,
|
|
42
|
+
EVERYAPP_DEV: "1",
|
|
43
|
+
EVERYAPP_IDENTITY_ISSUER: DEV_ISSUER,
|
|
44
|
+
EVERYAPP_IDENTITY_PUBLIC_KEYS: JSON.stringify([DEV_PUBLIC_KEY_PEM]),
|
|
45
|
+
GATEWAY_URL: "https://gateway.test",
|
|
46
|
+
...rest,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* An `ExecutionContext` for calling a Worker's `fetch` in a test:
|
|
51
|
+
* `waitUntil` keeps each promise in `waited`, so a test can await them.
|
|
52
|
+
*/
|
|
53
|
+
export function testContext() {
|
|
54
|
+
const waited = [];
|
|
55
|
+
return {
|
|
56
|
+
waited,
|
|
57
|
+
waitUntil: (promise) => void waited.push(promise),
|
|
58
|
+
passThroughOnException: () => { },
|
|
59
|
+
props: {},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** An identity token for `user`, as the Gateway sends in the identity header. */
|
|
63
|
+
export async function mintTestIdentity(options = {}) {
|
|
64
|
+
const user = { ...TEST_USER, ...options.user };
|
|
65
|
+
return mintIdentityJwt(DEV_PRIVATE_KEY_PEM, {
|
|
66
|
+
subject: {
|
|
67
|
+
sub: user.id,
|
|
68
|
+
email: user.email,
|
|
69
|
+
orgId: user.orgId,
|
|
70
|
+
orgRole: user.orgRole,
|
|
71
|
+
},
|
|
72
|
+
audience: options.appId ?? TEST_APP_ID,
|
|
73
|
+
issuer: DEV_ISSUER,
|
|
74
|
+
keyId: IDENTITY_DEV_KEY_ID,
|
|
75
|
+
channel: options.channel,
|
|
76
|
+
scopes: options.scopes,
|
|
77
|
+
toolCall: options.toolCall,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A request as the Gateway forwards it: `user` signed in (the default
|
|
82
|
+
* person when left out), or nobody with `user: null`, as on a public route.
|
|
83
|
+
*/
|
|
84
|
+
export async function testRequest(input, init = {}) {
|
|
85
|
+
const { user, appId, scopes, channel, ...requestInit } = init;
|
|
86
|
+
const headers = new Headers(requestInit.headers);
|
|
87
|
+
if (user !== null) {
|
|
88
|
+
headers.set(IDENTITY_HEADER, await mintTestIdentity({ user, appId, scopes, channel }));
|
|
89
|
+
}
|
|
90
|
+
return new Request(input, { ...requestInit, headers });
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Call one tool as the Gateway does for an AI client: a `chan: "mcp"`
|
|
94
|
+
* identity minted for this call, through the app's tools endpoint, so the
|
|
95
|
+
* SDK checks the scope, validates input and output and maps errors as it
|
|
96
|
+
* does deployed. `app` is the Worker `everyApp()` returned, or the
|
|
97
|
+
* `defineTools()` set alone. The answer is `{ result }`, or `{ error }`
|
|
98
|
+
* when the SDK refused the call.
|
|
99
|
+
*/
|
|
100
|
+
export async function callTool(app, name, args = {}, options = {}) {
|
|
101
|
+
const env = options.env ?? testEnv();
|
|
102
|
+
const callId = crypto.randomUUID();
|
|
103
|
+
const identity = await mintTestIdentity({
|
|
104
|
+
user: options.user,
|
|
105
|
+
appId: audienceOf(env),
|
|
106
|
+
channel: "mcp",
|
|
107
|
+
scopes: options.scopes ?? [
|
|
108
|
+
MCP_READ_SCOPE,
|
|
109
|
+
MCP_WRITE_SCOPE,
|
|
110
|
+
MCP_DESTRUCTIVE_SCOPE,
|
|
111
|
+
],
|
|
112
|
+
toolCall: { id: callId, name },
|
|
113
|
+
});
|
|
114
|
+
const response = await workerOf(app).fetch(new Request(`https://app.test${TOOLS_CALL_PATH}`, {
|
|
115
|
+
method: "POST",
|
|
116
|
+
headers: { [IDENTITY_HEADER]: identity },
|
|
117
|
+
body: JSON.stringify({ v: 1, name, arguments: args, callId }),
|
|
118
|
+
}), env, options.ctx ?? testContext());
|
|
119
|
+
return (await response.json());
|
|
120
|
+
}
|
|
121
|
+
/** The tool catalog, read as the Gateway reads it when the app deploys. */
|
|
122
|
+
export async function listTools(app, options = {}) {
|
|
123
|
+
const env = options.env ?? testEnv();
|
|
124
|
+
const token = await mintSystemJwt(DEV_PRIVATE_KEY_PEM, {
|
|
125
|
+
audience: audienceOf(env),
|
|
126
|
+
issuer: DEV_ISSUER,
|
|
127
|
+
purpose: "tools.list",
|
|
128
|
+
keyId: IDENTITY_DEV_KEY_ID,
|
|
129
|
+
});
|
|
130
|
+
const response = await workerOf(app).fetch(new Request(`https://app.test${TOOLS_LIST_PATH}`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: { [SYSTEM_HEADER]: token },
|
|
133
|
+
body: JSON.stringify({ v: 1 }),
|
|
134
|
+
}), env, options.ctx ?? testContext());
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
throw new Error(`the tools list answered ${response.status}: ${await response.text()}`);
|
|
137
|
+
}
|
|
138
|
+
return (await response.json());
|
|
139
|
+
}
|
|
140
|
+
/** Who the app's identities are minted for: the deployed audience, else the app id. */
|
|
141
|
+
function audienceOf(env) {
|
|
142
|
+
const values = env;
|
|
143
|
+
for (const key of [AUDIENCE_ENV, APP_ID_ENV]) {
|
|
144
|
+
const value = values[key];
|
|
145
|
+
if (typeof value === "string" && value)
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
return TEST_APP_ID;
|
|
149
|
+
}
|
|
150
|
+
function workerOf(app) {
|
|
151
|
+
if ("fetch" in app)
|
|
152
|
+
return app;
|
|
153
|
+
return everyApp(() => new Response("Not found", { status: 404 }), {
|
|
154
|
+
tools: app,
|
|
155
|
+
});
|
|
156
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@every-app/sdk",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
7
11
|
"./server": {
|
|
8
12
|
"types": "./dist/server/index.d.ts",
|
|
9
13
|
"default": "./dist/server/index.js"
|
|
@@ -24,27 +28,29 @@
|
|
|
24
28
|
"types": "./dist/dev/index.d.ts",
|
|
25
29
|
"default": "./dist/dev/index.js"
|
|
26
30
|
},
|
|
31
|
+
"./tanstack-start": {
|
|
32
|
+
"types": "./dist/tanstack-start/index.d.ts",
|
|
33
|
+
"default": "./dist/tanstack-start/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./testing": {
|
|
36
|
+
"types": "./dist/testing/index.d.ts",
|
|
37
|
+
"default": "./dist/testing/index.js"
|
|
38
|
+
},
|
|
27
39
|
"./package.json": "./package.json"
|
|
28
40
|
},
|
|
29
41
|
"files": [
|
|
30
42
|
"dist",
|
|
31
43
|
"README.md"
|
|
32
44
|
],
|
|
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
45
|
"dependencies": {
|
|
44
46
|
"jose": "^6.2.5"
|
|
45
47
|
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"@tanstack/react-start": "^1.168.61"
|
|
50
|
+
},
|
|
46
51
|
"devDependencies": {
|
|
47
52
|
"@cloudflare/workers-types": "^4.20251014.0",
|
|
53
|
+
"@tanstack/react-start": "1.168.61",
|
|
48
54
|
"@types/node": "^22.18.13",
|
|
49
55
|
"prettier": "^3.6.2",
|
|
50
56
|
"typescript": "^5.9.3",
|
|
@@ -52,5 +58,21 @@
|
|
|
52
58
|
"vite-tsconfig-paths": "^5.1.4",
|
|
53
59
|
"vitest": "^3.2.6",
|
|
54
60
|
"zod": "^4.2.0"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"@tanstack/react-start": {
|
|
64
|
+
"optional": true
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"scripts": {
|
|
68
|
+
"prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
69
|
+
"build": "tsc -p tsconfig.build.json",
|
|
70
|
+
"types:check": "tsc --noEmit",
|
|
71
|
+
"format:check": "prettier --check .",
|
|
72
|
+
"format:write": "prettier . --write",
|
|
73
|
+
"test": "vitest run --silent",
|
|
74
|
+
"test:watch": "vitest",
|
|
75
|
+
"check:esm-relative-imports": "node ./scripts/check-esm-relative-imports.mjs",
|
|
76
|
+
"catalog:update": "UPDATE_CATALOG=1 vitest run src/cloudflare/catalogSettings.test.ts --silent"
|
|
55
77
|
}
|
|
56
|
-
}
|
|
78
|
+
}
|