NYC
skills/smithery/ai/deno-core

deno-core

SKILL.md

Deno Core Best Practices

When to Use This Skill

Use this skill for ALL Deno TypeScript development:

  • Setting up new Deno projects
  • Writing Deno applications or libraries
  • Configuring build, test, and deployment
  • Working with dependencies and imports

Core Deno Philosophy

One Tool, Zero Dependencies

  • Deno is the only tool you need for TypeScript development
  • Built-in tooling: typecheck, lint, format, test, coverage, benchmark
  • Avoid node_modules at all costs - reduce supply chain attack surface
  • No need for: tsc, eslint, prettier, jest, vitest, webpack, etc.

TypeScript Excellence

  • Strict TypeScript adherence - not just "TS support"
  • Bleeding-edge TypeScript features by default - no flags, no config needed
  • No compilation step - just run your code
  • Target ES2024+ with Stage 3 TC39 proposals

Security First

  • Explicit permissions model (no implicit file system or network access)
  • Supply chain security through minimal external dependencies
  • First-class support for modern security patterns

Language & Compiler

TypeScript Configuration

  • Do not use tsconfig.json - Deno uses deno.json(c) as the single source of truth
  • Type-checking powered by deno check / deno test - do not rely on external tsc
  • Default module format is ESM only - no CommonJS interop
  • Prefer Deno's runtime-provided types (Deno.*, Web APIs, Fetch, URLPattern) over polyfills

Strictest Compiler Settings

Always use the strictest possible settings in deno.json:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "exactOptionalPropertyTypes": true
  }
}

Configuration & Tasks

deno.json - Single Source of Truth

Use deno.json or deno.jsonc as the single configuration file for:

  • Compiler options
  • Linting and formatting rules
  • Tasks (script aliases)
  • Import maps (dependency management)
  • Exclusions

Complete Configuration Example:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true
  },
  "tasks": {
    "dev": "deno run --watch --allow-net --allow-read --allow-env src/main.ts",
    "test": "deno test --allow-net --allow-read --allow-env --coverage=coverage src/",
    "test:unit": "deno test --allow-net --allow-read --allow-env --coverage=coverage src/",
    "test:e2e": "deno test --allow-net --allow-read --allow-env tests/e2e/",
    "test:watch": "deno test --allow-net --allow-read --allow-env --watch --fail-fast",
    "coverage": "deno coverage coverage --html",
    "check": "deno check $(find src -name '*.ts' -not -name '*.sql')",
    "lint": "deno lint",
    "fmt": "deno fmt"
  },
  "imports": {
    "@/": "./src/",
    "@/domain/": "./src/domain/",
    "@/infrastructure/": "./src/infrastructure/",
    "@/application/": "./src/application/",
    "@std/assert": "jsr:@std/assert@^1.0.14",
    "@std/fs": "jsr:@std/fs@^1.0.19",
    "@std/testing": "jsr:@std/testing@^1.0.15",
    "@std/ulid": "jsr:@std/ulid@1",
    "zod": "npm:zod@^3.23.8"
  },
  "exclude": [
    "coverage/",
    "node_modules/"
  ],
  "lock": true
}

Essential Tasks

Define these deno task aliases at minimum:

  • dev - Development with watch mode
  • test, test:watch - Testing
  • coverage - Generate coverage reports
  • check - Type-check all files
  • lint, fmt - Code quality

Lockfile Management

  • Always commit deno.lock to version control
  • Run with --lock=deno.lock --lock-write=false in CI
  • Update lockfile: deno cache --lock=deno.lock --lock-write

Imports & Module Resolution

Import Strategy

CRITICAL: Never use direct JSR/npm imports in source files. All external dependencies MUST be declared in deno.json import map.

Import Order in Source Files:

// 1. Standard library imports (via import map)
import { assertEquals } from "@std/assert";

// 2. Third-party imports (via import map)
import { z } from "zod";

// 3. Internal imports (absolute paths using import map)
import { Agent } from "@/domain/agent.ts";

// 4. Relative imports (only within same module/context)
import { validatePrompt } from "./validation.ts";

Dependency Source Priority

Use sources in this order:

  1. jsr: registry (first choice for TypeScript modules)

    "@std/assert": "jsr:@std/assert@^1.0.14"
    
  2. npm: specifier (when needed; prefer ESM-compatible)

    "zod": "npm:zod@^3.23.8"
    
  3. URL imports (rarely needed with import maps)

Version Pinning

CRITICAL: Version pin all external imports. No floating @latest in committed code.

{
  "imports": {
    "zod": "npm:zod@^3.23.8",           // GOOD - pinned
    "zod": "npm:zod",                    // BAD - no version
    "@std/assert": "jsr:@std/assert@1"   // GOOD - pinned
  }
}

Internal Path Aliases

Use import map aliases for clean internal imports:

{
  "imports": {
    "@/": "./src/",
    "@/domain/": "./src/domain/",
    "@/infrastructure/": "./src/infrastructure/"
  }
}
// GOOD - Clean, refactor-safe
import { Agent } from "@/domain/agent.ts";

// BAD - Brittle relative paths
import { Agent } from "../../../domain/agent.ts";

Type-Only Imports

Use type-only imports when importing types:

import type { Agent } from "@/domain/agent.ts";
import type { z } from "zod";

Testing

Test Organization

Unit Tests - Co-located with Source:

src/
└── domain/
    ├── agent.ts
    └── agent.test.ts          # Unit tests next to code

Integration & E2E Tests - Separate Directory:

tests/
├── integration/
│   └── openai_provider.test.ts
└── e2e/
    └── workflow.test.ts

Why Co-location:

  • Discoverability - tests next to code
  • Maintenance - easy to keep in sync
  • Deno convention - follows deno test discovery

Coverage Requirements

Non-Negotiable:

  • Line coverage: 80%+ - MUST be met
  • Branch coverage: 60-80% - MUST be met
deno test --coverage=coverage
deno coverage coverage --html

Test Structure

Always use explicit AAA (Arrange-Act-Assert):

import { assertEquals } from "@std/assert";

Deno.test("agent should process valid input", () => {
  // Arrange
  const agent = new Agent({ name: "TestAgent" });
  const input = "Hello, world!";

  // Act
  const result = agent.process(input);

  // Assert
  assertEquals(result.status, "success");
});

Test Development

Red-Green-Refactor with fast feedback:

# Watch mode with fail-fast
deno test --watch --fail-fast

# Run specific file
deno test src/domain/agent.test.ts --watch

Deterministic Tests

CRITICAL: All tests must be deterministic.

Test Flakiness Policy:

  • Flakiness = highest priority bug
  • Never ignore, retry, or "fix" with delays
  • Action: investigate, quarantine, fix
  • Do NOT merge flaky tests

Use stable seeds and fixtures:

import { FakeTime } from "@std/testing/time";

Deno.test("timer test", () => {
  using time = new FakeTime();
  // Deterministic time control
  time.tick(1000);
});

Test File Naming

All test files must end with .test.ts:

agent.test.ts     # GOOD
agent_test.ts     # BAD
agent.spec.ts     # BAD

Testing Tools

  • Use @std/assert for assertions
  • Use @std/testing/mock for test doubles
  • Use @std/testing/time for time control
  • Do NOT use Jest - use Deno's built-in runner

Permissions & Security

Principle of Least Privilege

Default to minimum required permissions:

# BAD
deno run --allow-all script.ts

# GOOD
deno run --allow-read=./data --allow-net=api.example.com script.ts

Common Permission Flags

--allow-read[=<path>]      # File system read
--allow-write[=<path>]     # File system write
--allow-net[=<domain>]     # Network access
--allow-env[=<var>]        # Environment variables
--allow-run[=<program>]    # Subprocess execution

Document Required Permissions

/**
 * Fetches data from API and caches locally.
 *
 * Required permissions:
 * - --allow-net=api.example.com
 * - --allow-read=./cache
 * - --allow-write=./cache
 */
export async function fetchData(): Promise<Data> {
  // ...
}

Secrets Management

// BAD - Hardcoded
const apiKey = "sk-1234";

// GOOD - From environment
const apiKey = Deno.env.get("API_KEY");
if (!apiKey) {
  throw new Error("API_KEY required");
}

Run with: deno run --allow-env=API_KEY script.ts


Anti-Patterns to Avoid

Import Anti-Patterns

// BAD - Direct JSR/npm imports in source
import { z } from "npm:zod@^3.23.8";

// GOOD - Use import map
import { z } from "zod";
// BAD - Floating versions
"zod": "npm:zod"

// GOOD - Pinned versions
"zod": "npm:zod@^3.23.8"

Node.js Anti-Patterns

// BAD - Node.js APIs
const fs = require("fs");
import * as fs from "node:fs";

// GOOD - Deno APIs
await Deno.readTextFile("file.txt");

Testing Anti-Patterns

// BAD - Unnecessary delay
await new Promise(r => setTimeout(r, 100));

// GOOD - Deterministic time
import { FakeTime } from "@std/testing/time";
using time = new FakeTime();
time.tick(100);

Permission Anti-Patterns

# BAD - Overly broad
deno run --allow-all script.ts

# GOOD - Specific
deno run --allow-read=./data script.ts

Async Anti-Patterns

// BAD - Unnecessary async
async function validate(input: string): Promise<boolean> {
  return input.length > 0;
}

// GOOD - Remove async if no await
function validate(input: string): boolean {
  return input.length > 0;
}

Quick Command Reference

Development

# Run with watch
deno run --watch src/main.ts

# Type-check
deno check src/**/*.ts

# Format
deno fmt

# Lint
deno lint

Testing

# Run all tests
deno test

# With coverage
deno test --coverage=coverage
deno coverage coverage --html

# Watch mode
deno test --watch --fail-fast

Dependencies

# Update dependencies
deno cache --reload

# Update lockfile
deno cache --lock=deno.lock --lock-write

Tasks

# Run tasks from deno.json
deno task dev
deno task test
deno task coverage

Key Principles Summary

  1. One tool - Deno replaces tsc, eslint, prettier, jest
  2. Security first - Explicit permissions, minimal dependencies
  3. Import maps - All deps in deno.json, never direct imports
  4. Version pinning - No floating versions
  5. Co-located tests - Unit tests next to source
  6. 80%/60% coverage - Line/branch, non-negotiable
  7. No flakiness - Highest priority, never ignore
  8. AAA pattern - Explicit in every test
  9. Least privilege - Minimal permissions
  10. ESM only - No CommonJS

Additional Resources

Weekly Installs
1
Repository
smithery/ai
First Seen
11 days ago
Installed on
cursor1