@swirls/sdk 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.
- package/README.md +35 -0
- package/dist/config/config.d.ts +29 -0
- package/dist/config/config.js +39 -0
- package/dist/form/form.d.ts +99 -0
- package/dist/form/form.js +75 -0
- package/dist/form/generate.d.ts +10 -0
- package/dist/form/generate.js +87 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @swirls/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for building applications powered by Swirls.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Swirls SDK provides utilities for integrating Swirls into your applications. It is designed to be type-safe, headless, and framework-agnostic.
|
|
8
|
+
|
|
9
|
+
### Configuration
|
|
10
|
+
|
|
11
|
+
The [`@swirls/sdk/config`](src/config/README.md) subpackage provides configuration "glue" that connects your application to a Swirls project and powers code generation.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { defineConfig } from '@swirls/sdk/config'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Forms
|
|
18
|
+
|
|
19
|
+
The [`@swirls/sdk/form`](src/form/README.md) subpackage provides a form library for building, validating, and submitting forms.
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { useSwirlsFormAdapter, SwirlsForm } from '@swirls/sdk/form'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
bun add @swirls/sdk react zod
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Learn More
|
|
32
|
+
|
|
33
|
+
- [Swirls Documentation](https://docs.swirls.dev): Full platform documentation
|
|
34
|
+
- [Config Documentation](src/config/README.md): Configuration reference
|
|
35
|
+
- [Form Documentation](src/form/README.md): Complete guide to forms
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const configFileName = "swirls.config.ts";
|
|
4
|
+
declare const configSchema: z.ZodObject<{
|
|
5
|
+
projectId: z.ZodUUID;
|
|
6
|
+
genPath: z.ZodString;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
type Config = z.infer<typeof configSchema>;
|
|
9
|
+
type ConfigOptions = {
|
|
10
|
+
/**
|
|
11
|
+
* Identifier of the Swirls project to use.
|
|
12
|
+
*/
|
|
13
|
+
projectId: string;
|
|
14
|
+
/**
|
|
15
|
+
* Path defining the location of the generated code.
|
|
16
|
+
* @default 'src/swirls.gen.ts'
|
|
17
|
+
*/
|
|
18
|
+
genPath?: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Defines a Swirls configuration with optional defaults.
|
|
22
|
+
*/
|
|
23
|
+
declare function defineConfig(config: ConfigOptions): Config;
|
|
24
|
+
/**
|
|
25
|
+
* Generates a Swirls configuration skeleton with default and empty values.
|
|
26
|
+
*/
|
|
27
|
+
declare function generateConfig(projectId?: string): string;
|
|
28
|
+
|
|
29
|
+
export { configFileName, configSchema, defineConfig, generateConfig };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/config/config.ts
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
var configFileName = "swirls.config.ts";
|
|
5
|
+
var configImportPath = "@swirls/sdk/config";
|
|
6
|
+
var generatedFileName = "swirls.gen.ts";
|
|
7
|
+
var configSchema = z.object({
|
|
8
|
+
projectId: z.uuid(),
|
|
9
|
+
genPath: z.string().min(1)
|
|
10
|
+
});
|
|
11
|
+
function defineConfig(config) {
|
|
12
|
+
const root = process.cwd();
|
|
13
|
+
const source = path.join(root, "src");
|
|
14
|
+
const output = path.join(source, generatedFileName);
|
|
15
|
+
return {
|
|
16
|
+
projectId: config.projectId,
|
|
17
|
+
genPath: config.genPath ?? output
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function generateConfig(projectId) {
|
|
21
|
+
const lines = [
|
|
22
|
+
`import { defineConfig } from '${configImportPath}'`,
|
|
23
|
+
"",
|
|
24
|
+
"export default defineConfig({",
|
|
25
|
+
" // swirls project identifier",
|
|
26
|
+
` projectId: '${projectId || ""}',`,
|
|
27
|
+
" // path to generated code",
|
|
28
|
+
` genPath: 'src/${generatedFileName}',`,
|
|
29
|
+
"})",
|
|
30
|
+
""
|
|
31
|
+
];
|
|
32
|
+
return lines.join("\n");
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
configFileName,
|
|
36
|
+
configSchema,
|
|
37
|
+
defineConfig,
|
|
38
|
+
generateConfig
|
|
39
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
type Schema = z.ZodTypeAny;
|
|
6
|
+
type Value<S extends Schema> = z.input<S>;
|
|
7
|
+
|
|
8
|
+
interface FormRegistry {
|
|
9
|
+
}
|
|
10
|
+
type RegistryFormName = Extract<keyof FormRegistry, string>;
|
|
11
|
+
type FormName = [RegistryFormName] extends [never] ? string : RegistryFormName;
|
|
12
|
+
type FormDefinition<K extends string = string, S extends Schema = Schema> = {
|
|
13
|
+
id: string;
|
|
14
|
+
name: K;
|
|
15
|
+
schema: S;
|
|
16
|
+
};
|
|
17
|
+
type FormEntry<K extends FormName> = K extends keyof FormRegistry ? FormRegistry[K] & FormDefinition : FormDefinition;
|
|
18
|
+
declare function registerForm<K extends FormName>(name: K, form: FormEntry<K>): void;
|
|
19
|
+
/**
|
|
20
|
+
* Hook that returns a form adapter for integrating Swirls forms with your form library.
|
|
21
|
+
*
|
|
22
|
+
* The adapter is a bridge object that provides validation, submission, and schema information
|
|
23
|
+
* for a specific Swirls form. It allows you to wire up Swirls forms with any form library
|
|
24
|
+
* (React Hook Form, TanStack Form, Formik, or plain React state).
|
|
25
|
+
*
|
|
26
|
+
* **Returns:** A form adapter object with:
|
|
27
|
+
* - `submit(value)` - Submits form data to the Swirls API and triggers the workflow
|
|
28
|
+
* - `schema` - The Zod schema for the form (useful for form library resolvers)
|
|
29
|
+
* - `defaultValues` - Typed default values for the form
|
|
30
|
+
*
|
|
31
|
+
* **Access:** The adapter is returned directly from the hook. Use it to wire up your form library.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* // TanStack Form
|
|
36
|
+
* import { useForm } from '@tanstack/react-form'
|
|
37
|
+
*
|
|
38
|
+
* // Swirls Form Adapter
|
|
39
|
+
* const adapter = useSwirlsFormAdapter('contact-form', { email: '' })
|
|
40
|
+
*
|
|
41
|
+
* const form = useForm({
|
|
42
|
+
* defaultValues: adapter.defaultValues,
|
|
43
|
+
* validators: {
|
|
44
|
+
* onChange: adapter.schema.parse,
|
|
45
|
+
* },
|
|
46
|
+
* onSubmit: ({ value }) => adapter.submit(value),
|
|
47
|
+
* })
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @param name - The name of the form (autocompleted from registered forms)
|
|
51
|
+
* @param defaultValues - Typed default values matching the form schema
|
|
52
|
+
* @returns Form adapter object for wiring up with your form library
|
|
53
|
+
*/
|
|
54
|
+
declare function useSwirlsFormAdapter<K extends FormName>(name: K, defaultValues: Value<FormEntry<K>['schema']>): {
|
|
55
|
+
submit: (value: Value<FormEntry<K>["schema"]>) => Promise<{
|
|
56
|
+
executionIds: string[];
|
|
57
|
+
message: string;
|
|
58
|
+
pizza: boolean;
|
|
59
|
+
}>;
|
|
60
|
+
schema: Schema;
|
|
61
|
+
defaultValues: Value<FormEntry<K>["schema"]>;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Render props component that provides a form adapter for integrating Swirls forms.
|
|
65
|
+
*
|
|
66
|
+
* The adapter is a bridge object that provides validation, submission, and schema information
|
|
67
|
+
* for a specific Swirls form. This component wraps `useSwirlsFormAdapter` and provides the
|
|
68
|
+
* adapter via a render prop, making it convenient for simpler form implementations.
|
|
69
|
+
*
|
|
70
|
+
* **Returns:** Renders the children function with the adapter as an argument.
|
|
71
|
+
*
|
|
72
|
+
* **Access:** The adapter is passed to the `children` render function. Use it within your
|
|
73
|
+
* form JSX to access validation, submission, schema, and defaultValues.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```tsx
|
|
77
|
+
* <SwirlsForm schemaName="contact-form" defaultValues={{ name: '', email: '' }}>
|
|
78
|
+
* {(adapter) => (
|
|
79
|
+
* <form onSubmit={async (e) => {
|
|
80
|
+
* e.preventDefault()
|
|
81
|
+
* await adapter.submit(formData)
|
|
82
|
+
* }}>
|
|
83
|
+
* {/* ... *\/}
|
|
84
|
+
* </form>
|
|
85
|
+
* )}
|
|
86
|
+
* </SwirlsForm>
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
89
|
+
* @param name - The name of the form (autocompleted from registered forms)
|
|
90
|
+
* @param defaultValues - Typed default values matching the form schema
|
|
91
|
+
* @param children - Render function that receives the adapter object
|
|
92
|
+
*/
|
|
93
|
+
declare function SwirlsForm<K extends FormName>({ name, defaultValues, children, }: {
|
|
94
|
+
name: K;
|
|
95
|
+
defaultValues: Value<FormEntry<K>['schema']>;
|
|
96
|
+
children: (adapter: ReturnType<typeof useSwirlsFormAdapter<K>>) => React.ReactNode;
|
|
97
|
+
}): react_jsx_runtime.JSX.Element;
|
|
98
|
+
|
|
99
|
+
export { type FormRegistry, SwirlsForm, registerForm, useSwirlsFormAdapter };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/form/submit.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var API_URL = "https://api.swirls.ai";
|
|
4
|
+
async function submitForm(form, value) {
|
|
5
|
+
const submission = parseSubmission(form.schema, value);
|
|
6
|
+
const response = await fetch(`${API_URL}/forms/${form.id}`, {
|
|
7
|
+
method: "POST",
|
|
8
|
+
headers: {
|
|
9
|
+
"Content-Type": "application/json"
|
|
10
|
+
},
|
|
11
|
+
body: JSON.stringify(submission)
|
|
12
|
+
});
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
const { message } = await response.json();
|
|
15
|
+
throw new Error(message);
|
|
16
|
+
}
|
|
17
|
+
return parseResponse(response);
|
|
18
|
+
}
|
|
19
|
+
function parseSubmission(schema, value) {
|
|
20
|
+
const parse = schema.safeParse(value);
|
|
21
|
+
if (!parse.success) {
|
|
22
|
+
const message = z.prettifyError(parse.error);
|
|
23
|
+
throw new Error(`Invalid submission: ${message}`, {
|
|
24
|
+
cause: parse.error
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return parse.data;
|
|
28
|
+
}
|
|
29
|
+
var responseSchema = z.object({
|
|
30
|
+
executionIds: z.array(z.string()),
|
|
31
|
+
message: z.string(),
|
|
32
|
+
pizza: z.boolean()
|
|
33
|
+
});
|
|
34
|
+
async function parseResponse(response) {
|
|
35
|
+
const result = await response.json();
|
|
36
|
+
return responseSchema.parse(result);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/form/form.tsx
|
|
40
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
41
|
+
var formRegistry = {};
|
|
42
|
+
function registerForm(name, form) {
|
|
43
|
+
formRegistry[name] = form;
|
|
44
|
+
}
|
|
45
|
+
function getForm(name) {
|
|
46
|
+
return formRegistry[name];
|
|
47
|
+
}
|
|
48
|
+
function useSwirlsFormAdapter(name, defaultValues) {
|
|
49
|
+
const form = getForm(name);
|
|
50
|
+
if (!form) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Form "${name}" is not registered. Did you call registerForms()?`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
submit: (value) => {
|
|
57
|
+
return submitForm(form, value);
|
|
58
|
+
},
|
|
59
|
+
schema: form.schema,
|
|
60
|
+
defaultValues
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function SwirlsForm({
|
|
64
|
+
name,
|
|
65
|
+
defaultValues,
|
|
66
|
+
children
|
|
67
|
+
}) {
|
|
68
|
+
const adapter = useSwirlsFormAdapter(name, defaultValues);
|
|
69
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(adapter) });
|
|
70
|
+
}
|
|
71
|
+
export {
|
|
72
|
+
SwirlsForm,
|
|
73
|
+
registerForm,
|
|
74
|
+
useSwirlsFormAdapter
|
|
75
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/form/generate.ts
|
|
2
|
+
import { jsonSchemaToZod } from "json-schema-to-zod";
|
|
3
|
+
import { camelCase, pascalCase } from "text-case";
|
|
4
|
+
function generateFormsCode(forms, header) {
|
|
5
|
+
const imports = [
|
|
6
|
+
`import { registerForm } from '@swirls/sdk/form'`,
|
|
7
|
+
`import { z } from 'zod'`,
|
|
8
|
+
""
|
|
9
|
+
];
|
|
10
|
+
const schemas = forms.flatMap(
|
|
11
|
+
({ name, schema }) => generateStandardSchema(name, schema)
|
|
12
|
+
);
|
|
13
|
+
return [
|
|
14
|
+
...header,
|
|
15
|
+
...imports,
|
|
16
|
+
...schemas,
|
|
17
|
+
...generateRegistry(forms),
|
|
18
|
+
...generateRegisterFunction(forms),
|
|
19
|
+
...generateModuleAugmentation(forms)
|
|
20
|
+
].join("\n");
|
|
21
|
+
}
|
|
22
|
+
function generateStandardSchema(name, schema) {
|
|
23
|
+
const output = jsonSchemaToZod(schema, {
|
|
24
|
+
module: "esm",
|
|
25
|
+
name: schemaName(name).camel,
|
|
26
|
+
noImport: true,
|
|
27
|
+
type: true
|
|
28
|
+
});
|
|
29
|
+
const [object, type] = output.split("\n");
|
|
30
|
+
if (!object || !type) {
|
|
31
|
+
throw new Error("Failed to generate standard schema");
|
|
32
|
+
}
|
|
33
|
+
return [`// Form schema: ${name}`, object, "", type, ""];
|
|
34
|
+
}
|
|
35
|
+
function generateRegistry(forms) {
|
|
36
|
+
return [
|
|
37
|
+
"// Form registry",
|
|
38
|
+
"export const registry = {",
|
|
39
|
+
...forms.flatMap(({ name, id }) => [
|
|
40
|
+
` '${name}': {`,
|
|
41
|
+
` id: '${id}',`,
|
|
42
|
+
` name: '${name}',`,
|
|
43
|
+
` schema: ${schemaName(name).camel},`,
|
|
44
|
+
" },"
|
|
45
|
+
]),
|
|
46
|
+
"} as const",
|
|
47
|
+
""
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
function generateRegisterFunction(forms) {
|
|
51
|
+
return [
|
|
52
|
+
"// Form registration",
|
|
53
|
+
"export function registerForms() {",
|
|
54
|
+
...forms.map(({ name }) => {
|
|
55
|
+
return ` registerForm('${name}', registry['${name}'])`;
|
|
56
|
+
}),
|
|
57
|
+
"}",
|
|
58
|
+
""
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
function generateModuleAugmentation(forms) {
|
|
62
|
+
const module = "@swirls/sdk/form";
|
|
63
|
+
return [
|
|
64
|
+
`// Module augmentation for ${module}`,
|
|
65
|
+
`declare module '${module}' {`,
|
|
66
|
+
" interface FormRegistry {",
|
|
67
|
+
...forms.flatMap(({ name, id }) => [
|
|
68
|
+
` '${name}': {`,
|
|
69
|
+
` id: '${id}'`,
|
|
70
|
+
` name: '${name}'`,
|
|
71
|
+
` schema: typeof ${schemaName(name).camel}`,
|
|
72
|
+
" }"
|
|
73
|
+
]),
|
|
74
|
+
" }",
|
|
75
|
+
"}",
|
|
76
|
+
""
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
function schemaName(name) {
|
|
80
|
+
return {
|
|
81
|
+
camel: `${camelCase(name)}Schema`,
|
|
82
|
+
pascal: `${pascalCase(name)}Schema`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
generateFormsCode
|
|
87
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@swirls/sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Swirls SDK",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Swirls",
|
|
7
|
+
"url": "https://swirls.ai"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
"./form": "./dist/form/form.js",
|
|
12
|
+
"./form/generate": "./dist/form/generate.js",
|
|
13
|
+
"./config": "./dist/config/config.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"pkg:publish": "bun publish",
|
|
22
|
+
"prepublishOnly": "bun run build"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@swirls/core": "0.0.1",
|
|
26
|
+
"json-schema-to-zod": "2.7.0",
|
|
27
|
+
"text-case": "1.2.9",
|
|
28
|
+
"zod": "4.3.5"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "24.10.0",
|
|
32
|
+
"@types/react": "19.2.6",
|
|
33
|
+
"react": "19.2.1",
|
|
34
|
+
"tsup": "8.5.1"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
38
|
+
"zod": "^4.0.0"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
}
|
|
43
|
+
}
|