| author | |
| committer | |
| log | 907e9083b681531d3be2f8ba0cafc63dede6e61d |
| tree | f06c4b574f11f33e55ec6bd15afc672165a82d17 |
| parent | e5053a06d43a46f83b18f1cc2acc436024a8bcb6 |
| signature | Signed by SSH key SHA256:xbd+BjjhyBfwk7GVoURf9Yx0gzDerHbvYv7SddNWmAs |
11 files changed, 593 insertions(+), 260 deletions(-)
framework/css.ts+1-1| ... | ... | @@ -81,7 +81,7 @@ export async function bundleCssFiles( |
| 81 | 81 | const build = await esbuild.build({ |
| 82 | 82 | bundle: true, |
| 83 | 83 | entryPoints: ["$input$"], |
| 84 | external: ["*.woff2", "*.ttf", "*.png", "*.jpeg"], | |
| 84 | external: ["*.woff2", "*.eot", "*.eot?#iefix", "*.ttf", "*.png", "*.jpeg"], | |
| 85 | 85 | metafile: true, |
| 86 | 86 | minify: !dev, |
| 87 | 87 | plugins: [ |
framework/font.ts+131-172| ... | ... | @@ -10,15 +10,6 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { |
| 10 | 10 | }); |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | function getHash(input: string, args: string[]) { | |
| 14 | return crypto | |
| 15 | .createHash("sha256") | |
| 16 | .update(`${input}${args.join(" ")}`) | |
| 17 | .digest("hex") | |
| 18 | .slice(0, 16) | |
| 19 | .toLowerCase(); | |
| 20 | } | |
| 21 | ||
| 22 | 13 | function getVariationAxes(vars: sg.FontVars) { |
| 23 | 14 | return Object.fromEntries( |
| 24 | 15 | Object.entries(vars).map(([key, value]) => [ |
| ... | ... | @@ -31,36 +22,56 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { |
| 31 | 22 | await Promise.all(fonts.map((font) => |
| 32 | 23 | incr.work(node, async (io, font) => { |
| 33 | 24 | using node = io.start(font.name); |
| 34 | const baseFile = await fetchFont(font.name, font.sources); | |
| 35 | const baseBuffer = await fs.readFile(baseFile); | |
| 25 | const baseFile = await fetchFont(node, font.name, font.sources); | |
| 26 | await using baseBuffer = async.deferred(fs.readFile(baseFile)); | |
| 36 | 27 | await Promise.all(font.subsets.map(async (subset) => { |
| 37 | 28 | const args = getArgs(subset.vars ?? {}); |
| 38 | const subsetText = fontRangesToText( | |
| 39 | Array.isArray(subset.unicodes) ? subset.unicodes : [subset.unicodes], | |
| 40 | ); | |
| 41 | using _ = node.start(args.join(" ")); | |
| 42 | const woff = await subsetFontBuffer(baseBuffer, subsetText, { | |
| 43 | targetFormat: "woff2", | |
| 44 | variationAxes: subset.vars | |
| 45 | ? getVariationAxes(subset.vars) | |
| 46 | : undefined, | |
| 47 | sourceName: baseFile, | |
| 48 | assetName: subset.asset.replace("[ext]", "woff2"), | |
| 49 | }); | |
| 29 | const unicodes = Array.isArray(subset.unicodes) ? subset.unicodes : [subset.unicodes]; | |
| 30 | const charCount = unicodes.reduce<number>((count, range) => { | |
| 31 | if (typeof range === "string") return count + [...range].length; | |
| 32 | if (typeof range === "number") return count + 1; | |
| 33 | return count + (range.end - range.start + 1); | |
| 34 | }, 0); | |
| 35 | const variationAxes = subset.vars ? getVariationAxes(subset.vars) : undefined; | |
| 36 | using _ = node.start("subset: " + [...args, `chars=${charCount}`].join(" ")); | |
| 37 | const [woff, ttf] = await Promise.all([ | |
| 38 | subsetFontBuffer(await baseBuffer, unicodes, { | |
| 39 | targetFormat: "woff2", | |
| 40 | variationAxes, | |
| 41 | sourceName: baseFile, | |
| 42 | assetName: subset.asset.replace("[ext]", "woff2"), | |
| 43 | }), | |
| 44 | subsetFontBuffer(await baseBuffer, unicodes, { | |
| 45 | targetFormat: "sfnt", | |
| 46 | variationAxes, | |
| 47 | sourceName: baseFile, | |
| 48 | assetName: subset.asset.replace("[ext]", "ttf"), | |
| 49 | }), | |
| 50 | ]); | |
| 51 | const eot = ttf2eot(ttf); | |
| 50 | 52 | await io.writeAsset({ |
| 51 | 53 | pathname: subset.asset.replace("[ext]", "woff2"), |
| 52 | 54 | buffer: woff, |
| 53 | 55 | }); |
| 56 | await io.writeAsset({ | |
| 57 | pathname: subset.asset.replace("[ext]", "ttf"), | |
| 58 | buffer: ttf, | |
| 59 | }); | |
| 60 | await io.writeAsset({ | |
| 61 | pathname: subset.asset.replace("[ext]", "eot"), | |
| 62 | buffer: eot, | |
| 63 | }); | |
| 54 | 64 | })); |
| 55 | 65 | }, font) |
| 56 | 66 | )); |
| 57 | 67 | } |
| 58 | 68 | |
| 59 | export async function fetchFont(name: string, sources: string[]) { | |
| 69 | export async function fetchFont(ref: progress.Ref, name: string, sources: string[]) { | |
| 70 | using _ = ref.start("fetch"); | |
| 60 | 71 | const errs = []; |
| 61 | 72 | |
| 62 | 73 | for (const source of sources) { |
| 63 | const cacheName = path.join("", name + path.extname(source)); | |
| 74 | const cacheName = name + path.extname(source); | |
| 64 | 75 | if (fs.existsSync(cacheName)) return cacheName; |
| 65 | 76 | |
| 66 | 77 | if (source.startsWith("https://")) { |
| ... | ... | @@ -106,11 +117,9 @@ export async function fetchFont(name: string, sources: string[]) { |
| 106 | 117 | path.posix.join(home, ".local/share/fonts"), |
| 107 | 118 | ]; |
| 108 | 119 | for (const base of bases) { |
| 109 | const found = fs.readDirRecOptionalSync(base) | |
| 110 | .find((file) => path.basename(file) === source); | |
| 111 | if (found) { | |
| 112 | return path.join(base, found); | |
| 113 | } | |
| 120 | const files = fs.readDirRecOptionalSync(base); | |
| 121 | const found = files.find((file) => path.basename(file) === source); | |
| 122 | if (found) return path.join(base, found); | |
| 114 | 123 | } |
| 115 | 124 | errs.push( |
| 116 | 125 | new Error( |
| ... | ... | @@ -123,46 +132,20 @@ export async function fetchFont(name: string, sources: string[]) { |
| 123 | 132 | throw new AggregateError(errs, `Failed to fetch the ${name} font`); |
| 124 | 133 | } |
| 125 | 134 | |
| 126 | function fontRangesToText(ranges: sg.FontRange[]): string { | |
| 127 | const chars = new Set<number>(); | |
| 128 | ranges.forEach((range) => { | |
| 129 | if (typeof range === "string") { | |
| 130 | for (const char of range) { | |
| 131 | chars.add(char.codePointAt(0) || 0); | |
| 132 | } | |
| 133 | } else if (typeof range === "number") { | |
| 134 | chars.add(range); | |
| 135 | } else { | |
| 136 | for (let cp = range.start; cp <= range.end; cp++) chars.add(cp); | |
| 137 | } | |
| 138 | }); | |
| 139 | return Array.from(chars) | |
| 140 | .sort((a, b) => a - b) | |
| 141 | .map((cp) => String.fromCodePoint(cp)) | |
| 142 | .join(""); | |
| 143 | } | |
| 144 | ||
| 145 | 135 | function normalizeAxisTag(tag: string) { |
| 146 | 136 | const lower = tag.toLowerCase(); |
| 147 | 137 | return lower === "wght" || lower === "slnt" ? lower : tag; |
| 148 | 138 | } |
| 149 | 139 | |
| 150 | function detectFontFormat(buffer: Uint8Array): "sfnt" | "woff2" { | |
| 140 | async function convertToSfnt(buffer: Uint8Array) { | |
| 151 | 141 | const signature = String.fromCharCode(...buffer.subarray(0, 4)); |
| 152 | if (signature === "wOF2") return "woff2"; | |
| 153 | 142 | if ( |
| 154 | 143 | signature === "true" |
| 155 | 144 | || signature === "OTTO" |
| 156 | 145 | || signature === "\x00\x01\x00\x00" |
| 157 | 146 | ) { |
| 158 | return "sfnt"; | |
| 147 | return buffer; | |
| 159 | 148 | } |
| 160 | throw new Error(`Unsupported font format signature ${JSON.stringify(signature)}`); | |
| 161 | } | |
| 162 | ||
| 163 | async function convertToSfnt(buffer: Uint8Array) { | |
| 164 | const format = detectFontFormat(buffer); | |
| 165 | if (format === "sfnt") return buffer; | |
| 166 | 149 | return new Uint8Array(await wawoff2.decompress(buffer)); |
| 167 | 150 | } |
| 168 | 151 | |
| ... | ... | @@ -174,18 +157,29 @@ async function convertFromSfnt( |
| 174 | 157 | return new Uint8Array(await wawoff2.compress(buffer)); |
| 175 | 158 | } |
| 176 | 159 | |
| 177 | function getSubsetContext(opt: { | |
| 178 | sourceName?: string; | |
| 179 | assetName?: string; | |
| 180 | variationAxes?: VariationAxes; | |
| 181 | targetFormat: "sfnt" | "woff2"; | |
| 182 | text: string; | |
| 183 | }) { | |
| 184 | const fields = [ | |
| 160 | const subsetQueue = new queue.PriorityQueue(1); | |
| 161 | const subsetFontBuffer = subsetQueue.wrap(subsetFontBufferUnqueued); | |
| 162 | ||
| 163 | async function subsetFontBufferUnqueued( | |
| 164 | originalFont: Uint8Array, | |
| 165 | unicodes: sg.FontRange[], | |
| 166 | opt: { | |
| 167 | targetFormat: "sfnt" | "woff2"; | |
| 168 | variationAxes?: VariationAxes; | |
| 169 | sourceName?: string; | |
| 170 | assetName?: string; | |
| 171 | }, | |
| 172 | ) { | |
| 173 | const charCount = unicodes.reduce<number>((count, range) => { | |
| 174 | if (typeof range === "string") return count + [...range].length; | |
| 175 | if (typeof range === "number") return count + 1; | |
| 176 | return count + (range.end - range.start + 1); | |
| 177 | }, 0); | |
| 178 | const ctx = [ | |
| 179 | `${charCount} chars`, | |
| 185 | 180 | opt.sourceName ? `source=${opt.sourceName}` : "", |
| 186 | 181 | opt.assetName ? `asset=${opt.assetName}` : "", |
| 187 | 182 | `target=${opt.targetFormat}`, |
| 188 | `textLength=${opt.text.length}`, | |
| 189 | 183 | opt.variationAxes |
| 190 | 184 | ? `axes=${ |
| 191 | 185 | Object.entries(opt.variationAxes).map(([key, value]) => |
| ... | ... | @@ -195,120 +189,90 @@ function getSubsetContext(opt: { |
| 195 | 189 | ).join(",") |
| 196 | 190 | }` |
| 197 | 191 | : "", |
| 198 | ].filter(Boolean); | |
| 199 | return fields.join(" "); | |
| 200 | } | |
| 192 | ].filter(Boolean).join(" "); | |
| 201 | 193 | |
| 202 | async function subsetFontBuffer( | |
| 203 | originalFont: Uint8Array, | |
| 204 | text: string, | |
| 205 | opt: { | |
| 206 | targetFormat: "sfnt" | "woff2"; | |
| 207 | variationAxes?: VariationAxes; | |
| 208 | sourceName?: string; | |
| 209 | assetName?: string; | |
| 210 | }, | |
| 211 | ) { | |
| 212 | return await subsetFontQueue(async () => { | |
| 213 | const ctx = getSubsetContext({ ...opt, text }); | |
| 214 | if (typeof text !== "string") { | |
| 215 | throw new Error(`Subset text must be a string. ${ctx}`); | |
| 216 | } | |
| 194 | const { harfbuzzJsWasm, getHeapU8 } = await loadAndInitializeHarfbuzz(); | |
| 195 | const sfntFont = await convertToSfnt(originalFont); | |
| 217 | 196 | |
| 218 | const { harfbuzzJsWasm, getHeapU8 } = await loadAndInitializeHarfbuzz(); | |
| 219 | const sfntFont = await convertToSfnt(originalFont); | |
| 197 | const input = harfbuzzJsWasm.hb_subset_input_create_or_fail(); | |
| 198 | if (input === 0) throw new Error(`Failed to create HarfBuzz subset input. ${ctx}`); | |
| 220 | 199 | |
| 221 | const input = harfbuzzJsWasm.hb_subset_input_create_or_fail(); | |
| 222 | if (input === 0) { | |
| 223 | throw new Error(`Failed to create HarfBuzz subset input. ${ctx}`); | |
| 224 | } | |
| 200 | const fontBuffer = harfbuzzJsWasm.malloc(sfntFont.byteLength); | |
| 201 | getHeapU8().set(new Uint8Array(sfntFont), fontBuffer); | |
| 225 | 202 | |
| 226 | const fontBuffer = harfbuzzJsWasm.malloc(sfntFont.byteLength); | |
| 227 | getHeapU8().set(new Uint8Array(sfntFont), fontBuffer); | |
| 203 | const blob = harfbuzzJsWasm.hb_blob_create( | |
| 204 | fontBuffer, | |
| 205 | sfntFont.byteLength, | |
| 206 | 2, | |
| 207 | 0, | |
| 208 | 0, | |
| 209 | ); | |
| 210 | const face = harfbuzzJsWasm.hb_face_create(blob, 0); | |
| 211 | harfbuzzJsWasm.hb_blob_destroy(blob); | |
| 228 | 212 | |
| 229 | const blob = harfbuzzJsWasm.hb_blob_create( | |
| 230 | fontBuffer, | |
| 231 | sfntFont.byteLength, | |
| 232 | 2, | |
| 233 | 0, | |
| 234 | 0, | |
| 235 | ); | |
| 236 | const face = harfbuzzJsWasm.hb_face_create(blob, 0); | |
| 237 | harfbuzzJsWasm.hb_blob_destroy(blob); | |
| 213 | try { | |
| 214 | const layoutFeatures = harfbuzzJsWasm.hb_subset_input_set(input, 6); | |
| 215 | ASSERT(layoutFeatures, `Failed to set layout features. ${ctx}`); | |
| 216 | harfbuzzJsWasm.hb_set_clear(layoutFeatures); | |
| 217 | harfbuzzJsWasm.hb_set_invert(layoutFeatures); | |
| 238 | 218 | |
| 239 | try { | |
| 240 | const layoutFeatures = harfbuzzJsWasm.hb_subset_input_set(input, 6); | |
| 241 | harfbuzzJsWasm.hb_set_clear(layoutFeatures); | |
| 242 | harfbuzzJsWasm.hb_set_invert(layoutFeatures); | |
| 243 | ||
| 244 | const inputUnicodes = harfbuzzJsWasm.hb_subset_input_unicode_set(input); | |
| 245 | for (const char of text) { | |
| 246 | harfbuzzJsWasm.hb_set_add(inputUnicodes, char.codePointAt(0) || 0); | |
| 219 | const inputUnicodes = harfbuzzJsWasm.hb_subset_input_unicode_set(input); | |
| 220 | for (const range of unicodes) { | |
| 221 | if (typeof range === "string") { | |
| 222 | for (const char of range) harfbuzzJsWasm.hb_set_add(inputUnicodes, char.codePointAt(0) || 0); | |
| 223 | } else if (typeof range === "number") { | |
| 224 | harfbuzzJsWasm.hb_set_add(inputUnicodes, range); | |
| 225 | } else { | |
| 226 | for (let cp = range.start; cp <= range.end; cp++) harfbuzzJsWasm.hb_set_add(inputUnicodes, cp); | |
| 247 | 227 | } |
| 228 | } | |
| 248 | 229 | |
| 249 | if (opt.variationAxes) { | |
| 250 | for (const [axisName, value] of Object.entries(opt.variationAxes)) { | |
| 251 | if (typeof value === "number") { | |
| 252 | if ( | |
| 253 | !harfbuzzJsWasm.hb_subset_input_pin_axis_location( | |
| 254 | input, | |
| 255 | face, | |
| 256 | hbTag(axisName), | |
| 257 | value, | |
| 258 | ) | |
| 259 | ) { | |
| 260 | throw new Error( | |
| 261 | `Failed to pin variation axis ${axisName}=${value}. ${ctx}`, | |
| 262 | ); | |
| 263 | } | |
| 264 | } else if ( | |
| 265 | !harfbuzzJsWasm.hb_subset_input_set_axis_range( | |
| 266 | input, | |
| 267 | face, | |
| 268 | hbTag(axisName), | |
| 269 | value.min, | |
| 270 | value.max, | |
| 271 | value.default ?? Number.NaN, | |
| 272 | ) | |
| 273 | ) { | |
| 274 | throw new Error( | |
| 275 | `Failed to set variation axis range ${axisName}=[${value.min}, ${value.max}]${ | |
| 276 | value.default == null ? "" : ` default=${value.default}` | |
| 277 | }. ${ctx}`, | |
| 278 | ); | |
| 279 | } | |
| 230 | if (opt.variationAxes) { | |
| 231 | for (const [axisName, value] of Object.entries(opt.variationAxes)) { | |
| 232 | if (typeof value === "number") { | |
| 233 | ASSERT(harfbuzzJsWasm.hb_subset_input_pin_axis_location( | |
| 234 | input, | |
| 235 | face, | |
| 236 | hbTag(axisName), | |
| 237 | value, | |
| 238 | )); | |
| 239 | } else { | |
| 240 | ASSERT(harfbuzzJsWasm.hb_subset_input_set_axis_range( | |
| 241 | input, | |
| 242 | face, | |
| 243 | hbTag(axisName), | |
| 244 | value.min, | |
| 245 | value.max, | |
| 246 | value.default ?? Number.NaN, | |
| 247 | )); | |
| 280 | 248 | } |
| 281 | 249 | } |
| 250 | } | |
| 282 | 251 | |
| 283 | const subset = harfbuzzJsWasm.hb_subset_or_fail(face, input); | |
| 284 | if (subset === 0) { | |
| 285 | throw new Error(`HarfBuzz failed to subset font. ${ctx}`); | |
| 286 | } | |
| 252 | const subset = harfbuzzJsWasm.hb_subset_or_fail(face, input); | |
| 253 | ASSERT(subset, `HarfBuzz failed to subset font. ${ctx}`); | |
| 287 | 254 | |
| 255 | try { | |
| 256 | const result = harfbuzzJsWasm.hb_face_reference_blob(subset); | |
| 288 | 257 | try { |
| 289 | const result = harfbuzzJsWasm.hb_face_reference_blob(subset); | |
| 290 | try { | |
| 291 | const offset = harfbuzzJsWasm.hb_blob_get_data(result, 0); | |
| 292 | const subsetByteLength = harfbuzzJsWasm.hb_blob_get_length(result); | |
| 293 | if (subsetByteLength === 0) { | |
| 294 | throw new Error(`Subset font was empty. ${ctx}`); | |
| 295 | } | |
| 296 | const subsetFont = Uint8Array.from( | |
| 297 | getHeapU8().subarray(offset, offset + subsetByteLength), | |
| 298 | ); | |
| 299 | return await convertFromSfnt(subsetFont, opt.targetFormat); | |
| 300 | } finally { | |
| 301 | harfbuzzJsWasm.hb_blob_destroy(result); | |
| 302 | } | |
| 258 | const offset = harfbuzzJsWasm.hb_blob_get_data(result, 0); | |
| 259 | const subsetByteLength = harfbuzzJsWasm.hb_blob_get_length(result); | |
| 260 | ASSERT(subsetByteLength > 0, `Subset font was empty. ${ctx}`); | |
| 261 | const subsetFont = Uint8Array.from( | |
| 262 | getHeapU8().subarray(offset, offset + subsetByteLength), | |
| 263 | ); | |
| 264 | return await convertFromSfnt(subsetFont, opt.targetFormat); | |
| 303 | 265 | } finally { |
| 304 | harfbuzzJsWasm.hb_face_destroy(subset); | |
| 266 | harfbuzzJsWasm.hb_blob_destroy(result); | |
| 305 | 267 | } |
| 306 | 268 | } finally { |
| 307 | harfbuzzJsWasm.hb_subset_input_destroy(input); | |
| 308 | harfbuzzJsWasm.hb_face_destroy(face); | |
| 309 | harfbuzzJsWasm.free(fontBuffer); | |
| 269 | harfbuzzJsWasm.hb_face_destroy(subset); | |
| 310 | 270 | } |
| 311 | }); | |
| 271 | } finally { | |
| 272 | harfbuzzJsWasm.hb_subset_input_destroy(input); | |
| 273 | harfbuzzJsWasm.hb_face_destroy(face); | |
| 274 | harfbuzzJsWasm.free(fontBuffer); | |
| 275 | } | |
| 312 | 276 | } |
| 313 | 277 | |
| 314 | 278 | type VariationAxisRange = { min: number; max: number; default?: number }; |
| ... | ... | @@ -367,25 +331,20 @@ const loadAndInitializeHarfbuzz = async.once(async () => { |
| 367 | 331 | }; |
| 368 | 332 | }); |
| 369 | 333 | |
| 370 | let subsetFontLock = Promise.resolve(); | |
| 371 | function subsetFontQueue<T>(fn: () => Promise<T>) { | |
| 372 | const run = subsetFontLock.then(fn, fn); | |
| 373 | subsetFontLock = run.then(() => undefined, () => undefined); | |
| 374 | return run; | |
| 375 | } | |
| 376 | ||
| 377 | 334 | function hbTag(str: string) { |
| 378 | 335 | return str.split("").reduce((a, ch) => (a << 8) + ch.charCodeAt(0), 0); |
| 379 | 336 | } |
| 380 | 337 | |
| 381 | 338 | import * as sg from "#sitegen"; |
| 382 | 339 | import * as fs from "#sitegen/fs"; |
| 340 | import { ASSERT } from "@clo/lib/assert"; | |
| 383 | 341 | import * as async from "@clo/lib/async"; |
| 384 | 342 | import * as error from "@clo/lib/error"; |
| 385 | 343 | import * as progress from "@clo/lib/progress"; |
| 386 | import * as crypto from "node:crypto"; | |
| 344 | import * as queue from "@clo/lib/queue"; | |
| 387 | 345 | import { readFile } from "node:fs/promises"; |
| 388 | 346 | import * as os from "node:os"; |
| 389 | 347 | import * as path from "node:path"; |
| 390 | 348 | import * as wawoff2 from "wawoff2"; |
| 349 | import { ttf2eot } from "./font/ttf2eot.ts"; | |
| 391 | 350 | import * as incr from "./incremental.ts"; |
framework/font/ttf2eot.ts created+301| ... | ... | @@ -0,0 +1,301 @@ |
| 1 | /** | |
| 2 | * ESM port of https://github.com/fontello/ttf2eot | |
| 3 | * @module | |
| 4 | */ | |
| 5 | ||
| 6 | /** | |
| 7 | * Offsets in EOT file structure. Refer to EOTPrefix in OpenTypeUtilities.cpp | |
| 8 | */ | |
| 9 | const offsets = { | |
| 10 | length: 0, | |
| 11 | fontLength: 4, | |
| 12 | version: 8, | |
| 13 | charset: 26, | |
| 14 | magic: 34, | |
| 15 | fontPanose: 16, | |
| 16 | italic: 27, | |
| 17 | weight: 28, | |
| 18 | unicodeRange: 36, | |
| 19 | codepageRange: 52, | |
| 20 | checksumAdjustment: 60, | |
| 21 | }; | |
| 22 | ||
| 23 | /** | |
| 24 | * Offsets in different SFNT (TTF) structures. See OpenTypeUtilities.cpp | |
| 25 | */ | |
| 26 | const sfntOffsets = { | |
| 27 | // sfntHeader: | |
| 28 | numTables: 4, | |
| 29 | ||
| 30 | // TableDirectoryEntry | |
| 31 | tableTag: 0, | |
| 32 | tableOffset: 8, | |
| 33 | tableLength: 12, | |
| 34 | ||
| 35 | // OS2Table | |
| 36 | os2Weight: 4, | |
| 37 | os2FontPanose: 32, | |
| 38 | os2UnicodeRange: 42, | |
| 39 | os2FsSelection: 62, | |
| 40 | os2CodepageRange: 78, | |
| 41 | ||
| 42 | // headTable | |
| 43 | headChecksumAdjustment: 8, | |
| 44 | ||
| 45 | // nameTable | |
| 46 | nametableFormat: 0, | |
| 47 | nametableCount: 2, | |
| 48 | nametableStringOffset: 4, | |
| 49 | ||
| 50 | // nameRecord | |
| 51 | namePlatformId: 0, | |
| 52 | nameEncodingId: 2, | |
| 53 | nameLanguageId: 4, | |
| 54 | nameNameId: 6, | |
| 55 | nameLength: 8, | |
| 56 | nameOffset: 10, | |
| 57 | }; | |
| 58 | ||
| 59 | /** | |
| 60 | * Sizes of structures | |
| 61 | */ | |
| 62 | const sizeof = { | |
| 63 | sfntTableEntry: 16, | |
| 64 | sfntHeader: 12, | |
| 65 | sfntNametable: 6, | |
| 66 | sfntNametableEntry: 12, | |
| 67 | eotPrefix: 82, | |
| 68 | }; | |
| 69 | ||
| 70 | /** | |
| 71 | * Magic numbers | |
| 72 | */ | |
| 73 | const magic = { | |
| 74 | version: 0x00020001, | |
| 75 | magic: 0x504c, | |
| 76 | charset: 1, | |
| 77 | english: 0x0409, | |
| 78 | }; | |
| 79 | ||
| 80 | interface SfntTableEntry { | |
| 81 | tag: string; | |
| 82 | offset: number; | |
| 83 | length: number; | |
| 84 | } | |
| 85 | ||
| 86 | interface NameTable { | |
| 87 | format: number; | |
| 88 | count: number; | |
| 89 | stringOffset: number; | |
| 90 | } | |
| 91 | ||
| 92 | interface NameRecord { | |
| 93 | platformID: number; | |
| 94 | encodingID: number; | |
| 95 | languageID: number; | |
| 96 | nameID: number; | |
| 97 | length: number; | |
| 98 | offset: number; | |
| 99 | } | |
| 100 | ||
| 101 | function dataView(bytes: Uint8Array): DataView { | |
| 102 | return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); | |
| 103 | } | |
| 104 | ||
| 105 | function readUint8(bytes: Uint8Array, offset = 0): number { | |
| 106 | return dataView(bytes).getUint8(offset); | |
| 107 | } | |
| 108 | ||
| 109 | function readUint16BE(bytes: Uint8Array, offset = 0): number { | |
| 110 | return dataView(bytes).getUint16(offset, false); | |
| 111 | } | |
| 112 | ||
| 113 | function readUint32BE(bytes: Uint8Array, offset = 0): number { | |
| 114 | return dataView(bytes).getUint32(offset, false); | |
| 115 | } | |
| 116 | ||
| 117 | function writeUint8(bytes: Uint8Array, value: number, offset: number): void { | |
| 118 | dataView(bytes).setUint8(offset, value); | |
| 119 | } | |
| 120 | ||
| 121 | function writeUint16LE(bytes: Uint8Array, value: number, offset: number): void { | |
| 122 | dataView(bytes).setUint16(offset, value, true); | |
| 123 | } | |
| 124 | ||
| 125 | function writeUint32LE(bytes: Uint8Array, value: number, offset: number): void { | |
| 126 | dataView(bytes).setUint32(offset, value, true); | |
| 127 | } | |
| 128 | ||
| 129 | function concat(parts: Uint8Array[]): Uint8Array { | |
| 130 | let length = 0; | |
| 131 | for (const part of parts) length += part.length; | |
| 132 | ||
| 133 | const out = new Uint8Array(length); | |
| 134 | let offset = 0; | |
| 135 | for (const part of parts) { | |
| 136 | out.set(part, offset); | |
| 137 | offset += part.length; | |
| 138 | } | |
| 139 | return out; | |
| 140 | } | |
| 141 | ||
| 142 | function readTag(bytes: Uint8Array, offset: number): string { | |
| 143 | return String.fromCharCode( | |
| 144 | bytes[offset]!, | |
| 145 | bytes[offset + 1]!, | |
| 146 | bytes[offset + 2]!, | |
| 147 | bytes[offset + 3]!, | |
| 148 | ); | |
| 149 | } | |
| 150 | ||
| 151 | /** | |
| 152 | * Utility function to convert buffer of utf16be chars to buffer of utf16le | |
| 153 | * chars prefixed with length and suffixed with zero | |
| 154 | */ | |
| 155 | function strbuf(str: Uint8Array): Uint8Array { | |
| 156 | const arr = new Uint8Array(str.length + 4); | |
| 157 | ||
| 158 | writeUint16LE(arr, str.length, 0); | |
| 159 | ||
| 160 | for (let i = 0; i < str.length; i += 2) { | |
| 161 | writeUint16LE(arr, readUint16BE(str, i), i + 2); | |
| 162 | } | |
| 163 | ||
| 164 | writeUint16LE(arr, 0, arr.length - 2); | |
| 165 | ||
| 166 | return arr; | |
| 167 | } | |
| 168 | ||
| 169 | /** | |
| 170 | * Convert a TTF font -> EOT font for use in older Internet Explorer versions. | |
| 171 | * | |
| 172 | * NOTE: The font's FullName MUST begin with FamilyName. For example, if | |
| 173 | * FamilyName is `fontello`, then FullName should be `fontello regular` and so on. | |
| 174 | */ | |
| 175 | export function ttf2eot(arr: Uint8Array): Uint8Array { | |
| 176 | arr = arr.slice(); | |
| 177 | ||
| 178 | const out = new Uint8Array(sizeof.eotPrefix); | |
| 179 | ||
| 180 | writeUint32LE(out, arr.length, offsets.fontLength); | |
| 181 | writeUint32LE(out, magic.version, offsets.version); | |
| 182 | writeUint8(out, magic.charset, offsets.charset); | |
| 183 | writeUint16LE(out, magic.magic, offsets.magic); | |
| 184 | ||
| 185 | let familyName: Uint8Array = new Uint8Array([0]); | |
| 186 | let subfamilyName: Uint8Array = new Uint8Array([0]); | |
| 187 | let fullName: Uint8Array = new Uint8Array([0]); | |
| 188 | let versionString: Uint8Array = new Uint8Array([0]); | |
| 189 | ||
| 190 | let haveOs2 = false; | |
| 191 | let haveName = false; | |
| 192 | let haveHead = false; | |
| 193 | ||
| 194 | const numTables = readUint16BE(arr, sfntOffsets.numTables); | |
| 195 | ||
| 196 | for (let i = 0; i < numTables; ++i) { | |
| 197 | const data = arr.subarray(sizeof.sfntHeader + i * sizeof.sfntTableEntry); | |
| 198 | const tableEntry: SfntTableEntry = { | |
| 199 | tag: readTag(data, sfntOffsets.tableTag), | |
| 200 | offset: readUint32BE(data, sfntOffsets.tableOffset), | |
| 201 | length: readUint32BE(data, sfntOffsets.tableLength), | |
| 202 | }; | |
| 203 | ||
| 204 | const table = arr.subarray(tableEntry.offset, tableEntry.offset + tableEntry.length); | |
| 205 | ||
| 206 | if (tableEntry.tag === "OS/2") { | |
| 207 | haveOs2 = true; | |
| 208 | const os2Version = readUint16BE(table); | |
| 209 | ||
| 210 | for (let j = 0; j < 10; ++j) { | |
| 211 | writeUint8(out, readUint8(table, sfntOffsets.os2FontPanose + j), offsets.fontPanose + j); | |
| 212 | } | |
| 213 | ||
| 214 | writeUint8(out, readUint16BE(table, sfntOffsets.os2FsSelection) & 0x01, offsets.italic); | |
| 215 | writeUint32LE(out, readUint16BE(table, sfntOffsets.os2Weight), offsets.weight); | |
| 216 | ||
| 217 | for (let j = 0; j < 4; ++j) { | |
| 218 | writeUint32LE( | |
| 219 | out, | |
| 220 | readUint32BE(table, sfntOffsets.os2UnicodeRange + j * 4), | |
| 221 | offsets.unicodeRange + j * 4, | |
| 222 | ); | |
| 223 | } | |
| 224 | ||
| 225 | if (os2Version >= 1) { | |
| 226 | for (let j = 0; j < 2; ++j) { | |
| 227 | writeUint32LE( | |
| 228 | out, | |
| 229 | readUint32BE(table, sfntOffsets.os2CodepageRange + j * 4), | |
| 230 | offsets.codepageRange + j * 4, | |
| 231 | ); | |
| 232 | } | |
| 233 | } | |
| 234 | } else if (tableEntry.tag === "head") { | |
| 235 | haveHead = true; | |
| 236 | writeUint32LE(out, readUint32BE(table, sfntOffsets.headChecksumAdjustment), offsets.checksumAdjustment); | |
| 237 | } else if (tableEntry.tag === "name") { | |
| 238 | haveName = true; | |
| 239 | ||
| 240 | const nameTable: NameTable = { | |
| 241 | format: readUint16BE(table, sfntOffsets.nametableFormat), | |
| 242 | count: readUint16BE(table, sfntOffsets.nametableCount), | |
| 243 | stringOffset: readUint16BE(table, sfntOffsets.nametableStringOffset), | |
| 244 | }; | |
| 245 | ||
| 246 | for (let j = 0; j < nameTable.count; ++j) { | |
| 247 | const nameRecordBytes = table.subarray(sizeof.sfntNametable + j * sizeof.sfntNametableEntry); | |
| 248 | const name: NameRecord = { | |
| 249 | platformID: readUint16BE(nameRecordBytes, sfntOffsets.namePlatformId), | |
| 250 | encodingID: readUint16BE(nameRecordBytes, sfntOffsets.nameEncodingId), | |
| 251 | languageID: readUint16BE(nameRecordBytes, sfntOffsets.nameLanguageId), | |
| 252 | nameID: readUint16BE(nameRecordBytes, sfntOffsets.nameNameId), | |
| 253 | length: readUint16BE(nameRecordBytes, sfntOffsets.nameLength), | |
| 254 | offset: readUint16BE(nameRecordBytes, sfntOffsets.nameOffset), | |
| 255 | }; | |
| 256 | ||
| 257 | if (name.platformID === 3 && name.encodingID === 1 && name.languageID === magic.english) { | |
| 258 | const s = strbuf(table.subarray( | |
| 259 | nameTable.stringOffset + name.offset, | |
| 260 | nameTable.stringOffset + name.offset + name.length, | |
| 261 | )); | |
| 262 | ||
| 263 | switch (name.nameID) { | |
| 264 | case 1: | |
| 265 | familyName = s; | |
| 266 | break; | |
| 267 | case 2: | |
| 268 | subfamilyName = s; | |
| 269 | break; | |
| 270 | case 4: | |
| 271 | fullName = s; | |
| 272 | break; | |
| 273 | case 5: | |
| 274 | versionString = s; | |
| 275 | break; | |
| 276 | } | |
| 277 | } | |
| 278 | } | |
| 279 | } | |
| 280 | if (haveOs2 && haveName && haveHead) break; | |
| 281 | } | |
| 282 | ||
| 283 | if (!(haveOs2 && haveName && haveHead)) { | |
| 284 | throw new Error("Required section not found"); | |
| 285 | } | |
| 286 | ||
| 287 | // Create final buffer with the the same array type as input one. | |
| 288 | const eot = concat([ | |
| 289 | out, | |
| 290 | familyName, | |
| 291 | subfamilyName, | |
| 292 | versionString, | |
| 293 | fullName, | |
| 294 | new Uint8Array([0, 0]), | |
| 295 | arr, | |
| 296 | ]); | |
| 297 | ||
| 298 | writeUint32LE(eot, eot.length, offsets.length); // Calculate overall length | |
| 299 | ||
| 300 | return eot; | |
| 301 | } |
framework/lib/sitegen.ts+1-1| ... | ... | @@ -44,7 +44,7 @@ export interface Font { |
| 44 | 44 | vars?: Record<string, FontVariableAxis>; |
| 45 | 45 | layoutFeatures?: string[]; |
| 46 | 46 | unicodes: FontRange | FontRange[]; |
| 47 | /** Include [ext] to autofill 'woff2' */ | |
| 47 | /** example: `/myfont.[ext]` */ | |
| 48 | 48 | asset: string; |
| 49 | 49 | }>; |
| 50 | 50 | } |
lib/async.ts+31| ... | ... | @@ -261,5 +261,36 @@ export function delay(ms: number): Cancelable<void> { |
| 261 | 261 | ); |
| 262 | 262 | } |
| 263 | 263 | |
| 264 | export interface Deferred<T> extends AsyncDisposable, PromiseLike<T> { | |
| 265 | } | |
| 266 | ||
| 267 | /** | |
| 268 | * Return something that can be awaited later without worry of an unhandled | |
| 269 | * rejection. By disposing it, the promise exception will always be reported. | |
| 270 | * | |
| 271 | * ```ts | |
| 272 | * // incorrect, possible unhandled rejection between a and b. | |
| 273 | * const a = promise(); | |
| 274 | * const b = await promise(); | |
| 275 | * const c = await promise(b, await a); | |
| 276 | * // correct | |
| 277 | * await using a = async.deferred(promise()); | |
| 278 | * const b = await promise(); | |
| 279 | * const c = await promise(b, await a); | |
| 280 | * ``` | |
| 281 | */ | |
| 282 | export function deferred<T>(promise: Promise<T>): Deferred<T> { | |
| 283 | let handled = false; | |
| 284 | promise.catch(() => {}); // potential error is re-thrown on asyncDispose | |
| 285 | return { | |
| 286 | async [Symbol.asyncDispose]() { | |
| 287 | if (!handled) await promise; | |
| 288 | }, | |
| 289 | then(onFufilled, onRejected) { | |
| 290 | return promise.finally(() => (handled = true)).then(onFufilled, onRejected); | |
| 291 | }, | |
| 292 | }; | |
| 293 | } | |
| 294 | ||
| 264 | 295 | import { ASSERT } from "./assert.ts"; |
| 265 | 296 | import * as ts from "./ts.ts"; |
lib/mime.ts+2-2| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | /** |
| 2 | * a small mime type lookup table library. | |
| 3 | * | |
| 2 | * a small mime type library. | |
| 4 | 3 | * @module |
| 5 | 4 | */ |
| 6 | 5 | |
| ... | ... | @@ -56,6 +55,7 @@ const extensions = new Map( |
| 56 | 55 | + ".dmg" + "#" + "application/octet-stream" + "\n" |
| 57 | 56 | + ".doc" + "#" + "application/msword" + "\n" |
| 58 | 57 | + ".docx" + "#" + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + "\n" |
| 58 | + ".eot" + "#" + "application/vnd.ms-fontobject" + "\n" | |
| 59 | 59 | + ".epub" + "#" + "application/epub+zip" + "\n" |
| 60 | 60 | + ".exe" + "#" + "application/x-msdownload" + "\n" |
| 61 | 61 | + ".flv" + "#" + "video/x-flv" + "\n" |
lib/readme.changes.md+7| ... | ... | @@ -1,5 +1,12 @@ |
| 1 | 1 | # notable changes in clover's typescript library |
| 2 | 2 | |
| 3 | ## v5 | |
| 4 | ||
| 5 | ### features | |
| 6 | ||
| 7 | - `mime`'s database contains `.eot` for embedded opentype fonts. | |
| 8 | - `async.deferred` | |
| 9 | ||
| 3 | 10 | ## v4 |
| 4 | 11 | |
| 5 | 12 | ### breaking |
package-lock.json+88-78| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | "@clo/markodown": "npm:@paperclover/markodown@1.0.0", |
| 10 | 10 | "@marko/compiler": "5.39.62", |
| 11 | 11 | "@types/node": "24.x", |
| 12 | "@types/wawoff2": "^1.0.2", | |
| 12 | 13 | "dashjs": "5.0.3", |
| 13 | 14 | "dprint": "0.53.1", |
| 14 | 15 | "esbuild": "0.25.5", |
| ... | ... | @@ -571,9 +572,9 @@ |
| 571 | 572 | } |
| 572 | 573 | }, |
| 573 | 574 | "node_modules/@esbuild/openharmony-arm64": { |
| 574 | "version": "0.27.4", | |
| 575 | "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", | |
| 576 | "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", | |
| 575 | "version": "0.27.7", | |
| 576 | "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", | |
| 577 | "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", | |
| 577 | 578 | "cpu": [ |
| 578 | 579 | "arm64" |
| 579 | 580 | ], |
| ... | ... | @@ -1116,6 +1117,15 @@ |
| 1116 | 1117 | "undici-types": "~7.16.0" |
| 1117 | 1118 | } |
| 1118 | 1119 | }, |
| 1120 | "node_modules/@types/wawoff2": { | |
| 1121 | "version": "1.0.2", | |
| 1122 | "resolved": "https://registry.npmjs.org/@types/wawoff2/-/wawoff2-1.0.2.tgz", | |
| 1123 | "integrity": "sha512-UVq4NxMuBywz5gklafg1HcENrK02trh/q+ifyvO6xktflGXRtf11GYsia3/l5nxg2edFQRlnUT6ivnk898WDiw==", | |
| 1124 | "license": "MIT", | |
| 1125 | "dependencies": { | |
| 1126 | "@types/node": "*" | |
| 1127 | } | |
| 1128 | }, | |
| 1119 | 1129 | "node_modules/@vitest/expect": { |
| 1120 | 1130 | "version": "4.1.2", |
| 1121 | 1131 | "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", |
| ... | ... | @@ -2493,9 +2503,9 @@ |
| 2493 | 2503 | } |
| 2494 | 2504 | }, |
| 2495 | 2505 | "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { |
| 2496 | "version": "0.27.4", | |
| 2497 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", | |
| 2498 | "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", | |
| 2506 | "version": "0.27.7", | |
| 2507 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", | |
| 2508 | "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", | |
| 2499 | 2509 | "cpu": [ |
| 2500 | 2510 | "ppc64" |
| 2501 | 2511 | ], |
| ... | ... | @@ -2509,9 +2519,9 @@ |
| 2509 | 2519 | } |
| 2510 | 2520 | }, |
| 2511 | 2521 | "node_modules/vitest/node_modules/@esbuild/android-arm": { |
| 2512 | "version": "0.27.4", | |
| 2513 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", | |
| 2514 | "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", | |
| 2522 | "version": "0.27.7", | |
| 2523 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", | |
| 2524 | "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", | |
| 2515 | 2525 | "cpu": [ |
| 2516 | 2526 | "arm" |
| 2517 | 2527 | ], |
| ... | ... | @@ -2525,9 +2535,9 @@ |
| 2525 | 2535 | } |
| 2526 | 2536 | }, |
| 2527 | 2537 | "node_modules/vitest/node_modules/@esbuild/android-arm64": { |
| 2528 | "version": "0.27.4", | |
| 2529 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", | |
| 2530 | "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", | |
| 2538 | "version": "0.27.7", | |
| 2539 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", | |
| 2540 | "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", | |
| 2531 | 2541 | "cpu": [ |
| 2532 | 2542 | "arm64" |
| 2533 | 2543 | ], |
| ... | ... | @@ -2541,9 +2551,9 @@ |
| 2541 | 2551 | } |
| 2542 | 2552 | }, |
| 2543 | 2553 | "node_modules/vitest/node_modules/@esbuild/android-x64": { |
| 2544 | "version": "0.27.4", | |
| 2545 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", | |
| 2546 | "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", | |
| 2554 | "version": "0.27.7", | |
| 2555 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", | |
| 2556 | "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", | |
| 2547 | 2557 | "cpu": [ |
| 2548 | 2558 | "x64" |
| 2549 | 2559 | ], |
| ... | ... | @@ -2557,9 +2567,9 @@ |
| 2557 | 2567 | } |
| 2558 | 2568 | }, |
| 2559 | 2569 | "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { |
| 2560 | "version": "0.27.4", | |
| 2561 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", | |
| 2562 | "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", | |
| 2570 | "version": "0.27.7", | |
| 2571 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", | |
| 2572 | "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", | |
| 2563 | 2573 | "cpu": [ |
| 2564 | 2574 | "arm64" |
| 2565 | 2575 | ], |
| ... | ... | @@ -2573,9 +2583,9 @@ |
| 2573 | 2583 | } |
| 2574 | 2584 | }, |
| 2575 | 2585 | "node_modules/vitest/node_modules/@esbuild/darwin-x64": { |
| 2576 | "version": "0.27.4", | |
| 2577 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", | |
| 2578 | "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", | |
| 2586 | "version": "0.27.7", | |
| 2587 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", | |
| 2588 | "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", | |
| 2579 | 2589 | "cpu": [ |
| 2580 | 2590 | "x64" |
| 2581 | 2591 | ], |
| ... | ... | @@ -2589,9 +2599,9 @@ |
| 2589 | 2599 | } |
| 2590 | 2600 | }, |
| 2591 | 2601 | "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { |
| 2592 | "version": "0.27.4", | |
| 2593 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", | |
| 2594 | "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", | |
| 2602 | "version": "0.27.7", | |
| 2603 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", | |
| 2604 | "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", | |
| 2595 | 2605 | "cpu": [ |
| 2596 | 2606 | "arm64" |
| 2597 | 2607 | ], |
| ... | ... | @@ -2605,9 +2615,9 @@ |
| 2605 | 2615 | } |
| 2606 | 2616 | }, |
| 2607 | 2617 | "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { |
| 2608 | "version": "0.27.4", | |
| 2609 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", | |
| 2610 | "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", | |
| 2618 | "version": "0.27.7", | |
| 2619 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", | |
| 2620 | "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", | |
| 2611 | 2621 | "cpu": [ |
| 2612 | 2622 | "x64" |
| 2613 | 2623 | ], |
| ... | ... | @@ -2621,9 +2631,9 @@ |
| 2621 | 2631 | } |
| 2622 | 2632 | }, |
| 2623 | 2633 | "node_modules/vitest/node_modules/@esbuild/linux-arm": { |
| 2624 | "version": "0.27.4", | |
| 2625 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", | |
| 2626 | "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", | |
| 2634 | "version": "0.27.7", | |
| 2635 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", | |
| 2636 | "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", | |
| 2627 | 2637 | "cpu": [ |
| 2628 | 2638 | "arm" |
| 2629 | 2639 | ], |
| ... | ... | @@ -2637,9 +2647,9 @@ |
| 2637 | 2647 | } |
| 2638 | 2648 | }, |
| 2639 | 2649 | "node_modules/vitest/node_modules/@esbuild/linux-arm64": { |
| 2640 | "version": "0.27.4", | |
| 2641 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", | |
| 2642 | "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", | |
| 2650 | "version": "0.27.7", | |
| 2651 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", | |
| 2652 | "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", | |
| 2643 | 2653 | "cpu": [ |
| 2644 | 2654 | "arm64" |
| 2645 | 2655 | ], |
| ... | ... | @@ -2653,9 +2663,9 @@ |
| 2653 | 2663 | } |
| 2654 | 2664 | }, |
| 2655 | 2665 | "node_modules/vitest/node_modules/@esbuild/linux-ia32": { |
| 2656 | "version": "0.27.4", | |
| 2657 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", | |
| 2658 | "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", | |
| 2666 | "version": "0.27.7", | |
| 2667 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", | |
| 2668 | "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", | |
| 2659 | 2669 | "cpu": [ |
| 2660 | 2670 | "ia32" |
| 2661 | 2671 | ], |
| ... | ... | @@ -2669,9 +2679,9 @@ |
| 2669 | 2679 | } |
| 2670 | 2680 | }, |
| 2671 | 2681 | "node_modules/vitest/node_modules/@esbuild/linux-loong64": { |
| 2672 | "version": "0.27.4", | |
| 2673 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", | |
| 2674 | "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", | |
| 2682 | "version": "0.27.7", | |
| 2683 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", | |
| 2684 | "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", | |
| 2675 | 2685 | "cpu": [ |
| 2676 | 2686 | "loong64" |
| 2677 | 2687 | ], |
| ... | ... | @@ -2685,9 +2695,9 @@ |
| 2685 | 2695 | } |
| 2686 | 2696 | }, |
| 2687 | 2697 | "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { |
| 2688 | "version": "0.27.4", | |
| 2689 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", | |
| 2690 | "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", | |
| 2698 | "version": "0.27.7", | |
| 2699 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", | |
| 2700 | "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", | |
| 2691 | 2701 | "cpu": [ |
| 2692 | 2702 | "mips64el" |
| 2693 | 2703 | ], |
| ... | ... | @@ -2701,9 +2711,9 @@ |
| 2701 | 2711 | } |
| 2702 | 2712 | }, |
| 2703 | 2713 | "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { |
| 2704 | "version": "0.27.4", | |
| 2705 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", | |
| 2706 | "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", | |
| 2714 | "version": "0.27.7", | |
| 2715 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", | |
| 2716 | "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", | |
| 2707 | 2717 | "cpu": [ |
| 2708 | 2718 | "ppc64" |
| 2709 | 2719 | ], |
| ... | ... | @@ -2717,9 +2727,9 @@ |
| 2717 | 2727 | } |
| 2718 | 2728 | }, |
| 2719 | 2729 | "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { |
| 2720 | "version": "0.27.4", | |
| 2721 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", | |
| 2722 | "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", | |
| 2730 | "version": "0.27.7", | |
| 2731 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", | |
| 2732 | "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", | |
| 2723 | 2733 | "cpu": [ |
| 2724 | 2734 | "riscv64" |
| 2725 | 2735 | ], |
| ... | ... | @@ -2733,9 +2743,9 @@ |
| 2733 | 2743 | } |
| 2734 | 2744 | }, |
| 2735 | 2745 | "node_modules/vitest/node_modules/@esbuild/linux-s390x": { |
| 2736 | "version": "0.27.4", | |
| 2737 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", | |
| 2738 | "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", | |
| 2746 | "version": "0.27.7", | |
| 2747 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", | |
| 2748 | "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", | |
| 2739 | 2749 | "cpu": [ |
| 2740 | 2750 | "s390x" |
| 2741 | 2751 | ], |
| ... | ... | @@ -2749,9 +2759,9 @@ |
| 2749 | 2759 | } |
| 2750 | 2760 | }, |
| 2751 | 2761 | "node_modules/vitest/node_modules/@esbuild/linux-x64": { |
| 2752 | "version": "0.27.4", | |
| 2753 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", | |
| 2754 | "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", | |
| 2762 | "version": "0.27.7", | |
| 2763 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", | |
| 2764 | "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", | |
| 2755 | 2765 | "cpu": [ |
| 2756 | 2766 | "x64" |
| 2757 | 2767 | ], |
| ... | ... | @@ -2765,9 +2775,9 @@ |
| 2765 | 2775 | } |
| 2766 | 2776 | }, |
| 2767 | 2777 | "node_modules/vitest/node_modules/@esbuild/netbsd-arm64": { |
| 2768 | "version": "0.27.4", | |
| 2769 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", | |
| 2770 | "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", | |
| 2778 | "version": "0.27.7", | |
| 2779 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", | |
| 2780 | "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", | |
| 2771 | 2781 | "cpu": [ |
| 2772 | 2782 | "arm64" |
| 2773 | 2783 | ], |
| ... | ... | @@ -2781,9 +2791,9 @@ |
| 2781 | 2791 | } |
| 2782 | 2792 | }, |
| 2783 | 2793 | "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { |
| 2784 | "version": "0.27.4", | |
| 2785 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", | |
| 2786 | "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", | |
| 2794 | "version": "0.27.7", | |
| 2795 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", | |
| 2796 | "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", | |
| 2787 | 2797 | "cpu": [ |
| 2788 | 2798 | "x64" |
| 2789 | 2799 | ], |
| ... | ... | @@ -2797,9 +2807,9 @@ |
| 2797 | 2807 | } |
| 2798 | 2808 | }, |
| 2799 | 2809 | "node_modules/vitest/node_modules/@esbuild/openbsd-arm64": { |
| 2800 | "version": "0.27.4", | |
| 2801 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", | |
| 2802 | "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", | |
| 2810 | "version": "0.27.7", | |
| 2811 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", | |
| 2812 | "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", | |
| 2803 | 2813 | "cpu": [ |
| 2804 | 2814 | "arm64" |
| 2805 | 2815 | ], |
| ... | ... | @@ -2813,9 +2823,9 @@ |
| 2813 | 2823 | } |
| 2814 | 2824 | }, |
| 2815 | 2825 | "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { |
| 2816 | "version": "0.27.4", | |
| 2817 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", | |
| 2818 | "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", | |
| 2826 | "version": "0.27.7", | |
| 2827 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", | |
| 2828 | "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", | |
| 2819 | 2829 | "cpu": [ |
| 2820 | 2830 | "x64" |
| 2821 | 2831 | ], |
| ... | ... | @@ -2829,9 +2839,9 @@ |
| 2829 | 2839 | } |
| 2830 | 2840 | }, |
| 2831 | 2841 | "node_modules/vitest/node_modules/@esbuild/sunos-x64": { |
| 2832 | "version": "0.27.4", | |
| 2833 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", | |
| 2834 | "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", | |
| 2842 | "version": "0.27.7", | |
| 2843 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", | |
| 2844 | "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", | |
| 2835 | 2845 | "cpu": [ |
| 2836 | 2846 | "x64" |
| 2837 | 2847 | ], |
| ... | ... | @@ -2845,9 +2855,9 @@ |
| 2845 | 2855 | } |
| 2846 | 2856 | }, |
| 2847 | 2857 | "node_modules/vitest/node_modules/@esbuild/win32-arm64": { |
| 2848 | "version": "0.27.4", | |
| 2849 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", | |
| 2850 | "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", | |
| 2858 | "version": "0.27.7", | |
| 2859 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", | |
| 2860 | "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", | |
| 2851 | 2861 | "cpu": [ |
| 2852 | 2862 | "arm64" |
| 2853 | 2863 | ], |
| ... | ... | @@ -2861,9 +2871,9 @@ |
| 2861 | 2871 | } |
| 2862 | 2872 | }, |
| 2863 | 2873 | "node_modules/vitest/node_modules/@esbuild/win32-ia32": { |
| 2864 | "version": "0.27.4", | |
| 2865 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", | |
| 2866 | "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", | |
| 2874 | "version": "0.27.7", | |
| 2875 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", | |
| 2876 | "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", | |
| 2867 | 2877 | "cpu": [ |
| 2868 | 2878 | "ia32" |
| 2869 | 2879 | ], |
| ... | ... | @@ -2877,9 +2887,9 @@ |
| 2877 | 2887 | } |
| 2878 | 2888 | }, |
| 2879 | 2889 | "node_modules/vitest/node_modules/@esbuild/win32-x64": { |
| 2880 | "version": "0.27.4", | |
| 2881 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", | |
| 2882 | "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", | |
| 2890 | "version": "0.27.7", | |
| 2891 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", | |
| 2892 | "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", | |
| 2883 | 2893 | "cpu": [ |
| 2884 | 2894 | "x64" |
| 2885 | 2895 | ], |
package.json+1| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | "@clo/markodown": "npm:@paperclover/markodown@1.0.0", |
| 6 | 6 | "@marko/compiler": "5.39.62", |
| 7 | 7 | "@types/node": "24.x", |
| 8 | "@types/wawoff2": "^1.0.2", | |
| 8 | 9 | "dashjs": "5.0.3", |
| 9 | 10 | "dprint": "0.53.1", |
| 10 | 11 | "esbuild": "0.25.5", |
src/global.css+20-4| ... | ... | @@ -1,6 +1,10 @@ |
| 1 | 1 | @font-face { |
| 2 | 2 | font-family: "recu"; |
| 3 | src: url("/recultramin.woff2") format("woff2"); | |
| 3 | src: url("/recultramin.eot"); | |
| 4 | src: | |
| 5 | url("/recultramin.eot?#iefix") format("embedded-opentype"), | |
| 6 | url("/recultramin.woff2") format("woff2"), | |
| 7 | url("/recultramin.ttf") format("truetype"); | |
| 4 | 8 | font-weight: 400 750; |
| 5 | 9 | font-style: normal; |
| 6 | 10 | font-display: swap; |
| ... | ... | @@ -10,7 +14,11 @@ |
| 10 | 14 | } |
| 11 | 15 | @font-face { |
| 12 | 16 | font-family: "rmo"; |
| 13 | src: url("/recmono.woff2") format("woff2"); | |
| 17 | src: url("/recmono.eot"); | |
| 18 | src: | |
| 19 | url("/recmono.eot?#iefix") format("embedded-opentype"), | |
| 20 | url("/recmono.woff2") format("woff2"), | |
| 21 | url("/recmono.ttf") format("truetype"); | |
| 14 | 22 | font-weight: 400 800; |
| 15 | 23 | font-style: normal; |
| 16 | 24 | font-display: swap; |
| ... | ... | @@ -20,7 +28,11 @@ |
| 20 | 28 | } |
| 21 | 29 | @font-face { |
| 22 | 30 | font-family: "rmo"; |
| 23 | src: url("/recmonorest.woff2") format("woff2"); | |
| 31 | src: url("/recmonorest.eot"); | |
| 32 | src: | |
| 33 | url("/recmonorest.eot?#iefix") format("embedded-opentype"), | |
| 34 | url("/recmonorest.woff2") format("woff2"), | |
| 35 | url("/recmonorest.ttf") format("truetype"); | |
| 24 | 36 | font-weight: 400 800; |
| 25 | 37 | font-style: normal; |
| 26 | 38 | font-display: swap; |
| ... | ... | @@ -42,7 +54,11 @@ |
| 42 | 54 | } |
| 43 | 55 | @font-face { |
| 44 | 56 | font-family: "recu"; |
| 45 | src: url("/recthin.woff2") format("woff2"); | |
| 57 | src: url("/recthin.eot"); | |
| 58 | src: | |
| 59 | url("/recthin.eot?#iefix") format("embedded-opentype"), | |
| 60 | url("/recthin.woff2") format("woff2"), | |
| 61 | url("/recthin.ttf") format("truetype"); | |
| 46 | 62 | font-weight: 350; |
| 47 | 63 | font-style: normal; |
| 48 | 64 | font-display: swap; |
src/q+a/questions.css+10-2| ... | ... | @@ -1,6 +1,10 @@ |
| 1 | 1 | @font-face { |
| 2 | 2 | font-family: "recu"; |
| 3 | src: url("/recqa.woff2") format("woff2"); | |
| 3 | src: url("/recqa.eot"); | |
| 4 | src: | |
| 5 | url("/recqa.eot?#iefix") format("embedded-opentype"), | |
| 6 | url("/recqa.woff2") format("woff2"), | |
| 7 | url("/recqa.ttf") format("truetype"); | |
| 4 | 8 | font-weight: 400 800; |
| 5 | 9 | font-style: normal; |
| 6 | 10 | font-display: swap; |
| ... | ... | @@ -10,7 +14,11 @@ |
| 10 | 14 | } |
| 11 | 15 | @font-face { |
| 12 | 16 | font-family: recu; |
| 13 | src: url("/recexotic.woff2") format("woff2"); | |
| 17 | src: url("/recexotic.eot"); | |
| 18 | src: | |
| 19 | url("/recexotic.eot?#iefix") format("embedded-opentype"), | |
| 20 | url("/recexotic.woff2") format("woff2"), | |
| 21 | url("/recexotic.ttf") format("truetype"); | |
| 14 | 22 | font-weight: 400 800; |
| 15 | 23 | font-style: normal; |
| 16 | 24 | font-display: swap; |