denoland / denoland/std

@std/yaml: expose/enable custom tag (!Foo / !!foo) support (I can implement)

Open
#7,020 0 comments 0 reactions 0 assignees View on GitHub
enhancement yaml
Dominant language
TypeScript
Stars
3.6k
Forks
681
PR merge metrics
No merged PRs in 30d

Description

Today `@std/yaml` parses tag properties, but end users cannot register custom tags. Any unknown explicit tag (for example `!User`, `!Entity`, `!!my/tag`) fails during resolution.

This blocks some YAML patterns used in some DSLs and configuration systems.

## What I'm trying to do

Parse YAML like:

``` yaml
- !Entity
name: Workspace

- !Entity
name: User
```

And have `!Entity` resolved into a custom runtime structure (or at least a predictable representation), instead of throwing an unknown tag error.

## Current limitation

- `parse()` only accepts a schema name (`"failsafe" | "json" | "core" | "default" | "extended"`).
- There is internal schema/type machinery (type maps and tag resolution).
- However, there is no public API to register custom explicit or implicit tag handlers.
- `parse()` explicitly states that parsing untrusted data is safe. Any custom tag mechanism would need to keep this guarantee explicit and opt-in.

## Question

Are you open to adding **custom tag support** to `@std/yaml`?

If yes, I would like to implement it.

My goals would be:

1. No breaking changes.
2. Default behavior remains unchanged.
3. Custom tags are fully opt-in.
4. The "safe for untrusted input" guarantee remains explicit and documented.
5. The design fits the current internal schema + type map architecture.

## Possible API Directions

### Option A: Allow passing explicit/implicit types via `parse` options

``` ts
parse(yamlText, {
schema: "default",
types: {
explicit: [
{
tag: "!Entity",
kind: "mapping",
resolve: (data) => true,
construct: (data) => new Entity(data),
},
],
},
});
```

### Option B: Expose a public `createSchema()` or extend `getSchema()`

Allow users to build a schema with custom explicit + implicit types, and pass it to `parse()`.

This would align with the existing internal schema design.

### Option C: Minimal tag resolver map

``` ts
parse(yamlText, {
tagResolvers: {
"!Entity": (data) => new Entity(data),
},
});
```

This would be a simpler abstraction layer over the internal type system.

## I can implement

If maintainers confirm that custom tag support is something you are open to, I can:

- Propose a concrete design
- Implement the feature
- Add documentation
- Add tests
- Ensure full backward compatibility

Thank you.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.