모든 샌드박스 플러그인은 package.json 옆에 emdash-plugin.jsonc를 가지고 있습니다. 이것은 수동으로 편집되며 플러그인의 아이덴티티, 신뢰 계약(capabilities, hosts, storage) 및 레지스트리가 표시하는 프로필 필드를 보유합니다. emdash-plugin init은 하나를 스캐폴딩하며, CLI는 build, dev, validate, bundle 및 publish를 위해 자동으로 ./emdash-plugin.jsonc를 읽습니다.
파일은 JSONC입니다: 주석과 후행 쉼표가 허용됩니다.
다음 예제는 이미지 갤러리 플러그인의 완전한 매니페스트를 보여줍니다:
{
"$schema": "./node_modules/@emdash-cms/plugin-cli/schemas/emdash-plugin.schema.json",
"slug": "gallery",
"publisher": "did:plc:abc123def456",
"license": "MIT",
"author": { "name": "Jane Doe", "url": "https://example.com" },
"security": { "email": "[email protected]" },
// Optional profile
"name": "Gallery",
"description": "Image gallery block for EmDash.",
"keywords": ["gallery", "images"],
"repo": "https://github.com/example/plugin-gallery",
// Trust contract
"capabilities": ["content:read"],
"allowedHosts": [],
"storage": {}
}
아이덴티티
| 필드 | 필수 | 비고 |
|---|---|---|
slug | 예 | 퍼블리셔의 네임스페이스 내 URL 안전 ID. /^[a-z][a-z0-9_-]*$/, 최대 64자. |
publisher | 예 | 귀하의 Atmosphere 계정의 DID 또는 핸들. 퍼블리셔 고정 참조. |
version | 아니오 | 빌드 메타데이터가 없는 Semver 2.0. 일반적으로 생략 — 아래 참조. |
slug와 publisher가 함께 패키지의 아이덴티티를 형성합니다. EmDash는 이들로부터 자동으로 패키지의 전체 식별자를 도출합니다.
version은 package.json에 있습니다
빌드는 매니페스트의 version을 package.json#version과 조정합니다:
- 둘 다 설정되고 동일함 → 괜찮음.
- 둘 다 설정되고 다름 → 하드 에러.
- 하나만 설정됨 → 해당 값이 우선됨.
- 둘 다 설정되지 않음 → 하드 에러.
npm 배포 플러그인의 권장 패턴은 매니페스트에서 version을 생략하고 package.json이 유일한 진실의 원천이 되도록 하는 것입니다(릴리스 도구가 이미 거기서 범프합니다). package.json이 없는 레지스트리 전용 플러그인은 매니페스트에 version을 설정해야 합니다 — 그것을 둘 다른 곳이 없습니다.
프로필
이들은 레지스트리 목록을 제공합니다. license, 저자(author 또는 authors) 및 보안 연락처(security 또는 securityContacts)가 필수이며, 나머지는 선택 사항입니다.
| 필드 | 필수 | 비고 |
|---|---|---|
license | 예 | SPDX 표현식("MIT", "Apache-2.0", "MIT OR Apache-2.0"). 첫 게시 시 사용됩니다. 이후 게시에서는 기존 프로필이 우선됩니다. |
author / authors | 예 | 둘 중 하나. 단일 저자의 경우 author: { name, url?, email? }, 여러 명의 경우 authors: [...](≤ 32). 둘 다 설정하면 오류입니다. |
security / securityContacts | 예 | 둘 중 하나. 각 연락처는 최소한 email 또는 url 중 하나가 필요합니다. 여러 개의 경우 securityContacts: [...](≤ 8). 둘 다 설정하면 오류입니다. |
name | 아니오 | 표시 이름. 기본값은 slug입니다. |
description | 아니오 | 짧게 유지하세요(약 140자). 긴 값은 목록에서 잘릴 수 있습니다. |
keywords | 아니오 | ≤ 5개 항목. |
repo | 아니오 | 소스 저장소의 https:// URL. |
진정으로 여러 개가 없는 한 단수형 author / security 형식을 사용하세요 — 이것이 일반적인 경우이며 스캐폴드가 이를 출력합니다.
신뢰 계약
신뢰 계약은 capabilities, allowedHosts 및 storage입니다. 세 가지 모두 기본적으로 비어 있으므로 추가 권한이 필요하지 않은 플러그인은 이들을 완전히 생략할 수 있습니다.
{
"capabilities": ["network:request", "content:read"],
"allowedHosts": ["api.example.com", "*.cdn.example.com"],
"storage": {
"events": { "indexes": ["timestamp"] },
"submissions": { "indexes": ["email"], "uniqueIndexes": ["token"] }
}
}
Capabilities
인식되는 이름:
| Capability | 부여 |
|---|---|
content:read / content:write | ctx를 통해 사이트 콘텐츠 읽기 / 변경. |
media:read / media:write | 미디어 읽기 / 쓰기. |
users:read | 사용자 레코드 읽기. |
email:send | ctx를 통해 이메일 전송. |
network:request | ctx.http를 통한 아웃바운드 HTTP, allowedHosts로 제한됨. |
network:request:unrestricted | 모든 호스트로의 아웃바운드 HTTP. network:request 대신 사용됨. |
hooks.email-transport:register | 이메일 전송 훅 등록. |
hooks.email-events:register | 이메일 수명 주기 훅 등록. |
hooks.page-fragments:register | page:fragments 훅 등록(네이티브 전용). |
CLI가 시행하는 두 가지 필드 간 규칙(에디터의 JSON-Schema 체크는 시행하지 않음 — emdash-plugin validate 실행):
network:request는 비어 있지 않은allowedHosts를 필요로 합니다. 플러그인이 정말로 모든 호스트에 도달해야 하는 경우 대신network:request:unrestricted를 사용하세요.network:request:unrestricted는allowedHosts가 비어 있어야 합니다 — 제한되지 않은 capability는 이미 모든 호스트를 부여하므로 목록이 모순됩니다.
호스트 패턴은 순수 호스트 이름입니다(스키마, 경로 또는 공백 없음). 선행 *.는 하위 도메인을 허용합니다: *.cdn.example.com.
Storage
컬렉션 이름 → 인덱스 구성의 맵. 컬렉션 이름은 동일한 /^[a-z][a-z0-9_]*$/ 규칙을 따릅니다(런타임은 이름을 SQL 테이블 접미사로 사용합니다). 인덱스는 필드 이름 또는 복합 배열입니다. uniqueIndexes도 쿼리 가능합니다 — indexes에도 나열하지 마세요.
"storage": {
"events": { "indexes": ["timestamp", ["collection", "timestamp"]] }
}
관리자 인터페이스
선택 사항. 샌드박스 플러그인은 Block Kit을 통해 관리자 페이지와 대시보드 위젯을 렌더링합니다. 매니페스트는 그것들이 나타나는 위치만 선언합니다. 플러그인에 관리자 UI가 없으면 admin 키를 완전히 생략하세요.
"admin": {
"pages": [{ "path": "/gallery", "label": "Gallery", "icon": "image" }],
"widgets": [{ "id": "recent-uploads", "title": "Recent uploads", "size": "half" }]
}
admin.pages 또는 admin.widgets를 선언하는 플러그인은 Block Kit 콘텐츠를 렌더링하는 src/plugin.ts의 admin 경로도 제공해야 합니다 — 스키마는 이를 강제할 수 없습니다(경로 이름은 매니페스트가 아닌 소스에서 조사됩니다)만 런타임은 이를 확인합니다.
퍼블리셔 고정
publisher는 게시 아이덴티티를 고정하여 실수로 잘못된 계정으로 플러그인을 게시할 수 없도록 합니다.
첫 번째 성공적인 게시에서, 매니페스트의 publisher가 활성 세션과 일치하면 작성된 대로 유지됩니다. emdash-plugin init으로 스캐폴딩하고 비워 두면 CLI는 활성 세션의 DID를 매니페스트에 다시 씁니다.
다음 예제는 CLI가 작성하는 라인을 보여주며, 가독성을 위해 해결된 핸들이 주석으로 추가되었습니다:
"publisher": "did:plc:abc123def456", // jane.example.com
모든 후속 게시에서 CLI는 활성 세션과 고정된 publisher를 DID로 해결하고 비교합니다. 불일치는 MANIFEST_PUBLISHER_MISMATCH로 즉시 실패합니다 — 재정의 플래그가 없습니다. 의도적으로 해결하세요:
- 잘못된 세션:
emdash-plugin switch <did>, 그런 다음 다시 게시합니다. - 플러그인을 새 퍼블리셔로 진정으로 이전: 매니페스트에서
publisher를 편집합니다.
게시하지 않고 검증
emdash-plugin validate # ./emdash-plugin.jsonc
emdash-plugin validate path/ # 특정 디렉토리
필드 간 규칙을 포함한 tsc 스타일 file:line:column 진단을 사용한 오프라인 스키마 체크. 사전 커밋 훅 또는 CI 단계에 적합합니다. 중복 키와 알 수 없는 키는 오류입니다(엄격 모드는 "licens" 오타를 잡습니다).
CLI 플래그가 여전히 우선합니다
명시적 플래그(--license, --author-name, …)는 둘 다 설정된 경우 매니페스트 값을 재정의합니다 — CI 재정의에 유용합니다. --no-manifest는 매니페스트를 완전히 건너뜁니다(기본 경로에 하나가 있으면 경고하여 퍼블리셔 핀 보안 스토리가 표시되도록 유지합니다).