@cargo-ai/worker-sdk 1.0.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.
Files changed (38) hide show
  1. package/README.md +90 -0
  2. package/build/src/createWorker.d.ts +51 -0
  3. package/build/src/createWorker.d.ts.map +1 -0
  4. package/build/src/createWorker.js +44 -0
  5. package/build/src/customIntegration.d.ts +97 -0
  6. package/build/src/customIntegration.d.ts.map +1 -0
  7. package/build/src/customIntegration.js +212 -0
  8. package/build/src/index.d.ts +9 -0
  9. package/build/src/index.d.ts.map +1 -0
  10. package/build/src/index.js +4 -0
  11. package/build/src/types.d.ts +15 -0
  12. package/build/src/types.d.ts.map +1 -0
  13. package/build/src/types.js +1 -0
  14. package/build/tsconfig.tsbuildinfo +1 -0
  15. package/package.json +47 -0
  16. package/templates/blank/README.md +112 -0
  17. package/templates/blank/manifest.json +3 -0
  18. package/templates/blank/package.json +20 -0
  19. package/templates/blank/scripts/copy-runtime-files.mjs +25 -0
  20. package/templates/blank/src/index.ts +46 -0
  21. package/templates/blank/tsconfig.json +24 -0
  22. package/templates/custom-integration/.env.example +5 -0
  23. package/templates/custom-integration/README.md +136 -0
  24. package/templates/custom-integration/manifest.json +3 -0
  25. package/templates/custom-integration/package.json +21 -0
  26. package/templates/custom-integration/scripts/copy-runtime-files.mjs +25 -0
  27. package/templates/custom-integration/src/actions/createRecord.ts +34 -0
  28. package/templates/custom-integration/src/actions/index.ts +20 -0
  29. package/templates/custom-integration/src/authenticate.ts +27 -0
  30. package/templates/custom-integration/src/autocompletes/index.ts +13 -0
  31. package/templates/custom-integration/src/completeOauth.ts +10 -0
  32. package/templates/custom-integration/src/connectorConfig.ts +16 -0
  33. package/templates/custom-integration/src/dynamicSchemas/index.ts +13 -0
  34. package/templates/custom-integration/src/extractors/index.ts +18 -0
  35. package/templates/custom-integration/src/extractors/listRecords.ts +51 -0
  36. package/templates/custom-integration/src/index.ts +44 -0
  37. package/templates/custom-integration/src/listUsers.ts +11 -0
  38. package/templates/custom-integration/tsconfig.json +24 -0
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @cargo-ai/worker-sdk
2
+
3
+ Build Cargo Hosting **workers** — serverless edge HTTP handlers running on `*.worker.getcargo.io` — with automatic OpenAPI 3.1 generation. Hono + Chanfana + Zod are wrapped in two opinionated factories so you define routes once and get runtime validation, a Cargo-native manifest, and live OpenAPI docs for free.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @cargo-ai/worker-sdk
9
+ ```
10
+
11
+ ## Write a worker
12
+
13
+ ```ts
14
+ import { type Context, createWorker, OpenAPIRoute } from "@cargo-ai/worker-sdk";
15
+ import { z } from "zod";
16
+
17
+ const { app, openapi } = createWorker({ title: "my-worker" });
18
+
19
+ class GetHello extends OpenAPIRoute {
20
+ override schema = {
21
+ request: {
22
+ query: z.object({ name: z.string().default("world") }),
23
+ },
24
+ responses: {
25
+ "200": {
26
+ description: "Greeting.",
27
+ content: {
28
+ "application/json": {
29
+ schema: z.object({ message: z.string() }),
30
+ },
31
+ },
32
+ },
33
+ },
34
+ };
35
+
36
+ override async handle(c: Context): Promise<Response> {
37
+ const { query } = await this.getValidatedData<typeof this.schema>();
38
+ return c.json({ message: `Hello, ${query.name}!` });
39
+ }
40
+ }
41
+
42
+ openapi.get("/hello", GetHello);
43
+
44
+ export default app;
45
+ ```
46
+
47
+ `createWorker` gives you:
48
+
49
+ - `GET /openapi.json` — OpenAPI 3.1 spec derived from every route's Zod schema.
50
+ - `GET /docs` — Swagger UI.
51
+
52
+ ## Write a custom integration
53
+
54
+ `createCustomIntegration({ info, connector, authenticate, actions, extractors, ... })` builds a worker that implements the [Cargo Custom Integration HTTP contract](https://docs.getcargo.ai/integration/custom-integration) from a single declarative spec. The same Zod schemas feed `GET /manifest` (Cargo-native wire format) and `GET /openapi.json`. See `templates/custom-integration/`.
55
+
56
+ Integration-domain types (`IntegrationActionExecutePayload`, `IntegrationExtractorFetchResult`, …) live in [`@cargo-ai/types`](https://www.npmjs.com/package/@cargo-ai/types) under the `ConnectionTypes` namespace — you rarely need to import them directly because the SDK's `CustomIntegrationAction` / `CustomIntegrationExtractor` types wire them in for you.
57
+
58
+ ## Scaffold a project
59
+
60
+ You almost never want to wire this from scratch. Use the CLI:
61
+
62
+ ```sh
63
+ # Blank edge handler
64
+ npx @cargo-ai/cli hosting worker init my-worker --template blank
65
+
66
+ # Worker that implements the Cargo Custom Integration HTTP contract
67
+ npx @cargo-ai/cli hosting worker init my-integration --template custom-integration
68
+ ```
69
+
70
+ The CLI copies the matching template from `@cargo-ai/worker-sdk/templates/<slug>/` into your target directory.
71
+
72
+ ## Build & deploy
73
+
74
+ Templates ship with a `build` script that runs `tsc` and copies `manifest.json`, `package.json`, and `package-lock.json` into `dist/`. The bundle uploaded to Cargo Hosting is the contents of `dist/`:
75
+
76
+ ```sh
77
+ npm install
78
+ npm run build
79
+ cargo-ai hosting deployment create --worker-uuid <workerUuid> --source ./dist
80
+ cargo-ai hosting deployment promote --uuid <deploymentUuid>
81
+ ```
82
+
83
+ The Cargo Hosting build pipeline runs `npm ci` against `dist/package.json` and `esbuild dist/index.js --bundle --format=esm --platform=neutral --target=es2022` to produce the final edge bundle. So you can ship multi-file TypeScript projects — esbuild will tree-shake everything into a single ESM file at deploy time.
84
+
85
+ ## Available templates
86
+
87
+ - `blank` — a ready-to-extend Hono app built by `createWorker()`, with one sample route and `/openapi.json` + `/docs` out of the box. Use this when the worker just responds to inbound HTTP and doesn't need to integrate into the Cargo connector catalog.
88
+ - `custom-integration` — a ready-to-extend spec passed to `createCustomIntegration()`. Handles `/manifest`, `/openapi.json`, `/docs`, `/authenticate`, `/actions/<slug>/execute`, `/extractors/<slug>/{fetch,count}`, `/autocompletes/<slug>`, `/dynamicSchemas/<slug>`, `/completeOauth`, `/listUsers` from a single declaration. Edge-native equivalent of [`getcargohq/dummy-integration`](https://github.com/getcargohq/dummy-integration). After deploying, register it as a connector with `cargo-ai connection custom-integration create --kind worker --worker-uuid <workerUuid>`.
89
+
90
+ See each template's own `README.md` for the file layout and how to extend it.
@@ -0,0 +1,51 @@
1
+ import { type HonoOpenAPIRouterType } from "chanfana";
2
+ import { Hono } from "hono";
3
+ import type { WorkerEnv } from "./types.js";
4
+ export interface CreateWorkerOptions {
5
+ /** Title shown in the generated OpenAPI spec. */
6
+ title: string;
7
+ /** Defaults to `"0.0.1"`. */
8
+ version?: string;
9
+ /** Optional long description shown in the generated OpenAPI spec. */
10
+ description?: string;
11
+ /** Path where Swagger UI is served. Defaults to `/docs`. */
12
+ docsUrl?: string;
13
+ /** Path where the OpenAPI 3.1 document is served. Defaults to `/openapi.json`. */
14
+ openapiUrl?: string;
15
+ }
16
+ export type WorkerApp<Env extends WorkerEnv = WorkerEnv> = Hono<{
17
+ Bindings: Env;
18
+ }>;
19
+ export type WorkerOpenAPI<Env extends WorkerEnv = WorkerEnv> = HonoOpenAPIRouterType<{
20
+ Bindings: Env;
21
+ }>;
22
+ /**
23
+ * Creates a Hono app wrapped with Chanfana so every route you register on the
24
+ * returned `openapi` handle is automatically described in the generated
25
+ * OpenAPI 3.1 spec served at `openapiUrl` (and browsable at `docsUrl`).
26
+ *
27
+ * `app` and `openapi` are two views of the same router: use `app` to add
28
+ * un-described Hono middleware / routes, and use `openapi` to register typed
29
+ * endpoints that extend `OpenAPIRoute`.
30
+ *
31
+ * ```ts
32
+ * import { createWorker, OpenAPIRoute } from "@cargo-ai/worker-sdk";
33
+ * import { z } from "zod";
34
+ *
35
+ * const { app, openapi } = createWorker({ title: "my-worker" });
36
+ *
37
+ * class GetHello extends OpenAPIRoute {
38
+ * schema = { ... };
39
+ * async handle(c) { ... }
40
+ * }
41
+ *
42
+ * openapi.get("/hello", GetHello);
43
+ *
44
+ * export default app;
45
+ * ```
46
+ */
47
+ export declare const createWorker: <Env extends WorkerEnv = WorkerEnv>(options: CreateWorkerOptions) => {
48
+ app: WorkerApp<Env>;
49
+ openapi: WorkerOpenAPI<Env>;
50
+ };
51
+ //# sourceMappingURL=createWorker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createWorker.d.ts","sourceRoot":"","sources":["../../src/createWorker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,SAAS,CAAC,GAAG,SAAS,SAAS,GAAG,SAAS,IAAI,IAAI,CAAC;IAC9D,QAAQ,EAAE,GAAG,CAAC;CACf,CAAC,CAAC;AAEH,MAAM,MAAM,aAAa,CAAC,GAAG,SAAS,SAAS,GAAG,SAAS,IACzD,qBAAqB,CAAC;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,YAAY,+CACd,mBAAmB;;;CAoB7B,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { fromHono } from "chanfana";
2
+ import { Hono } from "hono";
3
+ /**
4
+ * Creates a Hono app wrapped with Chanfana so every route you register on the
5
+ * returned `openapi` handle is automatically described in the generated
6
+ * OpenAPI 3.1 spec served at `openapiUrl` (and browsable at `docsUrl`).
7
+ *
8
+ * `app` and `openapi` are two views of the same router: use `app` to add
9
+ * un-described Hono middleware / routes, and use `openapi` to register typed
10
+ * endpoints that extend `OpenAPIRoute`.
11
+ *
12
+ * ```ts
13
+ * import { createWorker, OpenAPIRoute } from "@cargo-ai/worker-sdk";
14
+ * import { z } from "zod";
15
+ *
16
+ * const { app, openapi } = createWorker({ title: "my-worker" });
17
+ *
18
+ * class GetHello extends OpenAPIRoute {
19
+ * schema = { ... };
20
+ * async handle(c) { ... }
21
+ * }
22
+ *
23
+ * openapi.get("/hello", GetHello);
24
+ *
25
+ * export default app;
26
+ * ```
27
+ */
28
+ export const createWorker = (options) => {
29
+ const app = new Hono();
30
+ const openapi = fromHono(app, {
31
+ docs_url: options.docsUrl !== undefined ? options.docsUrl : "/docs",
32
+ openapi_url: options.openapiUrl !== undefined ? options.openapiUrl : "/openapi.json",
33
+ schema: {
34
+ info: {
35
+ title: options.title,
36
+ version: options.version !== undefined ? options.version : "0.0.1",
37
+ ...(options.description !== undefined
38
+ ? { description: options.description }
39
+ : {}),
40
+ },
41
+ },
42
+ });
43
+ return { app, openapi };
44
+ };
@@ -0,0 +1,97 @@
1
+ import type { ConnectionTypes } from "@cargo-ai/types";
2
+ import { z } from "zod";
3
+ import { type WorkerApp, type WorkerOpenAPI } from "./createWorker.js";
4
+ import type { WorkerEnv } from "./types.js";
5
+ /**
6
+ * A single Custom Integration action — e.g. "Create Record". The zod
7
+ * `ConfigSchema` is the source of truth for the action's per-invocation config
8
+ * and drives:
9
+ * - runtime validation of `payload.config`
10
+ * - the JSON Schema exposed at `GET /manifest#actions[slug].config.jsonSchema`
11
+ * - the OpenAPI schema for `POST /actions/<slug>/execute`
12
+ */
13
+ export interface CustomIntegrationAction<ConnectorConfig, ConfigSchema extends z.ZodType = z.ZodType> {
14
+ name: string;
15
+ description: string;
16
+ config: {
17
+ schema: ConfigSchema;
18
+ uiSchema?: Record<string, unknown>;
19
+ };
20
+ execute: (payload: ConnectionTypes.IntegrationActionExecutePayload<ConnectorConfig, z.infer<ConfigSchema>>) => Promise<ConnectionTypes.IntegrationActionExecuteResult>;
21
+ }
22
+ /**
23
+ * A single Custom Integration extractor — e.g. "List Records". Both
24
+ * `ConfigSchema` and `MetaSchema` are source-of-truth for the manifest +
25
+ * OpenAPI + runtime validation.
26
+ */
27
+ export interface CustomIntegrationExtractor<ConnectorConfig, ConfigSchema extends z.ZodType = z.ZodType, MetaSchema extends z.ZodType = z.ZodType> {
28
+ name: string;
29
+ description: string;
30
+ config: {
31
+ schema: ConfigSchema;
32
+ uiSchema?: Record<string, unknown>;
33
+ };
34
+ meta: {
35
+ schema: MetaSchema;
36
+ };
37
+ mode: ConnectionTypes.IntegrationExtractorMode;
38
+ preview: "none" | "records" | "count";
39
+ fetch: (payload: ConnectionTypes.IntegrationExtractorFetchPayload<ConnectorConfig, z.infer<ConfigSchema>, z.infer<MetaSchema>>) => Promise<ConnectionTypes.IntegrationExtractorFetchResult<z.infer<MetaSchema>>>;
40
+ count: (payload: ConnectionTypes.IntegrationExtractorCountPayload<ConnectorConfig, z.infer<ConfigSchema>>) => Promise<ConnectionTypes.IntegrationExtractorCountResult>;
41
+ }
42
+ export type CustomIntegrationAutocomplete<ConnectorConfig> = (payload: ConnectionTypes.IntegrationAutocompletePayload<ConnectorConfig>) => Promise<ConnectionTypes.IntegrationAutocompleteResult>;
43
+ export type CustomIntegrationDynamicSchema<ConnectorConfig> = (payload: ConnectionTypes.IntegrationDynamicSchemaGetPayload<ConnectorConfig>) => Promise<{
44
+ jsonSchema: Record<string, unknown>;
45
+ uiSchema?: Record<string, unknown>;
46
+ }>;
47
+ export interface CustomIntegration<ConnectorConfigSchema extends z.ZodType> {
48
+ info: {
49
+ name: string;
50
+ description: string;
51
+ icon?: string;
52
+ color?: string;
53
+ url?: string;
54
+ version?: string;
55
+ };
56
+ connector: {
57
+ config: {
58
+ schema: ConnectorConfigSchema;
59
+ uiSchema?: Record<string, unknown>;
60
+ };
61
+ rateLimit?: {
62
+ unit: "second" | "minute" | "hour";
63
+ max: number;
64
+ };
65
+ };
66
+ authenticate: (payload: ConnectionTypes.IntegrationAuthenticatePayload<z.infer<ConnectorConfigSchema>>) => Promise<ConnectionTypes.IntegrationAuthenticateResult>;
67
+ listUsers?: (payload: ConnectionTypes.IntegrationListUsersPayload<z.infer<ConnectorConfigSchema>>) => Promise<ConnectionTypes.IntegrationUserData[]>;
68
+ completeOauth?: (payload: ConnectionTypes.IntegrationCompleteOauthPayload) => Promise<ConnectionTypes.IntegrationCompleteOauthResult>;
69
+ actions?: Record<string, CustomIntegrationAction<z.infer<ConnectorConfigSchema>, any>>;
70
+ extractors?: Record<string, CustomIntegrationExtractor<z.infer<ConnectorConfigSchema>, any, any>>;
71
+ autocompletes?: Record<string, CustomIntegrationAutocomplete<z.infer<ConnectorConfigSchema>>>;
72
+ dynamicSchemas?: Record<string, CustomIntegrationDynamicSchema<z.infer<ConnectorConfigSchema>>>;
73
+ }
74
+ /**
75
+ * Build a Cargo Custom Integration worker from a declarative spec. Produces a
76
+ * Hono app that serves:
77
+ *
78
+ * - `GET /manifest` — Cargo-native connector manifest
79
+ * (actions/extractors/autocompletes/dynamicSchemas with JSON Schema configs).
80
+ * - `GET /openapi.json` + `GET /docs` — OpenAPI 3.1 spec + Swagger UI
81
+ * derived from the same Zod schemas.
82
+ * - `POST /authenticate` — runs `spec.authenticate`.
83
+ * - `POST /listUsers` — runs `spec.listUsers` (if set).
84
+ * - `POST /completeOauth` — runs `spec.completeOauth` (if set).
85
+ * - `POST /actions/<slug>/execute` — one route per action.
86
+ * - `POST /extractors/<slug>/{fetch,count}` — two routes per extractor.
87
+ * - `POST /autocompletes/<slug>` — one route per autocomplete.
88
+ * - `POST /dynamicSchemas/<slug>` — one route per dynamic schema.
89
+ *
90
+ * Per-handler request `config` payloads are validated against their Zod
91
+ * schemas; validation errors respond 400 with Chanfana's standard format.
92
+ */
93
+ export declare const createCustomIntegration: <ConnectorConfigSchema extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, Env extends WorkerEnv = WorkerEnv>(spec: CustomIntegration<ConnectorConfigSchema>) => {
94
+ app: WorkerApp<Env>;
95
+ openapi: WorkerOpenAPI<Env>;
96
+ };
97
+ //# sourceMappingURL=customIntegration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"customIntegration.d.ts","sourceRoot":"","sources":["../../src/customIntegration.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,212 @@
1
+ import { OpenAPIRoute } from "chanfana";
2
+ import { z } from "zod";
3
+ import { createWorker, } from "./createWorker.js";
4
+ // --- Route helpers -----------------------------------------------------------
5
+ // The Cargo backend controls the envelope shape, so we don't re-declare every
6
+ // field here — we just accept the envelope permissively and let the action /
7
+ // extractor Zod schemas validate the one field the worker author owns.
8
+ const looseEnvelope = z.object({}).passthrough();
9
+ const jsonResponse = (schema) => ({
10
+ "200": {
11
+ description: "OK",
12
+ content: { "application/json": { schema } },
13
+ },
14
+ });
15
+ const jsonBody = (schema) => ({
16
+ content: { "application/json": { schema } },
17
+ });
18
+ const makeRoute = (schema, handle) => {
19
+ return class extends OpenAPIRoute {
20
+ schema = schema;
21
+ async handle(c) {
22
+ return handle(c);
23
+ }
24
+ };
25
+ };
26
+ // --- Manifest builder --------------------------------------------------------
27
+ const buildManifest = (spec) => {
28
+ return {
29
+ name: spec.info.name,
30
+ description: spec.info.description,
31
+ ...(spec.info.icon !== undefined ? { icon: spec.info.icon } : {}),
32
+ ...(spec.info.color !== undefined ? { color: spec.info.color } : {}),
33
+ ...(spec.info.url !== undefined ? { url: spec.info.url } : {}),
34
+ connector: {
35
+ config: {
36
+ jsonSchema: z.toJSONSchema(spec.connector.config.schema),
37
+ ...(spec.connector.config.uiSchema !== undefined
38
+ ? { uiSchema: spec.connector.config.uiSchema }
39
+ : {}),
40
+ },
41
+ ...(spec.connector.rateLimit !== undefined
42
+ ? { rateLimit: spec.connector.rateLimit }
43
+ : {}),
44
+ },
45
+ actions: Object.fromEntries(Object.entries(spec.actions !== undefined ? spec.actions : {}).map(([slug, action]) => [
46
+ slug,
47
+ {
48
+ name: action.name,
49
+ description: action.description,
50
+ config: {
51
+ jsonSchema: z.toJSONSchema(action.config.schema),
52
+ ...(action.config.uiSchema !== undefined
53
+ ? { uiSchema: action.config.uiSchema }
54
+ : {}),
55
+ },
56
+ },
57
+ ])),
58
+ extractors: Object.fromEntries(Object.entries(spec.extractors !== undefined ? spec.extractors : {}).map(([slug, extractor]) => [
59
+ slug,
60
+ {
61
+ name: extractor.name,
62
+ description: extractor.description,
63
+ config: {
64
+ jsonSchema: z.toJSONSchema(extractor.config.schema),
65
+ ...(extractor.config.uiSchema !== undefined
66
+ ? { uiSchema: extractor.config.uiSchema }
67
+ : {}),
68
+ },
69
+ mode: extractor.mode,
70
+ preview: extractor.preview,
71
+ },
72
+ ])),
73
+ autocompletes: Object.fromEntries(Object.keys(spec.autocompletes !== undefined ? spec.autocompletes : {}).map((slug) => [
74
+ slug,
75
+ { params: { jsonSchema: {} }, cacheExpirationInSeconds: 300 },
76
+ ])),
77
+ dynamicSchemas: Object.fromEntries(Object.keys(spec.dynamicSchemas !== undefined ? spec.dynamicSchemas : {}).map((slug) => [slug, { params: { jsonSchema: {} } }])),
78
+ };
79
+ };
80
+ // --- createCustomIntegration -------------------------------------------------
81
+ /**
82
+ * Build a Cargo Custom Integration worker from a declarative spec. Produces a
83
+ * Hono app that serves:
84
+ *
85
+ * - `GET /manifest` — Cargo-native connector manifest
86
+ * (actions/extractors/autocompletes/dynamicSchemas with JSON Schema configs).
87
+ * - `GET /openapi.json` + `GET /docs` — OpenAPI 3.1 spec + Swagger UI
88
+ * derived from the same Zod schemas.
89
+ * - `POST /authenticate` — runs `spec.authenticate`.
90
+ * - `POST /listUsers` — runs `spec.listUsers` (if set).
91
+ * - `POST /completeOauth` — runs `spec.completeOauth` (if set).
92
+ * - `POST /actions/<slug>/execute` — one route per action.
93
+ * - `POST /extractors/<slug>/{fetch,count}` — two routes per extractor.
94
+ * - `POST /autocompletes/<slug>` — one route per autocomplete.
95
+ * - `POST /dynamicSchemas/<slug>` — one route per dynamic schema.
96
+ *
97
+ * Per-handler request `config` payloads are validated against their Zod
98
+ * schemas; validation errors respond 400 with Chanfana's standard format.
99
+ */
100
+ export const createCustomIntegration = (spec) => {
101
+ const { app, openapi } = createWorker({
102
+ title: spec.info.name,
103
+ version: spec.info.version !== undefined ? spec.info.version : "0.0.1",
104
+ description: spec.info.description,
105
+ });
106
+ const manifest = buildManifest(spec);
107
+ openapi.get("/manifest", makeRoute({
108
+ tags: ["manifest"],
109
+ summary: "Cargo connector manifest.",
110
+ responses: jsonResponse(z.object({}).passthrough()),
111
+ }, async (c) => c.json(manifest)));
112
+ openapi.post("/authenticate", makeRoute({
113
+ tags: ["connector"],
114
+ summary: "Validate the connector config against the upstream API.",
115
+ request: { body: jsonBody(looseEnvelope) },
116
+ responses: jsonResponse(z.object({}).passthrough()),
117
+ }, async (c) => {
118
+ const payload = (await c.req.json());
119
+ return c.json(await spec.authenticate(payload));
120
+ }));
121
+ if (spec.listUsers !== undefined) {
122
+ const { listUsers } = spec;
123
+ openapi.post("/listUsers", makeRoute({
124
+ tags: ["connector"],
125
+ summary: "List users visible to the connected account.",
126
+ request: { body: jsonBody(looseEnvelope) },
127
+ responses: jsonResponse(z.array(z.object({}).passthrough())),
128
+ }, async (c) => {
129
+ const payload = (await c.req.json());
130
+ return c.json(await listUsers(payload));
131
+ }));
132
+ }
133
+ if (spec.completeOauth !== undefined) {
134
+ const { completeOauth } = spec;
135
+ openapi.post("/completeOauth", makeRoute({
136
+ tags: ["connector"],
137
+ summary: "Exchange OAuth params for a credential.",
138
+ request: { body: jsonBody(looseEnvelope) },
139
+ responses: jsonResponse(z.object({}).passthrough()),
140
+ }, async (c) => {
141
+ const payload = (await c.req.json());
142
+ return c.json(await completeOauth(payload));
143
+ }));
144
+ }
145
+ for (const [slug, action] of Object.entries(spec.actions !== undefined ? spec.actions : {})) {
146
+ const capturedAction = action;
147
+ openapi.post(`/actions/${slug}/execute`, makeRoute({
148
+ tags: ["actions"],
149
+ summary: capturedAction.name,
150
+ description: capturedAction.description,
151
+ request: {
152
+ body: jsonBody(z.object({ config: capturedAction.config.schema }).passthrough()),
153
+ },
154
+ responses: jsonResponse(z.object({}).passthrough()),
155
+ }, async (c) => {
156
+ const payload = (await c.req.json());
157
+ return c.json(await capturedAction.execute(payload));
158
+ }));
159
+ }
160
+ for (const [slug, extractor] of Object.entries(spec.extractors !== undefined ? spec.extractors : {})) {
161
+ const captured = extractor;
162
+ openapi.post(`/extractors/${slug}/fetch`, makeRoute({
163
+ tags: ["extractors"],
164
+ summary: `${captured.name} — fetch`,
165
+ description: captured.description,
166
+ request: {
167
+ body: jsonBody(z.object({ config: captured.config.schema }).passthrough()),
168
+ },
169
+ responses: jsonResponse(z.object({}).passthrough()),
170
+ }, async (c) => {
171
+ const payload = (await c.req.json());
172
+ return c.json(await captured.fetch(payload));
173
+ }));
174
+ openapi.post(`/extractors/${slug}/count`, makeRoute({
175
+ tags: ["extractors"],
176
+ summary: `${captured.name} — count`,
177
+ description: captured.description,
178
+ request: {
179
+ body: jsonBody(z.object({ config: captured.config.schema }).passthrough()),
180
+ },
181
+ responses: jsonResponse(z.object({}).passthrough()),
182
+ }, async (c) => {
183
+ const payload = (await c.req.json());
184
+ return c.json(await captured.count(payload));
185
+ }));
186
+ }
187
+ for (const [slug, handler] of Object.entries(spec.autocompletes !== undefined ? spec.autocompletes : {})) {
188
+ const captured = handler;
189
+ openapi.post(`/autocompletes/${slug}`, makeRoute({
190
+ tags: ["autocompletes"],
191
+ summary: `Autocomplete: ${slug}`,
192
+ request: { body: jsonBody(looseEnvelope) },
193
+ responses: jsonResponse(z.object({}).passthrough()),
194
+ }, async (c) => {
195
+ const payload = (await c.req.json());
196
+ return c.json(await captured(payload));
197
+ }));
198
+ }
199
+ for (const [slug, handler] of Object.entries(spec.dynamicSchemas !== undefined ? spec.dynamicSchemas : {})) {
200
+ const captured = handler;
201
+ openapi.post(`/dynamicSchemas/${slug}`, makeRoute({
202
+ tags: ["dynamicSchemas"],
203
+ summary: `Dynamic schema: ${slug}`,
204
+ request: { body: jsonBody(looseEnvelope) },
205
+ responses: jsonResponse(z.object({}).passthrough()),
206
+ }, async (c) => {
207
+ const payload = (await c.req.json());
208
+ return c.json(await captured(payload));
209
+ }));
210
+ }
211
+ return { app, openapi };
212
+ };
@@ -0,0 +1,9 @@
1
+ export type { CreateWorkerOptions, WorkerApp, WorkerOpenAPI, } from "./createWorker.js";
2
+ export { createWorker } from "./createWorker.js";
3
+ export type { CustomIntegration, CustomIntegrationAction, CustomIntegrationAutocomplete, CustomIntegrationDynamicSchema, CustomIntegrationExtractor, } from "./customIntegration.js";
4
+ export { createCustomIntegration } from "./customIntegration.js";
5
+ export type { WorkerEnv, WorkerManifest } from "./types.js";
6
+ export { fromHono, OpenAPIRoute } from "chanfana";
7
+ export type { Context } from "hono";
8
+ export { Hono } from "hono";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mBAAmB,EACnB,SAAS,EACT,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EACV,iBAAiB,EACjB,uBAAuB,EACvB,6BAA6B,EAC7B,8BAA8B,EAC9B,0BAA0B,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAClD,YAAY,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { createWorker } from "./createWorker.js";
2
+ export { createCustomIntegration } from "./customIntegration.js";
3
+ export { fromHono, OpenAPIRoute } from "chanfana";
4
+ export { Hono } from "hono";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `env` shape passed to every handler by the Cargo Hosting runtime. Keys come
3
+ * from the worker's configured secrets / bindings. Override with a stricter
4
+ * interface in your worker if you want type-safety on individual env values.
5
+ */
6
+ export type WorkerEnv = Record<string, string | undefined>;
7
+ /**
8
+ * `manifest.json` at the root of the deployed bundle. Tells the Cargo Hosting
9
+ * sandbox which outbound hosts the worker is allowed to call (defence in depth
10
+ * — the runtime blocks `fetch(...)` calls to anything not on the allowlist).
11
+ */
12
+ export interface WorkerManifest {
13
+ outboundAllowlist: string[];
14
+ }
15
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B"}
@@ -0,0 +1 @@
1
+ export {};