pdf-forge

Use when building, extending or using pdf-forge multi-tenant PDF template engine with Typst

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "pdf-forge" with this command: npx skills add https://github.com/rendis/pdf-forge

pdf-forge

Go module for multi-tenant document templates with PDF generation via Typst.

Installation

npx skills add https://github.com/rendis/pdf-forge --skill pdf-forge

How It Works

Tenant → Workspace → Template → Version (DRAFT → [STAGING] → PUBLISHED)
                                    ↓
                              Injectables (variables)
                                    ↓
                              Render → PDF

Render Modes:
  1. By DocType:    POST /api/v1/workspace/document-types/{code}/render
  2. By Version ID: POST /api/v1/workspace/templates/versions/{id}/render

Render by Version ID bypasses document type resolution — useful for testing/sandbox scenarios where multiple templates share the same docType. Uses the full injectable pipeline (InitFuncs, registry, provider).

Staging Mode: Send X-Environment: dev header on render endpoints to resolve STAGING versions first, falling back to PUBLISHED. X-Environment is required (dev or prod). Only one STAGING version per template (DB-enforced).

Quick Start

// core/extensions/register.go
func Register(engine *sdk.Engine) {
    engine.RegisterInjector(&CustomerNameInjector{})
    engine.SetMapper(&MyMapper{})
    engine.SetInitFunc(MyInit())
}

// core/cmd/api/main.go
func main() {
    engine := sdk.NewWithConfig("settings/app.yaml").
        SetI18nFilePath("settings/injectors.i18n.yaml")
    extensions.Register(engine)
    if err := engine.Run(); err != nil {
        slog.Error("failed to run engine", slog.String("error", err.Error()))
        os.Exit(1)
    }
}

Creating an Injector

type CustomerNameInjector struct{}

func (i *CustomerNameInjector) Code() string { return "customer_name" }

func (i *CustomerNameInjector) Resolve() (sdk.ResolveFunc, []string) {
    return func(ctx context.Context, injCtx *sdk.InjectorContext) (*sdk.InjectorResult, error) {
        payload := injCtx.RequestPayload().(map[string]any)
        return &sdk.InjectorResult{Value: sdk.StringValue(payload["name"].(string))}, nil
    }, nil  // dependencies
}

func (i *CustomerNameInjector) IsCritical() bool              { return true }
func (i *CustomerNameInjector) Timeout() time.Duration        { return 5 * time.Second }
func (i *CustomerNameInjector) DataType() sdk.ValueType       { return sdk.ValueTypeString }
func (i *CustomerNameInjector) DefaultValue() *sdk.InjectableValue { return nil }
func (i *CustomerNameInjector) Formats() *sdk.FormatConfig    { return nil }

Value Types

TypeConstructorConstant
Textsdk.StringValue(s)sdk.ValueTypeString
Numbersdk.NumberValue(n)sdk.ValueTypeNumber
Booleansdk.BoolValue(b)sdk.ValueTypeBool
Date/Timesdk.TimeValue(t)sdk.ValueTypeTime
Imagesdk.ImageValue(url)sdk.ValueTypeImage
Tablesdk.TableValueData(t)sdk.ValueTypeTable
Listsdk.ListValueData(l)sdk.ValueTypeList

See types-reference.md for Tables, Lists, InjectorContext, FormatConfig.

Built-in Injectors

CodeTypeFormats
date_nowTIMEDD/MM/YYYY, MM/DD/YYYY, YYYY-MM-DD
time_nowTIMEHH:mm, HH:mm:ss, hh:mm a
date_time_nowTIMECombined
year_nowNUMBER-
month_nowNUMBERnumber, name, short_name
day_nowNUMBER-

Error Handling

IsCritical()On Error
trueAborts render
falseUses DefaultValue(), continues

Extension Points

ExtensionPurposeRegister
InjectorData resolversRegisterInjector()
MapperRequest parsingSetMapper()
InitFuncShared setupSetInitFunc()
ProviderDynamic injectablesSetWorkspaceInjectableProvider()
AuthCustom render authSetRenderAuthenticator()
MiddlewareRequest handlingUseMiddleware(), UseAPIMiddleware()
FrontendEmbedded SPASetFrontendFS() (nil to disable)
LifecycleStartup/shutdownOnStart(), OnShutdown()

See extensions-reference.md for implementation examples.

Configuration

See config-reference.md for all YAML keys, env vars, and auth setup.

Dummy auth: Omit auth config entirely for development mode.

CLI Commands

make build      # Build frontend + embed + Go binary (single binary)
make embed-app  # Build frontend and copy to Go embed location
make run        # Run API server (with embedded frontend)
make dev        # Hot reload backend (air)
make migrate    # Apply database migrations
make test       # Run tests
make lint       # Run linter
make swagger    # Regenerate OpenAPI spec
make doctor     # Check system dependencies

Common Mistakes

WrongCorrect
sdk.NewTextValue()sdk.StringValue()
sdk.ValueTypeTextsdk.ValueTypeString
Forgetting dependenciesreturn fn, []string{"dep1"}
IsCritical()=true without handlingProvide DefaultValue()

API Headers

HeaderPurposeUsed By
AuthorizationBearer <JWT> (omit in dummy mode)All auth routes
X-Tenant-IDTenant UUIDPanel routes
X-Workspace-IDWorkspace UUIDPanel routes
X-Tenant-CodeTenant code (e.g. CL)Render routes
X-Workspace-CodeWorkspace code (e.g. SYSTEM)Render routes
X-EnvironmentRequired: dev or prodRender routes

References

  • config-reference.md - YAML keys, env vars, auth, performance
  • types-reference.md - Tables, Lists, FormatConfig, InjectorContext
  • extensions-reference.md - Middleware, Lifecycle, Provider, Auth examples
  • patterns-reference.md - Logging, error handling, context, anti-patterns
  • enterprise-scenarios.md - CRM integration, Vault, validation patterns
  • domain-reference.md - Tenants, workspaces, roles, render flow

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

surveygo

No summary provided by upstream source.

Repository SourceNeeds Review
General

mock-loom

No summary provided by upstream source.

Repository SourceNeeds Review
General

feature-evaluator

No summary provided by upstream source.

Repository SourceNeeds Review