nodebb-plugin-ezoic-infinite 1.9.4 → 1.9.6

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/library.js CHANGED
@@ -101,6 +101,7 @@ async function getSettings() {
101
101
  messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
102
102
  excludedGroups: normalizeExcludedGroups(s.excludedGroups),
103
103
  };
104
+ data.excludedSet = new Set(data.excludedGroups);
104
105
  if (_settingsGen === gen) {
105
106
  _settingsCacheAt = Date.now();
106
107
  _settingsCache = data;
@@ -113,7 +114,7 @@ async function getSettings() {
113
114
  return _settingsInflight;
114
115
  }
115
116
 
116
- async function isUserExcluded(uid, excludedGroups) {
117
+ async function isUserExcluded(uid, excludedGroups, excludedSet) {
117
118
  if (!uid || !excludedGroups.length) return false;
118
119
 
119
120
  const key = `${uid}|${excludedGroups.join('|')}`;
@@ -121,16 +122,21 @@ async function isUserExcluded(uid, excludedGroups) {
121
122
  const hit = _excludeCache.get(key);
122
123
  if (hit && (t - hit.at) < EXCLUDE_TTL) return hit.value;
123
124
 
124
- const excludedSet = new Set(excludedGroups);
125
125
  const userGroups = await groups.getUserGroups([uid]);
126
126
  const value = (userGroups[0] || []).some(g => excludedSet.has(g.name));
127
127
 
128
128
  _excludeCache.set(key, { value, at: Date.now() });
129
129
  if (_excludeCache.size > 1000) {
130
- let n = 100;
131
- for (const k of _excludeCache.keys()) {
132
- if (!n--) break;
133
- _excludeCache.delete(k);
130
+ const expire = Date.now() - EXCLUDE_TTL;
131
+ for (const [k, v] of _excludeCache) {
132
+ if (v.at < expire) _excludeCache.delete(k);
133
+ }
134
+ if (_excludeCache.size > 900) {
135
+ let n = 100;
136
+ for (const k of _excludeCache.keys()) {
137
+ if (!n--) break;
138
+ _excludeCache.delete(k);
139
+ }
134
140
  }
135
141
  }
136
142
 
@@ -148,16 +154,13 @@ function clearCaches() {
148
154
  _excludeCache.clear();
149
155
  _inlineCfgNormal = null;
150
156
  _inlineCfgExcluded = null;
151
- _allGroupsGen++;
152
- _allGroupsCache = null;
153
- _allGroupsCacheAt = 0;
154
- _allGroupsInflight = null;
157
+ // _allGroupsCache is NOT cleared: the group list is independent of plugin settings.
155
158
  }
156
159
 
157
160
  // ── Client config ────────────────────────────────────────────────────────────
158
161
 
159
162
  function buildClientConfig(settings, excluded) {
160
- const { excludedGroups, ...rest } = settings;
163
+ const { excludedGroups, excludedSet, ...rest } = settings;
161
164
  return { excluded, ...rest };
162
165
  }
163
166
 
@@ -168,12 +171,24 @@ function serializeInlineConfig(cfg) {
168
171
  // ── Head injection ───────────────────────────────────────────────────────────
169
172
 
170
173
  const HEAD_PRECONNECTS = [
174
+ // Preload blocking scripts so the download starts before the parser reaches <script> tags
175
+ '<link rel="preload" as="script" href="https://cmp.gatekeeperconsent.com/min.js" crossorigin>',
176
+ '<link rel="preload" as="script" href="https://the.gatekeeperconsent.com/cmp.min.js" crossorigin>',
177
+ '<link rel="preload" as="script" href="https://www.ezojs.com/ezoic/sa.min.js" crossorigin>',
178
+ // Ezoic ad serving
171
179
  '<link rel="preconnect" href="https://g.ezoic.net" crossorigin>',
172
180
  '<link rel="preconnect" href="https://go.ezoic.net" crossorigin>',
173
181
  '<link rel="preconnect" href="https://securepubads.g.doubleclick.net" crossorigin>',
174
182
  '<link rel="preconnect" href="https://pagead2.googlesyndication.com" crossorigin>',
183
+ // Synchronous blocking scripts — preconnect to establish TCP+TLS before the parser reaches them
184
+ '<link rel="preconnect" href="https://cmp.gatekeeperconsent.com" crossorigin>',
185
+ '<link rel="preconnect" href="https://the.gatekeeperconsent.com" crossorigin>',
186
+ '<link rel="preconnect" href="https://www.ezojs.com" crossorigin>',
175
187
  '<link rel="dns-prefetch" href="https://g.ezoic.net">',
176
188
  '<link rel="dns-prefetch" href="https://securepubads.g.doubleclick.net">',
189
+ '<link rel="dns-prefetch" href="https://cmp.gatekeeperconsent.com">',
190
+ '<link rel="dns-prefetch" href="https://the.gatekeeperconsent.com">',
191
+ '<link rel="dns-prefetch" href="https://www.ezojs.com">',
177
192
  ].join('\n');
178
193
 
179
194
  // Working config: sa.min.js loaded synchronously (no async).
@@ -219,7 +234,7 @@ plugin.injectEzoicHead = async (data) => {
219
234
 
220
235
  const settings = await getSettings();
221
236
  const uid = data.req?.uid ?? 0;
222
- const excluded = await isUserExcluded(uid, settings.excludedGroups);
237
+ const excluded = await isUserExcluded(uid, settings.excludedGroups, settings.excludedSet);
223
238
 
224
239
  if (!_inlineCfgNormal) {
225
240
  _inlineCfgNormal = serializeInlineConfig(buildClientConfig(settings, false));
@@ -274,7 +289,7 @@ plugin.init = async ({ router, middleware }) => {
274
289
  router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
275
290
  try {
276
291
  const settings = await getSettings();
277
- const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
292
+ const excluded = await isUserExcluded(req.uid, settings.excludedGroups, settings.excludedSet);
278
293
  res.json(buildClientConfig(settings, excluded));
279
294
  } catch (err) {
280
295
  console.error('[ezoic-infinite] config API error:', err.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.9.4",
3
+ "version": "1.9.6",
4
4
  "description": "Production-ready Ezoic infinite ads integration for NodeBB 4.x",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -32,7 +32,7 @@
32
32
  BURST_COOLDOWN_MS: 200,
33
33
  BLOCK_DURATION_MS: 1_000,
34
34
  SHOW_TIMEOUT_MS: 4_500,
35
- SHOW_RELEASE_MS: 150,
35
+ SHOW_RELEASE_MS: 50,
36
36
  RECYCLE_DELAY_MS: 50,
37
37
  UNCOLLAPSE_CHECK_MS: [500, 5_000],
38
38
  };
@@ -42,8 +42,8 @@
42
42
  const MAX_BURST_STEPS = 8;
43
43
  const BURST_WINDOW_MS = 2_000;
44
44
 
45
- const IO_MARGIN_DESKTOP = '1200px 0px 1200px 0px';
46
- const IO_MARGIN_MOBILE = '1500px 0px 1500px 0px';
45
+ const IO_MARGIN_DESKTOP = '1500px 0px 1500px 0px';
46
+ const IO_MARGIN_MOBILE = '2000px 0px 2000px 0px';
47
47
 
48
48
  const SEL = {
49
49
  post: '[component="post"][data-pid]',
@@ -491,7 +491,7 @@
491
491
  const id = pickId(poolKey);
492
492
  if (id) {
493
493
  const w = insertAfter(el, id, klass, key);
494
- if (w) { observePh(id); inserted++; }
494
+ if (w) { observePh(id); enqueueShow(id); inserted++; }
495
495
  } else {
496
496
  if (!recycleWrap(klass, el, key)) break;
497
497
  inserted++;
@@ -580,7 +580,8 @@
580
580
  scheduleEmptyCheck(id, t);
581
581
  setTimeout(() => { clearTimeout(timer); release(); }, TIMING.SHOW_RELEASE_MS);
582
582
  };
583
- typeof ez.cmd?.push === 'function' ? ez.cmd.push(doShow) : doShow();
583
+ const ezReady = ez.loadingStatus === 'complete';
584
+ typeof ez.cmd?.push === 'function' && !ezReady ? ez.cmd.push(doShow) : doShow();
584
585
  } catch (_) { clearTimeout(timer); release(); }
585
586
  }
586
587
 
@@ -694,7 +695,7 @@
694
695
  S.burstCount++;
695
696
  scheduleRun(n => {
696
697
  if (!n && !S.pending.size) { S.burstActive = false; return; }
697
- setTimeout(step, n > 0 ? 100 : 250);
698
+ setTimeout(step, n > 0 ? 50 : 100);
698
699
  });
699
700
  };
700
701
  step();