No description
  • TypeScript 97.5%
  • Shell 2%
  • JavaScript 0.5%
Find a file
Steffen Rörtgen 2f68a7127e
All checks were successful
Publish Dev Build / publish (push) Successful in 33s
fix: exact round-trip for official AMB spec examples
Strict (no-tolerance) round-tripping of the 36 official examples from
dini-ag-kim/amb surfaced three defects:

- isAccessibleForFree came back as the string "true"/"false" — nostrToAmb
  now coerces string booleans, and the round-trip test tolerance that
  masked this is removed
- name-only isBasedOn relations gained a literal "undefined" id tag —
  the :id tag is now omitted when the relation has no id
- suggestedAge (draft schema: integer minValue/maxValue) was silently
  dropped — now mapped to suggestedAge:minValue/:maxValue tags with
  integer coercion on reverse

Fixture set re-synced with upstream (adds suggested-min-age.json and
invalid/suggestedAge-as-string.json). 33/36 examples now round-trip
byte-exactly; the remaining 3 use custom @context entries, which the
event format does not store (documented limitation in NIP-AMB).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:40:27 +02:00
.forgejo/workflows test new publishing workflow 2026-01-30 12:38:31 +01:00
docs feat: NIP-AMB realignment — nostr: creator ids, npub name fallback, naddr id derivation 2026-07-16 11:59:53 +02:00
scripts fix: exact round-trip for official AMB spec examples 2026-07-16 13:40:27 +02:00
src fix: exact round-trip for official AMB spec examples 2026-07-16 13:40:27 +02:00
tests fix: exact round-trip for official AMB spec examples 2026-07-16 13:40:27 +02:00
.gitignore update gitignore 2026-01-28 21:10:11 +01:00
CLAUDE.md fix bulk conversion pagination by using incrementing created_at timestamps 2026-02-05 09:37:32 +01:00
LICENSE Add license 2025-12-01 20:41:08 +01:00
package-lock.json build: esbuild browser bundle of converters 2026-06-23 13:08:26 +02:00
package.json build: esbuild browser bundle of converters 2026-06-23 13:08:26 +02:00
README.md feat: NIP-AMB realignment — nostr: creator ids, npub name fallback, naddr id derivation 2026-07-16 11:59:53 +02:00
tsconfig.base.json inital commit 2025-12-01 06:39:38 +01:00
tsconfig.json inital commit 2025-12-01 06:39:38 +01:00
tsconfig.types.json inital commit 2025-12-01 06:39:38 +01:00
vitest.config.ts inital commit 2025-12-01 06:39:38 +01:00

AMB-Nostr Converter

A TypeScript library for converting between AMB ("Allgemeines Metadatenprofil für Bildungsressourcen" - General Metadata Profile for Learning Resources) and Nostr educational events (kind:30142).

Overview

Bidirectional conversion between:

  • AMB: JSON-LD based metadata format for educational resources (used by European OER initiatives)
  • Nostr: Decentralized protocol for social media and content distribution

Usable as a library or CLI tool.

Installation

npm install amb-nostr-converter --registry=https://git.edufeed.org/api/packages/edufeed/npm/

For the latest development build (published on every push to main):

npm install amb-nostr-converter@dev --registry=https://git.edufeed.org/api/packages/edufeed/npm/

Quick Start

As a Library

import { ambToNostr, nostrToAmb, AmbLearningResource } from 'amb-nostr-converter';

const resource: AmbLearningResource = {
  "@context": ["https://w3id.org/kim/amb/context.jsonld"],
  "id": "https://example.org/course123",
  "type": ["LearningResource", "Course"],
  "name": "Introduction to TypeScript",
  "creator": [{ "type": "Person", "name": "Jane Smith" }],
  "description": "Learn TypeScript fundamentals",
  "keywords": ["TypeScript", "Programming"],
  "license": { "id": "https://creativecommons.org/licenses/by-sa/4.0/" }
};

// Convert to Nostr event
const result = ambToNostr(resource, { pubkey: 'your-nostr-public-key-hex' });

if (result.success) {
  console.log('Nostr Event:', result.data);

  // Convert back to AMB
  const ambResult = nostrToAmb(result.data!);
}

Creator identities (Nostr-native vs. external)

Per NIP-AMB, each creator/contributor gets exactly one representation in the event — never both:

  • Nostr identity: set the person's id to a nostr:npub… or nostr:nprofile… URI (NIP-21). ambToNostr decodes it and emits a ["p", <pubkey-hex>, <relay-hint>, "creator"|"contributor"] tag; no flattened creator:* tags are written for that person. Relay hint precedence: nprofile-embedded relay → defaultRelayHint option → empty. (The legacy nostrPubkey field still works but is deprecated.)
  • External identity: any other id (e.g. an ORCID URL) — or no id — produces flattened creator:name/creator:type/creator:id/… tags.
const resource: AmbLearningResource = {
  // ...
  "creator": [
    { "type": "Person", "name": "Jane Smith", "id": "nostr:npub1..." },          // → p tag
    { "type": "Person", "name": "John Doe", "id": "https://orcid.org/0000-..." } // → creator:* tags
  ],
};

On reverse conversion, nostrToAmb maps each creator/contributor p tag to { name, type: "Person", id: "nostr:<nprofile>" }. Because the AMB schema requires name and the base converter is offline, name falls back to the npub encoding; use nostrToAmbWithProfiles to resolve real names from kind:0 profiles (it replaces the npub fallback).

For events whose d tag is not an absolute URI (e.g. a slug), nostrToAmb derives the AMB id as nostr:<naddr> from the event's kind, pubkey, and d value.

As a CLI Tool

# AMB → Nostr
amb-convert amb:nostr course.json -o event.json

# Nostr → AMB
amb-convert nostr:amb event.json -o recovered.json

# Pretty-print and pipe
amb-convert amb:nostr course.json -p | jq .tags

# Sign events with private key
amb-convert amb:nostr course.json --nsec nsec1...

# Batch convert JSONL
amb-convert amb:nostr resources.jsonl --nsec $NOSTR_NSEC -o events.jsonl

CLI Reference

Option Description Direction
<direction> amb:nostr or nostr:amb Required
[input] Input file path (omit for stdin) Both
-o, --output <file> Output file path (omit for stdout) Both
-p, --pretty Pretty-print JSON Both
--tags Output only tags array AMB→Nostr
--nsec <key> Sign with private key (nsec or hex) AMB→Nostr
--private-key <key> Sign with private key (hex or nsec) AMB→Nostr

Batch Processing (JSONL)

JSONL input (one JSON object per line) is auto-detected. Errors on individual lines are reported with line numbers and don't stop processing of remaining lines.

cat resources.jsonl | amb-convert amb:nostr --nsec $NOSTR_NSEC -o events.jsonl

When converting multiple events, each event gets an incrementing created_at timestamp (base time + 1 second per event). This ensures relay clients that use cursor-based pagination on created_at can load all events correctly.

Event Signing

When --nsec or --private-key is provided (both accept nsec1 or hex format):

  • Public key is automatically derived
  • Event ID and Schnorr signature are generated
  • Output includes id, sig, and correct pubkey fields

Development

npm install
npm run build
npm test