Token导航 LogoToken导航TokenDH.com
研究检索需要联网github未标认证来源可访问许可证需确认审计通过

nullablesnullables 搜索

Agent Skill

nullables 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

360

周安装

15

GitHub Stars

公开资料未说明

下载量

120
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:nullables(nullables 搜索)
来源仓库:https://github.com/danielbush/skills
仓库路径:skills/nullables
安装命令:
npx skills add https://github.com/danielbush/skills --skill nullables
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/danielbush/skills --skill nullables

简介

nullables 用于查找、检索和筛选相关信息,适合在主流 Agent 宿主中快速定位候选结果。

  • 适用于围绕可空类型(nullable types)相关技术内容进行信息检索。
  • 通过 npx skills add 命令安装,需结合原始 README 确认具体用法。
  • 安装前建议确认权限范围、维护状态及是否触发联网或文件读写操作。
  • 可结合来源仓库进一步核验功能细节和使用限制。

SKILL.md

Nullables and A-Frame Architecture

Overview

This skill guides implementation of James Shore's Nullables pattern and A-Frame architecture. This approach enables fast, reliable testing without mocks through:

  • A-Frame Architecture: Separation of Logic, Infrastructure, and Application layers
  • Nullables Pattern: Production code with an "off switch" for testing
  • Static Factory Methods: .create() for production, .createNull() for tests
  • State-Based Testing: Narrow, sociable tests that verify outputs instead of interactions

When to Use This Pattern

Apply this pattern when:

  • If the code uses a dependency injection framework (eg effect-ts) then use it to perform injections; also inform me where this happens as we might need to think about where the boundary lies between a 3rd party injection approach and the use of Nullables (.create / .createNull).
  • Writing new code
  • Refactoring existing code to eliminate mocks and improve test reliability
  • Writing tests for code that uses nullable patterns such as classes that use static factory method .create.

Core Principles

1. Three-Layer Architecture

Organize code into distinct layers:

  • Logic: Pure business logic and value objects (no external dependencies)
  • Infrastructure: Code interfacing with external systems (network, filesystem, databases)
  • Application: High-level orchestration coordinating logic and infrastructure

- application code is effectively infrastructure code because it contains instances of instrastructure that talk to the outside world

2. Dual Factory Methods

Every application and infrastructure class implements:

class ServiceClass {
  static create(config) {
    // Production instance - calls .create() on dependencies
    const dependency = Dependency.create();
    return new ServiceClass(dependency);
  }

  static createNull(config = {}) {
    // Test instance - calls .createNull() on dependencies
    const dependency = Dependency.createNull();
    return new ServiceClass(dependency);
  }

  constructor(dependency) {
    // Constructor receives instantiated dependencies
  }
}

3. Testing Without Mocks

  • Unit tests: Use .createNull() - fast, sociable, no external calls to the outside world
  • Integration tests: Use .create() sparingly - verify live infrastructure
  • Configure responses via factory: Service.createNull({response: data})
  • Track outputs: service.trackSentEmails()
  • Verify state and outputs, not interaction patterns

4. Value Objects

  • Immutable by default - represent data without behavior
  • Mutable value objects allowed - use methods for in-memory transformations only
  • Infrastructure operations (network, I/O) should take/return value objects, not be methods on them

Nullable algorithm for writing and testing code

We will use names like Foo, foo, Bar, Client etc for the purposes of describing this algorithm, but more descriptive names should be used in the code. Most code should be classes except potentially for logic layer code. Foo, Bar, Client are classes; foo is an instance of Foo etc.

Suppose Foo is the class under test or being created.

  • If Foo is application layer or infrastructure layer

- Foo should be a class - Foo.create and injects dependencies into the constructor of Foo; - Foo.createNull injects nulled versions of the any infrastructure dependencies for unit tests; "nulled" ensures any infrastructure code is "nulled" and returns a configured response rather than perform an I/O operation (network, disk etc); "nulled" instance never interacts with the outside world. - always get instances of foo by calling Foo.create in production code - when testing Foo, instantiate it directly foo = new Foo(...) and pass in nulled versions of dependencies: eg foo = new Foo(Bar.createNull(...)). - when Foo is a dependency in a unit test, use Foo.createNull. - Foo.create should set valid production defaults as much as possible reducing the need to pass parameters to Foo.create - an instance foo created by Foo.createNull should execute in exactly the same way, line for line, as one created using Foo.create.

  • If Foo is logic layer code:

- if it's stateless, pure functions should be fine; in this case Foo becomes foo (upper case is for classes) - if it involves non-I/O non-network in-memory manipulation, a class may be suitable - use Foo.create - no need to create Foo.createNull since there should be no I/O or network operations

  • If Val is a value object (immutable or mutable)

- Val should be a class - we should have a static Val.create but we don't need a Val.createNull so some of the procedures below may not apply; this is because value objects shouldn't be doing I/O operations so we don't need nulled versions - it may not make sense to set defaults for all parameters when creating value objects; so Val.create may end up requiring the consumer to specify many or all parameters and should be typed accordingly - use a static Val.createTestInstance to create test values; this is to indicate that the value create is a test value; it's fine for .createTestInstance to set defaults to make tests less verbose

The following is the algorithm for refactoring and creating new code. It doesn't need to be followed precisely, it is meant to convery the end result.

  • Find the code (eg a class Foo) that you think is the most important to write or test

- we want to be able to test Foo using narrow, sociable unit tests without mocks... - suppose Bar represents any another class that an instance of Foo needs to perform its actions - we have 2 scenarios - (1) instantiate Bar straight away: ensure Foo.create calls Bar.create and passes the instance to Foos constructor - (2) delayed instantiation of Bar: we pass in a factory createBar that returns an instance of Bar and let the instance of Foo create Bar at a later time; we might do this if Bar is only created based on some condition/event at a later time. - if we are passing multiple infrastructure dependencies we can use a "create" object that we pass in to the constructor: {Bar: (...) => Bar.create(...), Baz:...}; foo can then call create.Bar(...) to get a Bar instance at a later time. - Foo.createNull can then pass in a "create" object with nulled factories so that when foo calls create.Bar(...) it gets a Bar instance created by Bar.createNull - NOTE: We wrap all create functions eg Bar.create / Bar.createNull in a function because when injecting delayed nullables (Bar.createNull), the .createNull may take non-production parameters to configure it. - Factories in the create object can accept runtime parameters from the instance. This keeps decision logic in the factory (inside Foo.create) rather than in the instance. For example, if foo needs to select between implementations based on runtime state, the factory should accept a parameter for that: {Bar: ({useFallback}) => useFallback? FallbackBar.create(): Bar.create()}. The instance just calls create.Bar({useFallback: this.flag}) — it doesn't know about the implementations. - If you see Bar.create within an instance of Foo refactor to inject it using either (1) or (2) - if you had to create Bar.create, repeat this process but with Bar in place of Foo - keep recursing as required

  • Now repeat the above but for Foo.createNull (if applicable)

- this means Foo.createNull will either (1) or (2) like Foo.create.

  • Code that directly makes I/O calls to access an external service or resource such as the network (eg fetch) or disk etc is infrastrucure layer code and should be wrapped up in a class with .create and .createNull making it into a client we can instantiate;

- call this Client (here) but give it an appropriately descriptive class name eg HttpClient, DiskClient etc - Client sole purpose is to interface with the outside world but have minimal business logic - we also call Client an "infrastructure wrapper" - Client should make an effort to provide data to the rest of the application in the form the application needs and no more - Client.createNull should instantiate a nulled version - Client.createNull mimics I/O calls (eg mimics calls to fetch) - by default it should return a default "happy-path" response - it should take parameters (use a param object) that can configure the responses ("configurable response parameters") - "configurable response parameters" should configure for possible responses or outcomes we might have to test for; as a human reading the tests I should be able to easily see at a glance what outcome is being configured for in the nulled object; let the implementation within .createNull worry about the details - in some situations an "embedded stub" might be appropriate that stubs out a built-in I/O or network operation; the embedded stub should be configued within Client.createNull. - if Client is not using a framework like effect-ts to manage errors, then use neverthrow and use an object with a .type field to discriminate the error at compile time; if there is an underlying error from a throw or a rejection, assign it to .cause

  • value objects (instances of Val) are often returned by Client's
  • If Foo is infrastructure or application layer code and its methods contain I/O or network operations, replace these operations with a client and inject an instance of Client via Foo.create as per the above algorithm.

- Where possible make Foo.create generate a default production-ready instance of Client without the need to specifying anything to Foo.create.

  • If the code makes use of a 3rd party library to perform I/O eg tanstack-query / react-query / effect.runPromise etc, then...

- determine if this library provides a test client and use it for the tests - think about how it should be incorporated into the .create/.createNull architecture and let me know

  • Now go back to Foo

- write narrow sociable unit tests against instances of Foo using Foo.createNull

  • For insfrastructure wrapper code (Client),

- we should test Client.createNull - in a separate folder designated for live integration tests we should also test Client.create - these shouldn't be too numerous, we're testing against a live service to test the actual non-nulled code and also that we get back the responses we expect - these live integration tests should be run in a separate command to the unit tests above

  • If there are standalone functions that perform I/O or network operations (foo), favour re-writing foo as a class Client and treating it as a client / infrastructure wrapper and implement .createNull accordingly.
  • Sometimes we want to know that changes to the outside world occurred in tests, maybe also in a certain order.

- this allows us to keep tests state-based and outcome-focused rather than relying on mocks and testing internal interactions - Usually this happens in a Client - Client should implement an EventEmitter or a similar mechanism using .emit and .on/.off; when some interaction with the outside world (X) occurs, it should emit an event for X - Client should additionally implement a trackX function where X represents the outcome we're tracking - .trackX should return a tracking object (let's call it tracker which is an instance of Tracker) - Tracker should have access to the event emitter in the instance of Client - Tracker can add itself as a listener for X using the event listener and log the events usually in sequential order - tracker.stop() should get Tracker to remove itself - tracker.clear() should clear the previously logged data - tracker.data should be a getter to access the data - in some cases, tracking may be useful to the application, in which this event emitter should be exposed publicly; we can still implement a Tracker using the above so tests can track the outcomes

End of algorithm.

Implementing a New Feature

  1. Identify the layer:

- Pure computation? → Logic layer - External system interaction? → Infrastructure layer - Coordinating workflow? → Application layer

  1. Create the class with factory methods:

- Add static create() with reasonable defaults - Add static createNull() for infrastructure and application classes - Use constructor for dependency injection

  1. Implement dependencies through factories:

- .create() calls .create() on dependencies - .createNull() calls .createNull() on dependencies - Pass instantiated dependencies to constructor

  1. Write tests:

- Use .createNull() for unit tests - Configure responses via factory: Service.createNull({response: data}) - Track outputs: service.trackOutput() - Verify state and return values

  1. Add integration tests (sparingly):

- Use .create() for a few narrow integration tests - Verify infrastructure wrappers correctly mimic third-party behavior

Refactoring Existing Code

  1. Separate concerns:

- Extract logic into pure functions/classes - Wrap external dependencies in infrastructure classes - Create application layer to orchestrate

  1. Add factory methods:

- Replace direct instantiation with .create() - Add .createNull() to infrastructure and application classes - Update constructors to receive dependencies

  1. Replace mocks with nullables:

- Remove mock setup - Use .createNull() instances - Configure responses on nullable infrastructure

  1. Refactor tests to be state-based:

- Remove interaction assertions (verify/toHaveBeenCalled) - Add state and output assertions - Use event emitters for observable state changes

Implementation Patterns

Pattern: Infrastructure Wrapper

Wrap third-party code. Use an embedded stub that mimics the third-party library:

class HttpClient {
  static create() {
    return new HttpClient(realHttp); // Real HTTP library
  }

  static createNull() {
    return new HttpClient(new StubbedHttp()); // Embedded stub
  }

  constructor(http) {
    this._http = http;
  }

  async request(options) {
    return await this._http.request(options);
  }
}

// Embedded stub - mimics third-party library interface
class StubbedHttp {
  async request(options) {
    return { status: 200, body: {} };
  }
}

Pattern: Logic Sandwich

Application code: Read → Process → Write

async transfer(fromId, toId, amount) {
  // Read from infrastructure
  const accounts = await this._repo.getAccounts(fromId, toId);

  // Process with logic
  const result = this._logic.transfer(accounts, amount);

  // Write through infrastructure
  await this._repo.saveAccounts(result);
}

Pattern: Consumer-Defined Interfaces

When a class depends on infrastructure, define the simplest interface the consumer needs rather than accepting a concrete class. The consumer owns the contract; implementations adapt to it.

// Consumer defines what it needs — just updateBindings, not the full store API
interface EditorStore {
  updateBindings(data: SerializableData): ResultAsync<string, StoreError>;
}

class Editor {
  static create(ctl, values: { store: EditorStore; /* ... */ }) {
    return new Editor(ctl, values.store);
  }
  constructor(private ctl, private store: EditorStore) {}
}

// Multiple implementations satisfy the same interface
class LocalService implements EditorStore { /* persists to IDB */ }
class TestService implements EditorStore { /* always fails */ }

This keeps infrastructure errors out of the consumer. Implementations map their internal errors to domain errors the consumer expects (see error mapping below).

Pattern: Error Mapping at Boundaries

When implementing a consumer-defined interface, map infrastructure errors to domain errors using .mapErr(). The consumer should never see IDBStoreError, FetchError, etc. — only the error types declared in the interface.

class StoreError extends Error {
  readonly _tag = 'StoreError' as const;
  constructor(message: string, options?: ErrorOptions) {
    super(message, options);
  }
}

class LocalService implements EditorStore {
  updateBindings = (data: SerializableData) =>
    this.db.updateBindings(data)
      .mapErr((err) => new StoreError(err.message, { cause: err }));
}

Use a _tag discriminant on error classes so consumers can exhaustively match when the interface has multiple error types in a union.

Pattern: Event Emitters for State

Expose state changes for testing:

class Account extends EventEmitter {
  deposit(amount) {
    this._balance += amount;
    this.emit('balance-changed', { balance: this._balance });
  }
}

// Test by observing events
account.on('balance-changed', (event) => events.push(event));

Detailed Reference

For comprehensive implementation details, see nullables-guide.md:

  • Complete A-Frame architecture details
  • Nullables pattern implementation
  • Value objects (immutable and mutable)
  • Testing strategies and examples
  • Infrastructure wrappers and embedded stubs
  • Configurable responses and output tracking
  • Event emitters for state-based tests
  • Implementation checklist

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.09%
按下载量换算40

Claude

30.42%
按下载量换算37

Cursor

19.41%
按下载量换算23

Gemini CLI

8.73%
按下载量换算10

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills