mount-observer 0.1.22 → 0.1.24
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/Synthesizer.js +59 -8
- package/Synthesizer.ts +66 -8
- package/package.json +2 -2
package/Synthesizer.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import './ElementMountExtension.js';
|
|
2
2
|
import { waitForEvent } from 'assign-gingerly/waitForEvent.js';
|
|
3
3
|
import { AddedScriptElementEvent } from './Events.js';
|
|
4
|
+
import 'mount-observer/handlers/MountObserverScript.js';
|
|
5
|
+
import { mos } from 'mount-observer/handlers/MountObserverScript.js';
|
|
6
|
+
import 'mount-observer/handlers/ScriptExport.js';
|
|
7
|
+
import { scriptExport } from 'mount-observer/handlers/ScriptExport.js';
|
|
8
|
+
import 'mount-observer/handlers/HTMLInclude.js';
|
|
9
|
+
import { include } from 'mount-observer/handlers/HTMLInclude.js';
|
|
10
|
+
import 'mount-observer/handlers/HoistTemplate.js';
|
|
11
|
+
import { hoist } from 'mount-observer/handlers/HoistTemplate.js';
|
|
12
|
+
import { emc } from 'mount-observer/handlers/EMCScript.js';
|
|
13
|
+
import 'mount-observer/handlers/EMCScript.js';
|
|
4
14
|
/**
|
|
5
15
|
* Track which root nodes have already had handlers activated.
|
|
6
16
|
* Uses WeakSet to avoid memory leaks when nodes are garbage collected.
|
|
@@ -43,11 +53,7 @@ export class Synthesizer extends HTMLElement {
|
|
|
43
53
|
* List of built-in handlers to activate.
|
|
44
54
|
*/
|
|
45
55
|
static builtInHandlers = [
|
|
46
|
-
|
|
47
|
-
'builtIns.scriptExport',
|
|
48
|
-
'builtIns.HTMLInclude',
|
|
49
|
-
'builtIns.hoistTemplate',
|
|
50
|
-
'builtIns.emcScript'
|
|
56
|
+
mos, scriptExport, include, hoist, emc
|
|
51
57
|
];
|
|
52
58
|
connectedCallback() {
|
|
53
59
|
// Synthesizer elements are infrastructure, not UI
|
|
@@ -95,6 +101,44 @@ export class Synthesizer extends HTMLElement {
|
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Check if a script element should be processed based on include/exclude attributes.
|
|
106
|
+
*
|
|
107
|
+
* Rules:
|
|
108
|
+
* - If passthrough attribute exists, return false (don't process any scripts)
|
|
109
|
+
* - If include or exclude attributes exist and script has no id, return false (security)
|
|
110
|
+
* - If include attribute exists, only process scripts with IDs in the include list
|
|
111
|
+
* - If exclude attribute exists, don't process scripts with IDs in the exclude list
|
|
112
|
+
* - Otherwise, return true (process the script)
|
|
113
|
+
*
|
|
114
|
+
* Made protected so subscribers can check if syndicator would allow a script.
|
|
115
|
+
*/
|
|
116
|
+
checkIfAllowed(scriptElement) {
|
|
117
|
+
// Passthrough mode - don't process any scripts
|
|
118
|
+
if (this.hasAttribute('passthrough')) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
const scriptId = scriptElement.getAttribute('id');
|
|
122
|
+
// Security: If include or exclude attributes exist, script must have an ID
|
|
123
|
+
if ((this.hasAttribute('include') || this.hasAttribute('exclude')) && !scriptId) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// Include list - only process scripts with IDs in the list
|
|
127
|
+
if (this.hasAttribute('include')) {
|
|
128
|
+
const includeList = this.getAttribute('include').split(' ').filter(s => s.trim());
|
|
129
|
+
if (!includeList.includes(scriptId)) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Exclude list - don't process scripts with IDs in the list
|
|
134
|
+
if (this.hasAttribute('exclude')) {
|
|
135
|
+
const excludeList = this.getAttribute('exclude').split(' ').filter(s => s.trim());
|
|
136
|
+
if (excludeList.includes(scriptId)) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
98
142
|
/**
|
|
99
143
|
* Initialize as syndicator (in document root).
|
|
100
144
|
* Watches for script elements and broadcasts them to subscribers.
|
|
@@ -103,7 +147,9 @@ export class Synthesizer extends HTMLElement {
|
|
|
103
147
|
// Process existing script elements
|
|
104
148
|
const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
|
|
105
149
|
scripts.forEach(script => {
|
|
106
|
-
this
|
|
150
|
+
if (this.checkIfAllowed(script)) {
|
|
151
|
+
this.#broadcastScript(script);
|
|
152
|
+
}
|
|
107
153
|
});
|
|
108
154
|
// Watch for new script elements
|
|
109
155
|
this.#mutationObserver = new MutationObserver((mutations) => {
|
|
@@ -112,7 +158,9 @@ export class Synthesizer extends HTMLElement {
|
|
|
112
158
|
if (node instanceof HTMLScriptElement) {
|
|
113
159
|
const type = node.getAttribute('type');
|
|
114
160
|
if (type === 'mountobserver' || type === 'emc') {
|
|
115
|
-
this
|
|
161
|
+
if (this.checkIfAllowed(node)) {
|
|
162
|
+
this.#broadcastScript(node);
|
|
163
|
+
}
|
|
116
164
|
}
|
|
117
165
|
}
|
|
118
166
|
}
|
|
@@ -141,9 +189,12 @@ export class Synthesizer extends HTMLElement {
|
|
|
141
189
|
return;
|
|
142
190
|
}
|
|
143
191
|
// Process existing scripts from syndicator
|
|
192
|
+
// Only process scripts that pass the syndicator's filtering
|
|
144
193
|
const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
|
|
145
194
|
scripts.forEach(script => {
|
|
146
|
-
|
|
195
|
+
if (syndicator.checkIfAllowed(script)) {
|
|
196
|
+
this.#processScript(script);
|
|
197
|
+
}
|
|
147
198
|
});
|
|
148
199
|
// Subscribe to new scripts
|
|
149
200
|
syndicator.addEventListener(AddedScriptElementEvent.eventName, (e) => {
|
package/Synthesizer.ts
CHANGED
|
@@ -2,6 +2,17 @@ import './ElementMountExtension.js';
|
|
|
2
2
|
import { waitForEvent } from 'assign-gingerly/waitForEvent.js';
|
|
3
3
|
import { AddedScriptElementEvent } from './Events.js';
|
|
4
4
|
|
|
5
|
+
import 'mount-observer/handlers/MountObserverScript.js';
|
|
6
|
+
import {mos} from 'mount-observer/handlers/MountObserverScript.js';
|
|
7
|
+
import 'mount-observer/handlers/ScriptExport.js';
|
|
8
|
+
import {scriptExport} from 'mount-observer/handlers/ScriptExport.js';
|
|
9
|
+
import 'mount-observer/handlers/HTMLInclude.js';
|
|
10
|
+
import {include} from 'mount-observer/handlers/HTMLInclude.js';
|
|
11
|
+
import 'mount-observer/handlers/HoistTemplate.js';
|
|
12
|
+
import {hoist} from 'mount-observer/handlers/HoistTemplate.js';
|
|
13
|
+
import {emc} from 'mount-observer/handlers/EMCScript.js';
|
|
14
|
+
import 'mount-observer/handlers/EMCScript.js';
|
|
15
|
+
|
|
5
16
|
/**
|
|
6
17
|
* Track which root nodes have already had handlers activated.
|
|
7
18
|
* Uses WeakSet to avoid memory leaks when nodes are garbage collected.
|
|
@@ -46,11 +57,7 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
46
57
|
* List of built-in handlers to activate.
|
|
47
58
|
*/
|
|
48
59
|
protected static builtInHandlers = [
|
|
49
|
-
|
|
50
|
-
'builtIns.scriptExport',
|
|
51
|
-
'builtIns.HTMLInclude',
|
|
52
|
-
'builtIns.hoistTemplate',
|
|
53
|
-
'builtIns.emcScript'
|
|
60
|
+
mos, scriptExport, include, hoist, emc
|
|
54
61
|
];
|
|
55
62
|
|
|
56
63
|
connectedCallback(): void {
|
|
@@ -107,6 +114,50 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Check if a script element should be processed based on include/exclude attributes.
|
|
119
|
+
*
|
|
120
|
+
* Rules:
|
|
121
|
+
* - If passthrough attribute exists, return false (don't process any scripts)
|
|
122
|
+
* - If include or exclude attributes exist and script has no id, return false (security)
|
|
123
|
+
* - If include attribute exists, only process scripts with IDs in the include list
|
|
124
|
+
* - If exclude attribute exists, don't process scripts with IDs in the exclude list
|
|
125
|
+
* - Otherwise, return true (process the script)
|
|
126
|
+
*
|
|
127
|
+
* Made protected so subscribers can check if syndicator would allow a script.
|
|
128
|
+
*/
|
|
129
|
+
protected checkIfAllowed(scriptElement: HTMLScriptElement): boolean {
|
|
130
|
+
// Passthrough mode - don't process any scripts
|
|
131
|
+
if (this.hasAttribute('passthrough')) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const scriptId = scriptElement.getAttribute('id');
|
|
136
|
+
|
|
137
|
+
// Security: If include or exclude attributes exist, script must have an ID
|
|
138
|
+
if ((this.hasAttribute('include') || this.hasAttribute('exclude')) && !scriptId) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Include list - only process scripts with IDs in the list
|
|
143
|
+
if (this.hasAttribute('include')) {
|
|
144
|
+
const includeList = this.getAttribute('include')!.split(' ').filter(s => s.trim());
|
|
145
|
+
if (!includeList.includes(scriptId!)) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Exclude list - don't process scripts with IDs in the list
|
|
151
|
+
if (this.hasAttribute('exclude')) {
|
|
152
|
+
const excludeList = this.getAttribute('exclude')!.split(' ').filter(s => s.trim());
|
|
153
|
+
if (excludeList.includes(scriptId!)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
110
161
|
/**
|
|
111
162
|
* Initialize as syndicator (in document root).
|
|
112
163
|
* Watches for script elements and broadcasts them to subscribers.
|
|
@@ -115,7 +166,9 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
115
166
|
// Process existing script elements
|
|
116
167
|
const scripts = this.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
|
|
117
168
|
scripts.forEach(script => {
|
|
118
|
-
this
|
|
169
|
+
if (this.checkIfAllowed(script as HTMLScriptElement)) {
|
|
170
|
+
this.#broadcastScript(script as HTMLScriptElement);
|
|
171
|
+
}
|
|
119
172
|
});
|
|
120
173
|
|
|
121
174
|
// Watch for new script elements
|
|
@@ -125,7 +178,9 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
125
178
|
if (node instanceof HTMLScriptElement) {
|
|
126
179
|
const type = node.getAttribute('type');
|
|
127
180
|
if (type === 'mountobserver' || type === 'emc') {
|
|
128
|
-
this
|
|
181
|
+
if (this.checkIfAllowed(node)) {
|
|
182
|
+
this.#broadcastScript(node);
|
|
183
|
+
}
|
|
129
184
|
}
|
|
130
185
|
}
|
|
131
186
|
}
|
|
@@ -159,9 +214,12 @@ export abstract class Synthesizer extends HTMLElement {
|
|
|
159
214
|
}
|
|
160
215
|
|
|
161
216
|
// Process existing scripts from syndicator
|
|
217
|
+
// Only process scripts that pass the syndicator's filtering
|
|
162
218
|
const scripts = syndicator.querySelectorAll('script[type="mountobserver"], script[type="emc"]');
|
|
163
219
|
scripts.forEach(script => {
|
|
164
|
-
|
|
220
|
+
if (syndicator.checkIfAllowed(script as HTMLScriptElement)) {
|
|
221
|
+
this.#processScript(script as HTMLScriptElement);
|
|
222
|
+
}
|
|
165
223
|
});
|
|
166
224
|
|
|
167
225
|
// Subscribe to new scripts
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mount-observer",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.24",
|
|
4
4
|
"description": "Observe and act on css matches.",
|
|
5
5
|
"main": "MountObserver.js",
|
|
6
6
|
"module": "MountObserver.js",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"assign-gingerly": "0.0.
|
|
8
|
+
"assign-gingerly": "0.0.24",
|
|
9
9
|
"id-generation": "0.0.4"
|
|
10
10
|
},
|
|
11
11
|
"devDependencies": {
|