Skip to main content

Local Setup

This document describes how to set up Delfino for local development, including integration with Zoo (the Docker environment for local development).

Prerequisites

  • Git
  • Docker running
  • Zoo Docker environment with delfino, volpe, and farfalla containers running

Note: Node.js and Yarn are not required on the host. The build and dependency installation run inside the delfino container via docker exec.

Required Directory Structure

For local development to work correctly, projects must be organized as follows:

~/Dev/
├── delfino/
│ └── src/
│ └── index.ts
├── farfalla/
├── volpe/
└── zoo/

Installation

Clone the Repository

cd ~/Dev
git clone git@gitlab.com:publicala/delfino.git

Note: Dependency installation is handled automatically inside the delfino container when running zdelfino sync for the first time. There is no need to run yarn install manually on the host.

Development Commands

The following commands run inside the delfino container via Zoo:

CommandDescription
yarn buildBuild the library (ESM + UMD)
yarn devWatch mode for development
yarn typecheckType checking only (no emit)
yarn testRun tests once
yarn test:watchRun tests in watch mode
yarn test:coverageRun tests with coverage report
yarn lintRun ESLint
yarn lint:fixFix ESLint errors
yarn formatFormat code with Prettier
yarn format:checkCheck code formatting
yarn cleanRemove build artifacts

Zoo Integration

Delfino integrates with Farfalla and Volpe through direct synchronization to node_modules/@publicala/delfino inside the Docker containers. This approach does not modify any configuration files and is fully compatible with CI/CD without changes.

Architecture

HOST (macOS)
~/Dev/delfino/ (bind-mounted into delfino container)


DELFINO CONTAINER (Docker)
yarn install + yarn build ──▶ ~/Dev/delfino/dist/ (visible on host)

┌────────────────────────┤
│ │ │
▼ ▼ ▼
docker cp Host IDE copy docker cp
(host → volpe) (type (host → farfalla)
resolution)

CONSUMER CONTAINERS (Docker)
┌──────────────────────────────────────────────────┐
│ volpe ▸ node_modules/@publicala/delfino/ │
│ farfalla ▸ node_modules/@publicala/delfino/ │
└──────────────────────────────────────────────────┘

The sync flow is:

  1. Verify that the delfino, volpe, and farfalla containers are running
  2. Install dependencies inside the delfino container with yarn install --frozen-lockfile (if node_modules does not exist)
  3. Build Delfino inside the delfino container with yarn build (the output in dist/ is visible on the host via bind mount)
  4. Copy the build artifacts (dist/ and package.json) to the consumer containers via docker cp
  5. Install production dependencies inside each consumer container
  6. Write sync metadata (.delfino_sync_meta with git hash, .delfino_sync_token for bind mount detection)
  7. Copy to the host's node_modules so the IDE can resolve types (only if node_modules is not a Docker volume)
  8. Restart the containers to clear the in-memory Vite module cache

Initial Setup

Run release-animals.sh and answer "y" when prompted with "Setup Delfino for local development?":

cd ~/Dev/zoo
./release-animals.sh

Or manually:

zdelfino sync

zdelfino Commands

CommandDescription
zdelfino syncBuild and sync to Volpe and Farfalla
zdelfino statusShow sync state across projects
zdelfino restoreRestore Delfino from the npm registry
zdelfino buildBuild Delfino only, without syncing

Daily Development

cd ~/Dev/delfino
# ... edit code ...
zdelfino sync
# Containers restart automatically

Check Sync Status

zdelfino status

Shows the version and git hash of Delfino in the source, Volpe, and Farfalla, and indicates whether they are in sync.

Restore from npm

To revert to the published version from the registry:

zdelfino restore

This cleans host IDE copies, temporarily disables the guard, and runs yarn install --force in both containers.

Delfino Guard

The guard automatically protects the locally synced copy of Delfino when package manager commands (yarn install, npm install, etc.) are executed via zex. Without this protection, yarn install would overwrite the locally synced version with the version from the registry.

How It Works

The guard activates automatically when all of the following conditions are met:

  1. DELFINO_GUARD_SKIP is not set
  2. The container is farfalla or volpe
  3. The command is a package manager operation that resolves dependencies
  4. @publicala/delfino exists in the container's package.json
  5. Volpe only: the installed version matches the version in package.json (if the version changed intentionally, the guard deactivates to allow the update)

Note on Volpe: The guard includes version change detection (delfino_guard_has_version_changed). If the version of @publicala/delfino in Volpe's package.json differs from the version installed in node_modules, the guard deactivates to allow yarn install to update to the new version. This behavior does not apply to Farfalla, where the guard always activates.

Commands That Activate the Guard

  • yarn install, yarn add, yarn upgrade, yarn remove, yarn (bare)
  • npm install, npm ci, npm add, npm update, npm uninstall, npm i

Commands That Do NOT Activate the Guard

  • yarn dev, yarn build, yarn test
  • npm run dev
  • php artisan migrate, composer install

Example

# The guard protects delfino automatically:
zex farfalla yarn add lodash
# 🛡️ Delfino Guard: protecting local sync in farfalla
# ... yarn add lodash runs without touching delfino ...
# 🛡️ Delfino Guard: node_modules/@publicala/delfino restored in farfalla
# 🛡️ Delfino Guard: package.json restored in farfalla

Manual Bypass

DELFINO_GUARD_SKIP=1 zex farfalla yarn install

Internal Guard Flow

When the guard activates, it executes the following phases:

  1. Backup: Backs up package.json, yarn.lock, and node_modules/@publicala/delfino inside the container. Removes the @publicala/delfino line from package.json.
  2. Execution: Runs the original command. Since @publicala/delfino is not in package.json, the package manager does not touch that directory. A trap is registered to ensure restoration even if the command fails.
  3. Restoration: Restores node_modules/@publicala/delfino from the tarball backup and restores the original package.json and yarn.lock.

Sync Metadata

The system uses two metadata files inside node_modules/@publicala/delfino/ in each container:

FileContentPurpose
.delfino_sync_metaGit hash + dirty flag (e.g. abc1234:dirty)Determine if projects are in sync via zdelfino status
.delfino_sync_tokenTimestamp + PID (e.g. 1709654321.12345)Detect whether the directory is a bind mount or Docker volume to avoid re-copying and losing installed production dependencies

Zoo File Structure

zoo/
├── bin/
│ ├── zdelfino # Main Delfino command
│ └── zex # Integrates delfino_guard
├── lib/
│ ├── delfino_sync.sh # Sync library
│ ├── delfino_guard.sh # Protection library against yarn/npm
│ └── project_info.sh
├── scripts/
│ └── setup_delfino.sh # Setup for release-animals
├── tests/
│ └── test_delfino_guard.bats # BATS tests for the guard
└── release-animals.sh # Includes Delfino setup option

Local Development Commands

Farfalla (inside Zoo/Docker)

# Start development with hot reload
zex farfalla yarn watch

# Development build
zex farfalla yarn dev

# TypeScript type checking
zex farfalla yarn type-check

Volpe (inside Zoo/Docker)

# Start Vite development server
zex volpe yarn dev

# Local build
zex volpe yarn build:local

# Preview build
zex volpe yarn preview

Package Registry Configuration

GitLab Package Registry

Delfino is published to the GitLab Package Registry under the @publicala scope.

.npmrc configuration:

@publicala:registry=https://gitlab.com/api/v4/packages/npm/
//gitlab.com/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}
//gitlab.com/api/v4/projects/:_authToken=${CI_JOB_TOKEN}

CI/CD Configuration Template

.npm_gitlab_auth: &npm_gitlab_auth
- |
echo "@publicala:registry=https://gitlab.com/api/v4/packages/npm/" > .npmrc
echo "//gitlab.com/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}" >> .npmrc
echo "//gitlab.com/api/v4/projects/:_authToken=${CI_JOB_TOKEN}" >> .npmrc

Testing

Running Tests

# Run all tests
yarn test

# Run tests in watch mode
yarn test:watch

# Run with coverage
yarn test:coverage

Test Environment

Tests use Vitest with jsdom environment. The setup file (vitest.setup.ts) configures the DOM simulation.

Test Files

Test FileCoverage
BridgeError.test.tsError handling and fluent helpers
SecurityManager.test.tsRate limiting, logging
HostBridge.test.tsiframe validation, retry logic
ClientBridge.test.tsMessageChannel creation, handshake
FullCycle.test.tsEnd-to-end communication tests

Build Outputs

After running yarn build (via Rollup), the following artifacts are created:

dist/
├── esm/
│ ├── index.js # ES Modules (tree-shakeable, comlink external)
│ └── types/
│ └── index.d.ts # TypeScript declarations
└── umd/
└── delfino.min.js # UMD (bundled + minified, comlink included)

Output Usage

OutputConsumerImport
ESMFarfalla, Volpe (npm)import { hostBridge } from '@publicala/delfino'
UMDFenice (CDN)<script src="delfino.min.js"> then window.Delfino
TypesTypeScript projectsAutomatic via types field in package.json

Troubleshooting

"Cannot find module '@publicala/delfino'"

  1. Verify that the delfino directory exists at ~/Dev/delfino
  2. Verify that the delfino, volpe, and farfalla containers are running (docker ps)
  3. Run zdelfino sync to synchronize
  4. Verify with zdelfino status that projects are in sync

"Types not found"

  1. Run yarn type-check to verify TypeScript configuration
  2. Restart the TypeScript server in your IDE
  3. Verify that zdelfino sync copied files to the host's node_modules

"Changes in Delfino are not reflected"

  1. Run zdelfino sync (synchronization is manual, not automatic)
  2. Containers restart automatically after sync
  3. If the issue persists, verify with zdelfino status that the hashes match

"HostBridge must run inside an iframe"

This error indicates Volpe is running standalone. Ensure:

  1. Volpe is loaded in an iframe element
  2. The iframe src points to the correct Volpe URL
  3. The Host is using createClientBridge(iframe) before Volpe calls hostBridge.ready()

"Timeout waiting for channel from host"

This indicates the handshake failed. Check:

  1. Host calls clientBridge.ready() after creating the iframe
  2. Iframe loads successfully
  3. No CORS issues blocking communication
  4. Both projects use compatible Delfino versions

Rate Limit Exceeded in Development

Switch to development security preset:

createClientBridge(iframe, { security: 'development' });

Guard Does Not Protect During yarn install

  1. Verify you are using zex and not running yarn install directly inside the container
  2. Verify that @publicala/delfino is in the project's package.json
  3. Verify that DELFINO_GUARD_SKIP is not set in the environment
  4. Volpe only: verify that the version in package.json has not changed from the installed version (if it changed, the guard deactivates intentionally)

Empty node_modules After zex yarn install (False Positive)

When deleting node_modules from Volpe or Farfalla and running zex yarn install, a false positive may occur: the command finishes without errors but the node_modules folder is empty or incomplete. This happens occasionally within the Docker environment.

Solution:

  1. Verify that node_modules actually contains the dependencies after running zex yarn install

  2. If the folder is empty or incomplete, delete it manually:

    # For Volpe
    zex volpe rm -rf node_modules

    # For Farfalla
    zex farfalla rm -rf node_modules
  3. Run the install command again:

    # For Volpe
    zex volpe yarn install

    # For Farfalla
    zex farfalla yarn install
  4. Verify that the dependencies were downloaded correctly

zdelfino sync Fails with "container not running"

  1. Verify the delfino container is running: docker ps | grep delfino
  2. Verify consumer containers are running: docker ps | grep -E 'volpe|farfalla'
  3. If any container is not running, start Zoo: cd ~/Dev/zoo && docker compose up -d
X

Graph View