Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
de777c3
Add VocabularyObject and raw property helpers
dahlia Nov 1, 2026
05451ed
Generate raw property readers in vocab modules
dahlia Nov 1, 2026
4991f97
Add the raw-property-iri-invalid lint rule
dahlia Nov 1, 2026
09b671e
Pair reparsed raw property nodes by id
dahlia Nov 2, 2026
f41f97a
Skip tokens and comments in lint IRI traversal
dahlia Nov 2, 2026
037b525
Check only the read functions in the IRI lint rule
dahlia Nov 2, 2026
7d750aa
Keep raw properties when a reparse changes class
dahlia Nov 2, 2026
88dc292
Merge raw properties across a class change
dahlia Nov 2, 2026
35b058b
Ignore URL objects bound to const in IRI lint
dahlia Nov 2, 2026
32a23df
Update vocab-tools snapshots after rebase
dahlia Nov 2, 2026
7365f13
Look up switch discriminants outside case scope
dahlia Nov 2, 2026
401c86c
Decode single-quoted literals in IRI lint exactly
dahlia Nov 2, 2026
09af565
Pair sibling-class properties by IRI
dahlia Nov 2, 2026
eddea18
Scope TypeScript namespace bodies in IRI lint
dahlia Nov 2, 2026
f3a54e5
Bind namespace and enum names in IRI lint
dahlia Nov 5, 2026
89bde9e
Decode cached embedded objects in parent context
dahlia Nov 5, 2026
f9e9d5f
Bind only namespaces with values in IRI lint
dahlia Nov 5, 2026
c64ab91
Scope enum members in IRI lint
dahlia Nov 5, 2026
afc31b4
Validate IP literals in raw property IRIs
dahlia Nov 5, 2026
cfb759c
Handle import aliases in IRI lint scopes
dahlia Nov 5, 2026
e089cb7
Reference the issue in embedded context entries
dahlia Nov 5, 2026
26bffc6
Keep the parent context order for embedded objects
dahlia Nov 5, 2026
2fff197
Keep decoded objects under non-propagating contexts
dahlia Nov 6, 2026
0a8cf22
Stop following outer constants from merged scopes
dahlia Nov 6, 2026
ecefc1a
Check embedded contexts for non-propagation too
dahlia Nov 6, 2026
9338289
Track merged exports when resolving IRI lint helpers
dahlia Nov 6, 2026
3865313
Reject invalid ports in raw property IRIs
dahlia Nov 6, 2026
03d5993
Accept both @emnapi lines in Astro compat tests
dahlia Nov 6, 2026
16a5c34
Keep inherited contexts out of cached JSON-LD
dahlia Nov 6, 2026
4a56ef4
Bound context loads when checking cached keys
dahlia Nov 6, 2026
79f49c5
Visit enum members once in IRI lint
dahlia Nov 6, 2026
bc83705
Keep received JSON-LD when contexts cannot be rebuilt
dahlia Nov 6, 2026
4a18a47
Harden decoding of cached embedded objects
dahlia Nov 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .agents/skills/add-vocab/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ The `@fedify/vocab-tools` snapshots are stored separately for Deno, Node.js,
and Bun. Always update all three with the aggregate task when generated output
changes; do not update only one runtime's snapshot.

Adding a property also changes what `getRawPropertyValues()` returns for its
IRI and for its `redundantProperties` aliases: they stop being retained as
unsupported properties and are encoded from the typed values instead. Mention
this in the changelog entry when applications may already be reading the
property through that function.


YAML file format
----------------
Expand Down
48 changes: 48 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,15 @@ To be released.
plugin rules as errors, so projects generated by `fedify init` exclude the
selected rule while keeping the deprecated option usable.
[[#1335], [#1340]]
- Added the `raw-property-iri-invalid` rule, which reports property IRIs
passed to `getRawPropertyValues()` and `getRawPropertyValue()` that throw
at runtime, such as terms and JSON-LD keywords, and compact IRIs such as
`as:content`, which are looked up literally. With ESLint and Oxlint, the
`modules` option adds other vocabulary modules generated by
`@fedify/vocab-tools` to the default `@fedify/vocab`. [[#1272], [#1356]]

[#1272]: https://github.com/fedify-dev/fedify/issues/1272
[#1356]: https://github.com/fedify-dev/fedify/pull/1356

### @fedify/postgres

Expand Down Expand Up @@ -310,6 +319,17 @@ To be released.
entries may not be visited. Accessor and serialization traversal costs are
unchanged. [[#1243], [#1337]]

- Added `getRawPropertyValues()` and `getRawPropertyValue()` functions,
which read a property of a vocabulary object by its full IRI and return
its values in expanded JSON-LD. They let applications read properties
that Fedify has no accessor for, such as an extension used by only a few
peers, without depending on the JSON-LD serialization cache: unknown
properties are now retained when an object is decoded, including embedded
objects, and survive `clone()` and `deepClone()`. For properties Fedify
does support, the functions return the current typed values encoded back
into JSON-LD. The values are not verified, so validate them before use.
[[#1272], [#1356]]

- Added the `attributionDomains` property to `Application`, `Group`,
`Organization`, `Person`, and `Service`, so applications can publish the
domains an actor claims for Mastodon's `fediverse:creator` link credits
Expand Down Expand Up @@ -338,6 +358,17 @@ To be released.
- Fedify does not fill in a default license for objects without license
metadata and never fetches a license URI.

- Fixed accessors of objects decoded by `fromJsonLd()` returning wrong
values for embedded objects that have their own `@context`. Such an
object was decoded again from its cached JSON-LD without the enclosing
document's context, so a type or property term that only the enclosing
context defined could yield an object of another class or drop
property values. It could also pick embedded objects under a term that
the enclosing context mapped to another property. The accessors now
apply the enclosing context and fall back to the object decoded with the
whole document when the cached JSON-LD cannot be matched with it.
[[#1272], [#1356]]

[FEP-6757]: https://w3id.org/fep/6757
[#1212]: https://github.com/fedify-dev/fedify/issues/1212
[#1241]: https://github.com/fedify-dev/fedify/pull/1241
Expand All @@ -355,6 +386,11 @@ To be released.
e.g., `zh-YUE` with `yue`. `Intl.Locale` rejects extlang tags, so
applications can normalize such tags before passing them to it.
[[#1229], [#1231]]
- Added the `VocabularyObject` interface, which every class generated by
`@fedify/vocab-tools` implements, and the `RawPropertyValue` type with its
`RawValueObject`, `RawNodeObject`, and `RawListObject` shapes for the
expanded JSON-LD values that `getRawPropertyValues()` returns.
[[#1272], [#1356]]

[#1229]: https://github.com/fedify-dev/fedify/issues/1229
[#1231]: https://github.com/fedify-dev/fedify/pull/1231
Expand Down Expand Up @@ -382,6 +418,18 @@ To be released.
`https://w3id.org/identity/v1`. Previously, such a property compacted to
a prefixed name like `dc:license` without the extra context.
[[#1212], [#1241]]
- Fixed generated accessors decoding embedded objects that have their own
`@context` without the enclosing document's context, which could
return objects of another class, drop property values, or pick objects
under a term the enclosing context mapped to another property.
[[#1272], [#1356]]
- Generated vocabulary modules now export `getRawPropertyValues()` and
`getRawPropertyValue()`, which read any property of a generated object
by its full IRI, and their root classes implement `VocabularyObject` from
`@fedify/vocab-runtime`. Decoding retains the properties a class does not
know, so third-party vocabulary modules get the same access to
unsupported properties as `@fedify/vocab`. The functions accept objects
from any generated module. [[#1272], [#1356]]
- Made the generated `xsd:string` decoder skip JSON-LD values whose `@value`
is not a string instead of placing them in string accessors. Malformed
remote values no longer leak numbers or booleans into properties such as
Expand Down
11 changes: 11 additions & 0 deletions changes.d/lint/raw-property-iri-invalid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
links:
'#1272': https://github.com/fedify-dev/fedify/issues/1272
'#1356': https://github.com/fedify-dev/fedify/pull/1356
---
- Added the `raw-property-iri-invalid` rule, which reports property IRIs
passed to `getRawPropertyValues()` and `getRawPropertyValue()` that throw
at runtime, such as terms and JSON-LD keywords, and compact IRIs such as
`as:content`, which are looked up literally. With ESLint and Oxlint, the
`modules` option adds other vocabulary modules generated by
`@fedify/vocab-tools` to the default `@fedify/vocab`. [[#1272], [#1356]]
10 changes: 10 additions & 0 deletions changes.d/vocab-runtime/vocabulary-object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
links:
'#1272': https://github.com/fedify-dev/fedify/issues/1272
'#1356': https://github.com/fedify-dev/fedify/pull/1356
---
- Added the `VocabularyObject` interface, which every class generated by
`@fedify/vocab-tools` implements, and the `RawPropertyValue` type with its
`RawValueObject`, `RawNodeObject`, and `RawListObject` shapes for the
expanded JSON-LD values that `getRawPropertyValues()` returns.
[[#1272], [#1356]]
10 changes: 10 additions & 0 deletions changes.d/vocab-tools/embedded-object-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
links:
'#1272': https://github.com/fedify-dev/fedify/issues/1272
'#1356': https://github.com/fedify-dev/fedify/pull/1356
Comment thread
dahlia marked this conversation as resolved.
Resolved
---
- Fixed generated accessors decoding embedded objects that have their own
`@context` without the enclosing document's context, which could
return objects of another class, drop property values, or pick objects
under a term the enclosing context mapped to another property.
[[#1272], [#1356]]
12 changes: 12 additions & 0 deletions changes.d/vocab-tools/raw-property-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
links:
'#1272': https://github.com/fedify-dev/fedify/issues/1272
'#1356': https://github.com/fedify-dev/fedify/pull/1356
---
- Generated vocabulary modules now export `getRawPropertyValues()` and
`getRawPropertyValue()`, which read any property of a generated object
by its full IRI, and their root classes implement `VocabularyObject` from
`@fedify/vocab-runtime`. Decoding retains the properties a class does not
know, so third-party vocabulary modules get the same access to
unsupported properties as `@fedify/vocab`. The functions accept objects
from any generated module. [[#1272], [#1356]]
15 changes: 15 additions & 0 deletions changes.d/vocab/embedded-object-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
links:
'#1272': https://github.com/fedify-dev/fedify/issues/1272
'#1356': https://github.com/fedify-dev/fedify/pull/1356
Comment thread
dahlia marked this conversation as resolved.
Resolved
---
- Fixed accessors of objects decoded by `fromJsonLd()` returning wrong
values for embedded objects that have their own `@context`. Such an
object was decoded again from its cached JSON-LD without the enclosing
document's context, so a type or property term that only the enclosing
context defined could yield an object of another class or drop
property values. It could also pick embedded objects under a term that
the enclosing context mapped to another property. The accessors now
apply the enclosing context and fall back to the object decoded with the
whole document when the cached JSON-LD cannot be matched with it.
[[#1272], [#1356]]
15 changes: 15 additions & 0 deletions changes.d/vocab/raw-property-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
links:
'#1272': https://github.com/fedify-dev/fedify/issues/1272
'#1356': https://github.com/fedify-dev/fedify/pull/1356
---
- Added `getRawPropertyValues()` and `getRawPropertyValue()` functions,
which read a property of a vocabulary object by its full IRI and return
its values in expanded JSON-LD. They let applications read properties
that Fedify has no accessor for, such as an extension used by only a few
peers, without depending on the JSON-LD serialization cache: unknown
properties are now retained when an object is decoded, including embedded
objects, and survive `clone()` and `deepClone()`. For properties Fedify
does support, the functions return the current typed values encoded back
into JSON-LD. The values are not verified, so validate them before use.
[[#1272], [#1356]]
10 changes: 10 additions & 0 deletions claude-plugin/skills/fedify/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,16 @@ overrides: `deepClone()` snapshots the existing indices of large sparse arrays.
Entries added during copying are not visited, so their nested objects may
remain shared. Copying preserves array length and holes.

To read a property that has no typed accessor, such as an application's
extension, use `getRawPropertyValues(object, propertyIri)` or
`getRawPropertyValue()` from `@fedify/vocab` (Fedify 2.5.0 and later) instead
of digging through `toJsonLd()` output. Pass the full property IRI, never a
compact IRI like `as:content` or a term; prefixes are not expanded. The
results are untyped expanded JSON-LD values, unverified like any received
data, so validate them before use. For properties Fedify does support, the
functions return the current typed values encoded back, not the received
document.

> [!WARNING]
> `crossOrigin: "trust"` on vocabulary deserialization trusts embedded
> objects without re-fetching. Treat it as you would
Expand Down
78 changes: 78 additions & 0 deletions docs/manual/lint.md
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,84 @@ federation.setFollowersDispatcher(

[*Filtering by server*]: ./collections.md#filtering-by-server

### `raw-property-iri-invalid`

Warns when statically known property IRIs passed to `getRawPropertyValues()`
or `getRawPropertyValue()` are invalid or use a scheme other than
`http:`/`https:`. Named imports, aliases, and namespace imports are
supported; shadowed local bindings are ignored. See [*Reading unsupported
properties*](./vocab.md#reading-unsupported-properties) for these functions.

Raw-property helpers require full IRIs. Empty strings, JSON-LD keywords,
blank node identifiers, bare terms, relative references, and other invalid
IRI strings throw `TypeError` at runtime. IRIs with other schemes are
accepted at runtime but both log a warning and trigger this rule: compact
IRIs such as `as:content` are looked up literally, and prefixes are never
expanded. For well-known prefixes, the report includes the full IRI, which
ESLint also offers as an editor suggestion.

~~~~ typescript
import { getRawPropertyValues } from "@fedify/vocab";

// Reported: a bare term throws TypeError.
await getRawPropertyValues(note, "content");

// Reported: a compact IRI is looked up literally.
await getRawPropertyValues(note, "as:content");

// Use the full property IRI.
await getRawPropertyValues(note, "https://www.w3.org/ns/activitystreams#content");
~~~~

The rule checks string literals, templates without expressions, `const`
bindings to those, and global `new URL("…")` calls without a base argument.
Dynamic values, `new URL(relative, base)`, and `URL` objects bound to
a variable are ignored, since a `URL` object can be changed after it is
constructed. Inside a TypeScript namespace or enum, `const` bindings declared
outside of it are ignored too, since another declaration of the same namespace
or enum can shadow them.

By default, only imports from `@fedify/vocab` are inspected, including
`jsr:`/`npm:` and versioned specifiers. The deprecated
`@fedify/fedify/vocab` re-export is also inspected. For other vocabulary
modules generated with `@fedify/vocab-tools`, add their exact import
specifiers to `modules`.
The option adds to the defaults. List each custom specifier form you use,
such as both `@example/vocab` and `npm:@example/vocab`.

~~~~ typescript
// eslint.config.ts
import { plugin } from "@fedify/lint";

export default [{
plugins: { "@fedify/lint": plugin },
rules: {
"@fedify/lint/raw-property-iri-invalid": ["warn", {
modules: ["@example/vocab"],
}],
},
}];
~~~~

Oxlint 1.31.0 or later is required to pass rule options:

~~~~ jsonc
// .oxlintrc.json
{
"jsPlugins": ["@fedify/lint/oxlint"],
"rules": {
"@fedify/lint/raw-property-iri-invalid": ["warn", {
"modules": ["@example/vocab"]
}]
}
}
~~~~

Deno Lint plugin rules cannot receive options, so this rule inspects only
the default `@fedify/vocab` module specifiers there. Like other Deno Lint
plugin rules, its reports are errors. ESLint's recommended configuration
enables it as a warning; the strict configuration enables it as an error.

### `deno-kv-store-deprecated`

Warns when `DenoKvStore` from the deprecated *@fedify/denokv* package is
Expand Down
Loading
Loading