Skip to content

Commit 42c82d4

Browse files
committed
feat(provider): add templatefox
1 parent 8542aa7 commit 42c82d4

4 files changed

Lines changed: 824 additions & 0 deletions

File tree

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
import type { ActionDefinition, JsonSchema } from "../../core/types.ts";
2+
3+
import { s } from "../../core/json-schema.ts";
4+
import { defineProviderAction } from "../../core/provider-definition.ts";
5+
6+
const service = "templatefox";
7+
8+
const templateIdSchema = s.string("The 12-character TemplateFox template ID.", {
9+
minLength: 12,
10+
maxLength: 12,
11+
});
12+
const templateDataSchema = s.looseObject("Template data keyed by TemplateFox variable names.");
13+
const expirationSchema = s.integer("Signed URL expiration in seconds.", {
14+
minimum: 60,
15+
maximum: 604800,
16+
});
17+
const filenameSchema = s.string("Custom output filename without the file extension.", {
18+
minLength: 1,
19+
maxLength: 100,
20+
});
21+
const versionSchema = s.string(
22+
"Template version tag or version number. When omitted, TemplateFox uses the current draft.",
23+
{ minLength: 1, maxLength: 50 },
24+
);
25+
const pdfVariantSchema = s.stringEnum("PDF standards-compliant output variant.", ["pdf/a-1b", "pdf/a-2b", "pdf/a-3b"]);
26+
const imageFormatSchema = s.stringEnum("Generated image format.", ["png", "jpeg", "webp"]);
27+
const pdfResultSchema = s.requiredObject("Generated PDF URL result returned by TemplateFox.", {
28+
url: s.url("Signed URL to download the generated PDF."),
29+
filename: s.string("Filename of the generated PDF."),
30+
creditsRemaining: s.integer("Remaining TemplateFox credits after the request."),
31+
expiresIn: s.integer("Seconds until the signed URL expires."),
32+
});
33+
const imageResultSchema = s.requiredObject("Generated image URL result returned by TemplateFox.", {
34+
url: s.url("Signed URL to download the generated image."),
35+
filename: s.string("Filename of the generated image."),
36+
creditsRemaining: s.integer("Remaining TemplateFox credits after the request."),
37+
expiresIn: s.integer("Seconds until the signed URL expires."),
38+
warnings: s.nullable(
39+
s.stringArray("Non-fatal warnings returned by TemplateFox.", {
40+
itemDescription: "A warning message.",
41+
}),
42+
),
43+
});
44+
const templateSchema = s.looseRequiredObject("TemplateFox template summary.", {
45+
id: s.string("Template ID."),
46+
name: s.string("Template name."),
47+
kind: s.nullable(s.stringEnum("Template kind.", ["pdf", "image"])),
48+
createdAt: s.nullableString("Template creation timestamp."),
49+
updatedAt: s.nullableString("Template update timestamp."),
50+
});
51+
const templateFieldSpecSchema = s.looseRequiredObject("TemplateFox nested array field spec.", {
52+
name: s.string("Nested field name."),
53+
label: s.string("Nested field label."),
54+
type: s.string("Nested field type."),
55+
});
56+
const templateFieldSchema = s.looseRequiredObject("TemplateFox template field definition.", {
57+
key: s.string("Template field key."),
58+
label: s.string("Template field label."),
59+
type: s.string("Template field type."),
60+
required: s.boolean("Whether the field is required."),
61+
helpText: s.nullableString("Help text returned by TemplateFox."),
62+
spec: s.nullable(s.array("Nested field spec for array fields.", templateFieldSpecSchema)),
63+
});
64+
const accountSchema = s.requiredObject("TemplateFox account information.", {
65+
credits: s.integer("Remaining TemplateFox credits."),
66+
email: s.nullableString("Account email address returned by TemplateFox."),
67+
});
68+
const modificationSchema = s.object(
69+
"A single image template layer modification.",
70+
{
71+
name: s.string("Layer name to modify.", { minLength: 1, maxLength: 200 }),
72+
text: s.string("Replacement text for the layer.", { maxLength: 10000 }),
73+
imageUrl: s.string("Replacement image URL for the layer.", {
74+
format: "uri",
75+
maxLength: 2000,
76+
}),
77+
color: s.string("CSS text color applied to the layer.", { maxLength: 100 }),
78+
background: s.string("CSS background color applied to the layer.", { maxLength: 100 }),
79+
hidden: s.boolean("Whether the layer should be hidden."),
80+
},
81+
{ optional: ["text", "imageUrl", "color", "background", "hidden"] },
82+
);
83+
const pdfUrlSchema = s.url("Public HTTPS URL for TemplateFox to download the PDF.");
84+
const pageRotationSchema: JsonSchema = {
85+
type: "integer",
86+
enum: [0, 90, 180, 270],
87+
description: "Rotation in degrees for this page.",
88+
};
89+
const pageRotationsSchema: JsonSchema = {
90+
type: "object",
91+
propertyNames: {
92+
pattern: "^[1-9][0-9]*$",
93+
},
94+
additionalProperties: pageRotationSchema,
95+
description: "Per-page rotations keyed by 1-indexed page number.",
96+
};
97+
const rotationSchema: JsonSchema = {
98+
type: "integer",
99+
enum: [0, 90, 180, 270],
100+
description: "Rotation in degrees applied to every page.",
101+
};
102+
103+
const createPdfInputSchema = s.object(
104+
"Input for generating a PDF from a TemplateFox template.",
105+
{
106+
templateId: templateIdSchema,
107+
data: templateDataSchema,
108+
expiration: expirationSchema,
109+
filename: filenameSchema,
110+
pdfVariant: pdfVariantSchema,
111+
version: versionSchema,
112+
},
113+
{ optional: ["expiration", "filename", "pdfVariant", "version"] },
114+
);
115+
116+
const createImageInputSchema = s.object(
117+
"Input for generating an image from a TemplateFox template.",
118+
{
119+
templateId: templateIdSchema,
120+
modifications: s.array("Layer modifications to apply before rendering.", modificationSchema, {
121+
maxItems: 100,
122+
}),
123+
data: templateDataSchema,
124+
format: imageFormatSchema,
125+
width: s.integer("Output width in pixels.", { minimum: 100, maximum: 4000 }),
126+
quality: s.integer("JPEG or WebP compression quality from 1 to 100.", {
127+
minimum: 1,
128+
maximum: 100,
129+
}),
130+
expiration: expirationSchema,
131+
filename: filenameSchema,
132+
version: versionSchema,
133+
},
134+
{
135+
optional: ["modifications", "data", "format", "width", "quality", "expiration", "filename", "version"],
136+
},
137+
);
138+
139+
const pdfToolOptionsSchema = {
140+
expiration: expirationSchema,
141+
filename: filenameSchema,
142+
};
143+
144+
const rotatePdfInputSchema: JsonSchema = {
145+
...s.object(
146+
"Input for rotating pages in a PDF.",
147+
{
148+
pdfUrl: pdfUrlSchema,
149+
rotation: rotationSchema,
150+
pageRotations: pageRotationsSchema,
151+
...pdfToolOptionsSchema,
152+
},
153+
{ optional: ["rotation", "pageRotations", "expiration", "filename"] },
154+
),
155+
oneOf: [
156+
{ required: ["rotation"], not: { required: ["pageRotations"] } },
157+
{ required: ["pageRotations"], not: { required: ["rotation"] } },
158+
],
159+
};
160+
161+
export const templatefoxActions: ActionDefinition[] = [
162+
defineProviderAction(service, {
163+
name: "create_pdf",
164+
description: "Generate a PDF from a TemplateFox template and return a signed download URL.",
165+
inputSchema: createPdfInputSchema,
166+
outputSchema: pdfResultSchema,
167+
}),
168+
defineProviderAction(service, {
169+
name: "create_image",
170+
description: "Generate an image from a TemplateFox image template and return a signed download URL.",
171+
inputSchema: createImageInputSchema,
172+
outputSchema: imageResultSchema,
173+
}),
174+
defineProviderAction(service, {
175+
name: "merge_pdfs",
176+
description: "Merge multiple PDF URLs with TemplateFox and return a signed download URL for the result.",
177+
inputSchema: s.object(
178+
"Input for merging PDF URLs.",
179+
{
180+
pdfUrls: s.array("Ordered PDF URLs to merge.", pdfUrlSchema, { minItems: 2 }),
181+
...pdfToolOptionsSchema,
182+
},
183+
{ optional: ["expiration", "filename"] },
184+
),
185+
outputSchema: pdfResultSchema,
186+
}),
187+
defineProviderAction(service, {
188+
name: "extract_pdf_pages",
189+
description: "Extract selected pages from a PDF URL with TemplateFox and return a signed download URL.",
190+
inputSchema: s.object(
191+
"Input for extracting pages from a PDF.",
192+
{
193+
pdfUrl: pdfUrlSchema,
194+
pages: s.nonEmptyString("1-indexed page selection such as `1-3, 5, 7-9`."),
195+
...pdfToolOptionsSchema,
196+
},
197+
{ optional: ["expiration", "filename"] },
198+
),
199+
outputSchema: pdfResultSchema,
200+
}),
201+
defineProviderAction(service, {
202+
name: "rotate_pdf",
203+
description: "Rotate all pages or selected pages in a PDF URL with TemplateFox and return a signed download URL.",
204+
inputSchema: rotatePdfInputSchema,
205+
outputSchema: pdfResultSchema,
206+
}),
207+
defineProviderAction(service, {
208+
name: "list_templates",
209+
description: "List TemplateFox templates visible to the API key.",
210+
inputSchema: s.object(
211+
"Input for listing TemplateFox templates.",
212+
{
213+
kind: s.stringEnum("Filter templates by product kind.", ["pdf", "image"]),
214+
},
215+
{ optional: ["kind"] },
216+
),
217+
outputSchema: s.requiredObject("Template list returned by TemplateFox.", {
218+
templates: s.array("Templates visible to the API key.", templateSchema),
219+
}),
220+
}),
221+
defineProviderAction(service, {
222+
name: "get_template_fields",
223+
description: "Get dynamic field definitions for a TemplateFox template.",
224+
inputSchema: s.requiredObject("Input for reading TemplateFox template fields.", {
225+
templateId: templateIdSchema,
226+
}),
227+
outputSchema: s.requiredObject("Template field definitions returned by TemplateFox.", {
228+
fields: s.array("Template fields.", templateFieldSchema),
229+
}),
230+
}),
231+
defineProviderAction(service, {
232+
name: "get_account",
233+
description: "Get TemplateFox account information including remaining credits.",
234+
inputSchema: s.object("No input is required.", {}),
235+
outputSchema: s.requiredObject("TemplateFox account output.", {
236+
account: accountSchema,
237+
}),
238+
}),
239+
];
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { ProviderDefinition } from "../../core/types.ts";
2+
3+
import { templatefoxActions } from "./actions.ts";
4+
5+
const service = "templatefox";
6+
7+
export const provider: ProviderDefinition = {
8+
service,
9+
displayName: "TemplateFox",
10+
categories: ["Design & Media", "Developer Tools"],
11+
authTypes: ["api_key"],
12+
auth: [
13+
{
14+
type: "api_key",
15+
label: "API Key",
16+
placeholder: "TEMPLATEFOX_API_KEY",
17+
description:
18+
"TemplateFox API key sent with the x-api-key header. Create or copy it from the API key section of the TemplateFox dashboard at https://app.templatefox.com/dashboard; see https://templatefox.com/docs/api-reference.",
19+
},
20+
],
21+
homepageUrl: "https://templatefox.com",
22+
actions: templatefoxActions,
23+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { CredentialValidators, ProviderExecutors, ProviderProxyExecutor } from "../../core/types.ts";
2+
3+
import { defineApiKeyProviderExecutors, defineProviderProxy } from "../provider-runtime.ts";
4+
import { templatefoxActionHandlers, templatefoxApiBaseUrl, validateTemplatefoxCredential } from "./runtime.ts";
5+
6+
const service = "templatefox";
7+
8+
export const executors: ProviderExecutors = defineApiKeyProviderExecutors(service, templatefoxActionHandlers, {
9+
skipDnsValidation: true,
10+
});
11+
12+
export const credentialValidators: CredentialValidators = {
13+
apiKey(input, { fetcher, signal }) {
14+
return validateTemplatefoxCredential(input.apiKey, fetcher, signal);
15+
},
16+
};
17+
18+
export const proxy: ProviderProxyExecutor = defineProviderProxy({
19+
service,
20+
baseUrl: templatefoxApiBaseUrl,
21+
auth: { type: "api_key_header", name: "x-api-key" },
22+
skipDnsValidation: true,
23+
customizeRequest({ headers }) {
24+
if (!headers.has("accept")) {
25+
headers.set("accept", "application/json");
26+
}
27+
},
28+
});

0 commit comments

Comments
 (0)