cloudflare

package
v0.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 18, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package cloudflare implements the Cloudflare deploy plugin (Cloudflare Pages direct-upload in PR1; R2 and Worker arrive in later PRs).

See docs/adr/0002-cloudflare-deploy-plugin.md for protocol details and docs/adr/0003-unified-plugin-system.md for the plugin host abstraction.

Index

Constants

View Source
const (
	MaxFileCount     = 20000            // total files per deployment
	MaxFileSize      = 25 * 1024 * 1024 // 25 MiB per file
	MaxFilesPerBatch = 2000             // files per upload bucket (POST request)
	MaxBatchSize     = 40 * 1024 * 1024 // bytes per upload bucket (POST request)
)

Hard limits enforced by Cloudflare Pages (per wrangler constants.ts). Manifest build rejects inputs exceeding these so we fail fast in the manifest stage rather than mid-upload.

"bucket" here = one POST /pages/assets/upload request body, NOT per- deployment total. A 20,000-file deployment is split into many buckets of at most MaxFilesPerBatch files or MaxBatchSize bytes (whichever hits first) per ADR §14.4.

View Source
const DefaultBaseURL = "https://api.cloudflare.com/client/v4"

DefaultBaseURL is the Cloudflare API root.

View Source
const DefaultWorkerCompatibilityDate = "2024-01-01"

DefaultWorkerCompatibilityDate is used when WorkerConfig.CompatibilityDate is empty. CF requires this field; we default to the same date wrangler uses.

View Source
const HTTPDegradedParallel = 1

HTTPDegradedParallel is the cap after a gateway 5xx triggers auto-degrade per ADR 0002 §9. Degrade is one-way (this constant never grows back during a single deploy invocation).

View Source
const HTTPMaxParallel = 3

HTTPMaxParallel is the hard cap on concurrent in-flight HTTP POST requests to Cloudflare (Pages assets/upload + R2 PutObject). Per ADR 0002 §14.3 this is not user-tunable — exceeding 3 typically trips CF gateway 5xx.

View Source
const MaxRetries = 3

MaxRetries is the number of retry attempts per request. The total number of attempts is MaxRetries+1 (initial + retries). Per ADR 0002 §9.

View Source
const R2EndpointHostPattern = "%s.r2.cloudflarestorage.com"

R2EndpointHostPattern is the hostname pattern for Cloudflare R2's S3- compatible API. minio.New takes a bare hostname (no scheme); the scheme is controlled by Options.Secure (set to true for R2).

View Source
const WorkerMIMEType = "application/javascript+module"

WorkerMIMEType is the Content-Type CF Workers API expects for ES module scripts. Not the standard application/javascript — CF distinguishes service-worker (legacy) from module (current) via this MIME type.

Variables

This section is empty.

Functions

func Backoff

func Backoff(attempt int) time.Duration

Backoff returns the wait duration before the (attempt+1)-th retry. attempt is 0-indexed: Backoff(0) is the wait before the 1st retry.

Returns zero when attempt exceeds the schedule length, indicating the caller should not retry further. Adds up to 20% jitter to avoid thundering-herd on simultaneous retries against the same CF edge.

Tests that need deterministic timing should call backoffSchedule[attempt] directly.

func Batch

func Batch(assets []Asset) [][]Asset

Batch splits assets into chunks suitable for POST /pages/assets/upload. Each chunk satisfies BOTH constraints:

  • len(chunk) <= MaxFilesPerBatch (2000)
  • sum(chunk[i].Size) <= MaxBatchSize (40 MiB)

Whichever constraint hits first triggers a new bucket. Per ADR §14.4 / wrangler constants.ts: "bucket" = one POST request body, not per- deployment total.

Returns nil if input is empty.

func Hash

func Hash(content []byte, ext string) string

Hash computes a Cloudflare Pages asset hash.

Per wrangler source (cloudflare/workers-sdk packages/deploy-helpers/src/ deploy/helpers/hash.ts):

hash = blake3(base64(content) + ext).hex()[:32]

where:

  • content is the raw file bytes
  • base64 is standard base64 encoding (with padding)
  • ext is the file extension WITHOUT leading dot ("html" for "index.html", "" for "Makefile" or any extensionless file)

Output is the first 32 hex characters of the blake3 hex digest.

Correctness is verified against python blake3 reference vectors in hash_test.go. A single byte error here invalidates the entire dedup mechanism.

func MD5Hex

func MD5Hex(content []byte) string

MD5Hex returns the hex-encoded MD5 of content. Used for R2 etag comparison (R2/S3 single-part uploads use MD5 as the etag). Exported for tests.

Types

type Asset

type Asset struct {
	// Path is the deployment-relative path with leading slash, e.g. "/index.html".
	// Cloudflare requires leading slash on manifest keys; BuildManifest enforces this.
	Path string

	// Hash is the 32-hex blake3 hash (see hash.go).
	Hash string

	// Size is the file size in bytes.
	Size int64

	// ContentType is the MIME type guessed from extension (e.g. "text/html; charset=utf-8").
	// Empty if no mapping found; Cloudflare accepts empty content-type.
	ContentType string

	// Content is the raw file bytes, loaded at manifest build time. For large
	// deployments this can be memory-heavy; if memory becomes a concern, switch
	// to lazy loading in pages.go upload step.
	Content []byte
}

Asset describes one file in a Pages deployment manifest.

func BuildManifest

func BuildManifest(publishDir string) ([]Asset, error)

BuildManifest walks publishDir and returns Asset entries ready for upload.

Path conventions:

  • publishDir itself becomes the deployment root; e.g. publishDir/index.html becomes Asset.Path "/index.html".
  • Subdirectories preserved: publishDir/blog/2024/post.html -> "/blog/2024/post.html".
  • All paths use forward slashes regardless of host OS (Cloudflare expects /).

Hard limits enforced (see constants). Returned error unwraps to *ManifestError for the first violation encountered.

Symbolic links are skipped (not followed) to avoid cycles and unexpected out-of-tree content. Hidden files (leading dot) are included — Pages typically wants .well-known/, robots.txt, etc. Callers can post-filter if needed.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client wraps Cloudflare API access. It handles:

  • Bearer auth (apiToken at account scope, project JWT at assets scope)
  • JSON envelope unwrapping ({result, success, errors, messages})
  • Retry with exponential backoff (delegated to retry.go)

Client is safe for concurrent use.

func NewClient

func NewClient(accountID, apiToken string, logger *observability.Logger) *Client

NewClient returns a Client configured for the Cloudflare API.

func (*Client) APITokenAuth

func (c *Client) APITokenAuth() string

APITokenAuth returns the Authorization header value for apiToken requests. Exposed so callers (pages.go) can pass it explicitly to GetJSON/PostJSON.

func (*Client) GetJSON

func (c *Client) GetJSON(ctx context.Context, path, auth string, out any) error

GetJSON sends a GET request and decodes the envelope result into out. auth is the full Authorization header value (use APITokenAuth() or JWTViaProject()).

func (*Client) InvalidateJWT

func (c *Client) InvalidateJWT(project string)

InvalidateJWT clears the cached JWT for a project. Call after a 401 on an assets/* endpoint to force a refresh on the next UploadToken call.

func (*Client) JWTViaProject

func (c *Client) JWTViaProject(ctx context.Context, project string) (string, error)

JWTViaProject returns the Authorization header value for JWT-authenticated assets/* requests, fetching a fresh JWT via UploadToken first.

func (*Client) PostForm

func (c *Client) PostForm(ctx context.Context, path, auth string, fields map[string]formField, out any) error

PostForm sends multipart/form-data. fields is name -> {contentType, value}. Used for POST deployment (manifest + branch + commit_*).

func (*Client) PostJSON

func (c *Client) PostJSON(ctx context.Context, path, auth string, body, out any) error

PostJSON sends a POST request with JSON body and decodes the envelope result.

func (*Client) PutForm

func (c *Client) PutForm(ctx context.Context, path, auth string, fields map[string]formField, out any) error

PutForm sends multipart/form-data via PUT. Used for Workers script upload (PUT /accounts/{id}/workers/scripts/{name}).

func (*Client) UploadToken

func (c *Client) UploadToken(ctx context.Context, project string) (string, error)

UploadToken returns a JWT for the given project. Cached per-project; auto- refreshed when within jwtRefreshMargin of expiry.

func (*Client) WithBaseURL

func (c *Client) WithBaseURL(url string) *Client

WithBaseURL substitutes the API base URL. Used by tests.

func (*Client) WithHTTPClient

func (c *Client) WithHTTPClient(h HTTPClient) *Client

WithHTTPClient substitutes the default http.Client. Used by tests to inject httptest mock clients.

type CommitMeta

type CommitMeta struct {
	SHA     string
	Message string
	Dirty   bool
}

CommitMeta attaches git metadata to a deployment. Empty values are allowed; Cloudflare accepts deployments without commit metadata.

func InferCommitMetadata

func InferCommitMetadata(flagSHA, flagMessage string) CommitMeta

InferCommitMetadata resolves commit metadata via three-layer fallback per ADR 0002 §14.2:

  1. Explicit flag values (--commit-sha / --commit-message) win if non-empty.
  2. Otherwise, infer from the current git repo via `git rev-parse HEAD` and `git log -1 --pretty=%s`.
  3. If git inference fails (not a repo, no commits, git not installed), return CommitMeta with empty values; Cloudflare accepts deployments without commit metadata.

flagSHA / flagMessage are typically passed from CLI flags. The working directory for git inference is the process's current working directory — callers should chdir to the project root before calling (or pass the project root via cwd parameter in future).

type Config

type Config struct {
	AccountID string         `yaml:"accountId" json:"accountId"`
	APIToken  string         `yaml:"apiToken"  json:"apiToken"`
	Pages     PagesConfig    `yaml:"pages"     json:"pages"`
	R2        R2Config       `yaml:"r2"        json:"r2"`
	Worker    WorkerConfig   `yaml:"worker"    json:"worker"` // singular (legacy)
	Workers   []WorkerConfig `yaml:"workers" json:"workers"`  // plural (preferred for 2+ workers)
}

Config is the typed Cloudflare plugin configuration, parsed from cfg.Plugins["cloudflare"] (the map[string]any from yaml). The map is already ${VAR}-interpolated by the config layer (see internal/config/interpolate.go).

func LoadConfigFromFile

func LoadConfigFromFile(path string) (Config, error)

LoadConfigFromFile is a convenience for tests that want to parse a yaml file directly without going through the full huan config pipeline.

func ParseConfig

func ParseConfig(raw map[string]any) (Config, error)

ParseConfig decodes raw (already-interpolated) yaml map into typed Config and validates required fields. Returns error on missing account_id, token, pages.project, or pages.branch.

func (Config) AllWorkers added in v0.3.0

func (c Config) AllWorkers() []WorkerConfig

AllWorkers returns the full list of workers to deploy, combining the plural `workers:` list (preferred) with the singular `worker:` block (legacy). Singular entries are appended AFTER plural entries so plural takes precedence on conflicts (same Name).

Used by deployWorker to iterate without caring which yaml form was used. Returns empty slice when neither form is configured.

func (Config) HasR2Configured

func (c Config) HasR2Configured() bool

HasR2Configured returns true if the R2 block looks intentional (any field set). Used by Plugin.Deploy to decide whether to error or skip when target="r2".

func (Config) HasWorkerConfigured

func (c Config) HasWorkerConfigured() bool

HasWorkerConfigured returns true if EITHER the singular Worker block OR the plural Workers list has at least one entry with Name/Script set. Callers that need to know which form was used should call AllWorkers.

type DeployPagesOptions

type DeployPagesOptions struct {
	Project      string
	Branch       string
	Commit       *CommitMeta
	Assets       []Asset
	HTTPParallel int // hard-capped at 3 internally per ADR 0002 §14.3
}

DeployPagesOptions captures deploy-time parameters for the Pages protocol.

type DeployWorkerOptions

type DeployWorkerOptions struct {
	// SourceDir is the project root for resolving relative Script paths.
	SourceDir string

	// DryRun skips the actual PUT; returns success if local file reads OK.
	DryRun bool
}

DeployWorkerOptions captures deploy-time parameters.

type DeploymentResult

type DeploymentResult struct {
	ID      string   `json:"id"`
	URL     string   `json:"url"`
	Aliases []string `json:"aliases"`
	Env     string   `json:"environment"`
	Stage   string   `json:"latest_stage"`
	Status  string   `json:"latest_stage_status"`
}

DeploymentResult models the response from POST .../deployments.

type HTTPClient

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPClient is the subset of *http.Client this package uses, extracted as an interface for test substitution.

type Limiter

type Limiter struct {
	// contains filtered or unexported fields
}

Limiter caps concurrent HTTP requests per ADR §14.3 (3 normal, 1 degraded).

Behavior:

  • Initially allows up to HTTPMaxParallel concurrent Acquire calls.
  • Degrade() switches the cap to HTTPDegradedParallel permanently (atomic store; not reversible). In-flight Acquire holders continue; future Acquires use the smaller semaphore.
  • Acquire respects ctx.Done() — goroutine can be cancelled while waiting.
  • Release is the caller's responsibility; pair with defer.

Limiter is safe for concurrent use.

func NewLimiter

func NewLimiter() *Limiter

NewLimiter returns a fresh Limiter in non-degraded state.

func (*Limiter) Acquire

func (l *Limiter) Acquire(ctx context.Context) error

Acquire blocks until a slot is available or ctx is cancelled. Callers MUST call Release when done with the slot.

func (*Limiter) Degrade

func (l *Limiter) Degrade()

Degrade permanently lowers the concurrency cap to HTTPDegradedParallel. Subsequent Acquire calls use the smaller semaphore. Idempotent — calling multiple times is a no-op after the first.

Per ADR §9 this fires on any 5xx response (gateway / app error); the conservative over-reaction cost is "deploy gets slower", the under-reaction cost is "continuous 5xx storms waste upload attempts".

func (*Limiter) IsDegraded

func (l *Limiter) IsDegraded() bool

IsDegraded returns whether Degrade has been called. Useful for tests.

func (*Limiter) Release

func (l *Limiter) Release()

Release frees one slot. Must be called exactly once per successful Acquire. Releases into whichever semaphore is current (post-Degrade, tokens go back to degradedSem even if originally acquired from normalSem — the slot is simply freed; over-counting in normalSem is benign because future acquires use degradedSem).

type ManifestError

type ManifestError struct {
	Path    string
	Limit   string // "MaxFileSize" / "MaxFileCount" / "MaxFilesPerBatch"
	Details string
}

ManifestError reports a structured error from manifest building, including the file path and limit name so the user can fix the input.

func (*ManifestError) Error

func (e *ManifestError) Error() string

type PagesConfig

type PagesConfig struct {
	// Project is the CF Pages project name (created in dashboard). Required.
	Project string `yaml:"project" json:"project"`

	// Branch is the default deployment branch (typically "main"). Required.
	// Override at deploy time via --branch=preview or similar.
	Branch string `yaml:"branch" json:"branch"`
}

PagesConfig captures Cloudflare Pages project settings.

type PagesDeployer

type PagesDeployer struct {
	// contains filtered or unexported fields
}

PagesDeployer orchestrates the 5-endpoint Cloudflare Pages direct-upload protocol per ADR 0002 §7.

func NewPagesDeployer

func NewPagesDeployer(client *Client, logger *observability.Logger) *PagesDeployer

NewPagesDeployer returns a deployer using the given client. logger carries the trace_id used to correlate all log lines for this deploy invocation.

func (*PagesDeployer) DeployPages

func (p *PagesDeployer) DeployPages(ctx context.Context, opts DeployPagesOptions) (*deploy.Report, error)

DeployPages runs the 5-endpoint Pages direct-upload protocol and returns a deploy.Report describing the outcome.

The 5 endpoints (see ADR 0002 §7 for protocol details):

  1. GET /accounts/{id}/pages/projects/{project}/upload-token → JWT
  2. POST /pages/assets/check-missing {hashes:[...]} → missing hashes
  3. POST /pages/assets/upload [{key,value,metadata,base64}] (batched ≤2000)
  4. POST /pages/assets/upsert-hashes {hashes:[...]} → ack
  5. POST /accounts/{id}/pages/projects/{project}/deployments multipart

Per ADR 0002 §9, individual file failures are collected into Report.Failures rather than aborting the deploy.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin is the Cloudflare deploy plugin. It implements both plugin.Plugin (via Name) and deploy.Deployer (via Deploy), so it is discoverable both as a base plugin and as a deployer via plugin.Find[*deploy.Deployer].

func New

func New(cfg Config) *Plugin

New constructs a Plugin from parsed Config. The cfg should come from ParseConfig; pass its output directly here.

func (*Plugin) Config

func (p *Plugin) Config() Config

Config returns the parsed configuration. Used by the CLI to display effective config in `huan plugin info`.

func (*Plugin) Deploy

func (p *Plugin) Deploy(ctx context.Context, opts deploy.Options) (*deploy.Report, error)

Deploy implements deploy.Deployer. It dispatches to the appropriate sub- target based on opts.Targets.

Supported targets:

  • "pages" — Cloudflare Pages direct-upload (PR1)
  • "r2" — R2 bucket sync via S3-compatible API (PR2)
  • "worker" — Worker modules upload (PR3, not yet implemented)

Mixed targets like ["pages", "r2"] return an error — invoke each separately.

func (*Plugin) Name

func (p *Plugin) Name() string

Name is the unique plugin identifier. Pairs with the yaml key under plugins: (i.e. plugins.cloudflare.*).

type R2Config

type R2Config struct {
	// AccountID is used to construct the S3 endpoint URL
	// (<accountID>.r2.cloudflarestorage.com). Required unless Endpoint is set.
	AccountID string `yaml:"accountId" json:"accountId"`

	// AccessKeyID and SecretAccessKey are S3-style credentials for R2.
	// Generate in CF dashboard under R2 > Manage R2 API Tokens.
	AccessKeyID     string `yaml:"accessKeyId" json:"accessKeyId"`
	SecretAccessKey string `yaml:"secretAccessKey" json:"secretAccessKey"`

	// Bucket is the R2 bucket name (pre-created in CF dashboard per ADR 0002 §10).
	Bucket string `yaml:"bucket" json:"bucket"`

	// Endpoint overrides the default R2 URL pattern (for testing).
	Endpoint string `yaml:"endpoint" json:"endpoint"`

	// Sync is the list of local-to-remote path mappings. Each entry uploads
	// files from local `From` directory to remote `To` key prefix.
	// Example: {from: "static/images", to: "images"} uploads static/images/a.jpg
	// to bucket key "images/a.jpg".
	Sync []SyncMapping `yaml:"sync" json:"sync"`
}

R2Config captures Cloudflare R2 (S3-compatible) settings.

type R2FileFailure

type R2FileFailure struct {
	LocalPath string
	Key       string
	Stage     string // "walk" / "list" / "hash" / "upload" / "prune"
	Error     string
}

R2FileFailure describes a single file that failed during R2 sync.

type R2Object

type R2Object struct {
	Key  string
	Size int64
	ETag string // hex-encoded MD5 without quotes
}

R2Object is the subset of remote-object metadata R2Syncer uses.

type R2SyncOptions

type R2SyncOptions struct {
	// Prune deletes remote objects whose keys aren't present in the local
	// set. Default false (keep orphans) per ADR 0002 §6.
	Prune bool

	// DryRun computes the diff but performs no network calls (upload/delete).
	DryRun bool

	// Concurrency caps CPU-bound work (file walk, MD5 hashing). HTTP upload
	// parallelism is governed separately (default 3).
	Concurrency int
}

R2SyncOptions configures a single sync invocation.

type R2SyncResult

type R2SyncResult struct {
	Attempted int // local files considered
	Succeeded int // uploads completed
	Skipped   int // remote already had matching MD5
	Failed    int // uploads that exhausted retries
	Pruned    int // remote objects deleted (--prune)
	Failures  []R2FileFailure
}

R2SyncResult is the per-invocation outcome. Counts mirror deploy.Report semantics but are scoped to R2 only.

type R2Syncer

type R2Syncer struct {
	// contains filtered or unexported fields
}

R2Syncer uploads local files to a Cloudflare R2 bucket via S3-compatible API. See ADR 0002 §6 for the strategy.

func NewR2Syncer

func NewR2Syncer(cfg R2Config, logger *observability.Logger) (*R2Syncer, error)

NewR2Syncer constructs an R2Syncer with a minio-go-backed client. accountID and access keys come from cloudflare.Config.R2; for test substitution use NewR2SyncerWithClient.

func NewR2SyncerWithClient

func NewR2SyncerWithClient(client r2ObjectClient, bucket string, logger *observability.Logger) *R2Syncer

NewR2SyncerWithClient lets tests inject a mock r2ObjectClient.

func (*R2Syncer) Sync

func (s *R2Syncer) Sync(ctx context.Context, mappings []SyncMapping, opts R2SyncOptions) (*R2SyncResult, error)

Sync runs the R2 sync algorithm against the given local-to-remote mappings.

Algorithm:

  1. Verify bucket exists (fail fast if not — ADR 0002 §10).
  2. Walk local paths → build {key → localPath} map for each sync.from→to.
  3. For each unique "to" prefix, list remote objects in one call.
  4. Compare: skip if remote exists with matching MD5; upload otherwise.
  5. If Prune: delete remote keys not in local set.

Per ADR 0002 §9 collection-not-interruption: per-file failures are collected into Failures; the sync continues.

type RetryDecision

type RetryDecision struct {
	Retryable bool
	Reason    string
}

RetryDecision classifies an HTTP response/error as retryable or fatal.

func ClassifyError

func ClassifyError(resp *http.Response, err error) RetryDecision

ClassifyError inspects the HTTP response (or underlying error) and decides whether the caller should retry.

Retryable conditions (per ADR 0002 §9):

  • HTTP 5xx (server errors, includes 500 gateway errors)
  • HTTP 429 (rate limit)
  • Network errors (DNS, connection refused, timeout) — but NOT context cancellation, which propagates immediately

Fatal conditions:

  • HTTP 4xx (other than 429) — auth failures, malformed requests, etc.
  • HTTP 2xx — success, no retry needed
  • nil response AND nil err — programmer error
  • Context cancellation / deadline exceeded

type SyncMapping

type SyncMapping struct {
	// From is the local directory (or single file) to upload.
	From string `yaml:"from" json:"from"`

	// To is the remote key prefix (without trailing slash).
	// For directory mappings, files become <To>/<relative-path>.
	// For single-file mappings, To becomes the key directly.
	To string `yaml:"to" json:"to"`
}

SyncMapping declares one local-to-remote path mapping for R2 sync.

type WorkerBinding

type WorkerBinding struct {
	Type        string `yaml:"type"        json:"type"`
	Name        string `yaml:"name"        json:"name"` // env var name in Worker (e.g. "R2_BUCKET")
	Bucket      string `yaml:"bucket"      json:"bucket,omitempty"`
	NamespaceID string `yaml:"namespaceId" json:"namespace_id,omitempty"`
	ID          string `yaml:"id"          json:"id,omitempty"`
	Value       string `yaml:"value"       json:"value,omitempty"`
}

WorkerBinding declares one resource binding for a Worker.

Type values supported by CF Workers modules API:

  • "r2_bucket" — R2 bucket binding (requires Bucket)
  • "kv_namespace" — KV namespace (requires NamespaceID)
  • "vars" — plain-text env var (requires Value)
  • "secret_text" — secret env var (requires Value; this is the non-Wrangler-managed variant)
  • "d1" — D1 database (requires ID)

huan does NOT validate binding types — it serializes what you declare and lets CF reject unknown types. This keeps the surface minimal as CF adds new binding kinds.

type WorkerConfig

type WorkerConfig struct {
	// Name is the Worker script name (must match across deploys; renames are
	// not supported by the CF API). Required.
	Name string `yaml:"name" json:"name"`

	// Script is the local path (relative to huan.yaml dir) of the single-file
	// ES module .js source. Required.
	Script string `yaml:"script" json:"script"`

	// CompatibilityDate defaults to "2024-01-01" if empty.
	CompatibilityDate string `yaml:"compatibilityDate" json:"compatibilityDate"`

	// Bindings declares resources the Worker can access (R2 buckets, KV
	// namespaces, env vars, etc.). huan serializes them into the upload
	// metadata JSON. See WorkerBinding for supported types.
	Bindings []WorkerBinding `yaml:"bindings" json:"bindings"`

	// Routes declares route patterns the Worker handles. Each entry must
	// include Pattern and Zone (zone name like "zhurongshuo.com").
	Routes []WorkerRoute `yaml:"routes" json:"routes"`
}

WorkerConfig captures Cloudflare Workers settings for the modules API (PUT /accounts/{id}/workers/scripts/{name}).

type WorkerDeployResult

type WorkerDeployResult struct {
	// ScriptName (echoes request).
	ScriptName string `json:"-"`

	// ModifiedOn is the raw timestamp string from CF (typically RFC3339).
	ModifiedOn string `json:"modified_on"`

	// UsageModel (default "bundled").
	UsageModel string `json:"usage_model"`

	// Handler is the entrypoint (typically "default" for ES modules).
	Handler string `json:"handler"`
}

WorkerDeployResult models the response from PUT .../workers/scripts/{name}.

Audit M3: ModifiedOn is a string (not time.Time) so we always log what CF actually returned, even if their format shifts (sub-microsecond precision, non-RFC3339 variants). Go's default time.Time JSON unmarshal requires strict RFC3339 — a parse failure leaves a zero time which is misleading. Strings preserve the raw value; callers can time.Parse on the string if needed.

type WorkerDeployer

type WorkerDeployer struct {
	// contains filtered or unexported fields
}

WorkerDeployer uploads a Worker script via the CF Workers modules API.

func NewWorkerDeployer

func NewWorkerDeployer(client *Client, logger *observability.Logger) *WorkerDeployer

NewWorkerDeployer returns a deployer using the given client.

func (*WorkerDeployer) Deploy

Deploy uploads the Worker script + metadata. The metadata JSON includes main_module (script basename), compatibility_date, bindings, and routes.

Algorithm per ADR 0002 §8:

  1. Read Script file relative to opts.SourceDir.
  2. Build metadata JSON.
  3. PUT multipart/form-data to /accounts/{id}/workers/scripts/{name}: - part "metadata": application/json - part "<basename>": application/javascript+module with filename set

type WorkerRoute

type WorkerRoute struct {
	Pattern string `yaml:"pattern" json:"pattern"`        // e.g. "r2.zhurongshuo.com/*"
	Zone    string `yaml:"zone"    json:"zone,omitempty"` // zone name (e.g. "zhurongshuo.com")
}

WorkerRoute declares one route pattern + zone for the Worker.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL