Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# EditorConfig helps maintain consistent coding styles
# This file takes precedence over VS Code user settings
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.{js,ts,json,css,html}]
indent_style = space
indent_size = 2

[*.py]
indent_style = space
indent_size = 4

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @dryan-org/code-reviewers
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: CI

on:
pull_request:
push:
branches: [main, master]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
ci:
uses: dryan/reusable-workflows/.github/workflows/dependabot-ci.yml@v1
150 changes: 150 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
### Node ###
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.*
!.env.example

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist
.output

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp directory
.temp

# Sveltekit cache directory
.svelte-kit/

# vitepress build output
**/.vitepress/dist

# vitepress cache directory
**/.vitepress/cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# Firebase cache directory
.firebase/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# pnpm
.pnpm-store

# yarn v3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/

# heartbeat
.wrangler/
.dev.vars
dist/
coverage/
4 changes: 4 additions & 0 deletions .prettierrc.json5
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
// Using Prettier defaults - no custom configuration
// This ensures consistency across all developers
}
158 changes: 158 additions & 0 deletions LICENSE.md

Large diffs are not rendered by default.

73 changes: 72 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,73 @@
# worker-kit
Shared helpers for Cloudflare Workers: transient D1 retry and Sentry/GlitchTip grouping

Shared helpers for Cloudflare Workers, so every Worker handles Cloudflare's
own transient failures the same way.

- **`retryD1(run, options?)`**: run a D1 statement or batch and retry it on a
transient D1 error (4 tries, full-jitter backoff capped at 200, 400, 800 ms).
Only wrap reads and writes that are safe to replay.
- **`isTransientD1Error(error)`**: the one list of D1 messages that clear on
their own ("Network connection lost", "storage caused object to be reset",
"internal error; reference = ...", and so on). A new variant gets added here
once, then every Worker picks it up on the next version bump.
- **`retryDurableObject(run, options?)`** / **`isRetryableDOError(error)`**:
the same for Durable Object calls, honouring the runtime's `retryable` and
`overloaded` flags.
- **`retryTransient(run, options)`**: the generic retry loop under both, for
anything else that is safe to replay (an upstream API, a token mint).
- **`groupCloudflareErrors(event, hint)`**: a Sentry `beforeSend` step. It
strips Cloudflare's per-occurrence `reference = ...` from the message (kept
as the `cf_reference` tag) and fingerprints any transient error that survived
the retries as `cloudflare-transient`. Without it, GlitchTip opened one issue
per failed request, since every message was unique.

## Install

Public on purpose: no token is needed for `npm ci` in GitHub Actions or
Workers Builds. Pin a tag:

```zsh
npm install github:dryan-org/worker-kit#v0.1.0
```

The package ships TypeScript source (`src/*.ts`). Wrangler's bundler, `tsc`
with `moduleResolution: "Bundler"` and Vitest all read it directly; there is
no build step.

## Use

```ts
import * as Sentry from "@sentry/cloudflare";
import { groupCloudflareErrors, retryD1 } from "@dryan/worker-kit";

const row = await retryD1(() =>
env.DB.prepare("SELECT * FROM t WHERE id = ?").bind(id).first(),
);

export default Sentry.withSentry(
(env: Env) => ({
dsn: env.SENTRY_DSN,
beforeSend: groupCloudflareErrors,
}),
handler,
);
```

Prepare the statement inside the callback: each retry calls it again, and a
statement from a reset connection is not guaranteed to be reusable.

## Release

Bump `version` in `package.json`, merge, tag `v<version>` on `main`, then
bump the tag in each consumer.

## Develop

```zsh
npm install
npm test
```

## License

[Hippocratic License 3.0](LICENSE.md).
Loading
Loading