me3-protocol 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -13
- package/dist/index.d.ts +18 -0
- package/dist/index.js +54 -0
- package/examples/full.json +12 -12
- package/package.json +1 -1
- package/schema.json +44 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ It treats your online identity as a **"Digital Business Card"**—a single JSON
|
|
|
6
6
|
|
|
7
7
|
## Why it exists (brief)
|
|
8
8
|
|
|
9
|
-
People increasingly ask AI to
|
|
9
|
+
People increasingly ask AI to _find_ a person (to hire, collaborate with, or learn from). Scraping arbitrary personal sites is a failure mode for agents: it’s brittle, slow, and ambiguous. `me.json` is a small, predictable identity endpoint that makes discovery and summarization reliable.
|
|
10
10
|
|
|
11
11
|
## 1. The Specification
|
|
12
12
|
|
|
@@ -36,18 +36,19 @@ Your `me.json` defines who you are and where to find you. It is strictly typed t
|
|
|
36
36
|
|
|
37
37
|
### Core Structure (`Me3Profile`)
|
|
38
38
|
|
|
39
|
-
| Field
|
|
40
|
-
|
|
|
41
|
-
| `version`
|
|
42
|
-
| `name`
|
|
43
|
-
| `handle`
|
|
44
|
-
| `bio`
|
|
45
|
-
| `avatar`
|
|
46
|
-
| `banner`
|
|
47
|
-
| `location
|
|
48
|
-
| `links`
|
|
49
|
-
| `buttons`
|
|
50
|
-
| `pages`
|
|
39
|
+
| Field | Type | Required | Description |
|
|
40
|
+
| :--------- | :------- | :------- | :------------------------------------------------ |
|
|
41
|
+
| `version` | `string` | **Yes** | Protocol version (currently "0.1"). |
|
|
42
|
+
| `name` | `string` | **Yes** | Your display name. |
|
|
43
|
+
| `handle` | `string` | No | Your preferred username/handle. |
|
|
44
|
+
| `bio` | `string` | No | Short bio (max 500 chars). |
|
|
45
|
+
| `avatar` | `string` | No | URL to your profile picture. |
|
|
46
|
+
| `banner` | `string` | No | URL to a header/banner image. |
|
|
47
|
+
| `location` | `string` | No | Freeform location string (e.g. "Remote"). |
|
|
48
|
+
| `links` | `object` | No | Social links (website, github, twitter, etc.). |
|
|
49
|
+
| `buttons` | `array` | No | Primary actions (e.g., "Book Call", "Subscribe"). |
|
|
50
|
+
| `pages` | `array` | No | Custom content pages. |
|
|
51
|
+
| `footer` | `object` | No | Custom footer config (or `false` to hide). |
|
|
51
52
|
|
|
52
53
|
### Examples
|
|
53
54
|
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,18 @@ export interface Me3Button {
|
|
|
35
35
|
/** Optional icon (emoji or icon identifier) */
|
|
36
36
|
icon?: string;
|
|
37
37
|
}
|
|
38
|
+
export interface Me3FooterLink {
|
|
39
|
+
/** Link text */
|
|
40
|
+
text: string;
|
|
41
|
+
/** URL to open when clicked */
|
|
42
|
+
url: string;
|
|
43
|
+
}
|
|
44
|
+
export interface Me3Footer {
|
|
45
|
+
/** Custom footer text (e.g. "Built by Jane") */
|
|
46
|
+
text?: string;
|
|
47
|
+
/** Optional custom footer link */
|
|
48
|
+
link?: Me3FooterLink;
|
|
49
|
+
}
|
|
38
50
|
export interface Me3Profile {
|
|
39
51
|
/** Protocol version */
|
|
40
52
|
version: string;
|
|
@@ -56,6 +68,12 @@ export interface Me3Profile {
|
|
|
56
68
|
buttons?: Me3Button[];
|
|
57
69
|
/** Custom pages (markdown) */
|
|
58
70
|
pages?: Me3Page[];
|
|
71
|
+
/**
|
|
72
|
+
* Custom footer configuration.
|
|
73
|
+
* - `undefined`: default footer behavior (renderer-defined)
|
|
74
|
+
* - `false`: hide footer (renderer may restrict this to Pro tiers)
|
|
75
|
+
*/
|
|
76
|
+
footer?: Me3Footer | false;
|
|
59
77
|
}
|
|
60
78
|
export interface ValidationError {
|
|
61
79
|
field: string;
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,8 @@ const MAX_BUTTONS = 3;
|
|
|
19
19
|
const MAX_BUTTON_TEXT_LENGTH = 30;
|
|
20
20
|
const VALID_BUTTON_STYLES = ["primary", "secondary", "outline"];
|
|
21
21
|
const URL_REGEX = /^https?:\/\/.+/i;
|
|
22
|
+
const MAX_FOOTER_TEXT_LENGTH = 200;
|
|
23
|
+
const MAX_FOOTER_LINK_TEXT_LENGTH = 60;
|
|
22
24
|
/**
|
|
23
25
|
* Validate a me3 profile object
|
|
24
26
|
*/
|
|
@@ -167,6 +169,58 @@ function validateProfile(data) {
|
|
|
167
169
|
});
|
|
168
170
|
}
|
|
169
171
|
}
|
|
172
|
+
// Footer (optional)
|
|
173
|
+
if (profile.footer !== undefined) {
|
|
174
|
+
if (profile.footer === false) {
|
|
175
|
+
// ok (renderer may enforce tier restrictions)
|
|
176
|
+
}
|
|
177
|
+
else if (typeof profile.footer !== "object" || profile.footer === null) {
|
|
178
|
+
errors.push({
|
|
179
|
+
field: "footer",
|
|
180
|
+
message: "Footer must be an object or false",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const footer = profile.footer;
|
|
185
|
+
if (footer.text !== undefined) {
|
|
186
|
+
if (typeof footer.text !== "string") {
|
|
187
|
+
errors.push({ field: "footer.text", message: "Footer text must be a string" });
|
|
188
|
+
}
|
|
189
|
+
else if (footer.text.length > MAX_FOOTER_TEXT_LENGTH) {
|
|
190
|
+
errors.push({
|
|
191
|
+
field: "footer.text",
|
|
192
|
+
message: `Footer text must be ${MAX_FOOTER_TEXT_LENGTH} characters or less`,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (footer.link !== undefined) {
|
|
197
|
+
if (typeof footer.link !== "object" || footer.link === null) {
|
|
198
|
+
errors.push({ field: "footer.link", message: "Footer link must be an object" });
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const link = footer.link;
|
|
202
|
+
if (!link.text || typeof link.text !== "string") {
|
|
203
|
+
errors.push({ field: "footer.link.text", message: "Footer link text is required" });
|
|
204
|
+
}
|
|
205
|
+
else if (link.text.length > MAX_FOOTER_LINK_TEXT_LENGTH) {
|
|
206
|
+
errors.push({
|
|
207
|
+
field: "footer.link.text",
|
|
208
|
+
message: `Footer link text must be ${MAX_FOOTER_LINK_TEXT_LENGTH} characters or less`,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (!link.url || typeof link.url !== "string") {
|
|
212
|
+
errors.push({ field: "footer.link.url", message: "Footer link URL is required" });
|
|
213
|
+
}
|
|
214
|
+
else if (!URL_REGEX.test(link.url)) {
|
|
215
|
+
errors.push({
|
|
216
|
+
field: "footer.link.url",
|
|
217
|
+
message: "Footer link URL must be a valid URL starting with http:// or https://",
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
170
224
|
// Pages (optional)
|
|
171
225
|
if (profile.pages !== undefined) {
|
|
172
226
|
if (!Array.isArray(profile.pages)) {
|
package/examples/full.json
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1",
|
|
3
|
-
"name": "
|
|
4
|
-
"handle": "
|
|
5
|
-
"location": "
|
|
6
|
-
"bio": "
|
|
7
|
-
"avatar": "https://api.dicebear.com/7.x/avataaars/svg?seed=
|
|
3
|
+
"name": "Example Name",
|
|
4
|
+
"handle": "example",
|
|
5
|
+
"location": "City, Country",
|
|
6
|
+
"bio": "Short example bio describing what this person does.",
|
|
7
|
+
"avatar": "https://api.dicebear.com/7.x/avataaars/svg?seed=example",
|
|
8
8
|
"banner": "https://images.unsplash.com/photo-1519681393784-d120267933ba?w=1200&h=400&fit=crop",
|
|
9
9
|
"links": {
|
|
10
|
-
"website": "https://
|
|
11
|
-
"github": "
|
|
12
|
-
"soulink": "
|
|
10
|
+
"website": "https://example.com",
|
|
11
|
+
"github": "example",
|
|
12
|
+
"soulink": "example"
|
|
13
13
|
},
|
|
14
14
|
"buttons": [
|
|
15
15
|
{
|
|
16
|
-
"text": "
|
|
17
|
-
"url": "https://cal.com/
|
|
16
|
+
"text": "Primary Call To Action",
|
|
17
|
+
"url": "https://cal.com/example",
|
|
18
18
|
"style": "primary",
|
|
19
19
|
"icon": "📅"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
|
-
"text": "
|
|
23
|
-
"url": "https://buymeacoffee.com/
|
|
22
|
+
"text": "Secondary Action",
|
|
23
|
+
"url": "https://buymeacoffee.com/example",
|
|
24
24
|
"style": "secondary",
|
|
25
25
|
"icon": "☕"
|
|
26
26
|
}
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -33,6 +33,38 @@
|
|
|
33
33
|
],
|
|
34
34
|
"type": "object"
|
|
35
35
|
},
|
|
36
|
+
"Me3Footer": {
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"properties": {
|
|
39
|
+
"link": {
|
|
40
|
+
"$ref": "#/definitions/Me3FooterLink",
|
|
41
|
+
"description": "Optional custom footer link"
|
|
42
|
+
},
|
|
43
|
+
"text": {
|
|
44
|
+
"description": "Custom footer text (e.g. \"Built by Jane\")",
|
|
45
|
+
"type": "string"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"type": "object"
|
|
49
|
+
},
|
|
50
|
+
"Me3FooterLink": {
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"properties": {
|
|
53
|
+
"text": {
|
|
54
|
+
"description": "Link text",
|
|
55
|
+
"type": "string"
|
|
56
|
+
},
|
|
57
|
+
"url": {
|
|
58
|
+
"description": "URL to open when clicked",
|
|
59
|
+
"type": "string"
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": [
|
|
63
|
+
"text",
|
|
64
|
+
"url"
|
|
65
|
+
],
|
|
66
|
+
"type": "object"
|
|
67
|
+
},
|
|
36
68
|
"Me3Links": {
|
|
37
69
|
"additionalProperties": {
|
|
38
70
|
"anyOf": [
|
|
@@ -123,6 +155,18 @@
|
|
|
123
155
|
},
|
|
124
156
|
"type": "array"
|
|
125
157
|
},
|
|
158
|
+
"footer": {
|
|
159
|
+
"anyOf": [
|
|
160
|
+
{
|
|
161
|
+
"$ref": "#/definitions/Me3Footer"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"const": false,
|
|
165
|
+
"type": "boolean"
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"description": "Custom footer configuration.\n- `undefined`: default footer behavior (renderer-defined)\n- `false`: hide footer (renderer may restrict this to Pro tiers)"
|
|
169
|
+
},
|
|
126
170
|
"handle": {
|
|
127
171
|
"description": "Username/handle",
|
|
128
172
|
"type": "string"
|