@keystrokehq/typeform 0.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 (46) hide show
  1. package/README.md +159 -0
  2. package/dist/_official/index.d.mts +3 -0
  3. package/dist/_official/index.mjs +3 -0
  4. package/dist/_runtime/index.d.mts +1 -0
  5. package/dist/_runtime/index.mjs +1 -0
  6. package/dist/accounts.d.mts +17 -0
  7. package/dist/accounts.mjs +20 -0
  8. package/dist/client.d.mts +104 -0
  9. package/dist/client.mjs +308 -0
  10. package/dist/connection.d.mts +2 -0
  11. package/dist/connection.mjs +3 -0
  12. package/dist/events.d.mts +160 -0
  13. package/dist/events.mjs +38 -0
  14. package/dist/factory-B_dyn39A.mjs +8 -0
  15. package/dist/forms.d.mts +390 -0
  16. package/dist/forms.mjs +147 -0
  17. package/dist/images.d.mts +112 -0
  18. package/dist/images.mjs +104 -0
  19. package/dist/index.d.mts +1 -0
  20. package/dist/index.mjs +1 -0
  21. package/dist/insights.d.mts +26 -0
  22. package/dist/insights.mjs +37 -0
  23. package/dist/integration-BCzgn7Dm.mjs +76 -0
  24. package/dist/integration-d_VqlGvZ.d.mts +38 -0
  25. package/dist/messaging.d.mts +1 -0
  26. package/dist/messaging.mjs +1 -0
  27. package/dist/provider-app-BnLMYj3B.d.mts +35 -0
  28. package/dist/responses.d.mts +118 -0
  29. package/dist/responses.mjs +107 -0
  30. package/dist/schemas/index.d.mts +564 -0
  31. package/dist/schemas/index.mjs +3 -0
  32. package/dist/shared-D0ONnQL8.mjs +49 -0
  33. package/dist/themes.d.mts +225 -0
  34. package/dist/themes.mjs +132 -0
  35. package/dist/triggers.d.mts +21 -0
  36. package/dist/triggers.mjs +45 -0
  37. package/dist/verification.d.mts +19 -0
  38. package/dist/verification.mjs +33 -0
  39. package/dist/videos.d.mts +20 -0
  40. package/dist/videos.mjs +29 -0
  41. package/dist/webhooks.d.mts +73 -0
  42. package/dist/webhooks.mjs +79 -0
  43. package/dist/workspace-C1CCnvgv.mjs +271 -0
  44. package/dist/workspaces.d.mts +145 -0
  45. package/dist/workspaces.mjs +119 -0
  46. package/package.json +131 -0
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ # @keystrokehq/typeform
2
+
3
+ Official Keystroke integration for [Typeform](https://www.typeform.com/).
4
+
5
+ Ship code-first automations over Typeform forms, responses, themes, images, workspaces, webhooks, and account metadata, plus a direct-binding webhook trigger that fires when a respondent submits a form.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add @keystrokehq/typeform
11
+ ```
12
+
13
+ ## Connect
14
+
15
+ Typeform supports two authentication modes. Both are handled by the same public connection credential set — a user connecting an account through Keystroke's OAuth flow and a server-to-server install using a Personal Access Token (PAT) share the same runtime contract.
16
+
17
+ ### OAuth (recommended — "click to connect")
18
+
19
+ ```bash
20
+ keystroke connect typeform
21
+ ```
22
+
23
+ This triggers the standard Keystroke OAuth 2.0 flow against `https://api.typeform.com/oauth/authorize`, requests the full read/write scopes plus `offline` (for refresh tokens), and stores the resulting token under the `TYPEFORM_ACCESS_TOKEN` vault key.
24
+
25
+ ### Personal Access Token (server-to-server)
26
+
27
+ Generate a PAT in your Typeform **Personal Tokens** settings and populate the vault entry directly:
28
+
29
+ ```bash
30
+ export TYPEFORM_ACCESS_TOKEN=tfp_xxxxxxxx…
31
+ ```
32
+
33
+ Typeform treats OAuth access tokens and PATs identically at the wire level (`Authorization: Bearer <token>`), so all operations and triggers in this package work against either.
34
+
35
+ ## Public surface
36
+
37
+ Authored workflow code should import **only** these subpaths:
38
+
39
+ | Subpath | What it exports |
40
+ |---|---|
41
+ | `@keystrokehq/typeform/connection` | `typeform` credential set + `TypeformCredentials` type |
42
+ | `@keystrokehq/typeform/client` | `createTypeformClient`, `TypeformApiError`, `createLeakyBucketRateLimiter` |
43
+ | `@keystrokehq/typeform/schemas` | Reusable Zod schemas (`typeformFormSchema`, `typeformResponseSchema`, …) |
44
+ | `@keystrokehq/typeform/events` | Curated trigger event schemas + types |
45
+ | `@keystrokehq/typeform/verification` | `verifyTypeformWebhookRequest`, `buildTypeformWebhookSignature` |
46
+ | `@keystrokehq/typeform/triggers` | `webhooks.formResponseSubmitted(...)` direct binding |
47
+ | `@keystrokehq/typeform/accounts` | `getAboutMe` |
48
+ | `@keystrokehq/typeform/forms` | `listForms`, `getForm`, `createForm`, `updateForm`, `patchForm`, `deleteForm` |
49
+ | `@keystrokehq/typeform/responses` | `getFormResponses`, `deleteResponses`, `getAllResponseFiles` |
50
+ | `@keystrokehq/typeform/themes` | `listThemes`, `getTheme`, `createTheme`, `updateTheme`, `patchTheme`, `deleteTheme` |
51
+ | `@keystrokehq/typeform/images` | `listImages`, `createImage`, `getImageBySize`, `getBackgroundBySize`, `getChoiceImageBySize`, `deleteImage` |
52
+ | `@keystrokehq/typeform/videos` | `uploadVideo` |
53
+ | `@keystrokehq/typeform/workspaces` | `listWorkspaces`, `getWorkspace`, `createWorkspace`, `createAccountWorkspace`, `updateWorkspace`, `deleteWorkspace` |
54
+ | `@keystrokehq/typeform/webhooks` | `listWebhooks`, `getWebhook`, `createOrUpdateWebhook`, `deleteWebhook` |
55
+ | `@keystrokehq/typeform/insights` | `getFormMessages`, `updateFormMessages` |
56
+
57
+ Hidden, repo-only subpaths (`_official`, `_runtime`) are for platform internals — **do not import these from authored code**.
58
+
59
+ ## Quickstart
60
+
61
+ ### Call a Typeform operation inside a workflow
62
+
63
+ ```ts
64
+ import { Workflow } from '@keystrokehq/core';
65
+ import { listForms } from '@keystrokehq/typeform/forms';
66
+ import { z } from 'zod';
67
+
68
+ export const triageNewForms = new Workflow({
69
+ id: 'triage_new_forms',
70
+ name: 'Triage new Typeform forms',
71
+ input: z.object({}),
72
+ output: z.object({ count: z.number() }),
73
+ async run() {
74
+ const { items } = await listForms.run({ pageSize: 20 });
75
+ return { count: items.length };
76
+ },
77
+ });
78
+ ```
79
+
80
+ ### React to form submissions (direct-binding webhook trigger)
81
+
82
+ ```ts
83
+ import { Workflow } from '@keystrokehq/core';
84
+ import { webhooks } from '@keystrokehq/typeform/triggers';
85
+ import { z } from 'zod';
86
+
87
+ export const captureLead = new Workflow({
88
+ id: 'capture_typeform_lead',
89
+ name: 'Capture Lead',
90
+ triggers: [
91
+ webhooks.formResponseSubmitted({
92
+ filter: (event) => event.formId === 'LEADS2026',
93
+ transform: (event) => ({
94
+ formId: event.formId,
95
+ token: event.token,
96
+ email: event.answers?.find((a) => a.field.type === 'email')?.email,
97
+ }),
98
+ }),
99
+ ],
100
+ input: z.object({ formId: z.string(), token: z.string(), email: z.string().optional() }),
101
+ output: z.object({ ok: z.boolean() }),
102
+ async run() {
103
+ return { ok: true };
104
+ },
105
+ });
106
+ ```
107
+
108
+ ## Support surfaces
109
+
110
+ ### Using the raw Typeform client
111
+
112
+ Most workflows should call operations directly. When you need more control (e.g. custom pagination), drop to the client:
113
+
114
+ ```ts
115
+ import { createTypeformClient } from '@keystrokehq/typeform/client';
116
+ import type { TypeformCredentials } from '@keystrokehq/typeform/connection';
117
+
118
+ export async function listAllForms(credentials: TypeformCredentials) {
119
+ const client = createTypeformClient(credentials);
120
+ return client.forms.list({ page_size: 200 });
121
+ }
122
+ ```
123
+
124
+ ### Signature verification
125
+
126
+ Use `verifyTypeformWebhookRequest` when you accept Typeform webhooks on a custom route (the built-in trigger already verifies for you):
127
+
128
+ ```ts
129
+ import { verifyTypeformWebhookRequest } from '@keystrokehq/typeform/verification';
130
+
131
+ const ok = verifyTypeformWebhookRequest(
132
+ { headers: request.headers, rawBody: request.rawBody },
133
+ webhookSigningSecret
134
+ );
135
+ ```
136
+
137
+ ### Provisioning a webhook
138
+
139
+ `createOrUpdateWebhook` wires the integration's shared signing secret onto a new or updated webhook subscription (unsigned webhooks are rejected at the schema boundary):
140
+
141
+ ```ts
142
+ import { createOrUpdateWebhook } from '@keystrokehq/typeform/webhooks';
143
+
144
+ await createOrUpdateWebhook.run({
145
+ formId: 'LEADS2026',
146
+ tag: 'keystroke-capture-lead',
147
+ url: 'https://webhooks.keystroke.dev/typeform',
148
+ secret: process.env.TYPEFORM_WEBHOOK_SECRET!,
149
+ });
150
+ ```
151
+
152
+ ## Caveats
153
+
154
+ - **Single native webhook event.** Typeform only emits `form_response` when a respondent completes a submission. There are no partial-response or form-mutation webhooks; use polling (`getFormResponses` with a cursor) if you need finer granularity.
155
+ - **Signed file URLs expire quickly.** `getAllResponseFiles` returns the signed URL path; fetch the content immediately and, if you need durable storage, re-host it yourself.
156
+ - **PAT vs OAuth parity.** OAuth access tokens refresh automatically through the Keystroke vault. PATs do not; rotate them on the Typeform side and update the vault entry.
157
+ - **Rate limits.** Typeform publishes a baseline of 2 requests/second per token. The client enforces a leaky-bucket limiter by default and retries `429` with `Retry-After`.
158
+ - **Irreversible response deletion.** `deleteResponses` requires an explicit `confirm: true` flag in input to prevent accidental data loss.
159
+ - **Webhook signing is mandatory.** `createOrUpdateWebhook` refuses to provision an unsigned webhook; the trigger likewise fails closed when the internal app credential set has no `webhookSecret`.
@@ -0,0 +1,3 @@
1
+ import { i as typeformOfficialIntegration, r as typeformBundle } from "../integration-d_VqlGvZ.mjs";
2
+ import { n as typeformAppCredentialSet, r as typeformPlatformProviderSeed, t as TypeformAppCredentials } from "../provider-app-BnLMYj3B.mjs";
3
+ export { TypeformAppCredentials, typeformAppCredentialSet, typeformBundle, typeformOfficialIntegration, typeformPlatformProviderSeed };
@@ -0,0 +1,3 @@
1
+ import { a as typeformPlatformProviderSeed, i as typeformAppCredentialSet, n as typeformBundle, r as typeformOfficialIntegration } from "../integration-BCzgn7Dm.mjs";
2
+
3
+ export { typeformAppCredentialSet, typeformBundle, typeformOfficialIntegration, typeformPlatformProviderSeed };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,17 @@
1
+ import { z } from "zod";
2
+ import * as _keystrokehq_core0 from "@keystrokehq/core";
3
+ import * as _keystrokehq_core_credential_set0 from "@keystrokehq/core/credential-set";
4
+
5
+ //#region src/accounts.d.ts
6
+ declare const getAboutMe: _keystrokehq_core0.Operation<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
7
+ alias: z.ZodOptional<z.ZodString>;
8
+ email: z.ZodOptional<z.ZodString>;
9
+ language: z.ZodOptional<z.ZodString>;
10
+ user_id: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$catchall<z.ZodUnknown>>, readonly [_keystrokehq_core0.CredentialSet<"typeform", z.ZodObject<{
12
+ TYPEFORM_ACCESS_TOKEN: z.ZodString;
13
+ }, z.core.$strip>, readonly _keystrokehq_core_credential_set0.CredentialConnection<z.ZodObject<{
14
+ TYPEFORM_ACCESS_TOKEN: z.ZodString;
15
+ }, z.core.$strip>>[] | undefined>], undefined>;
16
+ //#endregion
17
+ export { getAboutMe };
@@ -0,0 +1,20 @@
1
+ import { createTypeformClient } from "./client.mjs";
2
+ import { w as typeformAccountSchema } from "./workspace-C1CCnvgv.mjs";
3
+ import { t as typeformOperation } from "./factory-B_dyn39A.mjs";
4
+ import { z } from "zod";
5
+
6
+ //#region src/accounts.ts
7
+ const getAboutMe = typeformOperation({
8
+ id: "get_about_me",
9
+ name: "Get About Me",
10
+ description: "Retrieve basic information about the connected Typeform account.",
11
+ input: z.object({}),
12
+ output: typeformAccountSchema,
13
+ run: async (_input, credentials) => {
14
+ const response = await createTypeformClient(credentials).accounts.getAboutMe();
15
+ return typeformAccountSchema.parse(response);
16
+ }
17
+ });
18
+
19
+ //#endregion
20
+ export { getAboutMe };
@@ -0,0 +1,104 @@
1
+ import { t as TypeformCredentials } from "./integration-d_VqlGvZ.mjs";
2
+
3
+ //#region src/client.d.ts
4
+ type Primitive = string | number | boolean;
5
+ type QueryValue = Primitive | readonly Primitive[] | undefined;
6
+ type TypeformApiErrorKind = 'auth' | 'http' | 'rate_limit' | 'validation';
7
+ interface TypeformApiErrorOptions {
8
+ readonly kind: TypeformApiErrorKind;
9
+ readonly status: number;
10
+ readonly body: unknown;
11
+ readonly requestId?: string;
12
+ readonly providerCode?: string;
13
+ readonly retryAfterSeconds?: number;
14
+ readonly retryable: boolean;
15
+ }
16
+ declare class TypeformApiError extends Error {
17
+ readonly kind: TypeformApiErrorKind;
18
+ readonly status: number;
19
+ readonly body: unknown;
20
+ readonly requestId: string | undefined;
21
+ readonly providerCode: string | undefined;
22
+ readonly retryAfterSeconds: number | undefined;
23
+ readonly retryable: boolean;
24
+ constructor(message: string, options: TypeformApiErrorOptions);
25
+ }
26
+ interface TypeformRateLimiter {
27
+ readonly acquire: () => Promise<void>;
28
+ }
29
+ interface LeakyBucketOptions {
30
+ readonly ratePerSecond?: number;
31
+ readonly burst?: number;
32
+ }
33
+ declare function createLeakyBucketRateLimiter(options?: LeakyBucketOptions): TypeformRateLimiter;
34
+ interface TypeformClientConfig {
35
+ readonly baseUrl?: string;
36
+ readonly rateLimiter?: TypeformRateLimiter;
37
+ readonly fetch?: typeof fetch;
38
+ readonly maxRateLimitRetries?: number;
39
+ }
40
+ interface TypeformListResponse<T> {
41
+ readonly items: T[];
42
+ readonly pageCount?: number;
43
+ readonly totalItems?: number;
44
+ readonly pageSize?: number;
45
+ }
46
+ declare function createTypeformClient(credentials: TypeformCredentials, config?: TypeformClientConfig): {
47
+ accounts: {
48
+ getAboutMe: () => Promise<unknown>;
49
+ };
50
+ forms: {
51
+ list: (query?: Record<string, QueryValue>) => Promise<unknown>;
52
+ get: (formId: string) => Promise<unknown>;
53
+ create: (body: unknown) => Promise<unknown>;
54
+ update: (formId: string, body: unknown) => Promise<unknown>;
55
+ patch: (formId: string, body: unknown) => Promise<unknown>;
56
+ delete: (formId: string) => Promise<void>;
57
+ };
58
+ responses: {
59
+ list: (formId: string, query?: Record<string, QueryValue>) => Promise<unknown>;
60
+ delete: (formId: string, includedTokens: readonly string[]) => Promise<void>;
61
+ };
62
+ responseFiles: {
63
+ download: (formId: string, responseId: string, fieldId: string, filename: string) => Promise<unknown>;
64
+ };
65
+ themes: {
66
+ list: (query?: Record<string, QueryValue>) => Promise<unknown>;
67
+ get: (themeId: string) => Promise<unknown>;
68
+ create: (body: unknown) => Promise<unknown>;
69
+ update: (themeId: string, body: unknown) => Promise<unknown>;
70
+ patch: (themeId: string, body: unknown) => Promise<unknown>;
71
+ delete: (themeId: string) => Promise<void>;
72
+ };
73
+ images: {
74
+ list: () => Promise<unknown>;
75
+ create: (body: unknown) => Promise<unknown>;
76
+ getBySize: (imageId: string, size: string) => Promise<unknown>;
77
+ getBackgroundBySize: (imageId: string, size: string) => Promise<unknown>;
78
+ getChoiceBySize: (imageId: string, size: string) => Promise<unknown>;
79
+ delete: (imageId: string) => Promise<void>;
80
+ };
81
+ videos: {
82
+ upload: (body: unknown) => Promise<unknown>;
83
+ };
84
+ workspaces: {
85
+ list: (query?: Record<string, QueryValue>) => Promise<unknown>;
86
+ get: (workspaceId: string) => Promise<unknown>;
87
+ create: (body: unknown) => Promise<unknown>;
88
+ update: (workspaceId: string, body: unknown) => Promise<unknown>;
89
+ delete: (workspaceId: string) => Promise<void>;
90
+ };
91
+ webhooks: {
92
+ list: (formId: string) => Promise<unknown>;
93
+ get: (formId: string, tag: string) => Promise<unknown>;
94
+ createOrUpdate: (formId: string, tag: string, body: unknown) => Promise<unknown>;
95
+ delete: (formId: string, tag: string) => Promise<void>;
96
+ };
97
+ insights: {
98
+ getFormMessages: (formId: string) => Promise<unknown>;
99
+ updateFormMessages: (formId: string, body: unknown) => Promise<void>;
100
+ };
101
+ };
102
+ type TypeformClient = ReturnType<typeof createTypeformClient>;
103
+ //#endregion
104
+ export { TypeformApiError, TypeformApiErrorKind, TypeformApiErrorOptions, TypeformClient, TypeformClientConfig, type TypeformListResponse, TypeformRateLimiter, createLeakyBucketRateLimiter, createTypeformClient };
@@ -0,0 +1,308 @@
1
+ import { t as typeform } from "./integration-BCzgn7Dm.mjs";
2
+ import { n as TYPEFORM_CONNECT_HINT, t as TYPEFORM_API_BASE_URL } from "./shared-D0ONnQL8.mjs";
3
+ import { CredentialRevokedError } from "@keystrokehq/core/errors";
4
+ import { createErrorNormalizingProxy } from "@keystrokehq/integration-authoring";
5
+
6
+ //#region src/client.ts
7
+ var TypeformApiError = class extends Error {
8
+ kind;
9
+ status;
10
+ body;
11
+ requestId;
12
+ providerCode;
13
+ retryAfterSeconds;
14
+ retryable;
15
+ constructor(message, options) {
16
+ super(message);
17
+ this.name = "TypeformApiError";
18
+ this.kind = options.kind;
19
+ this.status = options.status;
20
+ this.body = options.body;
21
+ this.requestId = options.requestId;
22
+ this.providerCode = options.providerCode;
23
+ this.retryAfterSeconds = options.retryAfterSeconds;
24
+ this.retryable = options.retryable;
25
+ }
26
+ };
27
+ function isRecord(value) {
28
+ return typeof value === "object" && value !== null;
29
+ }
30
+ function extractErrorMessage(body, status) {
31
+ if (isRecord(body)) {
32
+ const description = typeof body.description === "string" ? body.description : void 0;
33
+ const message = typeof body.message === "string" ? body.message : void 0;
34
+ return description ?? message ?? `HTTP ${status}`;
35
+ }
36
+ return `HTTP ${status}`;
37
+ }
38
+ function extractProviderCode(body) {
39
+ if (isRecord(body) && typeof body.code === "string") return body.code;
40
+ }
41
+ function classifyError(status, _body, retryAfterSeconds) {
42
+ if (status === 429) return {
43
+ kind: "rate_limit",
44
+ retryable: true
45
+ };
46
+ if (status === 401 || status === 403) return {
47
+ kind: "auth",
48
+ retryable: false
49
+ };
50
+ if (status === 422) return {
51
+ kind: "validation",
52
+ retryable: false
53
+ };
54
+ if (status >= 500 && status < 600) return {
55
+ kind: "http",
56
+ retryable: true
57
+ };
58
+ if (retryAfterSeconds !== void 0) return {
59
+ kind: "http",
60
+ retryable: true
61
+ };
62
+ return {
63
+ kind: "http",
64
+ retryable: false
65
+ };
66
+ }
67
+ function parseRetryAfter(value) {
68
+ if (!value) return void 0;
69
+ const asNumber = Number(value);
70
+ if (Number.isFinite(asNumber) && asNumber >= 0) return asNumber;
71
+ const asDate = Date.parse(value);
72
+ if (!Number.isNaN(asDate)) return Math.max(0, Math.ceil((asDate - Date.now()) / 1e3));
73
+ }
74
+ function isAuthError(error) {
75
+ return error instanceof TypeformApiError && error.kind === "auth";
76
+ }
77
+ function normalizeTypeformError(error, context) {
78
+ if (isAuthError(error)) return new CredentialRevokedError(typeform.id, `Typeform authentication failed while calling \`${context.methodPath}\`. The stored credentials for ${typeform.id} may be revoked or expired. ${TYPEFORM_CONNECT_HINT}`);
79
+ if (error instanceof TypeformApiError) return new Error(`Typeform API call failed while calling \`${context.methodPath}\` (${error.status}): ${error.message}`, { cause: error });
80
+ if (error instanceof Error) return new Error(`Typeform API call failed while calling \`${context.methodPath}\`: ${error.message}`, { cause: error });
81
+ return /* @__PURE__ */ new Error(`Typeform API call failed while calling \`${context.methodPath}\`.`);
82
+ }
83
+ function appendQuery(searchParams, query) {
84
+ if (!query) return;
85
+ for (const [key, value] of Object.entries(query)) {
86
+ if (value === void 0) continue;
87
+ if (Array.isArray(value)) {
88
+ if (value.length === 0) continue;
89
+ searchParams.set(key, value.map(String).join(","));
90
+ continue;
91
+ }
92
+ searchParams.set(key, String(value));
93
+ }
94
+ }
95
+ function createLeakyBucketRateLimiter(options = {}) {
96
+ const ratePerSecond = options.ratePerSecond ?? 2;
97
+ const burst = options.burst ?? 2;
98
+ const intervalMs = 1e3 / ratePerSecond;
99
+ let tokens = burst;
100
+ let lastRefill = Date.now();
101
+ let chain = Promise.resolve();
102
+ async function acquireOne() {
103
+ const now = Date.now();
104
+ const elapsed = now - lastRefill;
105
+ if (elapsed > 0) {
106
+ const refill = Math.floor(elapsed / intervalMs);
107
+ if (refill > 0) {
108
+ tokens = Math.min(burst, tokens + refill);
109
+ lastRefill = lastRefill + refill * intervalMs;
110
+ }
111
+ }
112
+ if (tokens > 0) {
113
+ tokens -= 1;
114
+ return;
115
+ }
116
+ const waitMs = Math.max(0, lastRefill + intervalMs - now);
117
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
118
+ tokens = Math.min(burst, tokens + 1);
119
+ lastRefill = Date.now();
120
+ tokens -= 1;
121
+ }
122
+ return { acquire: () => {
123
+ const next = chain.then(acquireOne);
124
+ chain = next.catch(() => void 0);
125
+ return next;
126
+ } };
127
+ }
128
+ function makeRequest(accessToken, config) {
129
+ const baseUrl = config.baseUrl ?? TYPEFORM_API_BASE_URL;
130
+ const limiter = config.rateLimiter ?? createLeakyBucketRateLimiter();
131
+ const fetchImpl = config.fetch ?? globalThis.fetch.bind(globalThis);
132
+ const maxRateLimitRetries = config.maxRateLimitRetries ?? 3;
133
+ async function request(path, options = {}) {
134
+ let attempt = 0;
135
+ for (;;) {
136
+ await limiter.acquire();
137
+ const url = new URL(`${baseUrl}${path}`);
138
+ const query = new URLSearchParams();
139
+ appendQuery(query, options.query);
140
+ const search = query.toString();
141
+ if (search.length > 0) url.search = search;
142
+ const headers = {
143
+ Authorization: `Bearer ${accessToken}`,
144
+ "Content-Type": "application/json",
145
+ ...options.headers ?? {}
146
+ };
147
+ const response = await fetchImpl(url, {
148
+ method: options.method ?? "GET",
149
+ headers,
150
+ ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {}
151
+ });
152
+ if (response.status === 204) return;
153
+ const responseBody = (response.headers.get("content-type") ?? "").includes("application/json") ? await response.json() : await response.text();
154
+ if (response.ok) return responseBody;
155
+ const retryAfter = parseRetryAfter(response.headers.get("retry-after"));
156
+ const { kind, retryable } = classifyError(response.status, responseBody, retryAfter);
157
+ if (kind === "rate_limit" && attempt < maxRateLimitRetries) {
158
+ attempt += 1;
159
+ const waitMs = Math.max(0, (retryAfter ?? 1) * 1e3);
160
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
161
+ continue;
162
+ }
163
+ const requestId = response.headers.get("x-request-id") ?? void 0;
164
+ throw new TypeformApiError(extractErrorMessage(responseBody, response.status), {
165
+ kind,
166
+ status: response.status,
167
+ body: responseBody,
168
+ providerCode: extractProviderCode(responseBody),
169
+ retryAfterSeconds: retryAfter,
170
+ retryable,
171
+ ...requestId !== void 0 ? { requestId } : {}
172
+ });
173
+ }
174
+ }
175
+ return request;
176
+ }
177
+ function accountsApi(request) {
178
+ return { getAboutMe: () => request("/me") };
179
+ }
180
+ function formsApi(request) {
181
+ return {
182
+ list: (query) => request("/forms", { query }),
183
+ get: (formId) => request(`/forms/${encodeURIComponent(formId)}`),
184
+ create: (body) => request("/forms", {
185
+ method: "POST",
186
+ body
187
+ }),
188
+ update: (formId, body) => request(`/forms/${encodeURIComponent(formId)}`, {
189
+ method: "PUT",
190
+ body
191
+ }),
192
+ patch: (formId, body) => request(`/forms/${encodeURIComponent(formId)}`, {
193
+ method: "PATCH",
194
+ body
195
+ }),
196
+ delete: (formId) => request(`/forms/${encodeURIComponent(formId)}`, { method: "DELETE" })
197
+ };
198
+ }
199
+ function responsesApi(request) {
200
+ return {
201
+ list: (formId, query) => request(`/forms/${encodeURIComponent(formId)}/responses`, { query }),
202
+ delete: (formId, includedTokens) => request(`/forms/${encodeURIComponent(formId)}/responses`, {
203
+ method: "DELETE",
204
+ query: { included_tokens: includedTokens }
205
+ })
206
+ };
207
+ }
208
+ function responseFilesApi(request) {
209
+ return { download: async (formId, responseId, fieldId, filename) => {
210
+ return request(`/forms/${encodeURIComponent(formId)}/responses/${encodeURIComponent(responseId)}/fields/${encodeURIComponent(fieldId)}/files/${encodeURIComponent(filename)}`, { rawResponse: true });
211
+ } };
212
+ }
213
+ function themesApi(request) {
214
+ return {
215
+ list: (query) => request("/themes", { query }),
216
+ get: (themeId) => request(`/themes/${encodeURIComponent(themeId)}`),
217
+ create: (body) => request("/themes", {
218
+ method: "POST",
219
+ body
220
+ }),
221
+ update: (themeId, body) => request(`/themes/${encodeURIComponent(themeId)}`, {
222
+ method: "PUT",
223
+ body
224
+ }),
225
+ patch: (themeId, body) => request(`/themes/${encodeURIComponent(themeId)}`, {
226
+ method: "PATCH",
227
+ body
228
+ }),
229
+ delete: (themeId) => request(`/themes/${encodeURIComponent(themeId)}`, { method: "DELETE" })
230
+ };
231
+ }
232
+ function imagesApi(request) {
233
+ return {
234
+ list: () => request("/images"),
235
+ create: (body) => request("/images", {
236
+ method: "POST",
237
+ body
238
+ }),
239
+ getBySize: (imageId, size) => request(`/images/${encodeURIComponent(imageId)}/image/${encodeURIComponent(size)}`),
240
+ getBackgroundBySize: (imageId, size) => request(`/images/${encodeURIComponent(imageId)}/background/${encodeURIComponent(size)}`),
241
+ getChoiceBySize: (imageId, size) => request(`/images/${encodeURIComponent(imageId)}/choice/${encodeURIComponent(size)}`),
242
+ delete: (imageId) => request(`/images/${encodeURIComponent(imageId)}`, { method: "DELETE" })
243
+ };
244
+ }
245
+ function videosApi(request) {
246
+ return { upload: (body) => request("/videos", {
247
+ method: "POST",
248
+ body
249
+ }) };
250
+ }
251
+ function workspacesApi(request) {
252
+ return {
253
+ list: (query) => request("/workspaces", { query }),
254
+ get: (workspaceId) => request(`/workspaces/${encodeURIComponent(workspaceId)}`),
255
+ create: (body) => request("/workspaces", {
256
+ method: "POST",
257
+ body
258
+ }),
259
+ update: (workspaceId, body) => request(`/workspaces/${encodeURIComponent(workspaceId)}`, {
260
+ method: "PATCH",
261
+ body
262
+ }),
263
+ delete: (workspaceId) => request(`/workspaces/${encodeURIComponent(workspaceId)}`, { method: "DELETE" })
264
+ };
265
+ }
266
+ function webhooksApi(request) {
267
+ return {
268
+ list: (formId) => request(`/forms/${encodeURIComponent(formId)}/webhooks`),
269
+ get: (formId, tag) => request(`/forms/${encodeURIComponent(formId)}/webhooks/${encodeURIComponent(tag)}`),
270
+ createOrUpdate: (formId, tag, body) => request(`/forms/${encodeURIComponent(formId)}/webhooks/${encodeURIComponent(tag)}`, {
271
+ method: "PUT",
272
+ body
273
+ }),
274
+ delete: (formId, tag) => request(`/forms/${encodeURIComponent(formId)}/webhooks/${encodeURIComponent(tag)}`, { method: "DELETE" })
275
+ };
276
+ }
277
+ function insightsApi(request) {
278
+ return {
279
+ getFormMessages: (formId) => request(`/forms/${encodeURIComponent(formId)}/messages`),
280
+ updateFormMessages: (formId, body) => request(`/forms/${encodeURIComponent(formId)}/messages`, {
281
+ method: "PUT",
282
+ body
283
+ })
284
+ };
285
+ }
286
+ function createTypeformApiClient(accessToken, config) {
287
+ const request = makeRequest(accessToken, config);
288
+ return {
289
+ accounts: accountsApi(request),
290
+ forms: formsApi(request),
291
+ responses: responsesApi(request),
292
+ responseFiles: responseFilesApi(request),
293
+ themes: themesApi(request),
294
+ images: imagesApi(request),
295
+ videos: videosApi(request),
296
+ workspaces: workspacesApi(request),
297
+ webhooks: webhooksApi(request),
298
+ insights: insightsApi(request)
299
+ };
300
+ }
301
+ function createTypeformClient(credentials, config = {}) {
302
+ const accessToken = credentials.TYPEFORM_ACCESS_TOKEN.trim();
303
+ if (accessToken.length === 0) throw new Error(`Typeform authentication failed while calling Typeform API \`client initialization\`. ${TYPEFORM_CONNECT_HINT}`);
304
+ return createErrorNormalizingProxy(createTypeformApiClient(accessToken, config), normalizeTypeformError);
305
+ }
306
+
307
+ //#endregion
308
+ export { TypeformApiError, createLeakyBucketRateLimiter, createTypeformClient };
@@ -0,0 +1,2 @@
1
+ import { i as typeformOfficialIntegration, n as typeform, t as TypeformCredentials } from "./integration-d_VqlGvZ.mjs";
2
+ export { type TypeformCredentials, typeform, typeformOfficialIntegration };
@@ -0,0 +1,3 @@
1
+ import { r as typeformOfficialIntegration, t as typeform } from "./integration-BCzgn7Dm.mjs";
2
+
3
+ export { typeform, typeformOfficialIntegration };