jskelet 0.4.2 → 0.4.3
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 +18 -0
- package/package.json +1 -1
- package/src/build/paths.mjs +13 -3
- package/src/build/tasks/css.mjs +3 -1
- package/src/build/tasks/icons.mjs +1 -2
- package/src/server/create-app.js +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -24,9 +24,19 @@ one is listed under a **Breaking** heading.
|
|
|
24
24
|
query time and a default 24h lookback became `1d` plus network delay — Free
|
|
25
25
|
zones reject anything wider than one day. Both ends are now pinned from the
|
|
26
26
|
same clock (`datetime_leq` included).
|
|
27
|
+
- Missing `/assets/*` responses no longer keep the long-lived `immutable`
|
|
28
|
+
Cache-Control that `headersMiddleware` stamps for static prefixes. A deploy
|
|
29
|
+
race (prune-before-write) could 404 a hashed CSS URL for a moment; a CDN then
|
|
30
|
+
cached that HTML 404 for a year and browsers refused it as a stylesheet
|
|
31
|
+
(`MIME type 'text/html'`). Catch-all and `notFound` handlers now set
|
|
32
|
+
`Cache-Control: no-store`. CSS and sprite builds write the new file before
|
|
33
|
+
pruning older hashes so the same content hash never has a gap.
|
|
27
34
|
|
|
28
35
|
### Added
|
|
29
36
|
|
|
37
|
+
- Marketing homepage ops storyboard: Redis L2, `/_jskelet/admin` panel mock and
|
|
38
|
+
Cloudflare purge flow, with tabbed visual scenes animated by the vanilla
|
|
39
|
+
`motion` API (Framer Motion’s non-React package) via an `ops-story` island.
|
|
30
40
|
- VS Code / Cursor extension skeleton under `extensions/vscode-jsk`: `.jsk`
|
|
31
41
|
language id, TextMate highlighting (`{{ }}` / `{#if}` / `{#each}` /
|
|
32
42
|
components), language config, and snippets. Install from that folder or
|
|
@@ -144,6 +154,14 @@ one is listed under a **Breaking** heading.
|
|
|
144
154
|
island contract, path-based `invalidateHtmlCache` (replacing the outdated
|
|
145
155
|
“no targeted invalidation” claim), Redis / admin / data-cache callouts, and
|
|
146
156
|
bilingual doc links under `docs/` and `docs/en/`.
|
|
157
|
+
- Marketing compare live latency demo now measures two same-sized fragments
|
|
158
|
+
(cached vs `no-store`) with an explicit 80 ms simulated upstream inside the
|
|
159
|
+
shared producer — a hit skips that wait so the gap is visible even when RTT
|
|
160
|
+
dominates the wall clock. The island prints transferred bytes, Server-Timing
|
|
161
|
+
`produce` duration, and a View Source section contrasts `__NEXT_DATA__`
|
|
162
|
+
payload tax with plain JSkelet HTML. The measured-weight block also shows an
|
|
163
|
+
estimated Next.js App Router first-load breakdown beside this site’s real
|
|
164
|
+
gzip totals (clearly labelled estimate, not a build from this repo).
|
|
147
165
|
- Duplicate component named exports (or the same PascalCase tag in two files)
|
|
148
166
|
now **fail** at build and at server startup instead of warning and letting
|
|
149
167
|
the second definition win. Overwriting `components/index.js` barrel exports
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jskelet",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "A framework that feels like no framework: Express 5 + build-time .jsk (or EJS) SSR, vanilla JS islands, Tailwind v4 and an in-process HTML TTL cache.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/build/paths.mjs
CHANGED
|
@@ -131,13 +131,23 @@ export function patchManifest(key, url) {
|
|
|
131
131
|
/**
|
|
132
132
|
* Eski hash'li çıktıları temizler.
|
|
133
133
|
*
|
|
134
|
+
* Yeni dosya **önce** yazılmalı, prune sonra gelmeli: aynı içerik aynı hash'i
|
|
135
|
+
* üretir ve önce silmek `/assets/app.<hash>.css` için kısa bir 404 penceresi
|
|
136
|
+
* açar. CDN o 404'ü `immutable` ile saklarsa (eski headersMiddleware
|
|
137
|
+
* davranışı) tarayıcı bir yıl boyunca stilsiz kalır.
|
|
138
|
+
*
|
|
134
139
|
* @param {string[]} prefixes
|
|
140
|
+
* @param {{ keep?: string[] }} [options] Korunacak dosya adları (ör. yeni
|
|
141
|
+
* yazılan `app.<hash>.css`); `.br` / `.gz` sonekleri de eşleşir.
|
|
135
142
|
*/
|
|
136
|
-
export function pruneAssets(prefixes) {
|
|
143
|
+
export function pruneAssets(prefixes, { keep = [] } = {}) {
|
|
137
144
|
if (!fs.existsSync(paths.assets)) return;
|
|
145
|
+
|
|
138
146
|
for (const file of fs.readdirSync(paths.assets)) {
|
|
139
|
-
if (prefixes.some((prefix) => file.startsWith(prefix)))
|
|
140
|
-
|
|
147
|
+
if (!prefixes.some((prefix) => file.startsWith(prefix))) continue;
|
|
148
|
+
if (keep.some((name) => file === name || file.startsWith(`${name}.`))) {
|
|
149
|
+
continue;
|
|
141
150
|
}
|
|
151
|
+
fs.rmSync(path.join(paths.assets, file), { force: true });
|
|
142
152
|
}
|
|
143
153
|
}
|
package/src/build/tasks/css.mjs
CHANGED
|
@@ -64,9 +64,11 @@ export async function buildCss(config, { watch = false } = {}) {
|
|
|
64
64
|
|
|
65
65
|
const run = async () => {
|
|
66
66
|
const started = Date.now();
|
|
67
|
-
pruneAssets(["app."]);
|
|
68
67
|
const css = await compile(input);
|
|
68
|
+
// Önce yaz, sonra eski hash'leri sil — aynı hash'e düşen içerikte 404
|
|
69
|
+
// penceresi olmasın (CDN immutable zehirlenmesi).
|
|
69
70
|
const url = writeAsset("app.css", css);
|
|
71
|
+
pruneAssets(["app."], { keep: [path.basename(url)] });
|
|
70
72
|
return { url, bytes: Buffer.byteLength(css), elapsed: Date.now() - started };
|
|
71
73
|
};
|
|
72
74
|
|
|
@@ -192,8 +192,6 @@ export async function buildIconSprite(config) {
|
|
|
192
192
|
config.icons?.scan ?? ["views", "client", "routes", "lib", "features", "shared"]
|
|
193
193
|
).map((dir) => path.resolve(config.root, dir));
|
|
194
194
|
|
|
195
|
-
pruneAssets(["sprite."]);
|
|
196
|
-
|
|
197
195
|
const used = [...scanUsedIcons(scanDirs)].sort();
|
|
198
196
|
const symbols = [];
|
|
199
197
|
const missing = [];
|
|
@@ -214,6 +212,7 @@ export async function buildIconSprite(config) {
|
|
|
214
212
|
|
|
215
213
|
const sprite = `<svg xmlns="http://www.w3.org/2000/svg" style="display:none">${symbols.join("")}</svg>`;
|
|
216
214
|
const url = writeAsset("sprite.svg", sprite);
|
|
215
|
+
pruneAssets(["sprite."], { keep: [path.basename(url)] });
|
|
217
216
|
|
|
218
217
|
log.detail(`${symbols.length} symbols`);
|
|
219
218
|
if (missing.length) {
|
package/src/server/create-app.js
CHANGED
|
@@ -148,7 +148,10 @@ export async function createApp(options = {}) {
|
|
|
148
148
|
|
|
149
149
|
app.use(async (req, res, next) => {
|
|
150
150
|
try {
|
|
151
|
-
|
|
151
|
+
// headersMiddleware `/assets/*` için immutable basmış olabilir; eksik bir
|
|
152
|
+
// hash'li dosyanın 404'ü CDN'de bir yıl zehirlenmesin.
|
|
153
|
+
res.status(404).setHeader("Cache-Control", "no-store");
|
|
154
|
+
res.type("html").send(await renderNotFound());
|
|
152
155
|
} catch (error) {
|
|
153
156
|
next(error);
|
|
154
157
|
}
|
|
@@ -168,7 +171,8 @@ export async function createApp(options = {}) {
|
|
|
168
171
|
}
|
|
169
172
|
|
|
170
173
|
if (isNotFoundError(error)) {
|
|
171
|
-
res.status(404).
|
|
174
|
+
res.status(404).setHeader("Cache-Control", "no-store");
|
|
175
|
+
res.type("html").send(await renderNotFound());
|
|
172
176
|
return;
|
|
173
177
|
}
|
|
174
178
|
|