You've already forked tribes-plugin-aether
forked from tribes/tribes-plugin-template
feat: namespace plugin identity
Adopt canonical plugin id/slug manifest fields, vendor-prefixed OTP app naming, and fully-qualified capability ids for Aether.
This commit is contained in:
@@ -620,8 +620,8 @@ checkout during development. For the full contract, see
|
||||
`version`, `entry_module`, `host_api`, `otp_app`, `provides`, `requires`, and
|
||||
`enhances_with` aligned with the runtime spec returned by `register/1`.
|
||||
- `entry_module` must be a valid module ending in `.Plugin` under an owner-controlled namespace.
|
||||
- Capability versions are discrete breaking-change markers such as `ui@1` or
|
||||
`social@1`, not semver. Framework APIs are part of the `host_api` foundation, not separate manifest capabilities.
|
||||
- Capability versions are discrete breaking-change markers such as `org.tribe-one.caps.ui@1` or
|
||||
`org.tribe-one.caps.social@1`, not semver. Framework APIs are part of the `host_api` foundation, not separate manifest capabilities.
|
||||
- Use `requires` for hard dependencies and `enhances_with` for optional
|
||||
integrations that the plugin can run without.
|
||||
- Run `scripts/plugin validate` after changing `manifest.json` or the runtime
|
||||
@@ -630,8 +630,8 @@ checkout during development. For the full contract, see
|
||||
## UI And Assets
|
||||
|
||||
- Plugin LiveViews that use host chrome should render with
|
||||
`Tribes.Plugin.Layouts.app` and keep `ui@1` in `manifest.json` `requires`.
|
||||
- Consumers should target the `ui@1` facade with `use Tribes.UI` or
|
||||
`Tribes.Plugin.Layouts.app` and keep `org.tribe-one.caps.ui@1` in `manifest.json` `requires`.
|
||||
- Consumers should target the `org.tribe-one.caps.ui@1` facade with `use Tribes.UI` or
|
||||
`import Tribes.UI.Components`, not a concrete provider module.
|
||||
- Declare browser assets in `manifest.json` under `assets.global_js` and
|
||||
`assets.global_css`; the host serves them through the plugin asset surface.
|
||||
|
||||
@@ -5,8 +5,8 @@ Tribes node.
|
||||
|
||||
It provides:
|
||||
|
||||
- `social@1` — a local tribe feed backed by Nostr kind `1` notes.
|
||||
- `chat@1` — reusable chat surfaces for public rooms, embeddable context chat,
|
||||
- `org.tribe-one.caps.social@1` — a local tribe feed backed by Nostr kind `1` notes.
|
||||
- `org.tribe-one.caps.chat@1` — reusable chat surfaces for public rooms, embeddable context chat,
|
||||
NIP-17 direct messages, and future chat backends.
|
||||
|
||||
## Features
|
||||
@@ -52,13 +52,15 @@ The package pin is kept at `@internet-privacy/marmot-ts@0.5.1`.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "aether",
|
||||
"id": "org.tribe-one.plugins.aether",
|
||||
"slug": "aether",
|
||||
"display_name": "Aether",
|
||||
"version": "0.2.0",
|
||||
"entry_module": "TribeOne.TribesPlugin.Aether.Plugin",
|
||||
"host_api": "1",
|
||||
"otp_app": "aether",
|
||||
"provides": ["social@1", "chat@1"],
|
||||
"requires": ["ui@1"],
|
||||
"otp_app": "tribe_one_aether",
|
||||
"provides": ["org.tribe-one.caps.social@1", "org.tribe-one.caps.chat@1"],
|
||||
"requires": ["org.tribe-one.caps.ui@1"],
|
||||
"assets": {
|
||||
"global_js": ["aether.js"],
|
||||
"global_css": ["aether.css"]
|
||||
@@ -75,7 +77,7 @@ Runtime contributions from `TribeOne.TribesPlugin.Aether.Plugin` include:
|
||||
- plugin config schema for chat backend defaults and Marmot UI enablement,
|
||||
- global JS/CSS assets.
|
||||
|
||||
The `chat@1` provider surface is intentionally small and reusable:
|
||||
The `org.tribe-one.caps.chat@1` provider surface is intentionally small and reusable:
|
||||
|
||||
- `TribeOne.TribesPlugin.Aether.Chat.chat_panel_component/0` returns the reusable chat panel component.
|
||||
- `TribeOne.TribesPlugin.Aether.Chat.recipient_picker_component/0` returns the recipient picker.
|
||||
@@ -143,7 +145,7 @@ colocated hooks from external plugin OTP apps are not auto-imported by the host.
|
||||
|
||||
## Runtime requirements
|
||||
|
||||
- A Tribes host checkout/runtime with `ui@1`.
|
||||
- A Tribes host checkout/runtime with `org.tribe-one.caps.ui@1`.
|
||||
- Parrhesia available through the host for Nostr event storage and streaming.
|
||||
- `nak` available in the runtime path for the current NIP-17/NIP-59/NIP-04
|
||||
protocol implementation.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import Config
|
||||
|
||||
config :aether, ash_domains: [TribeOne.TribesPlugin.Aether.Chat]
|
||||
config :tribe_one_aether, ash_domains: [TribeOne.TribesPlugin.Aether.Chat]
|
||||
|
||||
import_config "#{config_env()}.exs"
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ defmodule TribeOne.TribesPlugin.Aether.Chat do
|
||||
"""
|
||||
|
||||
use Ash.Domain,
|
||||
otp_app: :aether
|
||||
otp_app: :tribe_one_aether
|
||||
|
||||
require Ash.Query
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule TribeOne.TribesPlugin.Aether.Chat.Channel do
|
||||
"""
|
||||
|
||||
use Ash.Resource,
|
||||
otp_app: :aether,
|
||||
otp_app: :tribe_one_aether,
|
||||
domain: TribeOne.TribesPlugin.Aether.Chat,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
extensions: [AshNostrSync]
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule TribeOne.TribesPlugin.Aether.Chat.Message do
|
||||
"""
|
||||
|
||||
use Ash.Resource,
|
||||
otp_app: :aether,
|
||||
otp_app: :tribe_one_aether,
|
||||
domain: TribeOne.TribesPlugin.Aether.Chat,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
extensions: [AshNostrSync]
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule TribeOne.TribesPlugin.Aether.Chat.Participant do
|
||||
"""
|
||||
|
||||
use Ash.Resource,
|
||||
otp_app: :aether,
|
||||
otp_app: :tribe_one_aether,
|
||||
domain: TribeOne.TribesPlugin.Aether.Chat,
|
||||
data_layer: AshPostgres.DataLayer,
|
||||
extensions: [AshNostrSync]
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule TribeOne.TribesPlugin.Aether.Plugin do
|
||||
Tribes plugin entry point.
|
||||
"""
|
||||
|
||||
use Tribes.Plugin.Base, otp_app: :aether
|
||||
use Tribes.Plugin.Base, otp_app: :tribe_one_aether
|
||||
|
||||
@behaviour Tribes.Capabilities.Chat.V1
|
||||
|
||||
@@ -94,7 +94,7 @@ defmodule TribeOne.TribesPlugin.Aether.Plugin do
|
||||
TribeOne.TribesPlugin.AetherWeb.ChatPanelComponent,
|
||||
Keyword.get(opts, :id, "aether-chat-panel-#{slug}"),
|
||||
assigns,
|
||||
capability: "chat@1",
|
||||
capability: "org.tribe-one.caps.chat@1",
|
||||
provider: __MODULE__
|
||||
)}
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
defmodule TribeOne.TribesPlugin.AetherWeb.ChatRecipientPickerComponent do
|
||||
@moduledoc """
|
||||
Recipient picker exposed as part of Aether's `chat@1` UI contract.
|
||||
Recipient picker exposed as part of Aether's `org.tribe-one.caps.chat@1` UI contract.
|
||||
"""
|
||||
|
||||
use Phoenix.LiveComponent
|
||||
|
||||
+17
-6
@@ -1,16 +1,27 @@
|
||||
{
|
||||
"name": "aether",
|
||||
"id": "org.tribe-one.plugins.aether",
|
||||
"slug": "aether",
|
||||
"display_name": "Aether",
|
||||
"version": "0.2.0",
|
||||
"description": "Local social feed and chat for Tribes, including NIP-17 direct messages.",
|
||||
"entry_module": "TribeOne.TribesPlugin.Aether.Plugin",
|
||||
"host_api": "1",
|
||||
"otp_app": "aether",
|
||||
"provides": ["social@1", "chat@1"],
|
||||
"requires": ["ui@1"],
|
||||
"otp_app": "tribe_one_aether",
|
||||
"provides": [
|
||||
"org.tribe-one.caps.social@1",
|
||||
"org.tribe-one.caps.chat@1"
|
||||
],
|
||||
"requires": [
|
||||
"org.tribe-one.caps.ui@1"
|
||||
],
|
||||
"enhances_with": [],
|
||||
"assets": {
|
||||
"global_js": ["aether.js"],
|
||||
"global_css": ["aether.css"]
|
||||
"global_js": [
|
||||
"aether.js"
|
||||
],
|
||||
"global_css": [
|
||||
"aether.css"
|
||||
]
|
||||
},
|
||||
"migrations": true,
|
||||
"children": false
|
||||
|
||||
@@ -3,7 +3,7 @@ defmodule TribeOne.TribesPlugin.Aether.MixProject do
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :aether,
|
||||
app: :tribe_one_aether,
|
||||
version: "0.2.0",
|
||||
elixir: "~> 1.18",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
|
||||
@@ -2,6 +2,7 @@ defmodule TribeOne.TribesPlugin.Aether.ManifestTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
@manifest_path Path.join(__DIR__, "../../manifest.json") |> Path.expand()
|
||||
@capability_regex ~r/^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)+@[1-9][0-9]*$/
|
||||
|
||||
setup_all do
|
||||
content = File.read!(@manifest_path)
|
||||
@@ -16,7 +17,9 @@ defmodule TribeOne.TribesPlugin.Aether.ManifestTest do
|
||||
|
||||
test "has required fields", %{manifest: manifest} do
|
||||
required = [
|
||||
"name",
|
||||
"id",
|
||||
"slug",
|
||||
"display_name",
|
||||
"version",
|
||||
"entry_module",
|
||||
"host_api",
|
||||
@@ -31,9 +34,10 @@ defmodule TribeOne.TribesPlugin.Aether.ManifestTest do
|
||||
end
|
||||
end
|
||||
|
||||
test "name matches OTP app name", %{manifest: manifest} do
|
||||
assert manifest["name"] == "aether"
|
||||
assert manifest["otp_app"] == manifest["name"]
|
||||
test "identity fields are namespaced", %{manifest: manifest} do
|
||||
assert manifest["id"] == "org.tribe-one.plugins.aether"
|
||||
assert manifest["slug"] == "aether"
|
||||
assert manifest["otp_app"] == "tribe_one_aether"
|
||||
end
|
||||
|
||||
test "entry_module uses the TribeOne namespace and Plugin suffix", %{manifest: manifest} do
|
||||
@@ -48,20 +52,20 @@ defmodule TribeOne.TribesPlugin.Aether.ManifestTest do
|
||||
|
||||
test "provides contains valid capability identifiers", %{manifest: manifest} do
|
||||
for cap <- manifest["provides"] do
|
||||
assert Regex.match?(~r/^[a-z][a-z0-9_]*(@\d+)?$/, cap),
|
||||
assert Regex.match?(@capability_regex, cap),
|
||||
"invalid capability identifier: #{inspect(cap)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "requires contains valid capability identifiers", %{manifest: manifest} do
|
||||
for cap <- manifest["requires"] do
|
||||
assert Regex.match?(~r/^[a-z][a-z0-9_]*(@\d+)?$/, cap),
|
||||
assert Regex.match?(@capability_regex, cap),
|
||||
"invalid capability identifier: #{inspect(cap)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "declares the chat capability", %{manifest: manifest} do
|
||||
assert "chat@1" in manifest["provides"]
|
||||
assert "org.tribe-one.caps.chat@1" in manifest["provides"]
|
||||
end
|
||||
|
||||
test "host_api is a string version", %{manifest: manifest} do
|
||||
|
||||
Reference in New Issue
Block a user