me3-protocol 2.6.0 → 2.7.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 +38 -0
- package/dist/index.js +140 -3
- package/package.json +1 -1
- package/schema.json +100 -0
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,26 @@ export interface Me3Post {
|
|
|
26
26
|
/** Short excerpt for archive/listing (optional) */
|
|
27
27
|
excerpt?: string;
|
|
28
28
|
}
|
|
29
|
+
export interface Me3Product {
|
|
30
|
+
/** URL-friendly identifier */
|
|
31
|
+
slug: string;
|
|
32
|
+
/** Product name */
|
|
33
|
+
title: string;
|
|
34
|
+
/** Path to markdown file (relative to me.json) */
|
|
35
|
+
file: string;
|
|
36
|
+
/** Product price in cents (e.g., 2999 for $29.99) */
|
|
37
|
+
price: number;
|
|
38
|
+
/** Currency code */
|
|
39
|
+
currency: "USD" | "GBP" | "EUR";
|
|
40
|
+
/** Product images (URLs) */
|
|
41
|
+
images?: string[];
|
|
42
|
+
/** Whether product is available for purchase */
|
|
43
|
+
available?: boolean;
|
|
44
|
+
/** ISO publish date (optional) */
|
|
45
|
+
publishedAt?: string;
|
|
46
|
+
/** Short excerpt for listings (optional) */
|
|
47
|
+
excerpt?: string;
|
|
48
|
+
}
|
|
29
49
|
export interface Me3Links {
|
|
30
50
|
website?: string;
|
|
31
51
|
github?: string;
|
|
@@ -137,6 +157,20 @@ export interface Me3IntentBook {
|
|
|
137
157
|
/** Pricing configuration for paid meetings (optional) */
|
|
138
158
|
pricing?: Me3BookingPricing;
|
|
139
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Shop intent.
|
|
162
|
+
* Declares that the person sells products via a simple shop.
|
|
163
|
+
*/
|
|
164
|
+
export interface Me3IntentShop {
|
|
165
|
+
/** Whether shop is enabled */
|
|
166
|
+
enabled: boolean;
|
|
167
|
+
/** Shop title/name */
|
|
168
|
+
title?: string;
|
|
169
|
+
/** Shop description */
|
|
170
|
+
description?: string;
|
|
171
|
+
/** Shop currency */
|
|
172
|
+
currency: "USD" | "GBP" | "EUR";
|
|
173
|
+
}
|
|
140
174
|
/**
|
|
141
175
|
* Intents object - declares what actions visitors/agents can take.
|
|
142
176
|
* This is the machine-readable API contract for interacting with a person.
|
|
@@ -146,6 +180,8 @@ export interface Me3Intents {
|
|
|
146
180
|
subscribe?: Me3IntentSubscribe;
|
|
147
181
|
/** Meeting booking */
|
|
148
182
|
book?: Me3IntentBook;
|
|
183
|
+
/** Shop intent */
|
|
184
|
+
shop?: Me3IntentShop;
|
|
149
185
|
}
|
|
150
186
|
export interface Me3Profile {
|
|
151
187
|
/** Protocol version */
|
|
@@ -170,6 +206,8 @@ export interface Me3Profile {
|
|
|
170
206
|
pages?: Me3Page[];
|
|
171
207
|
/** Blog posts (markdown) */
|
|
172
208
|
posts?: Me3Post[];
|
|
209
|
+
/** Products (markdown) */
|
|
210
|
+
products?: Me3Product[];
|
|
173
211
|
/**
|
|
174
212
|
* Custom footer configuration.
|
|
175
213
|
* - `undefined`: default footer behavior (renderer-defined)
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ const MAX_FOOTER_LINK_TEXT_LENGTH = 60;
|
|
|
24
24
|
const MAX_INTENT_TITLE_LENGTH = 100;
|
|
25
25
|
const MAX_INTENT_DESCRIPTION_LENGTH = 300;
|
|
26
26
|
const VALID_FREQUENCIES = ["daily", "weekly", "monthly", "irregular"];
|
|
27
|
+
const VALID_CURRENCIES = ["USD", "GBP", "EUR"];
|
|
27
28
|
/**
|
|
28
29
|
* Validate a me3 profile object
|
|
29
30
|
*/
|
|
@@ -326,6 +327,90 @@ function validateProfile(data) {
|
|
|
326
327
|
});
|
|
327
328
|
}
|
|
328
329
|
}
|
|
330
|
+
// Products (optional)
|
|
331
|
+
if (profile.products !== undefined) {
|
|
332
|
+
const products = profile.products;
|
|
333
|
+
if (!Array.isArray(products)) {
|
|
334
|
+
errors.push({ field: "products", message: "Products must be an array" });
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
products.forEach((product, index) => {
|
|
338
|
+
if (!product || typeof product !== "object") {
|
|
339
|
+
errors.push({
|
|
340
|
+
field: `products[${index}]`,
|
|
341
|
+
message: "Product must be an object",
|
|
342
|
+
});
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
if (!product.slug || typeof product.slug !== "string") {
|
|
346
|
+
errors.push({
|
|
347
|
+
field: `products[${index}].slug`,
|
|
348
|
+
message: "Product slug is required",
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
if (!product.title || typeof product.title !== "string") {
|
|
352
|
+
errors.push({
|
|
353
|
+
field: `products[${index}].title`,
|
|
354
|
+
message: "Product title is required",
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
if (!product.file || typeof product.file !== "string") {
|
|
358
|
+
errors.push({
|
|
359
|
+
field: `products[${index}].file`,
|
|
360
|
+
message: "Product file is required",
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
if (typeof product.price !== "number") {
|
|
364
|
+
errors.push({
|
|
365
|
+
field: `products[${index}].price`,
|
|
366
|
+
message: "Product price must be a number (in cents)",
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
if (typeof product.currency !== "string" ||
|
|
370
|
+
!VALID_CURRENCIES.includes(product.currency)) {
|
|
371
|
+
errors.push({
|
|
372
|
+
field: `products[${index}].currency`,
|
|
373
|
+
message: `Product currency must be one of: ${VALID_CURRENCIES.join(", ")}`,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (product.available !== undefined &&
|
|
377
|
+
typeof product.available !== "boolean") {
|
|
378
|
+
errors.push({
|
|
379
|
+
field: `products[${index}].available`,
|
|
380
|
+
message: "Product available must be a boolean",
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
if (product.images !== undefined) {
|
|
384
|
+
if (!Array.isArray(product.images)) {
|
|
385
|
+
errors.push({
|
|
386
|
+
field: `products[${index}].images`,
|
|
387
|
+
message: "Product images must be an array of strings",
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
else if (product.images.some((img) => typeof img !== "string")) {
|
|
391
|
+
errors.push({
|
|
392
|
+
field: `products[${index}].images`,
|
|
393
|
+
message: "Product images must be an array of strings",
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (product.publishedAt !== undefined &&
|
|
398
|
+
typeof product.publishedAt !== "string") {
|
|
399
|
+
errors.push({
|
|
400
|
+
field: `products[${index}].publishedAt`,
|
|
401
|
+
message: "Product publishedAt must be a string",
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (product.excerpt !== undefined &&
|
|
405
|
+
typeof product.excerpt !== "string") {
|
|
406
|
+
errors.push({
|
|
407
|
+
field: `products[${index}].excerpt`,
|
|
408
|
+
message: "Product excerpt must be a string",
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
329
414
|
// Intents (optional)
|
|
330
415
|
if (profile.intents !== undefined) {
|
|
331
416
|
if (typeof profile.intents !== "object" || profile.intents === null) {
|
|
@@ -563,12 +648,11 @@ function validateProfile(data) {
|
|
|
563
648
|
message: "Suggested amount must be at least $5",
|
|
564
649
|
});
|
|
565
650
|
}
|
|
566
|
-
const validCurrencies = ["USD", "GBP", "EUR"];
|
|
567
651
|
if (typeof pricing.currency !== "string" ||
|
|
568
|
-
!
|
|
652
|
+
!VALID_CURRENCIES.includes(pricing.currency)) {
|
|
569
653
|
errors.push({
|
|
570
654
|
field: "intents.book.pricing.currency",
|
|
571
|
-
message: `Currency must be one of: ${
|
|
655
|
+
message: `Currency must be one of: ${VALID_CURRENCIES.join(", ")}`,
|
|
572
656
|
});
|
|
573
657
|
}
|
|
574
658
|
if (pricing.minimumAmount !== 5) {
|
|
@@ -588,6 +672,59 @@ function validateProfile(data) {
|
|
|
588
672
|
}
|
|
589
673
|
}
|
|
590
674
|
}
|
|
675
|
+
// Validate shop intent
|
|
676
|
+
if (intents.shop !== undefined) {
|
|
677
|
+
if (typeof intents.shop !== "object" || intents.shop === null) {
|
|
678
|
+
errors.push({
|
|
679
|
+
field: "intents.shop",
|
|
680
|
+
message: "Shop intent must be an object",
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
const shop = intents.shop;
|
|
685
|
+
if (typeof shop.enabled !== "boolean") {
|
|
686
|
+
errors.push({
|
|
687
|
+
field: "intents.shop.enabled",
|
|
688
|
+
message: "Shop enabled must be a boolean",
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
if (shop.title !== undefined) {
|
|
692
|
+
if (typeof shop.title !== "string") {
|
|
693
|
+
errors.push({
|
|
694
|
+
field: "intents.shop.title",
|
|
695
|
+
message: "Shop title must be a string",
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
else if (shop.title.length > MAX_INTENT_TITLE_LENGTH) {
|
|
699
|
+
errors.push({
|
|
700
|
+
field: "intents.shop.title",
|
|
701
|
+
message: `Shop title must be ${MAX_INTENT_TITLE_LENGTH} characters or less`,
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
if (shop.description !== undefined) {
|
|
706
|
+
if (typeof shop.description !== "string") {
|
|
707
|
+
errors.push({
|
|
708
|
+
field: "intents.shop.description",
|
|
709
|
+
message: "Shop description must be a string",
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
else if (shop.description.length > MAX_INTENT_DESCRIPTION_LENGTH) {
|
|
713
|
+
errors.push({
|
|
714
|
+
field: "intents.shop.description",
|
|
715
|
+
message: `Shop description must be ${MAX_INTENT_DESCRIPTION_LENGTH} characters or less`,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (typeof shop.currency !== "string" ||
|
|
720
|
+
!VALID_CURRENCIES.includes(shop.currency)) {
|
|
721
|
+
errors.push({
|
|
722
|
+
field: "intents.shop.currency",
|
|
723
|
+
message: `Shop currency must be one of: ${VALID_CURRENCIES.join(", ")}`,
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
591
728
|
}
|
|
592
729
|
}
|
|
593
730
|
if (errors.length > 0) {
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -211,6 +211,38 @@
|
|
|
211
211
|
],
|
|
212
212
|
"type": "object"
|
|
213
213
|
},
|
|
214
|
+
"Me3IntentShop": {
|
|
215
|
+
"additionalProperties": false,
|
|
216
|
+
"description": "Shop intent. Declares that the person sells products via a simple shop.",
|
|
217
|
+
"properties": {
|
|
218
|
+
"currency": {
|
|
219
|
+
"description": "Shop currency",
|
|
220
|
+
"enum": [
|
|
221
|
+
"USD",
|
|
222
|
+
"GBP",
|
|
223
|
+
"EUR"
|
|
224
|
+
],
|
|
225
|
+
"type": "string"
|
|
226
|
+
},
|
|
227
|
+
"description": {
|
|
228
|
+
"description": "Shop description",
|
|
229
|
+
"type": "string"
|
|
230
|
+
},
|
|
231
|
+
"enabled": {
|
|
232
|
+
"description": "Whether shop is enabled",
|
|
233
|
+
"type": "boolean"
|
|
234
|
+
},
|
|
235
|
+
"title": {
|
|
236
|
+
"description": "Shop title/name",
|
|
237
|
+
"type": "string"
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
"required": [
|
|
241
|
+
"enabled",
|
|
242
|
+
"currency"
|
|
243
|
+
],
|
|
244
|
+
"type": "object"
|
|
245
|
+
},
|
|
214
246
|
"Me3IntentSubscribe": {
|
|
215
247
|
"additionalProperties": false,
|
|
216
248
|
"description": "Newsletter subscription intent. When enabled, the site accepts email subscriptions via POST /api/subscribe",
|
|
@@ -251,6 +283,10 @@
|
|
|
251
283
|
"$ref": "#/definitions/Me3IntentBook",
|
|
252
284
|
"description": "Meeting booking"
|
|
253
285
|
},
|
|
286
|
+
"shop": {
|
|
287
|
+
"$ref": "#/definitions/Me3IntentShop",
|
|
288
|
+
"description": "Shop intent"
|
|
289
|
+
},
|
|
254
290
|
"subscribe": {
|
|
255
291
|
"$ref": "#/definitions/Me3IntentSubscribe",
|
|
256
292
|
"description": "Newsletter subscription"
|
|
@@ -357,6 +393,63 @@
|
|
|
357
393
|
],
|
|
358
394
|
"type": "object"
|
|
359
395
|
},
|
|
396
|
+
"Me3Product": {
|
|
397
|
+
"additionalProperties": false,
|
|
398
|
+
"properties": {
|
|
399
|
+
"available": {
|
|
400
|
+
"description": "Whether product is available for purchase",
|
|
401
|
+
"type": "boolean"
|
|
402
|
+
},
|
|
403
|
+
"currency": {
|
|
404
|
+
"description": "Currency code",
|
|
405
|
+
"enum": [
|
|
406
|
+
"USD",
|
|
407
|
+
"GBP",
|
|
408
|
+
"EUR"
|
|
409
|
+
],
|
|
410
|
+
"type": "string"
|
|
411
|
+
},
|
|
412
|
+
"excerpt": {
|
|
413
|
+
"description": "Short excerpt for listings (optional)",
|
|
414
|
+
"type": "string"
|
|
415
|
+
},
|
|
416
|
+
"file": {
|
|
417
|
+
"description": "Path to markdown file (relative to me.json)",
|
|
418
|
+
"type": "string"
|
|
419
|
+
},
|
|
420
|
+
"images": {
|
|
421
|
+
"description": "Product images (URLs)",
|
|
422
|
+
"items": {
|
|
423
|
+
"type": "string"
|
|
424
|
+
},
|
|
425
|
+
"type": "array"
|
|
426
|
+
},
|
|
427
|
+
"price": {
|
|
428
|
+
"description": "Product price in cents (e.g., 2999 for $29.99)",
|
|
429
|
+
"type": "number"
|
|
430
|
+
},
|
|
431
|
+
"publishedAt": {
|
|
432
|
+
"description": "ISO publish date (optional)",
|
|
433
|
+
"type": "string"
|
|
434
|
+
},
|
|
435
|
+
"slug": {
|
|
436
|
+
"description": "URL-friendly identifier",
|
|
437
|
+
"type": "string"
|
|
438
|
+
},
|
|
439
|
+
"title": {
|
|
440
|
+
"description": "Product name",
|
|
441
|
+
"type": "string"
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
"required": [
|
|
445
|
+
"slug",
|
|
446
|
+
"title",
|
|
447
|
+
"file",
|
|
448
|
+
"price",
|
|
449
|
+
"currency"
|
|
450
|
+
],
|
|
451
|
+
"type": "object"
|
|
452
|
+
},
|
|
360
453
|
"Me3Profile": {
|
|
361
454
|
"additionalProperties": false,
|
|
362
455
|
"properties": {
|
|
@@ -425,6 +518,13 @@
|
|
|
425
518
|
},
|
|
426
519
|
"type": "array"
|
|
427
520
|
},
|
|
521
|
+
"products": {
|
|
522
|
+
"description": "Products (markdown)",
|
|
523
|
+
"items": {
|
|
524
|
+
"$ref": "#/definitions/Me3Product"
|
|
525
|
+
},
|
|
526
|
+
"type": "array"
|
|
527
|
+
},
|
|
428
528
|
"verification": {
|
|
429
529
|
"$ref": "#/definitions/Me3Verification",
|
|
430
530
|
"description": "Human verification status (populated from account, not editable per-site)"
|