@actuate-media/cms-core 0.85.3 → 0.87.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/CHANGELOG.md +39 -0
- package/dist/api/openapi.d.ts.map +1 -1
- package/dist/api/openapi.js +24 -0
- package/dist/api/openapi.js.map +1 -1
- package/dist/api/route-helpers.d.ts.map +1 -1
- package/dist/api/route-helpers.js +21 -2
- package/dist/api/route-helpers.js.map +1 -1
- package/dist/api/routes/documents.d.ts.map +1 -1
- package/dist/api/routes/documents.js +27 -1
- package/dist/api/routes/documents.js.map +1 -1
- package/dist/api/routes/media.d.ts.map +1 -1
- package/dist/api/routes/media.js +3 -0
- package/dist/api/routes/media.js.map +1 -1
- package/dist/api/routes/sections.d.ts.map +1 -1
- package/dist/api/routes/sections.js +2 -1
- package/dist/api/routes/sections.js.map +1 -1
- package/dist/api/routes/webhooks.d.ts.map +1 -1
- package/dist/api/routes/webhooks.js +22 -1
- package/dist/api/routes/webhooks.js.map +1 -1
- package/dist/db/model-types.d.ts +3 -0
- package/dist/db/model-types.d.ts.map +1 -1
- package/dist/next/script-tags.d.ts.map +1 -1
- package/dist/next/script-tags.js +1 -1
- package/dist/next/script-tags.js.map +1 -1
- package/dist/posts/query.d.ts +6 -0
- package/dist/posts/query.d.ts.map +1 -1
- package/dist/posts/query.js +21 -0
- package/dist/posts/query.js.map +1 -1
- package/dist/script-tags/index.d.ts +1 -1
- package/dist/script-tags/index.d.ts.map +1 -1
- package/dist/script-tags/index.js +1 -1
- package/dist/script-tags/index.js.map +1 -1
- package/dist/script-tags/resolve.d.ts +27 -1
- package/dist/script-tags/resolve.d.ts.map +1 -1
- package/dist/script-tags/resolve.js +64 -2
- package/dist/script-tags/resolve.js.map +1 -1
- package/package.json +1 -1
- package/prisma/migrations/0005_script_tag_delivery_controls/migration.sql +8 -0
- package/prisma/schema.prisma +16 -10
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
export const SCRIPT_TAGS_CACHE_TAG = 'actuate:script-tags';
|
|
2
|
+
export const SCRIPT_TAG_CONSENT_CATEGORIES = [
|
|
3
|
+
'none',
|
|
4
|
+
'analytics',
|
|
5
|
+
'marketing',
|
|
6
|
+
'functional',
|
|
7
|
+
'preferences',
|
|
8
|
+
];
|
|
2
9
|
const EMPTY = { head: [], body_open: [], body_close: [] };
|
|
3
10
|
export function normalizeScriptTagPath(pathParam) {
|
|
4
11
|
const trimmed = pathParam.trim() || '/';
|
|
@@ -7,11 +14,64 @@ export function normalizeScriptTagPath(pathParam) {
|
|
|
7
14
|
function hasScriptTagModel(db) {
|
|
8
15
|
return typeof db.scriptTag?.findMany === 'function';
|
|
9
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Add `async` to external `<script src>` tags that don't already declare
|
|
19
|
+
* `async` or `defer`. Inline scripts are left untouched — `async` only has
|
|
20
|
+
* meaning for external scripts.
|
|
21
|
+
*/
|
|
22
|
+
export function applyAsyncToScriptCode(code) {
|
|
23
|
+
return code.replace(/<script(\s[^>]*)?>/gi, (match, attrs) => {
|
|
24
|
+
const attrText = attrs ?? '';
|
|
25
|
+
if (!/\bsrc\s*=/i.test(attrText))
|
|
26
|
+
return match;
|
|
27
|
+
if (/\basync\b/i.test(attrText) || /\bdefer\b/i.test(attrText))
|
|
28
|
+
return match;
|
|
29
|
+
return `<script async${attrText}>`;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Wrap a snippet in a consent gate keyed to `@actuate-media/plugin-consent`
|
|
34
|
+
* storage (`am_cookie_consent`) and its `am-consent` event. The original
|
|
35
|
+
* snippet is injected only after the visitor grants full consent.
|
|
36
|
+
*
|
|
37
|
+
* Interim category model: the consent plugin currently stores a binary level
|
|
38
|
+
* (`all` | `essential`), so every non-`none` category requires `all`. The
|
|
39
|
+
* category is preserved as a `data-consent-category` marker for CMPs and for
|
|
40
|
+
* future granular gating.
|
|
41
|
+
*/
|
|
42
|
+
export function wrapCodeInConsentGate(code, category) {
|
|
43
|
+
// \u003C-escape so the payload can never terminate the wrapper script early.
|
|
44
|
+
const payload = JSON.stringify(code).replace(/</g, '\\u003C');
|
|
45
|
+
return (`<script data-consent-category="${category}">` +
|
|
46
|
+
'(function(){' +
|
|
47
|
+
`var p=${payload};` +
|
|
48
|
+
// currentScript is only defined during synchronous execution — capture it
|
|
49
|
+
// now so deferred injection (after the am-consent event) keeps placement.
|
|
50
|
+
'var s=document.currentScript;' +
|
|
51
|
+
'function g(){try{return window.localStorage.getItem("am_cookie_consent")==="all"}catch(e){return false}}' +
|
|
52
|
+
'var d=false;' +
|
|
53
|
+
'function i(){if(d)return;d=true;' +
|
|
54
|
+
'var f=document.createRange().createContextualFragment(p);' +
|
|
55
|
+
'if(s&&s.parentNode){s.parentNode.insertBefore(f,s.nextSibling)}else{document.body.appendChild(f)}}' +
|
|
56
|
+
'if(g()){i();return}' +
|
|
57
|
+
'window.addEventListener("am-consent",function(e){if((e.detail&&e.detail.level)==="all")i()});' +
|
|
58
|
+
'})()' +
|
|
59
|
+
'</script>');
|
|
60
|
+
}
|
|
61
|
+
function deliverableCode(tag) {
|
|
62
|
+
let code = tag.code;
|
|
63
|
+
if (tag.loadAsync)
|
|
64
|
+
code = applyAsyncToScriptCode(code);
|
|
65
|
+
const category = tag.consentCategory ?? 'none';
|
|
66
|
+
if (category !== 'none')
|
|
67
|
+
code = wrapCodeInConsentGate(code, category);
|
|
68
|
+
return code;
|
|
69
|
+
}
|
|
10
70
|
/**
|
|
11
71
|
* Resolve enabled script tags for a public URL path, grouped by placement.
|
|
12
72
|
* Shared by the public API route and Next.js site layouts.
|
|
13
73
|
*/
|
|
14
|
-
export async function resolveScriptTagsForPath(db, pathParam) {
|
|
74
|
+
export async function resolveScriptTagsForPath(db, pathParam, options = {}) {
|
|
15
75
|
if (!hasScriptTagModel(db))
|
|
16
76
|
return EMPTY;
|
|
17
77
|
const normalizedPath = normalizeScriptTagPath(pathParam);
|
|
@@ -21,6 +81,8 @@ export async function resolveScriptTagsForPath(db, pathParam) {
|
|
|
21
81
|
});
|
|
22
82
|
const matched = [];
|
|
23
83
|
for (const tag of tags) {
|
|
84
|
+
if (options.preview && tag.disableInPreview !== false)
|
|
85
|
+
continue;
|
|
24
86
|
if (tag.scope === 'site') {
|
|
25
87
|
matched.push(tag);
|
|
26
88
|
continue;
|
|
@@ -49,7 +111,7 @@ export async function resolveScriptTagsForPath(db, pathParam) {
|
|
|
49
111
|
for (const tag of matched) {
|
|
50
112
|
const bucket = grouped[tag.placement];
|
|
51
113
|
if (bucket)
|
|
52
|
-
bucket.push(tag
|
|
114
|
+
bucket.push(deliverableCode(tag));
|
|
53
115
|
}
|
|
54
116
|
return grouped;
|
|
55
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/script-tags/resolve.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,qBAAqB,GAAG,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/script-tags/resolve.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- Design-parity delivery controls for script tags:
|
|
2
|
+
-- loadAsync — inject `async` into external <script src> tags at resolve time
|
|
3
|
+
-- disableInPreview — skip the tag when rendering inside CMS preview mode
|
|
4
|
+
-- consentCategory — cookie-consent gate (none | analytics | marketing | functional | preferences)
|
|
5
|
+
ALTER TABLE "actuate_script_tags"
|
|
6
|
+
ADD COLUMN "loadAsync" BOOLEAN NOT NULL DEFAULT false,
|
|
7
|
+
ADD COLUMN "disableInPreview" BOOLEAN NOT NULL DEFAULT true,
|
|
8
|
+
ADD COLUMN "consentCategory" TEXT NOT NULL DEFAULT 'none';
|
package/prisma/schema.prisma
CHANGED
|
@@ -867,16 +867,22 @@ model Invite {
|
|
|
867
867
|
}
|
|
868
868
|
|
|
869
869
|
model ScriptTag {
|
|
870
|
-
id
|
|
871
|
-
name
|
|
872
|
-
code
|
|
873
|
-
placement
|
|
874
|
-
scope
|
|
875
|
-
targetPaths
|
|
876
|
-
priority
|
|
877
|
-
enabled
|
|
878
|
-
|
|
879
|
-
|
|
870
|
+
id String @id @default(cuid())
|
|
871
|
+
name String
|
|
872
|
+
code String @db.Text
|
|
873
|
+
placement String
|
|
874
|
+
scope String
|
|
875
|
+
targetPaths String[]
|
|
876
|
+
priority Int @default(100)
|
|
877
|
+
enabled Boolean @default(true)
|
|
878
|
+
/// Inject `async` into external <script src> tags so they don't block render.
|
|
879
|
+
loadAsync Boolean @default(false)
|
|
880
|
+
/// Skip this tag when the page is rendered inside CMS preview mode.
|
|
881
|
+
disableInPreview Boolean @default(true)
|
|
882
|
+
/// Cookie-consent category gate: none | analytics | marketing | functional | preferences.
|
|
883
|
+
consentCategory String @default("none")
|
|
884
|
+
createdAt DateTime @default(now())
|
|
885
|
+
updatedAt DateTime @updatedAt
|
|
880
886
|
|
|
881
887
|
@@index([enabled])
|
|
882
888
|
@@index([placement])
|