From 30b63c61b55a7c403f900931acd5e8446d1b856e Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 15:15:41 +0200
Subject: [PATCH 1/8] refactor: rename package scope from @mondaydotcomorg to
@mondaycom
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Update the npm scope across all three publishable packages:
- @mondaydotcomorg/hatcha-core → @mondaycom/hatcha-core
- @mondaydotcomorg/hatcha-server → @mondaycom/hatcha-server
- @mondaydotcomorg/hatcha-react → @mondaycom/hatcha-react
Includes package.json name fields, workspace:* dependency
references, and all source-level imports/re-exports.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
packages/core/package.json | 2 +-
packages/core/src/types.ts | 2 +-
packages/react/package.json | 4 ++--
packages/react/src/hatcha.tsx | 2 +-
packages/server/package.json | 4 ++--
packages/server/src/express.ts | 2 +-
packages/server/src/handler.ts | 2 +-
packages/server/src/index.ts | 4 ++--
packages/server/src/nextjs.ts | 2 +-
9 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/packages/core/package.json b/packages/core/package.json
index 6af846b..05dd74b 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,5 +1,5 @@
{
- "name": "@mondaydotcomorg/hatcha-core",
+ "name": "@mondaycom/hatcha-core",
"version": "0.1.0",
"description": "Challenge generation and cryptographic verification for HATCHA",
"type": "module",
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 3d82f7e..d069f3a 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -1,5 +1,5 @@
/* ------------------------------------------------------------------ */
-/* Public types for @mondaydotcomorg/hatcha-core */
+/* Public types for @mondaycom/hatcha-core */
/* ------------------------------------------------------------------ */
/** What the client sees — never includes the answer. */
diff --git a/packages/react/package.json b/packages/react/package.json
index eb603f7..0e28a99 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -1,5 +1,5 @@
{
- "name": "@mondaydotcomorg/hatcha-react",
+ "name": "@mondaycom/hatcha-react",
"version": "0.1.0",
"description": "React component for HATCHA — reverse CAPTCHA for agents",
"type": "module",
@@ -24,7 +24,7 @@
"clean": "rm -rf dist"
},
"dependencies": {
- "@mondaydotcomorg/hatcha-core": "workspace:*"
+ "@mondaycom/hatcha-core": "workspace:*"
},
"peerDependencies": {
"react": "^18 || ^19",
diff --git a/packages/react/src/hatcha.tsx b/packages/react/src/hatcha.tsx
index 0acc135..e2677f4 100644
--- a/packages/react/src/hatcha.tsx
+++ b/packages/react/src/hatcha.tsx
@@ -7,7 +7,7 @@ import {
useRef,
type KeyboardEvent,
} from "react";
-import type { ChallengeDisplay } from "@mondaydotcomorg/hatcha-core";
+import type { ChallengeDisplay } from "@mondaycom/hatcha-core";
/* ------------------------------------------------------------------ */
/* Props */
diff --git a/packages/server/package.json b/packages/server/package.json
index 4378199..d3ad588 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,5 +1,5 @@
{
- "name": "@mondaydotcomorg/hatcha-server",
+ "name": "@mondaycom/hatcha-server",
"version": "0.1.0",
"description": "Server middleware for HATCHA — Next.js and Express handlers",
"type": "module",
@@ -34,7 +34,7 @@
"test": "vitest run"
},
"dependencies": {
- "@mondaydotcomorg/hatcha-core": "workspace:*"
+ "@mondaycom/hatcha-core": "workspace:*"
},
"devDependencies": {
"@types/express": "^5",
diff --git a/packages/server/src/express.ts b/packages/server/src/express.ts
index 5a635c1..7737af9 100644
--- a/packages/server/src/express.ts
+++ b/packages/server/src/express.ts
@@ -10,7 +10,7 @@ import {
* Usage:
*
* import express from "express";
- * import { hatchaRouter } from "@mondaydotcomorg/hatcha-server/express";
+ * import { hatchaRouter } from "@mondaycom/hatcha-server/express";
*
* const app = express();
* app.use(express.json());
diff --git a/packages/server/src/handler.ts b/packages/server/src/handler.ts
index 00f2b87..b7cbb0f 100644
--- a/packages/server/src/handler.ts
+++ b/packages/server/src/handler.ts
@@ -2,7 +2,7 @@ import {
createChallenge,
verifyAnswer,
type HatchaConfig,
-} from "@mondaydotcomorg/hatcha-core";
+} from "@mondaycom/hatcha-core";
export interface HatchaServerConfig {
/** HMAC secret for signing challenge tokens. */
diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts
index 3c3e8d3..885bcaf 100644
--- a/packages/server/src/index.ts
+++ b/packages/server/src/index.ts
@@ -3,9 +3,9 @@ export type { HatchaServerConfig } from "./handler.js";
export { createHatchaHandler } from "./nextjs.js";
export { hatchaRouter } from "./express.js";
-export { registerChallenge, getGenerators } from "@mondaydotcomorg/hatcha-core";
+export { registerChallenge, getGenerators } from "@mondaycom/hatcha-core";
export type {
ChallengeGenerator,
ChallengeDisplay,
HatchaConfig,
-} from "@mondaydotcomorg/hatcha-core";
+} from "@mondaycom/hatcha-core";
diff --git a/packages/server/src/nextjs.ts b/packages/server/src/nextjs.ts
index 8ea9a8e..cf7a107 100644
--- a/packages/server/src/nextjs.ts
+++ b/packages/server/src/nextjs.ts
@@ -9,7 +9,7 @@ import {
*
* Usage (app/api/hatcha/[...hatcha]/route.ts):
*
- * import { createHatchaHandler } from "@mondaydotcomorg/hatcha-server/nextjs";
+ * import { createHatchaHandler } from "@mondaycom/hatcha-server/nextjs";
*
* const handler = createHatchaHandler({
* secret: process.env.HATCHA_SECRET!,
From 113e2271cd66488c386679c020d783d9ef5c5ac0 Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 15:15:59 +0200
Subject: [PATCH 2/8] refactor: update example app imports to @mondaycom scope
Rename all @mondaydotcomorg references in the Next.js example app:
- package.json workspace dependencies
- API route imports (challenge, verify)
- Layout provider and CSS imports
- Page hook import
Co-Authored-By: Claude Opus 4.6 (1M context)
---
examples/nextjs-app/app/api/hatcha/challenge/route.ts | 2 +-
examples/nextjs-app/app/api/hatcha/verify/route.ts | 2 +-
examples/nextjs-app/app/layout.tsx | 4 ++--
examples/nextjs-app/app/page.tsx | 2 +-
examples/nextjs-app/package.json | 6 +++---
5 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/examples/nextjs-app/app/api/hatcha/challenge/route.ts b/examples/nextjs-app/app/api/hatcha/challenge/route.ts
index a5982f7..46c6b87 100644
--- a/examples/nextjs-app/app/api/hatcha/challenge/route.ts
+++ b/examples/nextjs-app/app/api/hatcha/challenge/route.ts
@@ -1,4 +1,4 @@
-import { createChallenge } from "@mondaydotcomorg/hatcha-core";
+import { createChallenge } from "@mondaycom/hatcha-core";
export async function GET() {
const payload = await createChallenge({
diff --git a/examples/nextjs-app/app/api/hatcha/verify/route.ts b/examples/nextjs-app/app/api/hatcha/verify/route.ts
index f361e38..36adbdd 100644
--- a/examples/nextjs-app/app/api/hatcha/verify/route.ts
+++ b/examples/nextjs-app/app/api/hatcha/verify/route.ts
@@ -1,4 +1,4 @@
-import { verifyAnswer } from "@mondaydotcomorg/hatcha-core";
+import { verifyAnswer } from "@mondaycom/hatcha-core";
export async function POST(request: Request) {
const body = await request.json();
diff --git a/examples/nextjs-app/app/layout.tsx b/examples/nextjs-app/app/layout.tsx
index fb34c3c..8d00968 100644
--- a/examples/nextjs-app/app/layout.tsx
+++ b/examples/nextjs-app/app/layout.tsx
@@ -1,6 +1,6 @@
import type { Metadata } from "next";
-import { HatchaProvider } from "@mondaydotcomorg/hatcha-react";
-import "@mondaydotcomorg/hatcha-react/styles.css";
+import { HatchaProvider } from "@mondaycom/hatcha-react";
+import "@mondaycom/hatcha-react/styles.css";
import "./globals.css";
export const metadata: Metadata = {
diff --git a/examples/nextjs-app/app/page.tsx b/examples/nextjs-app/app/page.tsx
index da5e1d7..9caf12f 100644
--- a/examples/nextjs-app/app/page.tsx
+++ b/examples/nextjs-app/app/page.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState } from "react";
-import { useHatcha } from "@mondaydotcomorg/hatcha-react";
+import { useHatcha } from "@mondaycom/hatcha-react";
export default function Home() {
const { requestVerification } = useHatcha();
diff --git a/examples/nextjs-app/package.json b/examples/nextjs-app/package.json
index f1c9b4e..6a84f35 100644
--- a/examples/nextjs-app/package.json
+++ b/examples/nextjs-app/package.json
@@ -8,9 +8,9 @@
"start": "next start"
},
"dependencies": {
- "@mondaydotcomorg/hatcha-core": "workspace:*",
- "@mondaydotcomorg/hatcha-react": "workspace:*",
- "@mondaydotcomorg/hatcha-server": "workspace:*",
+ "@mondaycom/hatcha-core": "workspace:*",
+ "@mondaycom/hatcha-react": "workspace:*",
+ "@mondaycom/hatcha-server": "workspace:*",
"next": "^15",
"react": "^19",
"react-dom": "^19"
From e3d0bb23cb32cb78ff66ab16fd105b18eb26db49 Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 15:16:11 +0200
Subject: [PATCH 3/8] docs: update package references to @mondaycom scope
- README.md: update install commands, code examples, and packages
table to use @mondaycom scope
- CONTRIBUTING.md: fix stale @hatcha/core reference to
@mondaycom/hatcha-core
Co-Authored-By: Claude Opus 4.6 (1M context)
---
CONTRIBUTING.md | 2 +-
README.md | 22 +++++++++++-----------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fd40910..7fb5cd9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,7 +65,7 @@ pnpm test
## Code Style
- TypeScript throughout, strict mode.
-- No external runtime dependencies in `@hatcha/core`.
+- No external runtime dependencies in `@mondaycom/hatcha-core`.
- Keep bundle sizes minimal — the library should stay lightweight.
## License
diff --git a/README.md b/README.md
index 0cef88a..91710b8 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
-
+
@@ -28,14 +28,14 @@ HATCHA (**H**yperfast **A**gent **T**est for **C**omputational **H**euristic **A
### 1. Install
```bash
-npm install @mondaydotcomorg/hatcha-react @mondaydotcomorg/hatcha-server
+npm install @mondaycom/hatcha-react @mondaycom/hatcha-server
```
### 2. Add the API route
```typescript
// app/api/hatcha/[...hatcha]/route.ts
-import { createHatchaHandler } from "@mondaydotcomorg/hatcha-server/nextjs";
+import { createHatchaHandler } from "@mondaycom/hatcha-server/nextjs";
const handler = createHatchaHandler({
secret: process.env.HATCHA_SECRET!,
@@ -49,8 +49,8 @@ export const POST = handler;
```tsx
// app/layout.tsx
-import { HatchaProvider } from "@mondaydotcomorg/hatcha-react";
-import "@mondaydotcomorg/hatcha-react/styles.css";
+import { HatchaProvider } from "@mondaycom/hatcha-react";
+import "@mondaycom/hatcha-react/styles.css";
export default function RootLayout({ children }) {
return (
@@ -67,7 +67,7 @@ export default function RootLayout({ children }) {
```tsx
"use client";
-import { useHatcha } from "@mondaydotcomorg/hatcha-react";
+import { useHatcha } from "@mondaycom/hatcha-react";
function AgentModeButton() {
const { requestVerification } = useHatcha();
@@ -133,7 +133,7 @@ The answer **never** reaches the client. The signed token is opaque and contains
## Custom challenges
```typescript
-import { registerChallenge } from "@mondaydotcomorg/hatcha-server";
+import { registerChallenge } from "@mondaycom/hatcha-server";
registerChallenge({
type: "hex",
@@ -176,7 +176,7 @@ Pass `theme="dark"`, `theme="light"`, or `theme="auto"` to `` or
```typescript
import express from "express";
-import { hatchaRouter } from "@mondaydotcomorg/hatcha-server/express";
+import { hatchaRouter } from "@mondaycom/hatcha-server/express";
const app = express();
app.use(express.json());
@@ -189,9 +189,9 @@ app.listen(3000);
| Package | Description |
|---------|-------------|
-| [`@mondaydotcomorg/hatcha-core`](./packages/core) | Challenge generation and cryptographic verification |
-| [`@mondaydotcomorg/hatcha-react`](./packages/react) | React component, provider, and styles |
-| [`@mondaydotcomorg/hatcha-server`](./packages/server) | Next.js and Express server handlers |
+| [`@mondaycom/hatcha-core`](./packages/core) | Challenge generation and cryptographic verification |
+| [`@mondaycom/hatcha-react`](./packages/react) | React component, provider, and styles |
+| [`@mondaycom/hatcha-server`](./packages/server) | Next.js and Express server handlers |
## Development
From 8a15adfd85e7b1db8cd45c516c47419f21e89a88 Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 15:16:33 +0200
Subject: [PATCH 4/8] chore: regenerate pnpm-lock.yaml for @mondaycom scope
Lockfile regenerated after renaming all workspace packages
from @mondaydotcomorg to @mondaycom.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
pnpm-lock.yaml | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 20de558..408eb09 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -20,13 +20,13 @@ importers:
examples/nextjs-app:
dependencies:
- '@mondaydotcomorg/hatcha-core':
+ '@mondaycom/hatcha-core':
specifier: workspace:*
version: link:../../packages/core
- '@mondaydotcomorg/hatcha-react':
+ '@mondaycom/hatcha-react':
specifier: workspace:*
version: link:../../packages/react
- '@mondaydotcomorg/hatcha-server':
+ '@mondaycom/hatcha-server':
specifier: workspace:*
version: link:../../packages/server
next:
@@ -60,7 +60,7 @@ importers:
packages/react:
dependencies:
- '@mondaydotcomorg/hatcha-core':
+ '@mondaycom/hatcha-core':
specifier: workspace:*
version: link:../core
devDependencies:
@@ -85,7 +85,7 @@ importers:
packages/server:
dependencies:
- '@mondaydotcomorg/hatcha-core':
+ '@mondaycom/hatcha-core':
specifier: workspace:*
version: link:../core
devDependencies:
From 3becbc91e5cd8da43dfb0f325468833b8e4e9bc0 Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 15:17:10 +0200
Subject: [PATCH 5/8] ci: add manual release workflow for npm publishing
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add workflow_dispatch release workflow with:
- Input fields: bump type (patch/minor/major) with semver hints,
per-package release toggles (core, server, react)
- Validation: requires at least one package, blocks releasing
server/react without core
- Pipeline: bump versions → update lockfile → build → test →
typecheck → publish to npm → commit & tag → GitHub Releases
- Security: npm provenance attestation (--provenance), env vars
for all expressions, concurrency control
- Publish order: core first, then server, then react (respects
dependency graph)
- npm publish happens before git push to prevent orphaned
version bumps on publish failure
- GitHub Releases with auto-generated notes
- Summary table in workflow run UI
Requires NPM_TOKEN repository secret and contents:write +
id-token:write permissions (configured in workflow).
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.github/workflows/release.yml | 223 ++++++++++++++++++++++++++++++++++
1 file changed, 223 insertions(+)
create mode 100644 .github/workflows/release.yml
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..bce9c3d
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,223 @@
+name: Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ bump:
+ description: "patch: bug fixes (0.1.0→0.1.1) | minor: new features (0.1.0→0.2.0) | major: breaking changes (0.1.0→1.0.0)"
+ type: choice
+ default: "patch"
+ options:
+ - patch
+ - minor
+ - major
+ release_core:
+ description: "Release @mondaycom/hatcha-core"
+ type: boolean
+ default: true
+ release_server:
+ description: "Release @mondaycom/hatcha-server"
+ type: boolean
+ default: true
+ release_react:
+ description: "Release @mondaycom/hatcha-react"
+ type: boolean
+ default: true
+
+permissions:
+ contents: write
+ id-token: write
+
+concurrency:
+ group: release
+ cancel-in-progress: false
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - uses: pnpm/action-setup@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 22
+ cache: pnpm
+ registry-url: "https://registry.npmjs.org"
+
+ - run: pnpm install --frozen-lockfile
+
+ - name: Validate selection
+ env:
+ RELEASE_CORE: ${{ inputs.release_core }}
+ RELEASE_SERVER: ${{ inputs.release_server }}
+ RELEASE_REACT: ${{ inputs.release_react }}
+ run: |
+ if [[ "$RELEASE_CORE" != "true" && \
+ "$RELEASE_SERVER" != "true" && \
+ "$RELEASE_REACT" != "true" ]]; then
+ echo "::error::No packages selected for release"
+ exit 1
+ fi
+ if [[ "$RELEASE_CORE" != "true" ]]; then
+ if [[ "$RELEASE_SERVER" == "true" || "$RELEASE_REACT" == "true" ]]; then
+ echo "::error::Cannot release server/react without core. Core must be included to ensure dependency versions are correct."
+ exit 1
+ fi
+ fi
+
+ - name: Bump versions
+ id: versions
+ env:
+ BUMP: ${{ inputs.bump }}
+ RELEASE_CORE: ${{ inputs.release_core }}
+ RELEASE_SERVER: ${{ inputs.release_server }}
+ RELEASE_REACT: ${{ inputs.release_react }}
+ run: |
+ if [[ "$RELEASE_CORE" == "true" ]]; then
+ cd packages/core
+ NEW_VERSION=$(npm version "$BUMP" --no-git-tag-version)
+ echo "core_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
+ echo "Core bumped to ${NEW_VERSION}"
+ cd ../..
+ fi
+
+ if [[ "$RELEASE_SERVER" == "true" ]]; then
+ cd packages/server
+ NEW_VERSION=$(npm version "$BUMP" --no-git-tag-version)
+ echo "server_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
+ echo "Server bumped to ${NEW_VERSION}"
+ cd ../..
+ fi
+
+ if [[ "$RELEASE_REACT" == "true" ]]; then
+ cd packages/react
+ NEW_VERSION=$(npm version "$BUMP" --no-git-tag-version)
+ echo "react_version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
+ echo "React bumped to ${NEW_VERSION}"
+ cd ../..
+ fi
+
+ - name: Update lockfile
+ run: pnpm install --no-frozen-lockfile
+
+ - name: Build
+ run: pnpm build
+
+ - name: Test
+ run: pnpm test
+
+ - name: Typecheck
+ run: pnpm typecheck
+
+ - name: Publish core
+ if: inputs.release_core
+ run: pnpm --filter @mondaycom/hatcha-core publish --no-git-checks --provenance
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - name: Publish server
+ if: inputs.release_server
+ run: pnpm --filter @mondaycom/hatcha-server publish --no-git-checks --provenance
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - name: Publish react
+ if: inputs.release_react
+ run: pnpm --filter @mondaycom/hatcha-react publish --no-git-checks --provenance
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
+
+ - name: Commit and tag
+ env:
+ CORE_VERSION: ${{ steps.versions.outputs.core_version }}
+ SERVER_VERSION: ${{ steps.versions.outputs.server_version }}
+ REACT_VERSION: ${{ steps.versions.outputs.react_version }}
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ git add packages/*/package.json pnpm-lock.yaml
+
+ MSG="release:"
+ TAGS=()
+
+ if [[ -n "$CORE_VERSION" ]]; then
+ MSG="${MSG} core@${CORE_VERSION}"
+ TAGS+=("@mondaycom/hatcha-core@${CORE_VERSION}")
+ fi
+ if [[ -n "$SERVER_VERSION" ]]; then
+ MSG="${MSG} server@${SERVER_VERSION}"
+ TAGS+=("@mondaycom/hatcha-server@${SERVER_VERSION}")
+ fi
+ if [[ -n "$REACT_VERSION" ]]; then
+ MSG="${MSG} react@${REACT_VERSION}"
+ TAGS+=("@mondaycom/hatcha-react@${REACT_VERSION}")
+ fi
+
+ git commit -m "${MSG}"
+
+ for TAG in "${TAGS[@]}"; do
+ git tag "${TAG}"
+ done
+
+ git push origin HEAD --tags
+
+ - name: Create GitHub Releases
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ CORE_VERSION: ${{ steps.versions.outputs.core_version }}
+ SERVER_VERSION: ${{ steps.versions.outputs.server_version }}
+ REACT_VERSION: ${{ steps.versions.outputs.react_version }}
+ run: |
+ LAST_TAG=""
+
+ if [[ -n "$CORE_VERSION" ]]; then
+ LAST_TAG="@mondaycom/hatcha-core@${CORE_VERSION}"
+ gh release create "$LAST_TAG" \
+ --title "hatcha-core ${CORE_VERSION}" \
+ --generate-notes \
+ --latest=false
+ fi
+ if [[ -n "$SERVER_VERSION" ]]; then
+ LAST_TAG="@mondaycom/hatcha-server@${SERVER_VERSION}"
+ gh release create "$LAST_TAG" \
+ --title "hatcha-server ${SERVER_VERSION}" \
+ --generate-notes \
+ --latest=false
+ fi
+ if [[ -n "$REACT_VERSION" ]]; then
+ LAST_TAG="@mondaycom/hatcha-react@${REACT_VERSION}"
+ gh release create "$LAST_TAG" \
+ --title "hatcha-react ${REACT_VERSION}" \
+ --generate-notes \
+ --latest=false
+ fi
+
+ # Mark the last release as "Latest"
+ if [[ -n "$LAST_TAG" ]]; then
+ gh release edit "$LAST_TAG" --latest
+ fi
+
+ - name: Summary
+ env:
+ CORE_VERSION: ${{ steps.versions.outputs.core_version }}
+ SERVER_VERSION: ${{ steps.versions.outputs.server_version }}
+ REACT_VERSION: ${{ steps.versions.outputs.react_version }}
+ run: |
+ echo "## Release Summary" >> "$GITHUB_STEP_SUMMARY"
+ echo "" >> "$GITHUB_STEP_SUMMARY"
+ echo "| Package | Version |" >> "$GITHUB_STEP_SUMMARY"
+ echo "|---------|---------|" >> "$GITHUB_STEP_SUMMARY"
+ if [[ -n "$CORE_VERSION" ]]; then
+ echo "| @mondaycom/hatcha-core | ${CORE_VERSION} |" >> "$GITHUB_STEP_SUMMARY"
+ fi
+ if [[ -n "$SERVER_VERSION" ]]; then
+ echo "| @mondaycom/hatcha-server | ${SERVER_VERSION} |" >> "$GITHUB_STEP_SUMMARY"
+ fi
+ if [[ -n "$REACT_VERSION" ]]; then
+ echo "| @mondaycom/hatcha-react | ${REACT_VERSION} |" >> "$GITHUB_STEP_SUMMARY"
+ fi
From e6f58e1f227a5856f4aae336c8d45de80360c3f7 Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 16:37:05 +0200
Subject: [PATCH 6/8] fix: resolve TypeScript strict mode errors in core and
server tests
- crypto.ts: merge split try-catch blocks so Uint8Array uses const
inference (Uint8Array) instead of let annotation
(Uint8Array), fixing TS2345 with crypto.subtle.verify
- e2e.test.ts: replace unsafe cast to Record with
idiomatic "answer" in challenge check, fixing TS2352
- handler.test.ts: same Record cast fix for ChallengeDisplay, and
use "error" in result for union type narrowing on VerifyResult
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.github/workflows/release.yml | 2 +-
packages/core/src/__tests__/e2e.test.ts | 2 +-
packages/core/src/crypto.ts | 16 +++++-----------
packages/server/src/__tests__/handler.test.ts | 6 +++---
4 files changed, 10 insertions(+), 16 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bce9c3d..3d29e80 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -164,7 +164,7 @@ jobs:
git tag "${TAG}"
done
- git push origin HEAD --tags
+ git push origin HEAD --tags --atomic
- name: Create GitHub Releases
env:
diff --git a/packages/core/src/__tests__/e2e.test.ts b/packages/core/src/__tests__/e2e.test.ts
index 82a8f4d..6a1d09c 100644
--- a/packages/core/src/__tests__/e2e.test.ts
+++ b/packages/core/src/__tests__/e2e.test.ts
@@ -19,7 +19,7 @@ describe("createChallenge", () => {
expect(typeof challenge.prompt).toBe("string");
expect(typeof challenge.timeLimit).toBe("number");
expect(challenge.timeLimit).toBeGreaterThan(0);
- expect((challenge as Record).answer).toBeUndefined();
+ expect("answer" in challenge).toBe(false);
expect(typeof token).toBe("string");
expect(token).toContain(".");
});
diff --git a/packages/core/src/crypto.ts b/packages/core/src/crypto.ts
index a3a2fb2..8f54aa3 100644
--- a/packages/core/src/crypto.ts
+++ b/packages/core/src/crypto.ts
@@ -69,20 +69,14 @@ export async function verifyToken(
const [payloadB64, sigB64] = parts;
- let payloadBytes: Uint8Array;
- let sigBytes: Uint8Array;
try {
- payloadBytes = Uint8Array.from(atob(payloadB64), (c) => c.charCodeAt(0));
- sigBytes = Uint8Array.from(atob(sigB64), (c) => c.charCodeAt(0));
- } catch {
- return null;
- }
+ const payloadBytes = Uint8Array.from(atob(payloadB64), (c) => c.charCodeAt(0));
+ const sigBytes = Uint8Array.from(atob(sigB64), (c) => c.charCodeAt(0));
- const key = await getKey(secret);
- const valid = await crypto.subtle.verify("HMAC", key, sigBytes, payloadBytes);
- if (!valid) return null;
+ const key = await getKey(secret);
+ const valid = await crypto.subtle.verify("HMAC", key, sigBytes, payloadBytes);
+ if (!valid) return null;
- try {
return JSON.parse(new TextDecoder().decode(payloadBytes)) as TokenPayload;
} catch {
return null;
diff --git a/packages/server/src/__tests__/handler.test.ts b/packages/server/src/__tests__/handler.test.ts
index b6daab7..8251b7b 100644
--- a/packages/server/src/__tests__/handler.test.ts
+++ b/packages/server/src/__tests__/handler.test.ts
@@ -11,7 +11,7 @@ describe("handleChallenge", () => {
expect(typeof result.challenge.type).toBe("string");
expect(typeof result.challenge.prompt).toBe("string");
expect(typeof result.token).toBe("string");
- expect((result.challenge as Record).answer).toBeUndefined();
+ expect("answer" in result.challenge).toBe(false);
});
it("respects challengeTypes option", async () => {
@@ -48,7 +48,7 @@ describe("handleVerify", () => {
it("returns error when answer is missing", async () => {
const result = await handleVerify(config, { token: "some-token" });
expect(result.success).toBe(false);
- expect((result as Record).error).toBe(
+ expect("error" in result && result.error).toBe(
"Missing answer or token.",
);
});
@@ -56,7 +56,7 @@ describe("handleVerify", () => {
it("returns error when token is missing", async () => {
const result = await handleVerify(config, { answer: "some-answer" });
expect(result.success).toBe(false);
- expect((result as Record).error).toBe(
+ expect("error" in result && result.error).toBe(
"Missing answer or token.",
);
});
From a81d70964eb066dea186058cbfe8afd303c91ac4 Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 16:47:24 +0200
Subject: [PATCH 7/8] chore: add .nvmrc and use it in release workflow
Add .nvmrc with Node 22.14 as the project-level Node version.
Update release workflow to read from .nvmrc instead of
hardcoding the version.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.github/workflows/release.yml | 4 ++--
.nvmrc | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
create mode 100644 .nvmrc
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3d29e80..4f3df92 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -44,7 +44,7 @@ jobs:
- uses: actions/setup-node@v4
with:
- node-version: 22
+ node-version-file: '.nvmrc'
cache: pnpm
registry-url: "https://registry.npmjs.org"
@@ -142,7 +142,7 @@ jobs:
git add packages/*/package.json pnpm-lock.yaml
- MSG="release:"
+ MSG="release [skip ci]:"
TAGS=()
if [[ -n "$CORE_VERSION" ]]; then
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000..744ca17
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+22.14
From 5a676575d756eb07df61de4117064abad6bb2003 Mon Sep 17 00:00:00 2001
From: Yossi Saadi
Date: Tue, 10 Mar 2026 16:59:39 +0200
Subject: [PATCH 8/8] ci: drop Node 18 from CI matrix (EOL, no global crypto
support)
Node 18 reached end-of-life in April 2025 and does not support
the global Web Crypto API (crypto.subtle) that the codebase
uses. This caused all tests to fail with "crypto is not defined",
and fail-fast cancelled the Node 20/22 jobs.
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 22d7bda..798f4b3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
- node-version: [18, 20, 22]
+ node-version: [20, 22]
steps:
- uses: actions/checkout@v4