| 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,7 +81,7 @@ export async function bundleCssFiles( |
| 81 | const build = await esbuild.build({ | 81 | const build = await esbuild.build({ |
| 82 | bundle: true, | 82 | bundle: true, |
| 83 | entryPoints: ["$input$"], | 83 | entryPoints: ["$input$"], |
| 84 | external: ["*.woff2", "*.ttf", "*.png", "*.jpeg"], | 84 | external: ["*.woff2", "*.eot", "*.eot?#iefix", "*.ttf", "*.png", "*.jpeg"], |
| 85 | metafile: true, | 85 | metafile: true, |
| 86 | minify: !dev, | 86 | minify: !dev, |
| 87 | plugins: [ | 87 | plugins: [ |
framework/font.ts+131-172| ... | @@ -10,15 +10,6 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { | ... | @@ -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 | function getVariationAxes(vars: sg.FontVars) { | 13 | function getVariationAxes(vars: sg.FontVars) { |
| 23 | return Object.fromEntries( | 14 | return Object.fromEntries( |
| 24 | Object.entries(vars).map(([key, value]) => [ | 15 | Object.entries(vars).map(([key, value]) => [ |
| ... | @@ -31,36 +22,56 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { | ... | @@ -31,36 +22,56 @@ export async function buildFonts(p: progress.Ref, fonts: sg.Font[]) { |
| 31 | await Promise.all(fonts.map((font) => | 22 | await Promise.all(fonts.map((font) => |
| 32 | incr.work(node, async (io, font) => { | 23 | incr.work(node, async (io, font) => { |
| 33 | using node = io.start(font.name); | 24 | using node = io.start(font.name); |
| 34 | const baseFile = await fetchFont(font.name, font.sources); | 25 | const baseFile = await fetchFont(node, font.name, font.sources); |
| 35 | const baseBuffer = await fs.readFile(baseFile); | 26 | await using baseBuffer = async.deferred(fs.readFile(baseFile)); |
| 36 | await Promise.all(font.subsets.map(async (subset) => { | 27 | await Promise.all(font.subsets.map(async (subset) => { |
| 37 | const args = getArgs(subset.vars ?? {}); | 28 | const args = getArgs(subset.vars ?? {}); |
| 38 | const subsetText = fontRangesToText( | 29 | const unicodes = Array.isArray(subset.unicodes) ? subset.unicodes : [subset.unicodes]; |
| 39 | Array.isArray(subset.unicodes) ? subset.unicodes : [subset.unicodes], | 30 | const charCount = unicodes.reduce<number>((count, range) => { |
| 40 | ); | 31 | if (typeof range === "string") return count + [...range].length; |
| 41 | using _ = node.start(args.join(" ")); | 32 | if (typeof range === "number") return count + 1; |
| 42 | const woff = await subsetFontBuffer(baseBuffer, subsetText, { | 33 | return count + (range.end - range.start + 1); |
| 43 | targetFormat: "woff2", | 34 | }, 0); |
| 44 | variationAxes: subset.vars | 35 | const variationAxes = subset.vars ? getVariationAxes(subset.vars) : undefined; |
| 45 | ? getVariationAxes(subset.vars) | 36 | using _ = node.start("subset: " + [...args, `chars=${charCount}`].join(" ")); |
| 46 | : undefined, | 37 | const [woff, ttf] = await Promise.all([ |
| 47 | sourceName: baseFile, | 38 | subsetFontBuffer(await baseBuffer, unicodes, { |
| 48 | assetName: subset.asset.replace("[ext]", "woff2"), | 39 | targetFormat: "woff2", |
| 49 | }); | 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 | await io.writeAsset({ | 52 | await io.writeAsset({ |
| 51 | pathname: subset.asset.replace("[ext]", "woff2"), | 53 | pathname: subset.asset.replace("[ext]", "woff2"), |
| 52 | buffer: woff, | 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 | }, font) | 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 | const errs = []; | 71 | const errs = []; |
| 61 | 72 | ||
| 62 | for (const source of sources) { | 73 | for (const source of sources) { |
| 63 | const cacheName = path.join("", name + path.extname(source)); | 74 | const cacheName = name + path.extname(source); |
| 64 | if (fs.existsSync(cacheName)) return cacheName; | 75 | if (fs.existsSync(cacheName)) return cacheName; |
| 65 | 76 | ||
| 66 | if (source.startsWith("https://")) { | 77 | if (source.startsWith("https://")) { |
| ... | @@ -106,11 +117,9 @@ export async function fetchFont(name: string, sources: string[]) { | ... | @@ -106,11 +117,9 @@ export async function fetchFont(name: string, sources: string[]) { |
| 106 | path.posix.join(home, ".local/share/fonts"), | 117 | path.posix.join(home, ".local/share/fonts"), |
| 107 | ]; | 118 | ]; |
| 108 | for (const base of bases) { | 119 | for (const base of bases) { |
| 109 | const found = fs.readDirRecOptionalSync(base) | 120 | const files = fs.readDirRecOptionalSync(base); |
| 110 | .find((file) => path.basename(file) === source); | 121 | const found = files.find((file) => path.basename(file) === source); |
| 111 | if (found) { | 122 | if (found) return path.join(base, found); |
| 112 | return path.join(base, found); | ||
| 113 | } | ||
| 114 | } | 123 | } |
| 115 | errs.push( | 124 | errs.push( |
| 116 | new Error( | 125 | new Error( |
| ... | @@ -123,46 +132,20 @@ export async function fetchFont(name: string, sources: string[]) { | ... | @@ -123,46 +132,20 @@ export async function fetchFont(name: string, sources: string[]) { |
| 123 | throw new AggregateError(errs, `Failed to fetch the ${name} font`); | 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 | function normalizeAxisTag(tag: string) { | 135 | function normalizeAxisTag(tag: string) { |
| 146 | const lower = tag.toLowerCase(); | 136 | const lower = tag.toLowerCase(); |
| 147 | return lower === "wght" || lower === "slnt" ? lower : tag; | 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 | const signature = String.fromCharCode(...buffer.subarray(0, 4)); | 141 | const signature = String.fromCharCode(...buffer.subarray(0, 4)); |
| 152 | if (signature === "wOF2") return "woff2"; | ||
| 153 | if ( | 142 | if ( |
| 154 | signature === "true" | 143 | signature === "true" |
| 155 | || signature === "OTTO" | 144 | || signature === "OTTO" |
| 156 | || signature === "\x00\x01\x00\x00" | 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 | return new Uint8Array(await wawoff2.decompress(buffer)); | 149 | return new Uint8Array(await wawoff2.decompress(buffer)); |
| 167 | } | 150 | } |
| 168 | 151 | ||
| ... | @@ -174,18 +157,29 @@ async function convertFromSfnt( | ... | @@ -174,18 +157,29 @@ async function convertFromSfnt( |
| 174 | return new Uint8Array(await wawoff2.compress(buffer)); | 157 | return new Uint8Array(await wawoff2.compress(buffer)); |
| 175 | } | 158 | } |
| 176 | 159 | ||
| 177 | function getSubsetContext(opt: { | 160 | const subsetQueue = new queue.PriorityQueue(1); |
| 178 | sourceName?: string; | 161 | const subsetFontBuffer = subsetQueue.wrap(subsetFontBufferUnqueued); |
| 179 | assetName?: string; | 162 | |
| 180 | variationAxes?: VariationAxes; | 163 | async function subsetFontBufferUnqueued( |
| 181 | targetFormat: "sfnt" | "woff2"; | 164 | originalFont: Uint8Array, |
| 182 | text: string; | 165 | unicodes: sg.FontRange[], |
| 183 | }) { | 166 | opt: { |
| 184 | const fields = [ | 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 | opt.sourceName ? `source=${opt.sourceName}` : "", | 180 | opt.sourceName ? `source=${opt.sourceName}` : "", |
| 186 | opt.assetName ? `asset=${opt.assetName}` : "", | 181 | opt.assetName ? `asset=${opt.assetName}` : "", |
| 187 | `target=${opt.targetFormat}`, | 182 | `target=${opt.targetFormat}`, |
| 188 | `textLength=${opt.text.length}`, | ||
| 189 | opt.variationAxes | 183 | opt.variationAxes |
| 190 | ? `axes=${ | 184 | ? `axes=${ |
| 191 | Object.entries(opt.variationAxes).map(([key, value]) => | 185 | Object.entries(opt.variationAxes).map(([key, value]) => |
| ... | @@ -195,120 +189,90 @@ function getSubsetContext(opt: { | ... | @@ -195,120 +189,90 @@ function getSubsetContext(opt: { |
| 195 | ).join(",") | 189 | ).join(",") |
| 196 | }` | 190 | }` |
| 197 | : "", | 191 | : "", |
| 198 | ].filter(Boolean); | 192 | ].filter(Boolean).join(" "); |
| 199 | return fields.join(" "); | ||
| 200 | } | ||
| 201 | 193 | ||
| 202 | async function subsetFontBuffer( | 194 | const { harfbuzzJsWasm, getHeapU8 } = await loadAndInitializeHarfbuzz(); |
| 203 | originalFont: Uint8Array, | 195 | const sfntFont = await convertToSfnt(originalFont); |
| 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 | } | ||
| 217 | 196 | ||
| 218 | const { harfbuzzJsWasm, getHeapU8 } = await loadAndInitializeHarfbuzz(); | 197 | const input = harfbuzzJsWasm.hb_subset_input_create_or_fail(); |
| 219 | const sfntFont = await convertToSfnt(originalFont); | 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(); | 200 | const fontBuffer = harfbuzzJsWasm.malloc(sfntFont.byteLength); |
| 222 | if (input === 0) { | 201 | getHeapU8().set(new Uint8Array(sfntFont), fontBuffer); |
| 223 | throw new Error(`Failed to create HarfBuzz subset input. ${ctx}`); | ||
| 224 | } | ||
| 225 | 202 | ||
| 226 | const fontBuffer = harfbuzzJsWasm.malloc(sfntFont.byteLength); | 203 | const blob = harfbuzzJsWasm.hb_blob_create( |
| 227 | getHeapU8().set(new Uint8Array(sfntFont), fontBuffer); | 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( | 213 | try { |
| 230 | fontBuffer, | 214 | const layoutFeatures = harfbuzzJsWasm.hb_subset_input_set(input, 6); |
| 231 | sfntFont.byteLength, | 215 | ASSERT(layoutFeatures, `Failed to set layout features. ${ctx}`); |
| 232 | 2, | 216 | harfbuzzJsWasm.hb_set_clear(layoutFeatures); |
| 233 | 0, | 217 | harfbuzzJsWasm.hb_set_invert(layoutFeatures); |
| 234 | 0, | ||
| 235 | ); | ||
| 236 | const face = harfbuzzJsWasm.hb_face_create(blob, 0); | ||
| 237 | harfbuzzJsWasm.hb_blob_destroy(blob); | ||
| 238 | 218 | ||
| 239 | try { | 219 | const inputUnicodes = harfbuzzJsWasm.hb_subset_input_unicode_set(input); |
| 240 | const layoutFeatures = harfbuzzJsWasm.hb_subset_input_set(input, 6); | 220 | for (const range of unicodes) { |
| 241 | harfbuzzJsWasm.hb_set_clear(layoutFeatures); | 221 | if (typeof range === "string") { |
| 242 | harfbuzzJsWasm.hb_set_invert(layoutFeatures); | 222 | for (const char of range) harfbuzzJsWasm.hb_set_add(inputUnicodes, char.codePointAt(0) || 0); |
| 243 | 223 | } else if (typeof range === "number") { | |
| 244 | const inputUnicodes = harfbuzzJsWasm.hb_subset_input_unicode_set(input); | 224 | harfbuzzJsWasm.hb_set_add(inputUnicodes, range); |
| 245 | for (const char of text) { | 225 | } else { |
| 246 | harfbuzzJsWasm.hb_set_add(inputUnicodes, char.codePointAt(0) || 0); | 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) { | 230 | if (opt.variationAxes) { |
| 250 | for (const [axisName, value] of Object.entries(opt.variationAxes)) { | 231 | for (const [axisName, value] of Object.entries(opt.variationAxes)) { |
| 251 | if (typeof value === "number") { | 232 | if (typeof value === "number") { |
| 252 | if ( | 233 | ASSERT(harfbuzzJsWasm.hb_subset_input_pin_axis_location( |
| 253 | !harfbuzzJsWasm.hb_subset_input_pin_axis_location( | 234 | input, |
| 254 | input, | 235 | face, |
| 255 | face, | 236 | hbTag(axisName), |
| 256 | hbTag(axisName), | 237 | value, |
| 257 | value, | 238 | )); |
| 258 | ) | 239 | } else { |
| 259 | ) { | 240 | ASSERT(harfbuzzJsWasm.hb_subset_input_set_axis_range( |
| 260 | throw new Error( | 241 | input, |
| 261 | `Failed to pin variation axis ${axisName}=${value}. ${ctx}`, | 242 | face, |
| 262 | ); | 243 | hbTag(axisName), |
| 263 | } | 244 | value.min, |
| 264 | } else if ( | 245 | value.max, |
| 265 | !harfbuzzJsWasm.hb_subset_input_set_axis_range( | 246 | value.default ?? Number.NaN, |
| 266 | input, | 247 | )); |
| 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 | } | ||
| 280 | } | 248 | } |
| 281 | } | 249 | } |
| 250 | } | ||
| 282 | 251 | ||
| 283 | const subset = harfbuzzJsWasm.hb_subset_or_fail(face, input); | 252 | const subset = harfbuzzJsWasm.hb_subset_or_fail(face, input); |
| 284 | if (subset === 0) { | 253 | ASSERT(subset, `HarfBuzz failed to subset font. ${ctx}`); |
| 285 | throw new Error(`HarfBuzz failed to subset font. ${ctx}`); | ||
| 286 | } | ||
| 287 | 254 | ||
| 255 | try { | ||
| 256 | const result = harfbuzzJsWasm.hb_face_reference_blob(subset); | ||
| 288 | try { | 257 | try { |
| 289 | const result = harfbuzzJsWasm.hb_face_reference_blob(subset); | 258 | const offset = harfbuzzJsWasm.hb_blob_get_data(result, 0); |
| 290 | try { | 259 | const subsetByteLength = harfbuzzJsWasm.hb_blob_get_length(result); |
| 291 | const offset = harfbuzzJsWasm.hb_blob_get_data(result, 0); | 260 | ASSERT(subsetByteLength > 0, `Subset font was empty. ${ctx}`); |
| 292 | const subsetByteLength = harfbuzzJsWasm.hb_blob_get_length(result); | 261 | const subsetFont = Uint8Array.from( |
| 293 | if (subsetByteLength === 0) { | 262 | getHeapU8().subarray(offset, offset + subsetByteLength), |
| 294 | throw new Error(`Subset font was empty. ${ctx}`); | 263 | ); |
| 295 | } | 264 | return await convertFromSfnt(subsetFont, opt.targetFormat); |
| 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 | } | ||
| 303 | } finally { | 265 | } finally { |
| 304 | harfbuzzJsWasm.hb_face_destroy(subset); | 266 | harfbuzzJsWasm.hb_blob_destroy(result); |
| 305 | } | 267 | } |
| 306 | } finally { | 268 | } finally { |
| 307 | harfbuzzJsWasm.hb_subset_input_destroy(input); | 269 | harfbuzzJsWasm.hb_face_destroy(subset); |
| 308 | harfbuzzJsWasm.hb_face_destroy(face); | ||
| 309 | harfbuzzJsWasm.free(fontBuffer); | ||
| 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 | type VariationAxisRange = { min: number; max: number; default?: number }; | 278 | type VariationAxisRange = { min: number; max: number; default?: number }; |
| ... | @@ -367,25 +331,20 @@ const loadAndInitializeHarfbuzz = async.once(async () => { | ... | @@ -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 | function hbTag(str: string) { | 334 | function hbTag(str: string) { |
| 378 | return str.split("").reduce((a, ch) => (a << 8) + ch.charCodeAt(0), 0); | 335 | return str.split("").reduce((a, ch) => (a << 8) + ch.charCodeAt(0), 0); |
| 379 | } | 336 | } |
| 380 | 337 | ||
| 381 | import * as sg from "#sitegen"; | 338 | import * as sg from "#sitegen"; |
| 382 | import * as fs from "#sitegen/fs"; | 339 | import * as fs from "#sitegen/fs"; |
| 340 | import { ASSERT } from "@clo/lib/assert"; | ||
| 383 | import * as async from "@clo/lib/async"; | 341 | import * as async from "@clo/lib/async"; |
| 384 | import * as error from "@clo/lib/error"; | 342 | import * as error from "@clo/lib/error"; |
| 385 | import * as progress from "@clo/lib/progress"; | 343 | import * as progress from "@clo/lib/progress"; |
| 386 | import * as crypto from "node:crypto"; | 344 | import * as queue from "@clo/lib/queue"; |
| 387 | import { readFile } from "node:fs/promises"; | 345 | import { readFile } from "node:fs/promises"; |
| 388 | import * as os from "node:os"; | 346 | import * as os from "node:os"; |
| 389 | import * as path from "node:path"; | 347 | import * as path from "node:path"; |
| 390 | import * as wawoff2 from "wawoff2"; | 348 | import * as wawoff2 from "wawoff2"; |
| 349 | import { ttf2eot } from "./font/ttf2eot.ts"; | ||
| 391 | import * as incr from "./incremental.ts"; | 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,7 +44,7 @@ export interface Font { |
| 44 | vars?: Record<string, FontVariableAxis>; | 44 | vars?: Record<string, FontVariableAxis>; |
| 45 | layoutFeatures?: string[]; | 45 | layoutFeatures?: string[]; |
| 46 | unicodes: FontRange | FontRange[]; | 46 | unicodes: FontRange | FontRange[]; |
| 47 | /** Include [ext] to autofill 'woff2' */ | 47 | /** example: `/myfont.[ext]` */ |
| 48 | asset: string; | 48 | asset: string; |
| 49 | }>; | 49 | }>; |
| 50 | } | 50 | } |
lib/async.ts+31| ... | @@ -261,5 +261,36 @@ export function delay(ms: number): Cancelable<void> { | ... | @@ -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 | import { ASSERT } from "./assert.ts"; | 295 | import { ASSERT } from "./assert.ts"; |
| 265 | import * as ts from "./ts.ts"; | 296 | import * as ts from "./ts.ts"; |
lib/mime.ts+2-2| ... | @@ -1,6 +1,5 @@ | ... | @@ -1,6 +1,5 @@ |
| 1 | /** | 1 | /** |
| 2 | * a small mime type lookup table library. | 2 | * a small mime type library. |
| 3 | * | ||
| 4 | * @module | 3 | * @module |
| 5 | */ | 4 | */ |
| 6 | 5 | ||
| ... | @@ -56,6 +55,7 @@ const extensions = new Map( | ... | @@ -56,6 +55,7 @@ const extensions = new Map( |
| 56 | + ".dmg" + "#" + "application/octet-stream" + "\n" | 55 | + ".dmg" + "#" + "application/octet-stream" + "\n" |
| 57 | + ".doc" + "#" + "application/msword" + "\n" | 56 | + ".doc" + "#" + "application/msword" + "\n" |
| 58 | + ".docx" + "#" + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + "\n" | 57 | + ".docx" + "#" + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + "\n" |
| 58 | + ".eot" + "#" + "application/vnd.ms-fontobject" + "\n" | ||
| 59 | + ".epub" + "#" + "application/epub+zip" + "\n" | 59 | + ".epub" + "#" + "application/epub+zip" + "\n" |
| 60 | + ".exe" + "#" + "application/x-msdownload" + "\n" | 60 | + ".exe" + "#" + "application/x-msdownload" + "\n" |
| 61 | + ".flv" + "#" + "video/x-flv" + "\n" | 61 | + ".flv" + "#" + "video/x-flv" + "\n" |
lib/readme.changes.md+7| ... | @@ -1,5 +1,12 @@ | ... | @@ -1,5 +1,12 @@ |
| 1 | # notable changes in clover's typescript library | 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 | ## v4 | 10 | ## v4 |
| 4 | 11 | ||
| 5 | ### breaking | 12 | ### breaking |
package-lock.json+88-78| ... | @@ -9,6 +9,7 @@ | ... | @@ -9,6 +9,7 @@ |
| 9 | "@clo/markodown": "npm:@paperclover/markodown@1.0.0", | 9 | "@clo/markodown": "npm:@paperclover/markodown@1.0.0", |
| 10 | "@marko/compiler": "5.39.62", | 10 | "@marko/compiler": "5.39.62", |
| 11 | "@types/node": "24.x", | 11 | "@types/node": "24.x", |
| 12 | "@types/wawoff2": "^1.0.2", | ||
| 12 | "dashjs": "5.0.3", | 13 | "dashjs": "5.0.3", |
| 13 | "dprint": "0.53.1", | 14 | "dprint": "0.53.1", |
| 14 | "esbuild": "0.25.5", | 15 | "esbuild": "0.25.5", |
| ... | @@ -571,9 +572,9 @@ | ... | @@ -571,9 +572,9 @@ |
| 571 | } | 572 | } |
| 572 | }, | 573 | }, |
| 573 | "node_modules/@esbuild/openharmony-arm64": { | 574 | "node_modules/@esbuild/openharmony-arm64": { |
| 574 | "version": "0.27.4", | 575 | "version": "0.27.7", |
| 575 | "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", | 576 | "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", |
| 576 | "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", | 577 | "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", |
| 577 | "cpu": [ | 578 | "cpu": [ |
| 578 | "arm64" | 579 | "arm64" |
| 579 | ], | 580 | ], |
| ... | @@ -1116,6 +1117,15 @@ | ... | @@ -1116,6 +1117,15 @@ |
| 1116 | "undici-types": "~7.16.0" | 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 | "node_modules/@vitest/expect": { | 1129 | "node_modules/@vitest/expect": { |
| 1120 | "version": "4.1.2", | 1130 | "version": "4.1.2", |
| 1121 | "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", | 1131 | "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", |
| ... | @@ -2493,9 +2503,9 @@ | ... | @@ -2493,9 +2503,9 @@ |
| 2493 | } | 2503 | } |
| 2494 | }, | 2504 | }, |
| 2495 | "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { | 2505 | "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { |
| 2496 | "version": "0.27.4", | 2506 | "version": "0.27.7", |
| 2497 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", | 2507 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", |
| 2498 | "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", | 2508 | "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", |
| 2499 | "cpu": [ | 2509 | "cpu": [ |
| 2500 | "ppc64" | 2510 | "ppc64" |
| 2501 | ], | 2511 | ], |
| ... | @@ -2509,9 +2519,9 @@ | ... | @@ -2509,9 +2519,9 @@ |
| 2509 | } | 2519 | } |
| 2510 | }, | 2520 | }, |
| 2511 | "node_modules/vitest/node_modules/@esbuild/android-arm": { | 2521 | "node_modules/vitest/node_modules/@esbuild/android-arm": { |
| 2512 | "version": "0.27.4", | 2522 | "version": "0.27.7", |
| 2513 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", | 2523 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", |
| 2514 | "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", | 2524 | "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", |
| 2515 | "cpu": [ | 2525 | "cpu": [ |
| 2516 | "arm" | 2526 | "arm" |
| 2517 | ], | 2527 | ], |
| ... | @@ -2525,9 +2535,9 @@ | ... | @@ -2525,9 +2535,9 @@ |
| 2525 | } | 2535 | } |
| 2526 | }, | 2536 | }, |
| 2527 | "node_modules/vitest/node_modules/@esbuild/android-arm64": { | 2537 | "node_modules/vitest/node_modules/@esbuild/android-arm64": { |
| 2528 | "version": "0.27.4", | 2538 | "version": "0.27.7", |
| 2529 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", | 2539 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", |
| 2530 | "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", | 2540 | "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", |
| 2531 | "cpu": [ | 2541 | "cpu": [ |
| 2532 | "arm64" | 2542 | "arm64" |
| 2533 | ], | 2543 | ], |
| ... | @@ -2541,9 +2551,9 @@ | ... | @@ -2541,9 +2551,9 @@ |
| 2541 | } | 2551 | } |
| 2542 | }, | 2552 | }, |
| 2543 | "node_modules/vitest/node_modules/@esbuild/android-x64": { | 2553 | "node_modules/vitest/node_modules/@esbuild/android-x64": { |
| 2544 | "version": "0.27.4", | 2554 | "version": "0.27.7", |
| 2545 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", | 2555 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", |
| 2546 | "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", | 2556 | "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", |
| 2547 | "cpu": [ | 2557 | "cpu": [ |
| 2548 | "x64" | 2558 | "x64" |
| 2549 | ], | 2559 | ], |
| ... | @@ -2557,9 +2567,9 @@ | ... | @@ -2557,9 +2567,9 @@ |
| 2557 | } | 2567 | } |
| 2558 | }, | 2568 | }, |
| 2559 | "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { | 2569 | "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { |
| 2560 | "version": "0.27.4", | 2570 | "version": "0.27.7", |
| 2561 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", | 2571 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", |
| 2562 | "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", | 2572 | "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", |
| 2563 | "cpu": [ | 2573 | "cpu": [ |
| 2564 | "arm64" | 2574 | "arm64" |
| 2565 | ], | 2575 | ], |
| ... | @@ -2573,9 +2583,9 @@ | ... | @@ -2573,9 +2583,9 @@ |
| 2573 | } | 2583 | } |
| 2574 | }, | 2584 | }, |
| 2575 | "node_modules/vitest/node_modules/@esbuild/darwin-x64": { | 2585 | "node_modules/vitest/node_modules/@esbuild/darwin-x64": { |
| 2576 | "version": "0.27.4", | 2586 | "version": "0.27.7", |
| 2577 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", | 2587 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", |
| 2578 | "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", | 2588 | "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", |
| 2579 | "cpu": [ | 2589 | "cpu": [ |
| 2580 | "x64" | 2590 | "x64" |
| 2581 | ], | 2591 | ], |
| ... | @@ -2589,9 +2599,9 @@ | ... | @@ -2589,9 +2599,9 @@ |
| 2589 | } | 2599 | } |
| 2590 | }, | 2600 | }, |
| 2591 | "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { | 2601 | "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { |
| 2592 | "version": "0.27.4", | 2602 | "version": "0.27.7", |
| 2593 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", | 2603 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", |
| 2594 | "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", | 2604 | "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", |
| 2595 | "cpu": [ | 2605 | "cpu": [ |
| 2596 | "arm64" | 2606 | "arm64" |
| 2597 | ], | 2607 | ], |
| ... | @@ -2605,9 +2615,9 @@ | ... | @@ -2605,9 +2615,9 @@ |
| 2605 | } | 2615 | } |
| 2606 | }, | 2616 | }, |
| 2607 | "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { | 2617 | "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { |
| 2608 | "version": "0.27.4", | 2618 | "version": "0.27.7", |
| 2609 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", | 2619 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", |
| 2610 | "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", | 2620 | "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", |
| 2611 | "cpu": [ | 2621 | "cpu": [ |
| 2612 | "x64" | 2622 | "x64" |
| 2613 | ], | 2623 | ], |
| ... | @@ -2621,9 +2631,9 @@ | ... | @@ -2621,9 +2631,9 @@ |
| 2621 | } | 2631 | } |
| 2622 | }, | 2632 | }, |
| 2623 | "node_modules/vitest/node_modules/@esbuild/linux-arm": { | 2633 | "node_modules/vitest/node_modules/@esbuild/linux-arm": { |
| 2624 | "version": "0.27.4", | 2634 | "version": "0.27.7", |
| 2625 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", | 2635 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", |
| 2626 | "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", | 2636 | "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", |
| 2627 | "cpu": [ | 2637 | "cpu": [ |
| 2628 | "arm" | 2638 | "arm" |
| 2629 | ], | 2639 | ], |
| ... | @@ -2637,9 +2647,9 @@ | ... | @@ -2637,9 +2647,9 @@ |
| 2637 | } | 2647 | } |
| 2638 | }, | 2648 | }, |
| 2639 | "node_modules/vitest/node_modules/@esbuild/linux-arm64": { | 2649 | "node_modules/vitest/node_modules/@esbuild/linux-arm64": { |
| 2640 | "version": "0.27.4", | 2650 | "version": "0.27.7", |
| 2641 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", | 2651 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", |
| 2642 | "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", | 2652 | "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", |
| 2643 | "cpu": [ | 2653 | "cpu": [ |
| 2644 | "arm64" | 2654 | "arm64" |
| 2645 | ], | 2655 | ], |
| ... | @@ -2653,9 +2663,9 @@ | ... | @@ -2653,9 +2663,9 @@ |
| 2653 | } | 2663 | } |
| 2654 | }, | 2664 | }, |
| 2655 | "node_modules/vitest/node_modules/@esbuild/linux-ia32": { | 2665 | "node_modules/vitest/node_modules/@esbuild/linux-ia32": { |
| 2656 | "version": "0.27.4", | 2666 | "version": "0.27.7", |
| 2657 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", | 2667 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", |
| 2658 | "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", | 2668 | "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", |
| 2659 | "cpu": [ | 2669 | "cpu": [ |
| 2660 | "ia32" | 2670 | "ia32" |
| 2661 | ], | 2671 | ], |
| ... | @@ -2669,9 +2679,9 @@ | ... | @@ -2669,9 +2679,9 @@ |
| 2669 | } | 2679 | } |
| 2670 | }, | 2680 | }, |
| 2671 | "node_modules/vitest/node_modules/@esbuild/linux-loong64": { | 2681 | "node_modules/vitest/node_modules/@esbuild/linux-loong64": { |
| 2672 | "version": "0.27.4", | 2682 | "version": "0.27.7", |
| 2673 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", | 2683 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", |
| 2674 | "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", | 2684 | "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", |
| 2675 | "cpu": [ | 2685 | "cpu": [ |
| 2676 | "loong64" | 2686 | "loong64" |
| 2677 | ], | 2687 | ], |
| ... | @@ -2685,9 +2695,9 @@ | ... | @@ -2685,9 +2695,9 @@ |
| 2685 | } | 2695 | } |
| 2686 | }, | 2696 | }, |
| 2687 | "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { | 2697 | "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { |
| 2688 | "version": "0.27.4", | 2698 | "version": "0.27.7", |
| 2689 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", | 2699 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", |
| 2690 | "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", | 2700 | "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", |
| 2691 | "cpu": [ | 2701 | "cpu": [ |
| 2692 | "mips64el" | 2702 | "mips64el" |
| 2693 | ], | 2703 | ], |
| ... | @@ -2701,9 +2711,9 @@ | ... | @@ -2701,9 +2711,9 @@ |
| 2701 | } | 2711 | } |
| 2702 | }, | 2712 | }, |
| 2703 | "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { | 2713 | "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { |
| 2704 | "version": "0.27.4", | 2714 | "version": "0.27.7", |
| 2705 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", | 2715 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", |
| 2706 | "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", | 2716 | "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", |
| 2707 | "cpu": [ | 2717 | "cpu": [ |
| 2708 | "ppc64" | 2718 | "ppc64" |
| 2709 | ], | 2719 | ], |
| ... | @@ -2717,9 +2727,9 @@ | ... | @@ -2717,9 +2727,9 @@ |
| 2717 | } | 2727 | } |
| 2718 | }, | 2728 | }, |
| 2719 | "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { | 2729 | "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { |
| 2720 | "version": "0.27.4", | 2730 | "version": "0.27.7", |
| 2721 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", | 2731 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", |
| 2722 | "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", | 2732 | "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", |
| 2723 | "cpu": [ | 2733 | "cpu": [ |
| 2724 | "riscv64" | 2734 | "riscv64" |
| 2725 | ], | 2735 | ], |
| ... | @@ -2733,9 +2743,9 @@ | ... | @@ -2733,9 +2743,9 @@ |
| 2733 | } | 2743 | } |
| 2734 | }, | 2744 | }, |
| 2735 | "node_modules/vitest/node_modules/@esbuild/linux-s390x": { | 2745 | "node_modules/vitest/node_modules/@esbuild/linux-s390x": { |
| 2736 | "version": "0.27.4", | 2746 | "version": "0.27.7", |
| 2737 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", | 2747 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", |
| 2738 | "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", | 2748 | "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", |
| 2739 | "cpu": [ | 2749 | "cpu": [ |
| 2740 | "s390x" | 2750 | "s390x" |
| 2741 | ], | 2751 | ], |
| ... | @@ -2749,9 +2759,9 @@ | ... | @@ -2749,9 +2759,9 @@ |
| 2749 | } | 2759 | } |
| 2750 | }, | 2760 | }, |
| 2751 | "node_modules/vitest/node_modules/@esbuild/linux-x64": { | 2761 | "node_modules/vitest/node_modules/@esbuild/linux-x64": { |
| 2752 | "version": "0.27.4", | 2762 | "version": "0.27.7", |
| 2753 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", | 2763 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", |
| 2754 | "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", | 2764 | "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", |
| 2755 | "cpu": [ | 2765 | "cpu": [ |
| 2756 | "x64" | 2766 | "x64" |
| 2757 | ], | 2767 | ], |
| ... | @@ -2765,9 +2775,9 @@ | ... | @@ -2765,9 +2775,9 @@ |
| 2765 | } | 2775 | } |
| 2766 | }, | 2776 | }, |
| 2767 | "node_modules/vitest/node_modules/@esbuild/netbsd-arm64": { | 2777 | "node_modules/vitest/node_modules/@esbuild/netbsd-arm64": { |
| 2768 | "version": "0.27.4", | 2778 | "version": "0.27.7", |
| 2769 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", | 2779 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", |
| 2770 | "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", | 2780 | "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", |
| 2771 | "cpu": [ | 2781 | "cpu": [ |
| 2772 | "arm64" | 2782 | "arm64" |
| 2773 | ], | 2783 | ], |
| ... | @@ -2781,9 +2791,9 @@ | ... | @@ -2781,9 +2791,9 @@ |
| 2781 | } | 2791 | } |
| 2782 | }, | 2792 | }, |
| 2783 | "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { | 2793 | "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { |
| 2784 | "version": "0.27.4", | 2794 | "version": "0.27.7", |
| 2785 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", | 2795 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", |
| 2786 | "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", | 2796 | "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", |
| 2787 | "cpu": [ | 2797 | "cpu": [ |
| 2788 | "x64" | 2798 | "x64" |
| 2789 | ], | 2799 | ], |
| ... | @@ -2797,9 +2807,9 @@ | ... | @@ -2797,9 +2807,9 @@ |
| 2797 | } | 2807 | } |
| 2798 | }, | 2808 | }, |
| 2799 | "node_modules/vitest/node_modules/@esbuild/openbsd-arm64": { | 2809 | "node_modules/vitest/node_modules/@esbuild/openbsd-arm64": { |
| 2800 | "version": "0.27.4", | 2810 | "version": "0.27.7", |
| 2801 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", | 2811 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", |
| 2802 | "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", | 2812 | "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", |
| 2803 | "cpu": [ | 2813 | "cpu": [ |
| 2804 | "arm64" | 2814 | "arm64" |
| 2805 | ], | 2815 | ], |
| ... | @@ -2813,9 +2823,9 @@ | ... | @@ -2813,9 +2823,9 @@ |
| 2813 | } | 2823 | } |
| 2814 | }, | 2824 | }, |
| 2815 | "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { | 2825 | "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { |
| 2816 | "version": "0.27.4", | 2826 | "version": "0.27.7", |
| 2817 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", | 2827 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", |
| 2818 | "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", | 2828 | "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", |
| 2819 | "cpu": [ | 2829 | "cpu": [ |
| 2820 | "x64" | 2830 | "x64" |
| 2821 | ], | 2831 | ], |
| ... | @@ -2829,9 +2839,9 @@ | ... | @@ -2829,9 +2839,9 @@ |
| 2829 | } | 2839 | } |
| 2830 | }, | 2840 | }, |
| 2831 | "node_modules/vitest/node_modules/@esbuild/sunos-x64": { | 2841 | "node_modules/vitest/node_modules/@esbuild/sunos-x64": { |
| 2832 | "version": "0.27.4", | 2842 | "version": "0.27.7", |
| 2833 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", | 2843 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", |
| 2834 | "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", | 2844 | "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", |
| 2835 | "cpu": [ | 2845 | "cpu": [ |
| 2836 | "x64" | 2846 | "x64" |
| 2837 | ], | 2847 | ], |
| ... | @@ -2845,9 +2855,9 @@ | ... | @@ -2845,9 +2855,9 @@ |
| 2845 | } | 2855 | } |
| 2846 | }, | 2856 | }, |
| 2847 | "node_modules/vitest/node_modules/@esbuild/win32-arm64": { | 2857 | "node_modules/vitest/node_modules/@esbuild/win32-arm64": { |
| 2848 | "version": "0.27.4", | 2858 | "version": "0.27.7", |
| 2849 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", | 2859 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", |
| 2850 | "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", | 2860 | "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", |
| 2851 | "cpu": [ | 2861 | "cpu": [ |
| 2852 | "arm64" | 2862 | "arm64" |
| 2853 | ], | 2863 | ], |
| ... | @@ -2861,9 +2871,9 @@ | ... | @@ -2861,9 +2871,9 @@ |
| 2861 | } | 2871 | } |
| 2862 | }, | 2872 | }, |
| 2863 | "node_modules/vitest/node_modules/@esbuild/win32-ia32": { | 2873 | "node_modules/vitest/node_modules/@esbuild/win32-ia32": { |
| 2864 | "version": "0.27.4", | 2874 | "version": "0.27.7", |
| 2865 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", | 2875 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", |
| 2866 | "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", | 2876 | "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", |
| 2867 | "cpu": [ | 2877 | "cpu": [ |
| 2868 | "ia32" | 2878 | "ia32" |
| 2869 | ], | 2879 | ], |
| ... | @@ -2877,9 +2887,9 @@ | ... | @@ -2877,9 +2887,9 @@ |
| 2877 | } | 2887 | } |
| 2878 | }, | 2888 | }, |
| 2879 | "node_modules/vitest/node_modules/@esbuild/win32-x64": { | 2889 | "node_modules/vitest/node_modules/@esbuild/win32-x64": { |
| 2880 | "version": "0.27.4", | 2890 | "version": "0.27.7", |
| 2881 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", | 2891 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", |
| 2882 | "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", | 2892 | "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", |
| 2883 | "cpu": [ | 2893 | "cpu": [ |
| 2884 | "x64" | 2894 | "x64" |
| 2885 | ], | 2895 | ], |
package.json+1| ... | @@ -5,6 +5,7 @@ | ... | @@ -5,6 +5,7 @@ |
| 5 | "@clo/markodown": "npm:@paperclover/markodown@1.0.0", | 5 | "@clo/markodown": "npm:@paperclover/markodown@1.0.0", |
| 6 | "@marko/compiler": "5.39.62", | 6 | "@marko/compiler": "5.39.62", |
| 7 | "@types/node": "24.x", | 7 | "@types/node": "24.x", |
| 8 | "@types/wawoff2": "^1.0.2", | ||
| 8 | "dashjs": "5.0.3", | 9 | "dashjs": "5.0.3", |
| 9 | "dprint": "0.53.1", | 10 | "dprint": "0.53.1", |
| 10 | "esbuild": "0.25.5", | 11 | "esbuild": "0.25.5", |
src/global.css+20-4| ... | @@ -1,6 +1,10 @@ | ... | @@ -1,6 +1,10 @@ |
| 1 | @font-face { | 1 | @font-face { |
| 2 | font-family: "recu"; | 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 | font-weight: 400 750; | 8 | font-weight: 400 750; |
| 5 | font-style: normal; | 9 | font-style: normal; |
| 6 | font-display: swap; | 10 | font-display: swap; |
| ... | @@ -10,7 +14,11 @@ | ... | @@ -10,7 +14,11 @@ |
| 10 | } | 14 | } |
| 11 | @font-face { | 15 | @font-face { |
| 12 | font-family: "rmo"; | 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 | font-weight: 400 800; | 22 | font-weight: 400 800; |
| 15 | font-style: normal; | 23 | font-style: normal; |
| 16 | font-display: swap; | 24 | font-display: swap; |
| ... | @@ -20,7 +28,11 @@ | ... | @@ -20,7 +28,11 @@ |
| 20 | } | 28 | } |
| 21 | @font-face { | 29 | @font-face { |
| 22 | font-family: "rmo"; | 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 | font-weight: 400 800; | 36 | font-weight: 400 800; |
| 25 | font-style: normal; | 37 | font-style: normal; |
| 26 | font-display: swap; | 38 | font-display: swap; |
| ... | @@ -42,7 +54,11 @@ | ... | @@ -42,7 +54,11 @@ |
| 42 | } | 54 | } |
| 43 | @font-face { | 55 | @font-face { |
| 44 | font-family: "recu"; | 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 | font-weight: 350; | 62 | font-weight: 350; |
| 47 | font-style: normal; | 63 | font-style: normal; |
| 48 | font-display: swap; | 64 | font-display: swap; |
src/q+a/questions.css+10-2| ... | @@ -1,6 +1,10 @@ | ... | @@ -1,6 +1,10 @@ |
| 1 | @font-face { | 1 | @font-face { |
| 2 | font-family: "recu"; | 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 | font-weight: 400 800; | 8 | font-weight: 400 800; |
| 5 | font-style: normal; | 9 | font-style: normal; |
| 6 | font-display: swap; | 10 | font-display: swap; |
| ... | @@ -10,7 +14,11 @@ | ... | @@ -10,7 +14,11 @@ |
| 10 | } | 14 | } |
| 11 | @font-face { | 15 | @font-face { |
| 12 | font-family: recu; | 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 | font-weight: 400 800; | 22 | font-weight: 400 800; |
| 15 | font-style: normal; | 23 | font-style: normal; |
| 16 | font-display: swap; | 24 | font-display: swap; |