LogoCyanPrint
ResolversReference

Project Structure

Standard file layout for resolver projects

Project Structure

A resolver project follows a standard layout for consistency and ease of development.

Basic Structure

index.ts
package.json
bun.lock
Dockerfile
cyan.yaml
tsconfig.json
main.py
requirements.txt
Dockerfile
cyan.yaml
Program.cs
MyResolver.csproj
Dockerfile
cyan.yaml

File Descriptions

Entry Point File

The main entry point using StartResolverWithLambda:

index.ts
import { ResolverOutput, StartResolverWithLambda } from '@atomicloud/cyan-sdk';
StartResolverWithLambda(async (input): Promise<ResolverOutput> => {
// Sort files by layer
const sorted = [...input.files].sort(
(a, b) => a.origin.layer - b.origin.layer
);
// Merge content
let merged = {};
for (const file of sorted) {
merged = { ...merged, ...JSON.parse(file.content) };
}
return {
path: input.files[0].path,
content: JSON.stringify(merged, null, 2)
};
});

The main entry point using start_resolver_with_fn:

main.py
import json
from cyanprintsdk.resolver import start_resolver_with_fn
from cyanprintsdk.protocol import ResolverInput, ResolverOutput
async def resolver(input: ResolverInput) -> ResolverOutput:
# Sort files by layer
sorted_files = sorted(input.files, key=lambda f: f.origin.layer)
# Merge content
merged = {}
for file in sorted_files:
merged = {**merged, **json.loads(file.content)}
return ResolverOutput(
path=input.files[0].path,
content=json.dumps(merged, indent=2)
)
resolver_main = start_resolver_with_fn(resolver)

The main entry point using CyanEngine.StartResolver:

Program.cs
using CyanPrintSDK;
using System.Text.Json;
public static class Resolver
{
public static async Task Main(string[] args)
{
CyanEngine.StartResolver(args, Resolve);
}
private static Task<ResolverOutput> Resolve(ResolverInput input)
{
// Sort files by layer
var sorted = input.Files.OrderBy(f => f.Origin.Layer).ToList();
// Merge content
var merged = new Dictionary<string, object>();
foreach (var file in sorted)
{
var content = JsonSerializer.Deserialize<Dictionary<string, object>>(file.Content);
if (content != null)
{
foreach (var kvp in content)
merged[kvp.Key] = kvp.Value;
}
}
return Task.FromResult(new ResolverOutput(
input.Files[0].Path,
JsonSerializer.Serialize(merged, new JsonSerializerOptions { WriteIndented = true })
));
}
}

Dependency File

Define dependencies and scripts in package.json:

package.json
{
"name": "my-resolver",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "bun run index.ts"
},
"dependencies": {
"@atomicloud/cyan-sdk": "^1.0.0"
},
"devDependencies": {
"@types/bun": "latest"
}
}

Define dependencies in requirements.txt:

requirements.txt
cyanprintsdk>=1.0.0

Define dependencies in .csproj:

MyResolver.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Sulfone.Helium" Version="1.0.0" />
</ItemGroup>
</Project>

Dockerfile

Container definition for the resolver:

Dockerfile
FROM oven/bun:1.1.30
WORKDIR /app
# Required: Mark as CyanPrint resolver
LABEL cyanprint.dev=true
# Install dependencies
COPY package.json .
COPY bun.lock .
RUN bun install
# Copy resolver code
COPY . .
# Run resolver
CMD ["bun", "run", "index.ts"]

Container definition for the resolver:

Dockerfile
FROM python:3.11-slim
WORKDIR /app
# Required: Mark as CyanPrint resolver
LABEL cyanprint.dev=true
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy resolver code
COPY . .
# Run resolver
CMD ["python", "main.py"]

Container definition for the resolver:

Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app
# Required: Mark as CyanPrint resolver
LABEL cyanprint.dev=true
# Build
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o out
# Runtime
FROM mcr.microsoft.com/dotnet/runtime:8.0
WORKDIR /app
COPY --from=build /app/out .
CMD ["dotnet", "MyResolver.dll"]

The LABEL cyanprint.dev=true line is required for the container to be recognized as a CyanPrint resolver.

cyan.yaml

Metadata for registry publishing:

cyan.yaml
username: your-username
name: my-resolver
description: JSON file resolver for layered templates
project: https://github.com/org/my-project
source: https://github.com/org/my-resolver
tags:
- json
- merge
- resolver
readme: README.md

tsconfig.json

TypeScript configuration

tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"skipLibCheck": true,
"esModuleInterop": true
},
"include": ["*.ts", "src/**/*.ts"]
}

Optional Directories

src/

Organize larger resolvers into modules

index.ts

tests/

Unit tests for resolver logic

merge.test.ts
utils.test.ts