Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 25 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ To be released.

### @fedify/vocab

- Added vocabulary types for economic resource coordination
in federated networks. [[#578] by scammo]
- Added `Proposal` class for publishing offers or requests.
- Added `Intent` class for describing economic transactions within
a proposal, with `action`, `resourceConformsTo`, `resourceQuantity`,
`availableQuantity`, and `minimumQuantity` properties.
Comment on lines +84 to +89
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sub-items (Proposal, Intent, Measure) should be nested under the summary line rather than being flat siblings. Also, please run mise run fmt to fix Hongdown formatting issues.

Suggested structure:

 -  Added vocabulary types for economic resource coordination
    in federated networks.  [[#578] by Samuel Brinkmann]

     -  Added `Proposal` class for publishing offers or requests.
     -  Added `Intent` class for describing economic transactions within
        a proposal, with `action`, `resourceConformsTo`, `resourceQuantity`,
        `availableQuantity`, and `minimumQuantity` properties.
     -  Added `Measure` class for representing quantities with units of
        measure, with `hasUnit` and `hasNumericalValue` properties.

- Added `Measure` class for representing quantities with units of
measure, with `hasUnit` and `hasNumericalValue` properties.

- Fixed `Endpoints.toJsonLd()` to no longer emit invalid
`"type": "as:Endpoints"` in the serialized JSON-LD. The `as:Endpoints`
type does not exist in the ActivityStreams vocabulary, and its presence
Expand All @@ -91,6 +100,7 @@ To be released.
`"type": "as:Source"` in the serialized JSON-LD. The `as:Source` type
does not exist in the ActivityStreams vocabulary either.

[#578]: https://github.com/fedify-dev/fedify/issues/578
[browser.pub]: https://browser.pub/
[#576]: https://github.com/fedify-dev/fedify/issues/576

Expand Down Expand Up @@ -610,6 +620,21 @@ Released on February 22, 2026.
- This package is primarily used by generated vocabulary classes and
provides the runtime infrastructure for ActivityPub object processing.

### @fedify/vocab

- Added vocabulary types for economic resource coordination
in federated networks. [[#578]]

- Added `Proposal` class for publishing offers or requests.
- Added `Intent` class for describing economic transactions within
a proposal, with `action`, `resourceConformsTo`, `resourceQuantity`,
`availableQuantity`, and `minimumQuantity` properties.
- Added `Measure` class for representing quantities with units of
measure, with `hasUnit` and `hasNumericalValue` properties.

[#578]: https://github.com/fedify-dev/fedify/issues/578
[ValueFlows]: https://www.valueflo.ws/
Comment on lines 622 to +636
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These entries appear under the Version 1.5.0 section, which has already been released. Since this PR hasn't been merged yet, the changelog entry should only appear in the unreleased section at the top. Please remove this duplicate block.

Also, the [#578] reference link is defined twice (once in each section), and the [ValueFlows] reference link is defined but never used. Please clean these up.


### @fedify/elysia

- Added *deno.json* configuration file to enable proper Deno tooling support
Expand Down
3,011 changes: 2,860 additions & 151 deletions packages/vocab-tools/src/__snapshots__/class.test.ts.deno.snap

Large diffs are not rendered by default.

3,011 changes: 2,860 additions & 151 deletions packages/vocab-tools/src/__snapshots__/class.test.ts.node.snap

Large diffs are not rendered by default.

3,011 changes: 2,860 additions & 151 deletions packages/vocab-tools/src/__snapshots__/class.test.ts.snap

Large diffs are not rendered by default.

183 changes: 183 additions & 0 deletions packages/vocab/src/__snapshots__/vocab.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,30 @@ snapshot[`Deno.inspect(PropertyValue) [auto] 2`] = `'PropertyValue { id: URL "ht

snapshot[`Deno.inspect(PropertyValue) [auto] 3`] = `'PropertyValue { id: URL "https://example.com/", name: "hello", value: "hello" }'`;

snapshot[`Deno.inspect(Measure) [auto] 1`] = `
'Measure {
id: URL "https://example.com/",
hasUnit: "hello",
hasNumericalValue: "hello"
}'
`;

snapshot[`Deno.inspect(Measure) [auto] 2`] = `
'Measure {
id: URL "https://example.com/",
hasUnit: "hello",
hasNumericalValue: "hello"
}'
`;

snapshot[`Deno.inspect(Measure) [auto] 3`] = `
'Measure {
id: URL "https://example.com/",
hasUnit: "hello",
hasNumericalValue: "hello"
}'
`;

snapshot[`Deno.inspect(Export) [auto] 1`] = `
'Export {
id: URL "https://example.com/",
Expand Down Expand Up @@ -537,6 +561,165 @@ snapshot[`Deno.inspect(Multikey) [auto] 3`] = `
}'
`;

snapshot[`Deno.inspect(Intent) [auto] 1`] = `
'Intent {
id: URL "https://example.com/",
action: "hello",
resourceConformsTo: URL "https://example.com/",
resourceQuantity: Measure {},
availableQuantity: Measure {},
minimumQuantity: Measure {}
}'
`;

snapshot[`Deno.inspect(Intent) [auto] 2`] = `
'Intent {
id: URL "https://example.com/",
action: "hello",
resourceConformsTo: URL "https://example.com/",
resourceQuantity: Measure {},
availableQuantity: Measure {},
minimumQuantity: Measure {}
}'
`;

snapshot[`Deno.inspect(Intent) [auto] 3`] = `
'Intent {
id: URL "https://example.com/",
action: "hello",
resourceConformsTo: URL "https://example.com/",
resourceQuantity: Measure {},
availableQuantity: Measure {},
minimumQuantity: Measure {}
}'
`;

snapshot[`Deno.inspect(Proposal) [auto] 1`] = `
'Proposal {
id: URL "https://example.com/",
attachments: [ Object {}, Link {}, PropertyValue {} ],
attributions: [ Application {}, Group {}, Organization {}, Person {}, Service {} ],
audience: Object {},
contents: [ "hello", <en> "hello" ],
contexts: [ Object {}, Link {} ],
names: [ "hello", <en> "hello" ],
endTime: 2024-03-03T08:30:06.796196096Z,
generators: [ Object {}, Link {} ],
icon: Image {},
image: Image {},
replyTargets: [ Object {}, Link {} ],
locations: [ Object {}, Link {} ],
previews: [ Link {}, Object {} ],
published: 2024-03-03T08:30:06.796196096Z,
replies: Collection {},
shares: Collection {},
likes: Collection {},
emojiReactions: Collection {},
startTime: 2024-03-03T08:30:06.796196096Z,
summaries: [ "hello", <en> "hello" ],
tags: [ Object {}, Link {} ],
updated: 2024-03-03T08:30:06.796196096Z,
urls: [ URL "https://example.com/", Link {} ],
to: Object {},
bto: Object {},
cc: Object {},
bcc: Object {},
mediaType: "hello",
duration: PT1H,
sensitive: true,
source: Source {},
proof: DataIntegrityProof {},
purpose: "hello",
publishes: Intent {},
reciprocal: Intent {},
unitBased: true
}'
`;

snapshot[`Deno.inspect(Proposal) [auto] 2`] = `
'Proposal {
id: URL "https://example.com/",
attachments: [ URL "https://example.com/" ],
attribution: URL "https://example.com/",
audience: URL "https://example.com/",
contents: [ "hello", <en> "hello" ],
contexts: [ URL "https://example.com/" ],
names: [ "hello", <en> "hello" ],
endTime: 2024-03-03T08:30:06.796196096Z,
generators: [ URL "https://example.com/" ],
icon: URL "https://example.com/",
image: URL "https://example.com/",
replyTarget: URL "https://example.com/",
location: URL "https://example.com/",
preview: URL "https://example.com/",
published: 2024-03-03T08:30:06.796196096Z,
replies: URL "https://example.com/",
shares: URL "https://example.com/",
likes: URL "https://example.com/",
emojiReactions: URL "https://example.com/",
startTime: 2024-03-03T08:30:06.796196096Z,
summaries: [ "hello", <en> "hello" ],
tags: [ URL "https://example.com/" ],
updated: 2024-03-03T08:30:06.796196096Z,
urls: [ URL "https://example.com/", Link {} ],
to: URL "https://example.com/",
bto: URL "https://example.com/",
cc: URL "https://example.com/",
bcc: URL "https://example.com/",
mediaType: "hello",
duration: PT1H,
sensitive: true,
source: Source {},
proof: URL "https://example.com/",
purpose: "hello",
publishes: URL "https://example.com/",
reciprocal: URL "https://example.com/",
unitBased: true
}'
`;

snapshot[`Deno.inspect(Proposal) [auto] 3`] = `
'Proposal {
id: URL "https://example.com/",
attachments: [ Object {}, Object {} ],
attributions: [ Application {}, Application {} ],
audiences: [ Object {}, Object {} ],
contents: [ "hello", "hello" ],
contexts: [ Object {}, Object {} ],
names: [ "hello", "hello" ],
endTime: 2024-03-03T08:30:06.796196096Z,
generators: [ Object {}, Object {} ],
icons: [ Image {}, Image {} ],
images: [ Image {}, Image {} ],
replyTargets: [ Object {}, Object {} ],
locations: [ Object {}, Object {} ],
previews: [ Link {}, Link {} ],
published: 2024-03-03T08:30:06.796196096Z,
replies: Collection {},
shares: Collection {},
likes: Collection {},
emojiReactions: Collection {},
startTime: 2024-03-03T08:30:06.796196096Z,
summaries: [ "hello", "hello" ],
tags: [ Object {}, Object {} ],
updated: 2024-03-03T08:30:06.796196096Z,
urls: [ URL "https://example.com/", URL "https://example.com/" ],
tos: [ Object {}, Object {} ],
btos: [ Object {}, Object {} ],
ccs: [ Object {}, Object {} ],
bccs: [ Object {}, Object {} ],
mediaType: "hello",
duration: PT1H,
sensitive: true,
source: Source {},
proofs: [ DataIntegrityProof {}, DataIntegrityProof {} ],
purpose: "hello",
publishes: Intent {},
reciprocal: Intent {},
unitBased: true
}'
`;

snapshot[`Deno.inspect(Accept) [auto] 1`] = `
'Accept {
id: URL "https://example.com/",
Expand Down
72 changes: 72 additions & 0 deletions packages/vocab/src/intent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
$schema: ../../vocab-tools/schema.yaml
name: Intent
compactName: Intent
uri: "https://w3id.org/valueflows/ont/vf#Intent"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intent is marked as entity: true, which means it can be independently fetched by URI. In FEP-0837, Intents are typically embedded within a Proposal and use fragment URIs (e.g., #primary, #reciprocal), which suggests they function more as embedded value objects.

Is there a use case where an Intent would be fetched independently outside of its parent Proposal? If not, entity: false might be more appropriate, similar to how Measure is defined.

Also, unlike Proposal, Intent has no extends field. If Proposal extends Object for practical reasons (reusing name, content, etc.), should Intent also extend Object for consistency, or is the asymmetry intentional?

entity: true
description: |
A proposed economic transaction describing what is being offered or requested
in a {@link Proposal}.
defaultContext:
- "https://www.w3.org/ns/activitystreams"
- vf: "https://w3id.org/valueflows/ont/vf#"
om2: "http://www.ontology-of-units-of-measure.org/resource/om-2/"
Intent: "vf:Intent"
action: "vf:action"
resourceConformsTo:
"@id": "vf:resourceConformsTo"
"@type": "@id"
resourceQuantity: "vf:resourceQuantity"
availableQuantity: "vf:availableQuantity"
minimumQuantity: "vf:minimumQuantity"
hasUnit: "om2:hasUnit"
hasNumericalValue: "om2:hasNumericalValue"

properties:
- singularName: action
functional: true
compactName: action
uri: "https://w3id.org/valueflows/ont/vf#action"
description: |
The type of economic transaction. The value of this property should be
either `"deliverService"` or `"transfer"`.
range:
- "http://www.w3.org/2001/XMLSchema#string"

- singularName: resourceConformsTo
functional: true
compactName: resourceConformsTo
uri: "https://w3id.org/valueflows/ont/vf#resourceConformsTo"
description: |
The type of an economic resource. Could be any URI.
range:
- "http://www.w3.org/2001/XMLSchema#anyURI"

- singularName: resourceQuantity
functional: true
compactName: resourceQuantity
uri: "https://w3id.org/valueflows/ont/vf#resourceQuantity"
untyped: true
description: |
The amount and unit of the economic resource.
range:
- "http://www.ontology-of-units-of-measure.org/resource/om-2/Measure"

- singularName: availableQuantity
functional: true
compactName: availableQuantity
uri: "https://w3id.org/valueflows/ont/vf#availableQuantity"
untyped: true
description: |
The quantity of the offered resource currently available.
range:
- "http://www.ontology-of-units-of-measure.org/resource/om-2/Measure"

- singularName: minimumQuantity
functional: true
compactName: minimumQuantity
uri: "https://w3id.org/valueflows/ont/vf#minimumQuantity"
untyped: true
description: |
The minimum possible quantity of the resource.
range:
- "http://www.ontology-of-units-of-measure.org/resource/om-2/Measure"
32 changes: 32 additions & 0 deletions packages/vocab/src/measure.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
$schema: ../../vocab-tools/schema.yaml
name: Measure
compactName: om2:Measure
uri: "http://www.ontology-of-units-of-measure.org/resource/om-2/Measure"
entity: false
description: A quantity with a unit of measure.
defaultContext:
- "https://www.w3.org/ns/activitystreams"
- om2: "http://www.ontology-of-units-of-measure.org/resource/om-2/"
hasUnit: "om2:hasUnit"
hasNumericalValue: "om2:hasNumericalValue"

properties:
- singularName: hasUnit
functional: true
compactName: hasUnit
uri: "http://www.ontology-of-units-of-measure.org/resource/om-2/hasUnit"
description: |
The name of the unit, according to the Ontology of units of Measure
classification. The recommended unit for countable items is `"one"`.
range:
- "http://www.w3.org/2001/XMLSchema#string"

- singularName: hasNumericalValue
functional: true
compactName: hasNumericalValue
uri: "http://www.ontology-of-units-of-measure.org/resource/om-2/hasNumericalValue"
description: |
The amount of the resource. If not specified, arbitrary amounts can be
used when responding to the proposal.
range:
- "http://www.w3.org/2001/XMLSchema#string"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this was already discussed and xsd:string was agreed upon as a pragmatic stopgap until xsd:decimal support is added (#617). That's fine for now.

One small suggestion: it would be helpful to add a note in the description field mentioning that xsd:string is a temporary choice and that the value should represent a decimal number. This helps consumers of the API understand the expected format. For example:

description: |
  The amount of the resource, represented as a decimal string.  If not
  specified, arbitrary amounts can be used when responding to the proposal.

  NOTE: This property uses `xsd:string` as a temporary measure until
  `xsd:decimal` is supported.  See #617.

Loading
Loading