me3-protocol 2.1.0 → 2.2.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/dist/index.d.ts +47 -0
- package/dist/index.js +154 -4
- package/examples/full.json +17 -1
- package/package.json +1 -1
- package/schema.json +86 -0
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,48 @@ export interface Me3Footer {
|
|
|
47
47
|
/** Optional custom footer link */
|
|
48
48
|
link?: Me3FooterLink;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Newsletter subscription intent.
|
|
52
|
+
* When enabled, the site accepts email subscriptions via POST /api/subscribe
|
|
53
|
+
*/
|
|
54
|
+
export interface Me3IntentSubscribe {
|
|
55
|
+
/** Whether newsletter signups are enabled */
|
|
56
|
+
enabled: boolean;
|
|
57
|
+
/** Newsletter title (e.g., "AI Weekly") - for agents to present context */
|
|
58
|
+
title?: string;
|
|
59
|
+
/** What subscribers will receive - for agents to explain the value */
|
|
60
|
+
description?: string;
|
|
61
|
+
/** How often subscribers will hear from you */
|
|
62
|
+
frequency?: "daily" | "weekly" | "monthly" | "irregular";
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Booking/scheduling intent.
|
|
66
|
+
* Declares that the person accepts meeting bookings.
|
|
67
|
+
*/
|
|
68
|
+
export interface Me3IntentBook {
|
|
69
|
+
/** Whether booking is enabled */
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
/** Meeting title (e.g., "30-min Consultation") */
|
|
72
|
+
title?: string;
|
|
73
|
+
/** What the meeting is about */
|
|
74
|
+
description?: string;
|
|
75
|
+
/** Meeting duration in minutes */
|
|
76
|
+
duration?: number;
|
|
77
|
+
/** Booking provider (e.g., "cal.com", "calendly") */
|
|
78
|
+
provider?: string;
|
|
79
|
+
/** Direct booking URL */
|
|
80
|
+
url: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Intents object - declares what actions visitors/agents can take.
|
|
84
|
+
* This is the machine-readable API contract for interacting with a person.
|
|
85
|
+
*/
|
|
86
|
+
export interface Me3Intents {
|
|
87
|
+
/** Newsletter subscription */
|
|
88
|
+
subscribe?: Me3IntentSubscribe;
|
|
89
|
+
/** Meeting booking */
|
|
90
|
+
book?: Me3IntentBook;
|
|
91
|
+
}
|
|
50
92
|
export interface Me3Profile {
|
|
51
93
|
/** Protocol version */
|
|
52
94
|
version: string;
|
|
@@ -74,6 +116,11 @@ export interface Me3Profile {
|
|
|
74
116
|
* - `false`: hide footer (renderer may restrict this to Pro tiers)
|
|
75
117
|
*/
|
|
76
118
|
footer?: Me3Footer | false;
|
|
119
|
+
/**
|
|
120
|
+
* Intents - machine-readable actions that visitors/agents can take.
|
|
121
|
+
* This is the API contract for interacting with the person.
|
|
122
|
+
*/
|
|
123
|
+
intents?: Me3Intents;
|
|
77
124
|
}
|
|
78
125
|
export interface ValidationError {
|
|
79
126
|
field: string;
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,9 @@ const VALID_BUTTON_STYLES = ["primary", "secondary", "outline"];
|
|
|
21
21
|
const URL_REGEX = /^https?:\/\/.+/i;
|
|
22
22
|
const MAX_FOOTER_TEXT_LENGTH = 200;
|
|
23
23
|
const MAX_FOOTER_LINK_TEXT_LENGTH = 60;
|
|
24
|
+
const MAX_INTENT_TITLE_LENGTH = 100;
|
|
25
|
+
const MAX_INTENT_DESCRIPTION_LENGTH = 300;
|
|
26
|
+
const VALID_FREQUENCIES = ["daily", "weekly", "monthly", "irregular"];
|
|
24
27
|
/**
|
|
25
28
|
* Validate a me3 profile object
|
|
26
29
|
*/
|
|
@@ -184,7 +187,10 @@ function validateProfile(data) {
|
|
|
184
187
|
const footer = profile.footer;
|
|
185
188
|
if (footer.text !== undefined) {
|
|
186
189
|
if (typeof footer.text !== "string") {
|
|
187
|
-
errors.push({
|
|
190
|
+
errors.push({
|
|
191
|
+
field: "footer.text",
|
|
192
|
+
message: "Footer text must be a string",
|
|
193
|
+
});
|
|
188
194
|
}
|
|
189
195
|
else if (footer.text.length > MAX_FOOTER_TEXT_LENGTH) {
|
|
190
196
|
errors.push({
|
|
@@ -195,12 +201,18 @@ function validateProfile(data) {
|
|
|
195
201
|
}
|
|
196
202
|
if (footer.link !== undefined) {
|
|
197
203
|
if (typeof footer.link !== "object" || footer.link === null) {
|
|
198
|
-
errors.push({
|
|
204
|
+
errors.push({
|
|
205
|
+
field: "footer.link",
|
|
206
|
+
message: "Footer link must be an object",
|
|
207
|
+
});
|
|
199
208
|
}
|
|
200
209
|
else {
|
|
201
210
|
const link = footer.link;
|
|
202
211
|
if (!link.text || typeof link.text !== "string") {
|
|
203
|
-
errors.push({
|
|
212
|
+
errors.push({
|
|
213
|
+
field: "footer.link.text",
|
|
214
|
+
message: "Footer link text is required",
|
|
215
|
+
});
|
|
204
216
|
}
|
|
205
217
|
else if (link.text.length > MAX_FOOTER_LINK_TEXT_LENGTH) {
|
|
206
218
|
errors.push({
|
|
@@ -209,7 +221,10 @@ function validateProfile(data) {
|
|
|
209
221
|
});
|
|
210
222
|
}
|
|
211
223
|
if (!link.url || typeof link.url !== "string") {
|
|
212
|
-
errors.push({
|
|
224
|
+
errors.push({
|
|
225
|
+
field: "footer.link.url",
|
|
226
|
+
message: "Footer link URL is required",
|
|
227
|
+
});
|
|
213
228
|
}
|
|
214
229
|
else if (!URL_REGEX.test(link.url)) {
|
|
215
230
|
errors.push({
|
|
@@ -262,6 +277,141 @@ function validateProfile(data) {
|
|
|
262
277
|
});
|
|
263
278
|
}
|
|
264
279
|
}
|
|
280
|
+
// Intents (optional)
|
|
281
|
+
if (profile.intents !== undefined) {
|
|
282
|
+
if (typeof profile.intents !== "object" || profile.intents === null) {
|
|
283
|
+
errors.push({ field: "intents", message: "Intents must be an object" });
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
const intents = profile.intents;
|
|
287
|
+
// Validate subscribe intent
|
|
288
|
+
if (intents.subscribe !== undefined) {
|
|
289
|
+
if (typeof intents.subscribe !== "object" ||
|
|
290
|
+
intents.subscribe === null) {
|
|
291
|
+
errors.push({
|
|
292
|
+
field: "intents.subscribe",
|
|
293
|
+
message: "Subscribe intent must be an object",
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
const subscribe = intents.subscribe;
|
|
298
|
+
if (typeof subscribe.enabled !== "boolean") {
|
|
299
|
+
errors.push({
|
|
300
|
+
field: "intents.subscribe.enabled",
|
|
301
|
+
message: "Subscribe enabled must be a boolean",
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (subscribe.title !== undefined) {
|
|
305
|
+
if (typeof subscribe.title !== "string") {
|
|
306
|
+
errors.push({
|
|
307
|
+
field: "intents.subscribe.title",
|
|
308
|
+
message: "Subscribe title must be a string",
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
else if (subscribe.title.length > MAX_INTENT_TITLE_LENGTH) {
|
|
312
|
+
errors.push({
|
|
313
|
+
field: "intents.subscribe.title",
|
|
314
|
+
message: `Subscribe title must be ${MAX_INTENT_TITLE_LENGTH} characters or less`,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (subscribe.description !== undefined) {
|
|
319
|
+
if (typeof subscribe.description !== "string") {
|
|
320
|
+
errors.push({
|
|
321
|
+
field: "intents.subscribe.description",
|
|
322
|
+
message: "Subscribe description must be a string",
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
else if (subscribe.description.length > MAX_INTENT_DESCRIPTION_LENGTH) {
|
|
326
|
+
errors.push({
|
|
327
|
+
field: "intents.subscribe.description",
|
|
328
|
+
message: `Subscribe description must be ${MAX_INTENT_DESCRIPTION_LENGTH} characters or less`,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (subscribe.frequency !== undefined &&
|
|
333
|
+
!VALID_FREQUENCIES.includes(subscribe.frequency)) {
|
|
334
|
+
errors.push({
|
|
335
|
+
field: "intents.subscribe.frequency",
|
|
336
|
+
message: `Subscribe frequency must be one of: ${VALID_FREQUENCIES.join(", ")}`,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Validate book intent
|
|
342
|
+
if (intents.book !== undefined) {
|
|
343
|
+
if (typeof intents.book !== "object" || intents.book === null) {
|
|
344
|
+
errors.push({
|
|
345
|
+
field: "intents.book",
|
|
346
|
+
message: "Book intent must be an object",
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
const book = intents.book;
|
|
351
|
+
if (typeof book.enabled !== "boolean") {
|
|
352
|
+
errors.push({
|
|
353
|
+
field: "intents.book.enabled",
|
|
354
|
+
message: "Book enabled must be a boolean",
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if (book.title !== undefined) {
|
|
358
|
+
if (typeof book.title !== "string") {
|
|
359
|
+
errors.push({
|
|
360
|
+
field: "intents.book.title",
|
|
361
|
+
message: "Book title must be a string",
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
else if (book.title.length > MAX_INTENT_TITLE_LENGTH) {
|
|
365
|
+
errors.push({
|
|
366
|
+
field: "intents.book.title",
|
|
367
|
+
message: `Book title must be ${MAX_INTENT_TITLE_LENGTH} characters or less`,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
if (book.description !== undefined) {
|
|
372
|
+
if (typeof book.description !== "string") {
|
|
373
|
+
errors.push({
|
|
374
|
+
field: "intents.book.description",
|
|
375
|
+
message: "Book description must be a string",
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
else if (book.description.length > MAX_INTENT_DESCRIPTION_LENGTH) {
|
|
379
|
+
errors.push({
|
|
380
|
+
field: "intents.book.description",
|
|
381
|
+
message: `Book description must be ${MAX_INTENT_DESCRIPTION_LENGTH} characters or less`,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (book.duration !== undefined &&
|
|
386
|
+
typeof book.duration !== "number") {
|
|
387
|
+
errors.push({
|
|
388
|
+
field: "intents.book.duration",
|
|
389
|
+
message: "Book duration must be a number (minutes)",
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
if (book.provider !== undefined &&
|
|
393
|
+
typeof book.provider !== "string") {
|
|
394
|
+
errors.push({
|
|
395
|
+
field: "intents.book.provider",
|
|
396
|
+
message: "Book provider must be a string",
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
if (!book.url || typeof book.url !== "string") {
|
|
400
|
+
errors.push({
|
|
401
|
+
field: "intents.book.url",
|
|
402
|
+
message: "Book URL is required",
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
else if (!URL_REGEX.test(book.url)) {
|
|
406
|
+
errors.push({
|
|
407
|
+
field: "intents.book.url",
|
|
408
|
+
message: "Book URL must be a valid URL starting with http:// or https://",
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
265
415
|
if (errors.length > 0) {
|
|
266
416
|
return { valid: false, errors };
|
|
267
417
|
}
|
package/examples/full.json
CHANGED
|
@@ -27,5 +27,21 @@
|
|
|
27
27
|
],
|
|
28
28
|
"pages": [
|
|
29
29
|
{ "slug": "about", "title": "About", "file": "about.md", "visible": true }
|
|
30
|
-
]
|
|
30
|
+
],
|
|
31
|
+
"intents": {
|
|
32
|
+
"subscribe": {
|
|
33
|
+
"enabled": true,
|
|
34
|
+
"title": "Weekly Insights",
|
|
35
|
+
"description": "Curated thoughts on design, tech, and creativity every Sunday",
|
|
36
|
+
"frequency": "weekly"
|
|
37
|
+
},
|
|
38
|
+
"book": {
|
|
39
|
+
"enabled": true,
|
|
40
|
+
"title": "30-min Consultation",
|
|
41
|
+
"description": "Let's discuss your project",
|
|
42
|
+
"duration": 30,
|
|
43
|
+
"provider": "cal.com",
|
|
44
|
+
"url": "https://cal.com/example"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
31
47
|
}
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -65,6 +65,88 @@
|
|
|
65
65
|
],
|
|
66
66
|
"type": "object"
|
|
67
67
|
},
|
|
68
|
+
"Me3IntentBook": {
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"description": "Booking/scheduling intent. Declares that the person accepts meeting bookings.",
|
|
71
|
+
"properties": {
|
|
72
|
+
"description": {
|
|
73
|
+
"description": "What the meeting is about",
|
|
74
|
+
"type": "string"
|
|
75
|
+
},
|
|
76
|
+
"duration": {
|
|
77
|
+
"description": "Meeting duration in minutes",
|
|
78
|
+
"type": "number"
|
|
79
|
+
},
|
|
80
|
+
"enabled": {
|
|
81
|
+
"description": "Whether booking is enabled",
|
|
82
|
+
"type": "boolean"
|
|
83
|
+
},
|
|
84
|
+
"provider": {
|
|
85
|
+
"description": "Booking provider (e.g., \"cal.com\", \"calendly\")",
|
|
86
|
+
"type": "string"
|
|
87
|
+
},
|
|
88
|
+
"title": {
|
|
89
|
+
"description": "Meeting title (e.g., \"30-min Consultation\")",
|
|
90
|
+
"type": "string"
|
|
91
|
+
},
|
|
92
|
+
"url": {
|
|
93
|
+
"description": "Direct booking URL",
|
|
94
|
+
"type": "string"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"required": [
|
|
98
|
+
"enabled",
|
|
99
|
+
"url"
|
|
100
|
+
],
|
|
101
|
+
"type": "object"
|
|
102
|
+
},
|
|
103
|
+
"Me3IntentSubscribe": {
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"description": "Newsletter subscription intent. When enabled, the site accepts email subscriptions via POST /api/subscribe",
|
|
106
|
+
"properties": {
|
|
107
|
+
"description": {
|
|
108
|
+
"description": "What subscribers will receive - for agents to explain the value",
|
|
109
|
+
"type": "string"
|
|
110
|
+
},
|
|
111
|
+
"enabled": {
|
|
112
|
+
"description": "Whether newsletter signups are enabled",
|
|
113
|
+
"type": "boolean"
|
|
114
|
+
},
|
|
115
|
+
"frequency": {
|
|
116
|
+
"description": "How often subscribers will hear from you",
|
|
117
|
+
"enum": [
|
|
118
|
+
"daily",
|
|
119
|
+
"weekly",
|
|
120
|
+
"monthly",
|
|
121
|
+
"irregular"
|
|
122
|
+
],
|
|
123
|
+
"type": "string"
|
|
124
|
+
},
|
|
125
|
+
"title": {
|
|
126
|
+
"description": "Newsletter title (e.g., \"AI Weekly\") - for agents to present context",
|
|
127
|
+
"type": "string"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"required": [
|
|
131
|
+
"enabled"
|
|
132
|
+
],
|
|
133
|
+
"type": "object"
|
|
134
|
+
},
|
|
135
|
+
"Me3Intents": {
|
|
136
|
+
"additionalProperties": false,
|
|
137
|
+
"description": "Intents object - declares what actions visitors/agents can take. This is the machine-readable API contract for interacting with a person.",
|
|
138
|
+
"properties": {
|
|
139
|
+
"book": {
|
|
140
|
+
"$ref": "#/definitions/Me3IntentBook",
|
|
141
|
+
"description": "Meeting booking"
|
|
142
|
+
},
|
|
143
|
+
"subscribe": {
|
|
144
|
+
"$ref": "#/definitions/Me3IntentSubscribe",
|
|
145
|
+
"description": "Newsletter subscription"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"type": "object"
|
|
149
|
+
},
|
|
68
150
|
"Me3Links": {
|
|
69
151
|
"additionalProperties": {
|
|
70
152
|
"anyOf": [
|
|
@@ -171,6 +253,10 @@
|
|
|
171
253
|
"description": "Username/handle",
|
|
172
254
|
"type": "string"
|
|
173
255
|
},
|
|
256
|
+
"intents": {
|
|
257
|
+
"$ref": "#/definitions/Me3Intents",
|
|
258
|
+
"description": "Intents - machine-readable actions that visitors/agents can take. This is the API contract for interacting with the person."
|
|
259
|
+
},
|
|
174
260
|
"links": {
|
|
175
261
|
"$ref": "#/definitions/Me3Links",
|
|
176
262
|
"description": "Social and external links"
|