@sonordev/site-kit 2.8.2 → 3.0.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/EngageWidget-LRE4VD7O.js +14 -0
- package/dist/{EngageWidget-JIXUBG47.js.map → EngageWidget-LRE4VD7O.js.map} +1 -1
- package/dist/EngageWidget-OAOLB6ED.mjs +5 -0
- package/dist/{EngageWidget-4W5EM4UO.mjs.map → EngageWidget-OAOLB6ED.mjs.map} +1 -1
- package/dist/blog/index.js +2 -1
- package/dist/blog/index.js.map +1 -1
- package/dist/blog/index.mjs +2 -1
- package/dist/blog/index.mjs.map +1 -1
- package/dist/chunk-2FEQ6TYD.mjs +37 -0
- package/dist/chunk-2FEQ6TYD.mjs.map +1 -0
- package/dist/{chunk-5SQ4NRPH.mjs → chunk-3AEOGV6V.mjs} +6 -4
- package/dist/chunk-3AEOGV6V.mjs.map +1 -0
- package/dist/chunk-662YHKWW.mjs +10 -0
- package/dist/chunk-662YHKWW.mjs.map +1 -0
- package/dist/{chunk-MNOVPHL6.mjs → chunk-72MRAYAS.mjs} +19 -14
- package/dist/chunk-72MRAYAS.mjs.map +1 -0
- package/dist/{chunk-5QLM6LZ2.mjs → chunk-APRAGA5G.mjs} +3 -3
- package/dist/{chunk-5QLM6LZ2.mjs.map → chunk-APRAGA5G.mjs.map} +1 -1
- package/dist/{chunk-MWE2HRPU.js → chunk-D5XTX5EB.js} +19 -14
- package/dist/chunk-D5XTX5EB.js.map +1 -0
- package/dist/{chunk-HANRPNKG.js → chunk-FMI65E5Y.js} +3 -3
- package/dist/{chunk-HANRPNKG.js.map → chunk-FMI65E5Y.js.map} +1 -1
- package/dist/{chunk-SLJJJWMV.js → chunk-FPU2QPO4.js} +338 -33
- package/dist/chunk-FPU2QPO4.js.map +1 -0
- package/dist/{chunk-PKUTYMN7.js → chunk-J25JPU77.js} +2 -2
- package/dist/chunk-J25JPU77.js.map +1 -0
- package/dist/chunk-J7QI3GDX.js +12 -0
- package/dist/chunk-J7QI3GDX.js.map +1 -0
- package/dist/chunk-MLJXELPA.js +693 -0
- package/dist/chunk-MLJXELPA.js.map +1 -0
- package/dist/{chunk-L7XPPFSS.mjs → chunk-QDKQZ5XH.mjs} +2 -2
- package/dist/chunk-QDKQZ5XH.mjs.map +1 -0
- package/dist/{chunk-IAV32YHL.mjs → chunk-RV45YMBJ.mjs} +338 -33
- package/dist/chunk-RV45YMBJ.mjs.map +1 -0
- package/dist/chunk-TCVHI3JP.mjs +686 -0
- package/dist/chunk-TCVHI3JP.mjs.map +1 -0
- package/dist/{chunk-PAF5IGGF.js → chunk-XNUPJC6R.js} +6 -4
- package/dist/chunk-XNUPJC6R.js.map +1 -0
- package/dist/chunk-YBKALLBH.js +46 -0
- package/dist/chunk-YBKALLBH.js.map +1 -0
- package/dist/engage/index.d.mts +2 -2
- package/dist/engage/index.d.ts +2 -2
- package/dist/engage/index.js +5 -4
- package/dist/engage/index.js.map +1 -1
- package/dist/engage/index.mjs +2 -1
- package/dist/engage/index.mjs.map +1 -1
- package/dist/forms/index.js +19 -683
- package/dist/forms/index.js.map +1 -1
- package/dist/forms/index.mjs +4 -676
- package/dist/forms/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/layout/client.js +2 -2
- package/dist/layout/client.mjs +1 -1
- package/dist/layout/index.js +5 -5
- package/dist/layout/index.mjs +2 -2
- package/dist/llms/index.d.mts +18 -4
- package/dist/llms/index.d.ts +18 -4
- package/dist/llms/index.js +26 -51
- package/dist/llms/index.js.map +1 -1
- package/dist/llms/index.mjs +16 -41
- package/dist/llms/index.mjs.map +1 -1
- package/dist/middleware/index.d.mts +62 -1
- package/dist/middleware/index.d.ts +62 -1
- package/dist/middleware/index.js +105 -0
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/index.mjs +103 -1
- package/dist/middleware/index.mjs.map +1 -1
- package/dist/seo/index.d.mts +10 -205
- package/dist/seo/index.d.ts +10 -205
- package/dist/seo/index.js +16 -15
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/index.mjs +7 -6
- package/dist/seo/index.mjs.map +1 -1
- package/dist/seo/server.d.mts +2 -106
- package/dist/seo/server.d.ts +2 -106
- package/dist/seo/server.js +56 -197
- package/dist/seo/server.js.map +1 -1
- package/dist/seo/server.mjs +2 -183
- package/dist/seo/server.mjs.map +1 -1
- package/dist/server-l6LMNvF1.d.ts +360 -0
- package/dist/server-mX5At_6x.d.mts +360 -0
- package/dist/sitemap/index.d.mts +1 -1
- package/dist/sitemap/index.d.ts +1 -1
- package/dist/sitemap/index.js +2 -2
- package/dist/sitemap/index.mjs +1 -1
- package/dist/slots/contract.d.mts +83 -0
- package/dist/slots/contract.d.ts +83 -0
- package/dist/slots/contract.js +41 -0
- package/dist/slots/contract.js.map +1 -0
- package/dist/slots/contract.mjs +4 -0
- package/dist/slots/contract.mjs.map +1 -0
- package/dist/slots/index.d.mts +104 -0
- package/dist/slots/index.d.ts +104 -0
- package/dist/slots/index.js +186 -0
- package/dist/slots/index.js.map +1 -0
- package/dist/slots/index.mjs +146 -0
- package/dist/slots/index.mjs.map +1 -0
- package/dist/{types-DYyIAgQg.d.ts → types-DPsbBKiu.d.mts} +1 -1
- package/dist/{types-DYyIAgQg.d.mts → types-DPsbBKiu.d.ts} +1 -1
- package/dist/{types-BnCwwUX3.d.mts → types-D_1iUxOQ.d.mts} +18 -1
- package/dist/{types-BnCwwUX3.d.ts → types-D_1iUxOQ.d.ts} +18 -1
- package/dist/{types-J7Z_FqmV.d.mts → types-DhCAWUmd.d.mts} +8 -0
- package/dist/{types-0NuBL1Gg.d.ts → types-xcP_owwn.d.ts} +8 -0
- package/package.json +11 -1
- package/dist/EngageWidget-4W5EM4UO.mjs +0 -4
- package/dist/EngageWidget-JIXUBG47.js +0 -13
- package/dist/chunk-5SQ4NRPH.mjs.map +0 -1
- package/dist/chunk-IAV32YHL.mjs.map +0 -1
- package/dist/chunk-L7XPPFSS.mjs.map +0 -1
- package/dist/chunk-MNOVPHL6.mjs.map +0 -1
- package/dist/chunk-MWE2HRPU.js.map +0 -1
- package/dist/chunk-PAF5IGGF.js.map +0 -1
- package/dist/chunk-PKUTYMN7.js.map +0 -1
- package/dist/chunk-SLJJJWMV.js.map +0 -1
- package/dist/routing-C7gmHWm9.d.ts +0 -163
- package/dist/routing-trNzR1Pz.d.mts +0 -163
package/dist/sitemap/index.d.mts
CHANGED
package/dist/sitemap/index.d.ts
CHANGED
package/dist/sitemap/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkD5XTX5EB_js = require('../chunk-D5XTX5EB.js');
|
|
4
4
|
var chunkJP5WGL75_js = require('../chunk-JP5WGL75.js');
|
|
5
5
|
require('../chunk-ATG4FJY6.js');
|
|
6
6
|
require('../chunk-ZSMWDLMK.js');
|
|
@@ -500,7 +500,7 @@ function createSitemap(config) {
|
|
|
500
500
|
}
|
|
501
501
|
if (config.optimizedLLMsTxt !== false) {
|
|
502
502
|
try {
|
|
503
|
-
const writeResult = await
|
|
503
|
+
const writeResult = await chunkD5XTX5EB_js.writeLLMsTxtToPublic({
|
|
504
504
|
apiUrl,
|
|
505
505
|
apiKey,
|
|
506
506
|
full: config.optimizedLLMsFullTxt ?? false,
|
package/dist/sitemap/index.mjs
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sonordev/site-kit/slots/contract — Managed Slots wire contract (v1)
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the slot payload shape and content signing,
|
|
5
|
+
* shared by site-kit (verify side) and sonor-api (sign side) — same pattern
|
|
6
|
+
* as `@sonordev/site-kit/llms/contract`.
|
|
7
|
+
*
|
|
8
|
+
* Security model: every SlotContent is HMAC-signed with the project API key.
|
|
9
|
+
* site-kit verifies before rendering and falls back to the developer's static
|
|
10
|
+
* content on any mismatch, so platform-originated content carries provenance
|
|
11
|
+
* end to end. See docs/SITE-KIT-3.0-SPEC-SLOTS.md.
|
|
12
|
+
*/
|
|
13
|
+
/** Bump on breaking payload changes. */
|
|
14
|
+
declare const SLOTS_CONTRACT_VERSION = 1;
|
|
15
|
+
/**
|
|
16
|
+
* v1 supports plain text only — rendered as React text (escaped), never
|
|
17
|
+
* dangerouslySetInnerHTML. 'markdown' / 'rich' are reserved for a future
|
|
18
|
+
* contract version behind a real sanitizer decision.
|
|
19
|
+
*/
|
|
20
|
+
type SlotContentType = 'text';
|
|
21
|
+
declare const SLOT_CONTENT_TYPES: readonly SlotContentType[];
|
|
22
|
+
/** Hard cap on managed content length; longer payloads are rejected → fallback. */
|
|
23
|
+
declare const SLOT_CONTENT_MAX_LENGTH = 10000;
|
|
24
|
+
/** Slot ids: lowercase, page-scoped by convention, e.g. "home-hero-headline". */
|
|
25
|
+
declare const SLOT_ID_PATTERN: RegExp;
|
|
26
|
+
interface SlotContent {
|
|
27
|
+
slot_id: string;
|
|
28
|
+
type: SlotContentType;
|
|
29
|
+
/** The managed text. Rendered escaped — never as HTML. */
|
|
30
|
+
content: string;
|
|
31
|
+
/** Forward-compat: experiment/personalization variant that produced this content. */
|
|
32
|
+
variant_id?: string | null;
|
|
33
|
+
experiment_id?: string | null;
|
|
34
|
+
/** ISO timestamp of the last content change. */
|
|
35
|
+
updated_at?: string | null;
|
|
36
|
+
/** Hex HMAC-SHA256 over the canonical payload, keyed with the project API key. */
|
|
37
|
+
signature: string;
|
|
38
|
+
}
|
|
39
|
+
interface SlotResolveRequestEntry {
|
|
40
|
+
id: string;
|
|
41
|
+
/**
|
|
42
|
+
* The developer's current static text for this slot (when statically
|
|
43
|
+
* extractable). Stored as `fallback_text` on registration so the dashboard
|
|
44
|
+
* editor shows the live copy instead of a bare slot id. Never rendered by
|
|
45
|
+
* the API anywhere — display + drafting context only.
|
|
46
|
+
*/
|
|
47
|
+
fallback?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The route this slot renders on (e.g. "/" or "/pricing"). A static literal,
|
|
50
|
+
* never derived from the request — it feeds the Copy Evidence Loop's
|
|
51
|
+
* slot↔page mapping so Signal can judge the slot against its page's metrics.
|
|
52
|
+
*/
|
|
53
|
+
page?: string;
|
|
54
|
+
}
|
|
55
|
+
interface SlotsResolveRequest {
|
|
56
|
+
contract_version: number;
|
|
57
|
+
slots: SlotResolveRequestEntry[];
|
|
58
|
+
/** Multi-site sub-dimension: normalized lowercase host (see CLAUDE.md "site" convention). */
|
|
59
|
+
site?: string;
|
|
60
|
+
}
|
|
61
|
+
interface SlotsResolveResponse {
|
|
62
|
+
contract_version: number;
|
|
63
|
+
slots: Record<string, SlotContent | null>;
|
|
64
|
+
}
|
|
65
|
+
declare function isValidSlotId(id: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Canonical string both sides sign/verify. Exported separately from the HMAC
|
|
68
|
+
* helpers so an edge-runtime resolver can reproduce it with WebCrypto.
|
|
69
|
+
*/
|
|
70
|
+
declare function slotSigningPayload(content: Pick<SlotContent, 'slot_id' | 'type' | 'content'>): string;
|
|
71
|
+
/**
|
|
72
|
+
* Sign slot content with the project API key (sonor-api side).
|
|
73
|
+
* Returns the hex HMAC-SHA256 signature.
|
|
74
|
+
*/
|
|
75
|
+
declare function signSlotContent(content: Pick<SlotContent, 'slot_id' | 'type' | 'content'>, apiKey: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Verify a slot payload's shape + signature (site-kit side).
|
|
78
|
+
* Returns true only when the payload is well-formed, within limits, and the
|
|
79
|
+
* signature matches the project API key. Constant-time comparison.
|
|
80
|
+
*/
|
|
81
|
+
declare function verifySlotContent(content: unknown, apiKey: string): content is SlotContent;
|
|
82
|
+
|
|
83
|
+
export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, type SlotContent, type SlotContentType, type SlotResolveRequestEntry, type SlotsResolveRequest, type SlotsResolveResponse, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sonordev/site-kit/slots/contract — Managed Slots wire contract (v1)
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the slot payload shape and content signing,
|
|
5
|
+
* shared by site-kit (verify side) and sonor-api (sign side) — same pattern
|
|
6
|
+
* as `@sonordev/site-kit/llms/contract`.
|
|
7
|
+
*
|
|
8
|
+
* Security model: every SlotContent is HMAC-signed with the project API key.
|
|
9
|
+
* site-kit verifies before rendering and falls back to the developer's static
|
|
10
|
+
* content on any mismatch, so platform-originated content carries provenance
|
|
11
|
+
* end to end. See docs/SITE-KIT-3.0-SPEC-SLOTS.md.
|
|
12
|
+
*/
|
|
13
|
+
/** Bump on breaking payload changes. */
|
|
14
|
+
declare const SLOTS_CONTRACT_VERSION = 1;
|
|
15
|
+
/**
|
|
16
|
+
* v1 supports plain text only — rendered as React text (escaped), never
|
|
17
|
+
* dangerouslySetInnerHTML. 'markdown' / 'rich' are reserved for a future
|
|
18
|
+
* contract version behind a real sanitizer decision.
|
|
19
|
+
*/
|
|
20
|
+
type SlotContentType = 'text';
|
|
21
|
+
declare const SLOT_CONTENT_TYPES: readonly SlotContentType[];
|
|
22
|
+
/** Hard cap on managed content length; longer payloads are rejected → fallback. */
|
|
23
|
+
declare const SLOT_CONTENT_MAX_LENGTH = 10000;
|
|
24
|
+
/** Slot ids: lowercase, page-scoped by convention, e.g. "home-hero-headline". */
|
|
25
|
+
declare const SLOT_ID_PATTERN: RegExp;
|
|
26
|
+
interface SlotContent {
|
|
27
|
+
slot_id: string;
|
|
28
|
+
type: SlotContentType;
|
|
29
|
+
/** The managed text. Rendered escaped — never as HTML. */
|
|
30
|
+
content: string;
|
|
31
|
+
/** Forward-compat: experiment/personalization variant that produced this content. */
|
|
32
|
+
variant_id?: string | null;
|
|
33
|
+
experiment_id?: string | null;
|
|
34
|
+
/** ISO timestamp of the last content change. */
|
|
35
|
+
updated_at?: string | null;
|
|
36
|
+
/** Hex HMAC-SHA256 over the canonical payload, keyed with the project API key. */
|
|
37
|
+
signature: string;
|
|
38
|
+
}
|
|
39
|
+
interface SlotResolveRequestEntry {
|
|
40
|
+
id: string;
|
|
41
|
+
/**
|
|
42
|
+
* The developer's current static text for this slot (when statically
|
|
43
|
+
* extractable). Stored as `fallback_text` on registration so the dashboard
|
|
44
|
+
* editor shows the live copy instead of a bare slot id. Never rendered by
|
|
45
|
+
* the API anywhere — display + drafting context only.
|
|
46
|
+
*/
|
|
47
|
+
fallback?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The route this slot renders on (e.g. "/" or "/pricing"). A static literal,
|
|
50
|
+
* never derived from the request — it feeds the Copy Evidence Loop's
|
|
51
|
+
* slot↔page mapping so Signal can judge the slot against its page's metrics.
|
|
52
|
+
*/
|
|
53
|
+
page?: string;
|
|
54
|
+
}
|
|
55
|
+
interface SlotsResolveRequest {
|
|
56
|
+
contract_version: number;
|
|
57
|
+
slots: SlotResolveRequestEntry[];
|
|
58
|
+
/** Multi-site sub-dimension: normalized lowercase host (see CLAUDE.md "site" convention). */
|
|
59
|
+
site?: string;
|
|
60
|
+
}
|
|
61
|
+
interface SlotsResolveResponse {
|
|
62
|
+
contract_version: number;
|
|
63
|
+
slots: Record<string, SlotContent | null>;
|
|
64
|
+
}
|
|
65
|
+
declare function isValidSlotId(id: string): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Canonical string both sides sign/verify. Exported separately from the HMAC
|
|
68
|
+
* helpers so an edge-runtime resolver can reproduce it with WebCrypto.
|
|
69
|
+
*/
|
|
70
|
+
declare function slotSigningPayload(content: Pick<SlotContent, 'slot_id' | 'type' | 'content'>): string;
|
|
71
|
+
/**
|
|
72
|
+
* Sign slot content with the project API key (sonor-api side).
|
|
73
|
+
* Returns the hex HMAC-SHA256 signature.
|
|
74
|
+
*/
|
|
75
|
+
declare function signSlotContent(content: Pick<SlotContent, 'slot_id' | 'type' | 'content'>, apiKey: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Verify a slot payload's shape + signature (site-kit side).
|
|
78
|
+
* Returns true only when the payload is well-formed, within limits, and the
|
|
79
|
+
* signature matches the project API key. Constant-time comparison.
|
|
80
|
+
*/
|
|
81
|
+
declare function verifySlotContent(content: unknown, apiKey: string): content is SlotContent;
|
|
82
|
+
|
|
83
|
+
export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, type SlotContent, type SlotContentType, type SlotResolveRequestEntry, type SlotsResolveRequest, type SlotsResolveResponse, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkYBKALLBH_js = require('../chunk-YBKALLBH.js');
|
|
4
|
+
require('../chunk-ZSMWDLMK.js');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "SLOTS_CONTRACT_VERSION", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunkYBKALLBH_js.SLOTS_CONTRACT_VERSION; }
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "SLOT_CONTENT_MAX_LENGTH", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () { return chunkYBKALLBH_js.SLOT_CONTENT_MAX_LENGTH; }
|
|
15
|
+
});
|
|
16
|
+
Object.defineProperty(exports, "SLOT_CONTENT_TYPES", {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return chunkYBKALLBH_js.SLOT_CONTENT_TYPES; }
|
|
19
|
+
});
|
|
20
|
+
Object.defineProperty(exports, "SLOT_ID_PATTERN", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
get: function () { return chunkYBKALLBH_js.SLOT_ID_PATTERN; }
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "isValidSlotId", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () { return chunkYBKALLBH_js.isValidSlotId; }
|
|
27
|
+
});
|
|
28
|
+
Object.defineProperty(exports, "signSlotContent", {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
get: function () { return chunkYBKALLBH_js.signSlotContent; }
|
|
31
|
+
});
|
|
32
|
+
Object.defineProperty(exports, "slotSigningPayload", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: function () { return chunkYBKALLBH_js.slotSigningPayload; }
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(exports, "verifySlotContent", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () { return chunkYBKALLBH_js.verifySlotContent; }
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=contract.js.map
|
|
41
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"contract.js"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent } from '../chunk-2FEQ6TYD.mjs';
|
|
2
|
+
import '../chunk-4XPGGLVP.mjs';
|
|
3
|
+
//# sourceMappingURL=contract.mjs.map
|
|
4
|
+
//# sourceMappingURL=contract.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"contract.mjs"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { SlotContent } from './contract.mjs';
|
|
3
|
+
export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, SlotContentType, SlotsResolveRequest, SlotsResolveResponse, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent } from './contract.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @sonordev/site-kit/slots — Server-Only Resolution
|
|
7
|
+
*
|
|
8
|
+
* SECURITY: uses the private SONOR_API_KEY. Import only in server code
|
|
9
|
+
* (RSC, route handlers, server actions).
|
|
10
|
+
*
|
|
11
|
+
* Design rules (see docs/SITE-KIT-3.0-SPEC-SLOTS.md):
|
|
12
|
+
* - Static-safe: plain fetch with ISR caching; never touches request state,
|
|
13
|
+
* so pages using slots stay prerenderable.
|
|
14
|
+
* - Fallback-first: EVERY failure mode resolves to null (component renders
|
|
15
|
+
* its children). Slots must never break a build or a page.
|
|
16
|
+
* - Signed: unsigned/tampered payloads are dropped with a warning.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface SlotResolveOptions {
|
|
20
|
+
/** Multi-site sub-dimension: normalized lowercase host. */
|
|
21
|
+
site?: string;
|
|
22
|
+
/** ISR revalidate window in seconds (default 300; instant updates come from the revalidate webhook). */
|
|
23
|
+
revalidate?: number;
|
|
24
|
+
/**
|
|
25
|
+
* The slot's current static text, reported on resolve so the dashboard
|
|
26
|
+
* editor shows the live copy. ManagedSlot supplies this automatically when
|
|
27
|
+
* its children are a plain string.
|
|
28
|
+
*/
|
|
29
|
+
fallback?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The route this slot renders on (static literal, e.g. "/pricing") — feeds
|
|
32
|
+
* the Copy Evidence Loop's slot↔page mapping.
|
|
33
|
+
*/
|
|
34
|
+
page?: string;
|
|
35
|
+
}
|
|
36
|
+
/** Resolve one slot. Null on any failure → caller renders fallback. */
|
|
37
|
+
declare function getSlot(id: string, options?: SlotResolveOptions): Promise<SlotContent | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Batch resolution for layout-level prefetch. v1 fans out to the per-slot
|
|
40
|
+
* cached resolver (one wire shape, no behavioral difference); the batch
|
|
41
|
+
* endpoint exists in the contract so this can become a single request
|
|
42
|
+
* without a wire change.
|
|
43
|
+
*/
|
|
44
|
+
declare function resolveSlots(ids: string[], options?: SlotResolveOptions): Promise<Record<string, SlotContent | null>>;
|
|
45
|
+
|
|
46
|
+
interface ManagedSlotProps extends Omit<SlotResolveOptions, 'fallback'> {
|
|
47
|
+
/** Stable slot id, lowercase, page-scoped by convention (e.g. "home-hero-headline"). */
|
|
48
|
+
id: string;
|
|
49
|
+
/**
|
|
50
|
+
* The developer-owned static content — exactly what the page ships without
|
|
51
|
+
* Sonor. Rendered verbatim whenever no managed content resolves.
|
|
52
|
+
*/
|
|
53
|
+
children: React.ReactNode;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ManagedSlot — a Sonor-managed text region inside an element the developer
|
|
57
|
+
* still owns. Renders a fragment: no wrapper tag, no className opinion — put
|
|
58
|
+
* it INSIDE your element and keep your markup.
|
|
59
|
+
*
|
|
60
|
+
* Server component. Resolution is static-safe (ISR-cached fetch, no request
|
|
61
|
+
* access) and fallback-first: any failure renders the children. Managed
|
|
62
|
+
* content is signature-verified and rendered as escaped text, never HTML.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* <h1 className="hero-title">
|
|
67
|
+
* <ManagedSlot id="home-hero-headline" page="/">
|
|
68
|
+
* New Homes in Cincinnati & Northern Kentucky
|
|
69
|
+
* </ManagedSlot>
|
|
70
|
+
* </h1>
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare function ManagedSlot({ id, children, site, revalidate, page, }: ManagedSlotProps): Promise<React.ReactElement>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @sonordev/site-kit/slots — On-demand Revalidation
|
|
77
|
+
*
|
|
78
|
+
* Sonor calls this when slot content changes (owner edit or approved Signal
|
|
79
|
+
* proposal) so the change is live in seconds instead of at the next ISR
|
|
80
|
+
* window. Mirrors the llms revalidation handler pattern.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* // app/api/revalidate-slots/route.ts
|
|
85
|
+
* import { createSlotsRevalidateHandler } from '@sonordev/site-kit/slots'
|
|
86
|
+
*
|
|
87
|
+
* export const POST = createSlotsRevalidateHandler(process.env.REVALIDATION_SECRET!)
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
type RevalidateTagFn = (tag: string) => void;
|
|
91
|
+
declare const SLOTS_CACHE_TAG = "sonor-slots";
|
|
92
|
+
/**
|
|
93
|
+
* Create a POST handler that busts the slots data cache.
|
|
94
|
+
* Protected by a shared secret. (POST handlers are never statically
|
|
95
|
+
* prerendered, so reading the request here is fine.)
|
|
96
|
+
*/
|
|
97
|
+
declare function createSlotsRevalidateHandler(secret: string, options?: {
|
|
98
|
+
/** Additional cache tags to revalidate alongside slots. */
|
|
99
|
+
extraTags?: string[];
|
|
100
|
+
/** Custom revalidateTag (for testing or frameworks other than Next.js). */
|
|
101
|
+
revalidateTag?: RevalidateTagFn;
|
|
102
|
+
}): (request: Request) => Promise<Response>;
|
|
103
|
+
|
|
104
|
+
export { ManagedSlot, type ManagedSlotProps, SLOTS_CACHE_TAG, SlotContent, type SlotResolveOptions, createSlotsRevalidateHandler, ManagedSlot as default, getSlot, resolveSlots };
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { SlotContent } from './contract.js';
|
|
3
|
+
export { SLOTS_CONTRACT_VERSION, SLOT_CONTENT_MAX_LENGTH, SLOT_CONTENT_TYPES, SLOT_ID_PATTERN, SlotContentType, SlotsResolveRequest, SlotsResolveResponse, isValidSlotId, signSlotContent, slotSigningPayload, verifySlotContent } from './contract.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @sonordev/site-kit/slots — Server-Only Resolution
|
|
7
|
+
*
|
|
8
|
+
* SECURITY: uses the private SONOR_API_KEY. Import only in server code
|
|
9
|
+
* (RSC, route handlers, server actions).
|
|
10
|
+
*
|
|
11
|
+
* Design rules (see docs/SITE-KIT-3.0-SPEC-SLOTS.md):
|
|
12
|
+
* - Static-safe: plain fetch with ISR caching; never touches request state,
|
|
13
|
+
* so pages using slots stay prerenderable.
|
|
14
|
+
* - Fallback-first: EVERY failure mode resolves to null (component renders
|
|
15
|
+
* its children). Slots must never break a build or a page.
|
|
16
|
+
* - Signed: unsigned/tampered payloads are dropped with a warning.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface SlotResolveOptions {
|
|
20
|
+
/** Multi-site sub-dimension: normalized lowercase host. */
|
|
21
|
+
site?: string;
|
|
22
|
+
/** ISR revalidate window in seconds (default 300; instant updates come from the revalidate webhook). */
|
|
23
|
+
revalidate?: number;
|
|
24
|
+
/**
|
|
25
|
+
* The slot's current static text, reported on resolve so the dashboard
|
|
26
|
+
* editor shows the live copy. ManagedSlot supplies this automatically when
|
|
27
|
+
* its children are a plain string.
|
|
28
|
+
*/
|
|
29
|
+
fallback?: string;
|
|
30
|
+
/**
|
|
31
|
+
* The route this slot renders on (static literal, e.g. "/pricing") — feeds
|
|
32
|
+
* the Copy Evidence Loop's slot↔page mapping.
|
|
33
|
+
*/
|
|
34
|
+
page?: string;
|
|
35
|
+
}
|
|
36
|
+
/** Resolve one slot. Null on any failure → caller renders fallback. */
|
|
37
|
+
declare function getSlot(id: string, options?: SlotResolveOptions): Promise<SlotContent | null>;
|
|
38
|
+
/**
|
|
39
|
+
* Batch resolution for layout-level prefetch. v1 fans out to the per-slot
|
|
40
|
+
* cached resolver (one wire shape, no behavioral difference); the batch
|
|
41
|
+
* endpoint exists in the contract so this can become a single request
|
|
42
|
+
* without a wire change.
|
|
43
|
+
*/
|
|
44
|
+
declare function resolveSlots(ids: string[], options?: SlotResolveOptions): Promise<Record<string, SlotContent | null>>;
|
|
45
|
+
|
|
46
|
+
interface ManagedSlotProps extends Omit<SlotResolveOptions, 'fallback'> {
|
|
47
|
+
/** Stable slot id, lowercase, page-scoped by convention (e.g. "home-hero-headline"). */
|
|
48
|
+
id: string;
|
|
49
|
+
/**
|
|
50
|
+
* The developer-owned static content — exactly what the page ships without
|
|
51
|
+
* Sonor. Rendered verbatim whenever no managed content resolves.
|
|
52
|
+
*/
|
|
53
|
+
children: React.ReactNode;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* ManagedSlot — a Sonor-managed text region inside an element the developer
|
|
57
|
+
* still owns. Renders a fragment: no wrapper tag, no className opinion — put
|
|
58
|
+
* it INSIDE your element and keep your markup.
|
|
59
|
+
*
|
|
60
|
+
* Server component. Resolution is static-safe (ISR-cached fetch, no request
|
|
61
|
+
* access) and fallback-first: any failure renders the children. Managed
|
|
62
|
+
* content is signature-verified and rendered as escaped text, never HTML.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* <h1 className="hero-title">
|
|
67
|
+
* <ManagedSlot id="home-hero-headline" page="/">
|
|
68
|
+
* New Homes in Cincinnati & Northern Kentucky
|
|
69
|
+
* </ManagedSlot>
|
|
70
|
+
* </h1>
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare function ManagedSlot({ id, children, site, revalidate, page, }: ManagedSlotProps): Promise<React.ReactElement>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @sonordev/site-kit/slots — On-demand Revalidation
|
|
77
|
+
*
|
|
78
|
+
* Sonor calls this when slot content changes (owner edit or approved Signal
|
|
79
|
+
* proposal) so the change is live in seconds instead of at the next ISR
|
|
80
|
+
* window. Mirrors the llms revalidation handler pattern.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* // app/api/revalidate-slots/route.ts
|
|
85
|
+
* import { createSlotsRevalidateHandler } from '@sonordev/site-kit/slots'
|
|
86
|
+
*
|
|
87
|
+
* export const POST = createSlotsRevalidateHandler(process.env.REVALIDATION_SECRET!)
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
type RevalidateTagFn = (tag: string) => void;
|
|
91
|
+
declare const SLOTS_CACHE_TAG = "sonor-slots";
|
|
92
|
+
/**
|
|
93
|
+
* Create a POST handler that busts the slots data cache.
|
|
94
|
+
* Protected by a shared secret. (POST handlers are never statically
|
|
95
|
+
* prerendered, so reading the request here is fine.)
|
|
96
|
+
*/
|
|
97
|
+
declare function createSlotsRevalidateHandler(secret: string, options?: {
|
|
98
|
+
/** Additional cache tags to revalidate alongside slots. */
|
|
99
|
+
extraTags?: string[];
|
|
100
|
+
/** Custom revalidateTag (for testing or frameworks other than Next.js). */
|
|
101
|
+
revalidateTag?: RevalidateTagFn;
|
|
102
|
+
}): (request: Request) => Promise<Response>;
|
|
103
|
+
|
|
104
|
+
export { ManagedSlot, type ManagedSlotProps, SLOTS_CACHE_TAG, SlotContent, type SlotResolveOptions, createSlotsRevalidateHandler, ManagedSlot as default, getSlot, resolveSlots };
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var chunkYBKALLBH_js = require('../chunk-YBKALLBH.js');
|
|
6
|
+
require('../chunk-ZSMWDLMK.js');
|
|
7
|
+
require('server-only');
|
|
8
|
+
var react = require('react');
|
|
9
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
|
|
11
|
+
function getApiConfig() {
|
|
12
|
+
const apiUrl = process.env.SONOR_API_URL || "https://api.sonor.io";
|
|
13
|
+
const apiKey = process.env.SONOR_API_KEY || "";
|
|
14
|
+
if (!apiKey) return null;
|
|
15
|
+
return { apiUrl, apiKey };
|
|
16
|
+
}
|
|
17
|
+
var warned = /* @__PURE__ */ new Set();
|
|
18
|
+
function warnOnce(key, message) {
|
|
19
|
+
if (warned.has(key)) return;
|
|
20
|
+
warned.add(key);
|
|
21
|
+
console.warn(message);
|
|
22
|
+
}
|
|
23
|
+
async function fetchSlot(id, site, revalidate, fallback, page) {
|
|
24
|
+
if (!chunkYBKALLBH_js.isValidSlotId(id)) {
|
|
25
|
+
warnOnce(`id:${id}`, `[site-kit/slots] Invalid slot id "${id}" (expected ${"`"}[a-z0-9-_.:]${"`"}, lowercase) \u2014 rendering fallback`);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const config = getApiConfig();
|
|
29
|
+
if (!config) {
|
|
30
|
+
warnOnce("no-key", "[site-kit/slots] SONOR_API_KEY is not set \u2014 all slots render their fallbacks");
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const response = await fetch(`${config.apiUrl}/api/public/slots/resolve`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
"x-api-key": config.apiKey
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
contract_version: chunkYBKALLBH_js.SLOTS_CONTRACT_VERSION,
|
|
42
|
+
slots: [
|
|
43
|
+
{
|
|
44
|
+
id,
|
|
45
|
+
...fallback ? { fallback: fallback.slice(0, chunkYBKALLBH_js.SLOT_CONTENT_MAX_LENGTH) } : {},
|
|
46
|
+
...page ? { page } : {}
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
...site ? { site } : {}
|
|
50
|
+
}),
|
|
51
|
+
next: { revalidate, tags: ["sonor-slots"] }
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
if (response.status !== 404) {
|
|
55
|
+
warnOnce(
|
|
56
|
+
`http:${response.status}`,
|
|
57
|
+
`[site-kit/slots] Resolve returned ${response.status} \u2014 rendering fallbacks`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
await response.text().catch(() => {
|
|
61
|
+
});
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
const payload = data?.slots?.[id];
|
|
66
|
+
if (!payload) return null;
|
|
67
|
+
if (!chunkYBKALLBH_js.verifySlotContent(payload, config.apiKey)) {
|
|
68
|
+
warnOnce(
|
|
69
|
+
`sig:${id}`,
|
|
70
|
+
`[site-kit/slots] Dropped slot "${id}": payload failed shape/signature verification \u2014 rendering fallback`
|
|
71
|
+
);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
return payload;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
warnOnce("network", `[site-kit/slots] Resolve failed (${err?.message}) \u2014 rendering fallbacks`);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
var getSlotCached = react.cache(
|
|
81
|
+
(id, site, revalidate, fallback, page) => fetchSlot(id, site, revalidate, fallback, page)
|
|
82
|
+
);
|
|
83
|
+
function getSlot(id, options = {}) {
|
|
84
|
+
const fallback = options.fallback?.trim() || void 0;
|
|
85
|
+
const page = options.page?.trim() || void 0;
|
|
86
|
+
return getSlotCached(id, options.site, options.revalidate ?? 300, fallback, page);
|
|
87
|
+
}
|
|
88
|
+
async function resolveSlots(ids, options = {}) {
|
|
89
|
+
const entries = await Promise.all(
|
|
90
|
+
ids.map(async (id) => [id, await getSlot(id, options)])
|
|
91
|
+
);
|
|
92
|
+
return Object.fromEntries(entries);
|
|
93
|
+
}
|
|
94
|
+
async function ManagedSlot({
|
|
95
|
+
id,
|
|
96
|
+
children,
|
|
97
|
+
site,
|
|
98
|
+
revalidate,
|
|
99
|
+
page
|
|
100
|
+
}) {
|
|
101
|
+
const fallback = typeof children === "string" ? children : void 0;
|
|
102
|
+
const managed = await getSlot(id, { site, revalidate, fallback, page });
|
|
103
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: managed ? managed.content : children });
|
|
104
|
+
}
|
|
105
|
+
var ManagedSlot_default = ManagedSlot;
|
|
106
|
+
|
|
107
|
+
// src/slots/revalidate.ts
|
|
108
|
+
var SLOTS_CACHE_TAG = "sonor-slots";
|
|
109
|
+
function createSlotsRevalidateHandler(secret, options) {
|
|
110
|
+
return async function POST(request) {
|
|
111
|
+
try {
|
|
112
|
+
const authHeader = request.headers.get("authorization");
|
|
113
|
+
const url = new URL(request.url);
|
|
114
|
+
const tokenFromQuery = url.searchParams.get("secret");
|
|
115
|
+
const token = authHeader?.replace(/^Bearer\s+/i, "") || tokenFromQuery;
|
|
116
|
+
if (!secret || token !== secret) {
|
|
117
|
+
return new Response(JSON.stringify({ error: "Invalid secret" }), {
|
|
118
|
+
status: 401,
|
|
119
|
+
headers: { "Content-Type": "application/json" }
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
let revalidateTag;
|
|
123
|
+
if (options?.revalidateTag) {
|
|
124
|
+
revalidateTag = options.revalidateTag;
|
|
125
|
+
} else {
|
|
126
|
+
const nextCache = await import('next/cache');
|
|
127
|
+
revalidateTag = nextCache.revalidateTag;
|
|
128
|
+
}
|
|
129
|
+
const tags = [SLOTS_CACHE_TAG, ...options?.extraTags || []];
|
|
130
|
+
for (const tag of tags) {
|
|
131
|
+
revalidateTag(tag);
|
|
132
|
+
}
|
|
133
|
+
return new Response(JSON.stringify({ revalidated: true, tags }), {
|
|
134
|
+
status: 200,
|
|
135
|
+
headers: { "Content-Type": "application/json" }
|
|
136
|
+
});
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error("[site-kit/slots] Revalidation failed:", err);
|
|
139
|
+
return new Response(JSON.stringify({ error: "Revalidation failed" }), {
|
|
140
|
+
status: 500,
|
|
141
|
+
headers: { "Content-Type": "application/json" }
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
Object.defineProperty(exports, "SLOTS_CONTRACT_VERSION", {
|
|
148
|
+
enumerable: true,
|
|
149
|
+
get: function () { return chunkYBKALLBH_js.SLOTS_CONTRACT_VERSION; }
|
|
150
|
+
});
|
|
151
|
+
Object.defineProperty(exports, "SLOT_CONTENT_MAX_LENGTH", {
|
|
152
|
+
enumerable: true,
|
|
153
|
+
get: function () { return chunkYBKALLBH_js.SLOT_CONTENT_MAX_LENGTH; }
|
|
154
|
+
});
|
|
155
|
+
Object.defineProperty(exports, "SLOT_CONTENT_TYPES", {
|
|
156
|
+
enumerable: true,
|
|
157
|
+
get: function () { return chunkYBKALLBH_js.SLOT_CONTENT_TYPES; }
|
|
158
|
+
});
|
|
159
|
+
Object.defineProperty(exports, "SLOT_ID_PATTERN", {
|
|
160
|
+
enumerable: true,
|
|
161
|
+
get: function () { return chunkYBKALLBH_js.SLOT_ID_PATTERN; }
|
|
162
|
+
});
|
|
163
|
+
Object.defineProperty(exports, "isValidSlotId", {
|
|
164
|
+
enumerable: true,
|
|
165
|
+
get: function () { return chunkYBKALLBH_js.isValidSlotId; }
|
|
166
|
+
});
|
|
167
|
+
Object.defineProperty(exports, "signSlotContent", {
|
|
168
|
+
enumerable: true,
|
|
169
|
+
get: function () { return chunkYBKALLBH_js.signSlotContent; }
|
|
170
|
+
});
|
|
171
|
+
Object.defineProperty(exports, "slotSigningPayload", {
|
|
172
|
+
enumerable: true,
|
|
173
|
+
get: function () { return chunkYBKALLBH_js.slotSigningPayload; }
|
|
174
|
+
});
|
|
175
|
+
Object.defineProperty(exports, "verifySlotContent", {
|
|
176
|
+
enumerable: true,
|
|
177
|
+
get: function () { return chunkYBKALLBH_js.verifySlotContent; }
|
|
178
|
+
});
|
|
179
|
+
exports.ManagedSlot = ManagedSlot;
|
|
180
|
+
exports.SLOTS_CACHE_TAG = SLOTS_CACHE_TAG;
|
|
181
|
+
exports.createSlotsRevalidateHandler = createSlotsRevalidateHandler;
|
|
182
|
+
exports.default = ManagedSlot_default;
|
|
183
|
+
exports.getSlot = getSlot;
|
|
184
|
+
exports.resolveSlots = resolveSlots;
|
|
185
|
+
//# sourceMappingURL=index.js.map
|
|
186
|
+
//# sourceMappingURL=index.js.map
|