ultimate-jekyll-manager 1.0.9 → 1.0.11
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
CHANGED
|
@@ -15,6 +15,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
|
+
## [1.0.11] - 2026-03-24
|
|
19
|
+
### Added
|
|
20
|
+
- Firestore version + transport test page at `/test/libraries/firestore` for diagnosing SDK connectivity across browsers
|
|
21
|
+
|
|
22
|
+
## [1.0.10] - 2026-03-24
|
|
23
|
+
### Fixed
|
|
24
|
+
- `getUJMConfig()` now throws descriptive errors when config file is missing, empty, or malformed instead of crashing silently
|
|
25
|
+
- Admin dashboard subscription queries now filter by `subscription.status == 'active'` instead of expiry timestamp
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- Webpack watch path for web-manager changed from `src/` to `dist/`
|
|
29
|
+
|
|
30
|
+
## [1.0.9] - 2026-03-20
|
|
31
|
+
### Changed
|
|
32
|
+
- `authorizedFetch` no longer throws when no user is logged in; logs a warning and proceeds without the Authorization header
|
|
33
|
+
|
|
18
34
|
## [1.0.7] - 2026-03-20
|
|
19
35
|
### Changed
|
|
20
36
|
- Upgrade `web-manager` from ^4.1.29 to ^4.1.30
|
|
@@ -77,7 +77,7 @@ async function loadStatCards() {
|
|
|
77
77
|
const [totalUsers, newUsers, activeSubscriptions, pushSubscribers] = await Promise.allSettled([
|
|
78
78
|
getCountFromServer(collection(db, 'users')),
|
|
79
79
|
getCountFromServer(query(collection(db, 'users'), where('metadata.created.timestampUNIX', '>=', thirtyDaysAgo))),
|
|
80
|
-
getCountFromServer(query(collection(db, 'users'), where('subscription.
|
|
80
|
+
getCountFromServer(query(collection(db, 'users'), where('subscription.status', '==', 'active'), where('subscription.product.id', '!=', 'basic'))),
|
|
81
81
|
getCountFromServer(collection(db, 'notifications')),
|
|
82
82
|
]);
|
|
83
83
|
|
|
@@ -92,10 +92,9 @@ async function loadStatCards() {
|
|
|
92
92
|
// ============================================
|
|
93
93
|
async function loadSubscriberData() {
|
|
94
94
|
const firestore = webManager.firestore();
|
|
95
|
-
const now = Math.floor(Date.now() / 1000);
|
|
96
95
|
|
|
97
96
|
const snapshot = await firestore.collection('users')
|
|
98
|
-
.where('subscription.
|
|
97
|
+
.where('subscription.status', '==', 'active')
|
|
99
98
|
.get();
|
|
100
99
|
|
|
101
100
|
// Group by plan
|
package/dist/build.js
CHANGED
|
@@ -142,7 +142,18 @@ Manager.prototype.getPackage = Manager.getPackage;
|
|
|
142
142
|
// getUJMConfig: requires and parses ultimate-jekyll-manager.json
|
|
143
143
|
Manager.getUJMConfig = function () {
|
|
144
144
|
const configPath = path.join(process.cwd(), 'config', 'ultimate-jekyll-manager.json');
|
|
145
|
-
|
|
145
|
+
if (!jetpack.exists(configPath)) {
|
|
146
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
147
|
+
}
|
|
148
|
+
const content = jetpack.read(configPath);
|
|
149
|
+
if (!content) {
|
|
150
|
+
throw new Error(`Config file is empty: ${configPath}`);
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
return JSON5.parse(content);
|
|
154
|
+
} catch (e) {
|
|
155
|
+
throw new Error(`Failed to parse ${configPath}: ${e.message}`);
|
|
156
|
+
}
|
|
146
157
|
}
|
|
147
158
|
Manager.prototype.getUJMConfig = Manager.getUJMConfig;
|
|
148
159
|
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: none
|
|
3
|
+
permalink: /test/libraries/firestore
|
|
4
|
+
sitemap:
|
|
5
|
+
include: false
|
|
6
|
+
---
|
|
7
|
+
<!DOCTYPE html>
|
|
8
|
+
<html>
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset="utf-8">
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
12
|
+
<title>Firestore Version Test</title>
|
|
13
|
+
<style>
|
|
14
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
15
|
+
body { font-family: monospace; padding: 16px; font-size: 13px; background: #111; color: #eee; }
|
|
16
|
+
h1 { font-size: 18px; margin-bottom: 8px; }
|
|
17
|
+
h2 { font-size: 14px; margin-bottom: 4px; color: #aaa; }
|
|
18
|
+
.test { margin-bottom: 12px; border: 1px solid #333; padding: 8px; border-radius: 4px; }
|
|
19
|
+
.test.success { border-color: #0a0; }
|
|
20
|
+
.test.fail { border-color: #a00; }
|
|
21
|
+
pre { white-space: pre-wrap; font-size: 11px; line-height: 1.3; max-height: 150px; overflow-y: auto; }
|
|
22
|
+
#ua { font-size: 11px; color: #666; margin-bottom: 12px; word-break: break-all; }
|
|
23
|
+
#status { font-size: 14px; margin-bottom: 12px; color: #ff0; }
|
|
24
|
+
.summary { margin-bottom: 16px; padding: 12px; border: 2px solid #555; border-radius: 4px; }
|
|
25
|
+
</style>
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<h1>Firestore Version + Transport Test</h1>
|
|
29
|
+
<div id="ua"></div>
|
|
30
|
+
<div id="status">Loading...</div>
|
|
31
|
+
<div id="sw-status" style="color: #f80; margin-bottom: 12px;"></div>
|
|
32
|
+
<div class="summary" id="summary" style="display:none"></div>
|
|
33
|
+
|
|
34
|
+
<div class="test" id="rest"><h2>Baseline: REST API (v12.11.0 auth only)</h2><pre id="rest-log"></pre></div>
|
|
35
|
+
<div class="test" id="v11"><h2>Firebase 11.0.0 - getFirestore</h2><pre id="v11-log"></pre></div>
|
|
36
|
+
<div class="test" id="v11lp"><h2>Firebase 11.0.0 - initializeFirestore + forceLongPolling</h2><pre id="v11lp-log"></pre></div>
|
|
37
|
+
<div class="test" id="v12_0"><h2>Firebase 12.0.0 - getFirestore</h2><pre id="v12_0-log"></pre></div>
|
|
38
|
+
<div class="test" id="v12_0lp"><h2>Firebase 12.0.0 - initializeFirestore + forceLongPolling</h2><pre id="v12_0lp-log"></pre></div>
|
|
39
|
+
<div class="test" id="v12_11"><h2>Firebase 12.11.0 (current) - getFirestore</h2><pre id="v12_11-log"></pre></div>
|
|
40
|
+
<div class="test" id="v12_11lp"><h2>Firebase 12.11.0 (current) - initializeFirestore + forceLongPolling</h2><pre id="v12_11lp-log"></pre></div>
|
|
41
|
+
<div class="test" id="v10"><h2>Firebase 10.14.0 - getFirestore</h2><pre id="v10-log"></pre></div>
|
|
42
|
+
<div class="test" id="v10lp"><h2>Firebase 10.14.0 - initializeFirestore + forceLongPolling</h2><pre id="v10lp-log"></pre></div>
|
|
43
|
+
|
|
44
|
+
<script type="module">
|
|
45
|
+
// Use 12.11.0 for auth only (default app)
|
|
46
|
+
import { initializeApp } from 'https://www.gstatic.com/firebasejs/12.11.0/firebase-app.js';
|
|
47
|
+
import { getAuth, onAuthStateChanged } from 'https://www.gstatic.com/firebasejs/12.11.0/firebase-auth.js';
|
|
48
|
+
|
|
49
|
+
const config = {{ page.resolved.web_manager.firebase.app.config | jsonify }};
|
|
50
|
+
|
|
51
|
+
document.getElementById('ua').textContent = navigator.userAgent;
|
|
52
|
+
|
|
53
|
+
// Unregister all service workers first
|
|
54
|
+
if ('serviceWorker' in navigator) {
|
|
55
|
+
const regs = await navigator.serviceWorker.getRegistrations();
|
|
56
|
+
console.log(`[Setup] Found ${regs.length} service worker(s)`);
|
|
57
|
+
for (const reg of regs) {
|
|
58
|
+
const result = await reg.unregister();
|
|
59
|
+
console.log(`[Setup] Unregistered service worker: ${reg.scope} (success: ${result})`);
|
|
60
|
+
}
|
|
61
|
+
const swMsg = regs.length > 0
|
|
62
|
+
? `Unregistered ${regs.length} service worker(s)`
|
|
63
|
+
: 'No service workers found';
|
|
64
|
+
document.getElementById('sw-status').textContent = swMsg;
|
|
65
|
+
document.getElementById('status').textContent = swMsg + '. Running tests...';
|
|
66
|
+
} else {
|
|
67
|
+
console.log('[Setup] Service workers not supported');
|
|
68
|
+
document.getElementById('sw-status').textContent = 'Service workers not supported';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function makeLogger(id) {
|
|
72
|
+
const $log = document.getElementById(id + '-log');
|
|
73
|
+
const $box = document.getElementById(id);
|
|
74
|
+
return {
|
|
75
|
+
log: (msg) => {
|
|
76
|
+
console.log(`[${id}]`, msg);
|
|
77
|
+
$log.textContent += msg + '\n';
|
|
78
|
+
$log.scrollTop = $log.scrollHeight;
|
|
79
|
+
},
|
|
80
|
+
pass: () => { $box.classList.add('success'); return 'PASS'; },
|
|
81
|
+
fail: () => { $box.classList.add('fail'); return 'FAIL'; },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const results = {};
|
|
86
|
+
|
|
87
|
+
// REST baseline
|
|
88
|
+
async function testRest(uid, token) {
|
|
89
|
+
const t = makeLogger('rest');
|
|
90
|
+
try {
|
|
91
|
+
const url = `https://firestore.googleapis.com/v1/projects/${config.projectId}/databases/(default)/documents/users/${uid}`;
|
|
92
|
+
t.log('Fetching via REST...');
|
|
93
|
+
const res = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } });
|
|
94
|
+
t.log(`Status: ${res.status}`);
|
|
95
|
+
if (res.ok) {
|
|
96
|
+
const data = await res.json();
|
|
97
|
+
t.log('SUCCESS');
|
|
98
|
+
results['REST'] = t.pass();
|
|
99
|
+
} else {
|
|
100
|
+
const text = await res.text();
|
|
101
|
+
t.log('ERROR: ' + text.substring(0, 150));
|
|
102
|
+
results['REST'] = t.fail();
|
|
103
|
+
}
|
|
104
|
+
} catch (e) { t.log('ERROR: ' + e.message); results['REST'] = t.fail(); }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Generic version test using dynamic import
|
|
108
|
+
async function testVersion(id, version, uid, useLP) {
|
|
109
|
+
const t = makeLogger(id);
|
|
110
|
+
try {
|
|
111
|
+
const label = useLP ? 'initializeFirestore+forceLongPolling' : 'getFirestore';
|
|
112
|
+
t.log(`Loading Firebase ${version}...`);
|
|
113
|
+
|
|
114
|
+
const appMod = await import(`https://www.gstatic.com/firebasejs/${version}/firebase-app.js`);
|
|
115
|
+
const fsMod = await import(`https://www.gstatic.com/firebasejs/${version}/firebase-firestore.js`);
|
|
116
|
+
|
|
117
|
+
const appName = id;
|
|
118
|
+
let app;
|
|
119
|
+
try {
|
|
120
|
+
app = appMod.initializeApp(config, appName);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
app = appMod.getApp(appName);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
let db;
|
|
126
|
+
if (useLP) {
|
|
127
|
+
t.log(`initializeFirestore + experimentalForceLongPolling...`);
|
|
128
|
+
db = fsMod.initializeFirestore(app, { experimentalForceLongPolling: true });
|
|
129
|
+
} else {
|
|
130
|
+
t.log(`getFirestore...`);
|
|
131
|
+
db = fsMod.getFirestore(app);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
t.log(`Fetching users/${uid} (10s timeout)...`);
|
|
135
|
+
|
|
136
|
+
// Race against a timeout
|
|
137
|
+
const fetchPromise = fsMod.getDoc(fsMod.doc(db, 'users', uid));
|
|
138
|
+
const timeoutPromise = new Promise((_, reject) =>
|
|
139
|
+
setTimeout(() => reject(new Error('TIMEOUT after 10s')), 10000)
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const snap = await Promise.race([fetchPromise, timeoutPromise]);
|
|
143
|
+
|
|
144
|
+
if (snap.exists()) {
|
|
145
|
+
t.log('SUCCESS: ' + JSON.stringify(snap.data()).substring(0, 100));
|
|
146
|
+
results[id] = t.pass();
|
|
147
|
+
} else {
|
|
148
|
+
t.log('Doc not found');
|
|
149
|
+
results[id] = t.fail();
|
|
150
|
+
}
|
|
151
|
+
} catch (e) {
|
|
152
|
+
const code = e.code || '';
|
|
153
|
+
if (code === 'permission-denied') {
|
|
154
|
+
t.log('TRANSPORT OK (permission-denied = network works, just no auth)');
|
|
155
|
+
results[id] = t.pass();
|
|
156
|
+
} else {
|
|
157
|
+
t.log(`ERROR: ${code} ${e.message}`);
|
|
158
|
+
results[id] = t.fail();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Main
|
|
164
|
+
const defaultApp = initializeApp(config);
|
|
165
|
+
const auth = getAuth(defaultApp);
|
|
166
|
+
|
|
167
|
+
onAuthStateChanged(auth, async (user) => {
|
|
168
|
+
if (!user) {
|
|
169
|
+
document.getElementById('status').textContent = 'Not signed in. Sign in at /dashboard first.';
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const uid = user.uid;
|
|
174
|
+
const token = await user.getIdToken();
|
|
175
|
+
document.getElementById('status').textContent = `Signed in: ${uid}. Running tests...`;
|
|
176
|
+
|
|
177
|
+
// Run REST first
|
|
178
|
+
await testRest(uid, token);
|
|
179
|
+
|
|
180
|
+
// Run version tests (sequential to avoid interference)
|
|
181
|
+
await testVersion('v10', '10.14.0', uid, false);
|
|
182
|
+
await testVersion('v10lp', '10.14.0', uid, true);
|
|
183
|
+
await testVersion('v11', '11.0.0', uid, false);
|
|
184
|
+
await testVersion('v11lp', '11.0.0', uid, true);
|
|
185
|
+
await testVersion('v12_0', '12.0.0', uid, false);
|
|
186
|
+
await testVersion('v12_0lp', '12.0.0', uid, true);
|
|
187
|
+
await testVersion('v12_11', '12.11.0', uid, false);
|
|
188
|
+
await testVersion('v12_11lp', '12.11.0', uid, true);
|
|
189
|
+
|
|
190
|
+
// Show summary
|
|
191
|
+
document.getElementById('status').textContent = 'All tests complete.';
|
|
192
|
+
const $summary = document.getElementById('summary');
|
|
193
|
+
$summary.style.display = 'block';
|
|
194
|
+
$summary.innerHTML = '<h2 style="margin-bottom:8px">Results Summary</h2>' +
|
|
195
|
+
Object.entries(results).map(([k, v]) =>
|
|
196
|
+
`<div style="color:${v === 'PASS' ? '#0f0' : '#f00'}">${v}: ${k}</div>`
|
|
197
|
+
).join('');
|
|
198
|
+
});
|
|
199
|
+
</script>
|
|
200
|
+
</body>
|
|
201
|
+
</html>
|
|
@@ -62,7 +62,7 @@ const watchInput = [
|
|
|
62
62
|
`${rootPathPackage}/dist/service-worker.js`,
|
|
63
63
|
|
|
64
64
|
// So we can watch for changes while we're developing web-manager
|
|
65
|
-
`${rootPathPackage}/../web-manager/
|
|
65
|
+
`${rootPathPackage}/../web-manager/dist`,
|
|
66
66
|
];
|
|
67
67
|
|
|
68
68
|
// Files to copy directly without webpack processing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-jekyll-manager",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Ultimate Jekyll dependency manager",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"sass": "^1.98.0",
|
|
105
105
|
"spellchecker": "^3.7.1",
|
|
106
106
|
"through2": "^4.0.2",
|
|
107
|
-
"web-manager": "^4.1.
|
|
107
|
+
"web-manager": "^4.1.31",
|
|
108
108
|
"webpack": "^5.105.4",
|
|
109
109
|
"wonderful-fetch": "^2.0.4",
|
|
110
110
|
"wonderful-version": "^1.3.2",
|