From 2382600e324082578a6313d80921495d62a2db47 Mon Sep 17 00:00:00 2001 From: authenticarc <30170691+authenticarc@users.noreply.github.com> Date: Fri, 15 May 2026 14:36:56 +0800 Subject: [PATCH] Add W3C namespace adapters --- packages/cli/src/adapter-registry.ts | 6 ++++ packages/core/src/index.ts | 1 + packages/core/src/w3c.ts | 33 ++++++++++++++++++++++ packages/w3c/activitypub/package.json | 26 +++++++++++++++++ packages/w3c/activitypub/src/index.test.ts | 17 +++++++++++ packages/w3c/activitypub/src/index.ts | 17 +++++++++++ packages/w3c/activitypub/tsconfig.json | 6 ++++ packages/w3c/micropub/package.json | 26 +++++++++++++++++ packages/w3c/micropub/src/index.test.ts | 16 +++++++++++ packages/w3c/micropub/src/index.ts | 17 +++++++++++ packages/w3c/micropub/tsconfig.json | 6 ++++ packages/w3c/websub/package.json | 26 +++++++++++++++++ packages/w3c/websub/src/index.test.ts | 16 +++++++++++ packages/w3c/websub/src/index.ts | 17 +++++++++++ packages/w3c/websub/tsconfig.json | 6 ++++ pnpm-lock.yaml | 18 ++++++++++++ 16 files changed, 254 insertions(+) create mode 100644 packages/core/src/w3c.ts create mode 100644 packages/w3c/activitypub/package.json create mode 100644 packages/w3c/activitypub/src/index.test.ts create mode 100644 packages/w3c/activitypub/src/index.ts create mode 100644 packages/w3c/activitypub/tsconfig.json create mode 100644 packages/w3c/micropub/package.json create mode 100644 packages/w3c/micropub/src/index.test.ts create mode 100644 packages/w3c/micropub/src/index.ts create mode 100644 packages/w3c/micropub/tsconfig.json create mode 100644 packages/w3c/websub/package.json create mode 100644 packages/w3c/websub/src/index.test.ts create mode 100644 packages/w3c/websub/src/index.ts create mode 100644 packages/w3c/websub/tsconfig.json diff --git a/packages/cli/src/adapter-registry.ts b/packages/cli/src/adapter-registry.ts index 40e298d3..c6148497 100644 --- a/packages/cli/src/adapter-registry.ts +++ b/packages/cli/src/adapter-registry.ts @@ -135,6 +135,12 @@ export const CATEGORIES: readonly AdapterCategory[] = [ description: 'VCS — GitHub, GitLab, Gitea', adapters: ['gitea', 'github', 'gitlab'], }, + { + id: 'w3c', + pkgPrefix: '@profullstack/sh1pt-w3c', + description: 'W3C social web namespaces — ActivityPub, Micropub, WebSub', + adapters: ['activitypub', 'micropub', 'websub'], + }, { id: 'webhooks', pkgPrefix: '@profullstack/sh1pt-webhooks', diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1793d799..2791c18b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -22,6 +22,7 @@ export * from './service.js'; export * from './secrets.js'; export * from './observability.js'; export * from './security.js'; +export * from './w3c.js'; export * from './exec.js'; export * from './config-store.js'; export * from './setup.js'; diff --git a/packages/core/src/w3c.ts b/packages/core/src/w3c.ts new file mode 100644 index 00000000..1e4be937 --- /dev/null +++ b/packages/core/src/w3c.ts @@ -0,0 +1,33 @@ +import { autoSetup } from './setup-helpers.js'; + +export type W3cCapability = + | 'discover' + | 'publish' + | 'receive' + | 'subscribe' + | 'notify' + | 'verify'; + +export interface W3cEndpoint { + id: string; + label: string; + method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; + rel?: string; + pathHint?: string; +} + +export interface W3cNamespace { + id: string; + label: string; + specUrl: string; + namespace: string; + capabilities: readonly W3cCapability[]; + endpoints: readonly W3cEndpoint[]; + discover?(ctx: { log(m: string): void }, config: Config): Promise; + setup?(ctx: import('./setup.js').SetupContext): Promise>; +} + +export function defineW3cNamespace(n: W3cNamespace): W3cNamespace { + return autoSetup(n); +} + diff --git a/packages/w3c/activitypub/package.json b/packages/w3c/activitypub/package.json new file mode 100644 index 00000000..1aac8b44 --- /dev/null +++ b/packages/w3c/activitypub/package.json @@ -0,0 +1,26 @@ +{ + "name": "@profullstack/sh1pt-w3c-activitypub", + "version": "0.1.15", + "type": "module", + "main": "./src/index.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "prepublishOnly": "pnpm build" + }, + "dependencies": { + "@profullstack/sh1pt-core": "workspace:*" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/profullstack/sh1pt.git", + "directory": "packages/w3c/activitypub" + }, + "homepage": "https://sh1pt.com", + "bugs": "https://github.com/profullstack/sh1pt/issues", + "files": [ + "dist" + ] +} + diff --git a/packages/w3c/activitypub/src/index.test.ts b/packages/w3c/activitypub/src/index.test.ts new file mode 100644 index 00000000..78994f6a --- /dev/null +++ b/packages/w3c/activitypub/src/index.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest'; +import { smokeTest } from '@profullstack/sh1pt-core/testing'; +import namespace from './index.js'; + +smokeTest(namespace, { idPrefix: 'w3c' }); + +describe('w3c-activitypub namespace', () => { + it('declares ActivityPub endpoints needed for actor federation', () => { + expect(namespace.specUrl).toBe('https://www.w3.org/TR/activitypub/'); + expect(namespace.namespace).toBe('https://www.w3.org/ns/activitystreams'); + expect(namespace.capabilities).toEqual(expect.arrayContaining(['publish', 'receive', 'verify'])); + expect(namespace.endpoints.map((endpoint) => endpoint.id)).toEqual( + expect.arrayContaining(['actor', 'inbox', 'outbox', 'followers', 'following']), + ); + }); +}); + diff --git a/packages/w3c/activitypub/src/index.ts b/packages/w3c/activitypub/src/index.ts new file mode 100644 index 00000000..6a5ce51b --- /dev/null +++ b/packages/w3c/activitypub/src/index.ts @@ -0,0 +1,17 @@ +import { defineW3cNamespace } from '@profullstack/sh1pt-core'; + +export default defineW3cNamespace({ + id: 'w3c-activitypub', + label: 'ActivityPub', + specUrl: 'https://www.w3.org/TR/activitypub/', + namespace: 'https://www.w3.org/ns/activitystreams', + capabilities: ['discover', 'publish', 'receive', 'verify'], + endpoints: [ + { id: 'actor', label: 'Actor object', method: 'GET', pathHint: '/users/{actor}' }, + { id: 'inbox', label: 'Inbox', method: 'POST', pathHint: '/users/{actor}/inbox' }, + { id: 'outbox', label: 'Outbox', method: 'GET', pathHint: '/users/{actor}/outbox' }, + { id: 'followers', label: 'Followers collection', method: 'GET', pathHint: '/users/{actor}/followers' }, + { id: 'following', label: 'Following collection', method: 'GET', pathHint: '/users/{actor}/following' }, + ], +}); + diff --git a/packages/w3c/activitypub/tsconfig.json b/packages/w3c/activitypub/tsconfig.json new file mode 100644 index 00000000..010065dd --- /dev/null +++ b/packages/w3c/activitypub/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { "outDir": "dist", "rootDir": "src" }, + "include": ["src/**/*"] +} + diff --git a/packages/w3c/micropub/package.json b/packages/w3c/micropub/package.json new file mode 100644 index 00000000..b05502bf --- /dev/null +++ b/packages/w3c/micropub/package.json @@ -0,0 +1,26 @@ +{ + "name": "@profullstack/sh1pt-w3c-micropub", + "version": "0.1.15", + "type": "module", + "main": "./src/index.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "prepublishOnly": "pnpm build" + }, + "dependencies": { + "@profullstack/sh1pt-core": "workspace:*" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/profullstack/sh1pt.git", + "directory": "packages/w3c/micropub" + }, + "homepage": "https://sh1pt.com", + "bugs": "https://github.com/profullstack/sh1pt/issues", + "files": [ + "dist" + ] +} + diff --git a/packages/w3c/micropub/src/index.test.ts b/packages/w3c/micropub/src/index.test.ts new file mode 100644 index 00000000..e31d2cf1 --- /dev/null +++ b/packages/w3c/micropub/src/index.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; +import { smokeTest } from '@profullstack/sh1pt-core/testing'; +import namespace from './index.js'; + +smokeTest(namespace, { idPrefix: 'w3c' }); + +describe('w3c-micropub namespace', () => { + it('declares discovery and mutation endpoints', () => { + expect(namespace.specUrl).toBe('https://www.w3.org/TR/micropub/'); + expect(namespace.capabilities).toEqual(expect.arrayContaining(['discover', 'publish'])); + expect(namespace.endpoints.map((endpoint) => endpoint.id)).toEqual( + expect.arrayContaining(['endpoint-discovery', 'create', 'update', 'delete', 'media']), + ); + }); +}); + diff --git a/packages/w3c/micropub/src/index.ts b/packages/w3c/micropub/src/index.ts new file mode 100644 index 00000000..ece95de7 --- /dev/null +++ b/packages/w3c/micropub/src/index.ts @@ -0,0 +1,17 @@ +import { defineW3cNamespace } from '@profullstack/sh1pt-core'; + +export default defineW3cNamespace({ + id: 'w3c-micropub', + label: 'Micropub', + specUrl: 'https://www.w3.org/TR/micropub/', + namespace: 'micropub', + capabilities: ['discover', 'publish'], + endpoints: [ + { id: 'endpoint-discovery', label: 'Micropub endpoint discovery', method: 'GET', rel: 'micropub' }, + { id: 'create', label: 'Create post', method: 'POST' }, + { id: 'update', label: 'Update post', method: 'POST' }, + { id: 'delete', label: 'Delete post', method: 'POST' }, + { id: 'media', label: 'Media endpoint', method: 'POST', rel: 'micropub_media' }, + ], +}); + diff --git a/packages/w3c/micropub/tsconfig.json b/packages/w3c/micropub/tsconfig.json new file mode 100644 index 00000000..010065dd --- /dev/null +++ b/packages/w3c/micropub/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { "outDir": "dist", "rootDir": "src" }, + "include": ["src/**/*"] +} + diff --git a/packages/w3c/websub/package.json b/packages/w3c/websub/package.json new file mode 100644 index 00000000..dcc4cb24 --- /dev/null +++ b/packages/w3c/websub/package.json @@ -0,0 +1,26 @@ +{ + "name": "@profullstack/sh1pt-w3c-websub", + "version": "0.1.15", + "type": "module", + "main": "./src/index.ts", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "prepublishOnly": "pnpm build" + }, + "dependencies": { + "@profullstack/sh1pt-core": "workspace:*" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/profullstack/sh1pt.git", + "directory": "packages/w3c/websub" + }, + "homepage": "https://sh1pt.com", + "bugs": "https://github.com/profullstack/sh1pt/issues", + "files": [ + "dist" + ] +} + diff --git a/packages/w3c/websub/src/index.test.ts b/packages/w3c/websub/src/index.test.ts new file mode 100644 index 00000000..8980721b --- /dev/null +++ b/packages/w3c/websub/src/index.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; +import { smokeTest } from '@profullstack/sh1pt-core/testing'; +import namespace from './index.js'; + +smokeTest(namespace, { idPrefix: 'w3c' }); + +describe('w3c-websub namespace', () => { + it('declares hub discovery, subscription, callback, and distribution endpoints', () => { + expect(namespace.specUrl).toBe('https://www.w3.org/TR/websub/'); + expect(namespace.capabilities).toEqual(expect.arrayContaining(['subscribe', 'notify', 'verify'])); + expect(namespace.endpoints.map((endpoint) => endpoint.id)).toEqual( + expect.arrayContaining(['topic-discovery', 'hub-discovery', 'subscribe', 'callback-verification', 'content-distribution']), + ); + }); +}); + diff --git a/packages/w3c/websub/src/index.ts b/packages/w3c/websub/src/index.ts new file mode 100644 index 00000000..781d610b --- /dev/null +++ b/packages/w3c/websub/src/index.ts @@ -0,0 +1,17 @@ +import { defineW3cNamespace } from '@profullstack/sh1pt-core'; + +export default defineW3cNamespace({ + id: 'w3c-websub', + label: 'WebSub', + specUrl: 'https://www.w3.org/TR/websub/', + namespace: 'websub', + capabilities: ['discover', 'subscribe', 'notify', 'verify'], + endpoints: [ + { id: 'topic-discovery', label: 'Topic discovery', method: 'GET', rel: 'self' }, + { id: 'hub-discovery', label: 'Hub discovery', method: 'GET', rel: 'hub' }, + { id: 'subscribe', label: 'Subscription request', method: 'POST' }, + { id: 'callback-verification', label: 'Subscriber callback verification', method: 'GET' }, + { id: 'content-distribution', label: 'Content distribution', method: 'POST' }, + ], +}); + diff --git a/packages/w3c/websub/tsconfig.json b/packages/w3c/websub/tsconfig.json new file mode 100644 index 00000000..010065dd --- /dev/null +++ b/packages/w3c/websub/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { "outDir": "dist", "rootDir": "src" }, + "include": ["src/**/*"] +} + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62c5efd3..5546d0fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1898,6 +1898,24 @@ importers: specifier: workspace:* version: link:../../core + packages/w3c/activitypub: + dependencies: + '@profullstack/sh1pt-core': + specifier: workspace:* + version: link:../../core + + packages/w3c/micropub: + dependencies: + '@profullstack/sh1pt-core': + specifier: workspace:* + version: link:../../core + + packages/w3c/websub: + dependencies: + '@profullstack/sh1pt-core': + specifier: workspace:* + version: link:../../core + packages/web: {} packages/webhooks/discord: