@absolutejs/absolute 0.19.0-beta.352 → 0.19.0-beta.354

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39818,6 +39818,171 @@ var require_cjs = __commonJS((exports) => {
39818
39818
  } });
39819
39819
  });
39820
39820
 
39821
+ // src/utils/imageProcessing.ts
39822
+ import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
39823
+ import { join as join3, resolve as resolve4 } from "path";
39824
+ var DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, DEFAULT_QUALITY = 75, OPTIMIZATION_ENDPOINT = "/_absolute/image", BLUR_DEVIATION = 20, sharpModule = undefined, sharpLoaded = false, sharpWarned = false, snapToSize = (target, sizes) => {
39825
+ for (const size of sizes) {
39826
+ if (size >= target)
39827
+ return size;
39828
+ }
39829
+ return sizes[sizes.length - 1] ?? target;
39830
+ }, matchHostname = (actual, pattern) => {
39831
+ if (pattern === actual)
39832
+ return true;
39833
+ if (pattern.startsWith("*.")) {
39834
+ const suffix = pattern.slice(1);
39835
+ return actual.endsWith(suffix) && actual.length > suffix.length;
39836
+ }
39837
+ return false;
39838
+ }, matchPathname = (actual, pattern) => {
39839
+ if (pattern.endsWith("/**")) {
39840
+ const prefix = pattern.slice(0, -2);
39841
+ return actual.startsWith(prefix);
39842
+ }
39843
+ return actual === pattern;
39844
+ }, MIME_MAP, callSharp = (sharpRef, input3) => {
39845
+ const factory = sharpRef;
39846
+ return factory(input3);
39847
+ }, toBuffer = (input3) => {
39848
+ if (Buffer.isBuffer(input3))
39849
+ return input3;
39850
+ return Buffer.from(input3);
39851
+ }, buildOptimizedUrl = (src, width, quality, basePath = OPTIMIZATION_ENDPOINT) => `${basePath}?url=${encodeURIComponent(src)}&w=${width}&q=${quality}`, formatToMime = (format) => MIME_MAP[format], generateBlurSvg = (base64Thumbnail) => {
39852
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320"><filter id="b" color-interpolation-filters="sRGB"><feGaussianBlur stdDeviation="${BLUR_DEVIATION}"/><feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1"/></filter><image filter="url(#b)" x="0" y="0" width="100%" height="100%" href="${base64Thumbnail}"/></svg>`;
39853
+ const encoded = encodeURIComponent(svg);
39854
+ return `url("data:image/svg+xml,${encoded}")`;
39855
+ }, generateSrcSet = (src, width, sizes, config, loader) => {
39856
+ const quality = config?.quality ?? DEFAULT_QUALITY;
39857
+ const basePath = config?.path ?? OPTIMIZATION_ENDPOINT;
39858
+ const buildUrl = loader ?? ((params) => buildOptimizedUrl(params.src, params.width, params.quality, basePath));
39859
+ if (sizes) {
39860
+ const allSizes = getAllSizes(config);
39861
+ return allSizes.map((sizeWidth) => `${buildUrl({ quality, src, width: sizeWidth })} ${sizeWidth}w`).join(", ");
39862
+ }
39863
+ if (width) {
39864
+ const allSizes = getAllSizes(config);
39865
+ const w1x = snapToSize(width, allSizes);
39866
+ const w2x = snapToSize(width * 2, allSizes);
39867
+ return `${buildUrl({ quality, src, width: w1x })} 1x, ${buildUrl({ quality, src, width: w2x })} 2x`;
39868
+ }
39869
+ const deviceSizes = config?.deviceSizes ?? DEFAULT_DEVICE_SIZES;
39870
+ return deviceSizes.map((sizeWidth) => `${buildUrl({ quality, src, width: sizeWidth })} ${sizeWidth}w`).join(", ");
39871
+ }, getAllSizes = (config) => {
39872
+ const device = config?.deviceSizes ?? DEFAULT_DEVICE_SIZES;
39873
+ const image = config?.imageSizes ?? DEFAULT_IMAGE_SIZES;
39874
+ return [...device, ...image].sort((left, right) => left - right);
39875
+ }, getCacheDir = (buildDir) => {
39876
+ const dir = join3(buildDir, ".cache", "images");
39877
+ if (!existsSync3(dir))
39878
+ mkdirSync(dir, { recursive: true });
39879
+ return dir;
39880
+ }, getCacheKey = (url, width, quality, format) => {
39881
+ const hasher = new Bun.CryptoHasher("sha256");
39882
+ hasher.update(`${url}|${width}|${quality}|${format}`);
39883
+ return hasher.digest("hex");
39884
+ }, isCacheStale = (meta) => Date.now() > meta.expireAt, matchRemotePattern = (urlString, patterns) => {
39885
+ let parsed;
39886
+ try {
39887
+ parsed = new URL(urlString);
39888
+ } catch {
39889
+ return false;
39890
+ }
39891
+ return patterns.some((pattern) => {
39892
+ if (pattern.protocol && parsed.protocol !== `${pattern.protocol}:`)
39893
+ return false;
39894
+ if (!matchHostname(parsed.hostname, pattern.hostname))
39895
+ return false;
39896
+ if (pattern.port && parsed.port !== pattern.port)
39897
+ return false;
39898
+ if (pattern.pathname && !matchPathname(parsed.pathname, pattern.pathname))
39899
+ return false;
39900
+ return true;
39901
+ });
39902
+ }, negotiateFormat = (acceptHeader, configuredFormats) => {
39903
+ for (const format of configuredFormats) {
39904
+ const mime = MIME_MAP[format];
39905
+ if (mime && acceptHeader.includes(mime))
39906
+ return format;
39907
+ }
39908
+ if (configuredFormats.includes("webp") && acceptHeader.includes("image/webp")) {
39909
+ return "webp";
39910
+ }
39911
+ return "jpeg";
39912
+ }, AVIF_QUALITY_OFFSET = 20, AVIF_EFFORT = 3, optimizeImage = async (buffer, width, quality, format) => {
39913
+ const sharp = await tryLoadSharp();
39914
+ if (!sharp)
39915
+ return toBuffer(buffer);
39916
+ const pipeline = callSharp(sharp, toBuffer(buffer)).rotate().resize(width, undefined, { withoutEnlargement: true });
39917
+ switch (format) {
39918
+ case "avif":
39919
+ return pipeline.avif({
39920
+ effort: AVIF_EFFORT,
39921
+ quality: Math.max(1, quality - AVIF_QUALITY_OFFSET)
39922
+ }).toBuffer();
39923
+ case "jpeg":
39924
+ return pipeline.jpeg({ mozjpeg: true, quality }).toBuffer();
39925
+ case "png":
39926
+ return pipeline.png({ quality }).toBuffer();
39927
+ case "webp":
39928
+ return pipeline.webp({ quality }).toBuffer();
39929
+ default:
39930
+ return toBuffer(buffer);
39931
+ }
39932
+ }, readFromCache = (cacheDir, cacheKey) => {
39933
+ const metaPath = join3(cacheDir, `${cacheKey}.meta`);
39934
+ const dataPath = join3(cacheDir, `${cacheKey}.data`);
39935
+ if (!existsSync3(metaPath) || !existsSync3(dataPath))
39936
+ return null;
39937
+ try {
39938
+ const meta = JSON.parse(readFileSync2(metaPath, "utf-8"));
39939
+ const buffer = readFileSync2(dataPath);
39940
+ return { buffer, meta };
39941
+ } catch {
39942
+ return null;
39943
+ }
39944
+ }, tryLoadSharp = async () => {
39945
+ if (sharpLoaded)
39946
+ return sharpModule;
39947
+ sharpLoaded = true;
39948
+ try {
39949
+ const sharpPath = resolve4(process.cwd(), "node_modules/sharp");
39950
+ const mod = await import(sharpPath);
39951
+ sharpModule = mod.default ?? mod;
39952
+ return sharpModule;
39953
+ } catch {
39954
+ if (sharpWarned)
39955
+ return null;
39956
+ sharpWarned = true;
39957
+ console.warn("[image] sharp not installed \u2014 serving unoptimized images. Install with: bun add sharp");
39958
+ return null;
39959
+ }
39960
+ }, writeToCache = (cacheDir, cacheKey, buffer, meta) => {
39961
+ const metaPath = join3(cacheDir, `${cacheKey}.meta`);
39962
+ const dataPath = join3(cacheDir, `${cacheKey}.data`);
39963
+ writeFileSync2(dataPath, buffer);
39964
+ writeFileSync2(metaPath, JSON.stringify(meta));
39965
+ };
39966
+ var init_imageProcessing = __esm(() => {
39967
+ DEFAULT_DEVICE_SIZES = [
39968
+ 640,
39969
+ 750,
39970
+ 828,
39971
+ 1080,
39972
+ 1200,
39973
+ 1920,
39974
+ 2048,
39975
+ 3840
39976
+ ];
39977
+ DEFAULT_IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 384];
39978
+ MIME_MAP = {
39979
+ avif: "image/avif",
39980
+ jpeg: "image/jpeg",
39981
+ png: "image/png",
39982
+ webp: "image/webp"
39983
+ };
39984
+ });
39985
+
39821
39986
  // src/client/streamSwap.ts
39822
39987
  var streamSwapRuntime = () => {
39823
39988
  if (window.__ABS_SLOT_RUNTIME__ === true)
@@ -40579,6 +40744,224 @@ var IslandStore = IslandStoreImpl;
40579
40744
  // src/angular/renderIsland.ts
40580
40745
  init_renderIslandMarkup();
40581
40746
  var renderIsland = (props) => renderIslandMarkup(requireCurrentIslandRegistry(), props);
40747
+ // src/angular/components/defer-slot.component.ts
40748
+ import { Component as Component5, computed, input as input2 } from "@angular/core";
40749
+
40750
+ // src/angular/components/stream-slot.component.ts
40751
+ import { Component as Component4, input } from "@angular/core";
40752
+ class StreamSlotComponent {
40753
+ className = input();
40754
+ errorHtml = input();
40755
+ fallbackHtml = input("");
40756
+ id = input.required();
40757
+ resolve = input.required();
40758
+ timeoutMs = input();
40759
+ ngOnInit() {
40760
+ if (typeof window !== "undefined")
40761
+ return;
40762
+ registerStreamingSlot({
40763
+ errorHtml: this.errorHtml(),
40764
+ fallbackHtml: this.fallbackHtml(),
40765
+ id: this.id(),
40766
+ resolve: this.resolve(),
40767
+ timeoutMs: this.timeoutMs()
40768
+ });
40769
+ }
40770
+ }
40771
+ StreamSlotComponent = __legacyDecorateClassTS([
40772
+ Component4({
40773
+ selector: "abs-stream-slot",
40774
+ standalone: true,
40775
+ template: `
40776
+ <div
40777
+ [attr.id]="'slot-' + id()"
40778
+ [attr.class]="className()"
40779
+ data-absolute-slot="true"
40780
+ [innerHTML]="fallbackHtml()"
40781
+ ></div>
40782
+ `
40783
+ })
40784
+ ], StreamSlotComponent);
40785
+
40786
+ // src/angular/components/defer-slot.component.ts
40787
+ class DeferSlotComponent {
40788
+ className = input2();
40789
+ errorHtml = input2();
40790
+ fallbackHtml = input2("");
40791
+ id = input2.required();
40792
+ promise = input2();
40793
+ resolve = input2();
40794
+ timeoutMs = input2();
40795
+ resolvedResolver = computed(() => {
40796
+ const resolver = this.resolve();
40797
+ if (resolver)
40798
+ return resolver;
40799
+ const promise = this.promise();
40800
+ if (promise)
40801
+ return () => promise;
40802
+ return () => "";
40803
+ });
40804
+ }
40805
+ DeferSlotComponent = __legacyDecorateClassTS([
40806
+ Component5({
40807
+ selector: "abs-defer-slot",
40808
+ standalone: true,
40809
+ template: `
40810
+ <abs-stream-slot
40811
+ [className]="className()"
40812
+ [errorHtml]="errorHtml()"
40813
+ [fallbackHtml]="fallbackHtml()"
40814
+ [id]="id()"
40815
+ [resolve]="resolvedResolver()"
40816
+ [timeoutMs]="timeoutMs()"
40817
+ ></abs-stream-slot>
40818
+ `,
40819
+ imports: [StreamSlotComponent]
40820
+ })
40821
+ ], DeferSlotComponent);
40822
+ // src/angular/components/image.component.ts
40823
+ init_imageProcessing();
40824
+ import { Component as Component6, computed as computed2, input as input3, signal } from "@angular/core";
40825
+ import { NgStyle } from "@angular/common";
40826
+ var resolveBlurBg = (placeholderValue, blurDataUrl) => {
40827
+ if (typeof placeholderValue === "string" && placeholderValue !== "blur" && placeholderValue.startsWith("data:")) {
40828
+ return generateBlurSvg(placeholderValue);
40829
+ }
40830
+ if (blurDataUrl)
40831
+ return generateBlurSvg(blurDataUrl);
40832
+ return;
40833
+ };
40834
+
40835
+ class ImageComponent {
40836
+ alt = input3.required();
40837
+ blurDataURL = input3();
40838
+ className = input3();
40839
+ crossOrigin = input3();
40840
+ fetchPriority = input3();
40841
+ fill = input3(false);
40842
+ height = input3();
40843
+ loader = input3();
40844
+ loading = input3("lazy");
40845
+ onError = input3();
40846
+ onLoad = input3();
40847
+ overrideSrc = input3();
40848
+ placeholder = input3("empty");
40849
+ priority = input3(false);
40850
+ quality = input3(DEFAULT_QUALITY);
40851
+ referrerPolicy = input3();
40852
+ sizes = input3();
40853
+ src = input3.required();
40854
+ style = input3();
40855
+ unoptimized = input3(false);
40856
+ width = input3();
40857
+ blurRemoved = signal(false);
40858
+ resolvedSrc = computed2(() => {
40859
+ const override = this.overrideSrc();
40860
+ if (override)
40861
+ return override;
40862
+ if (this.unoptimized())
40863
+ return this.src();
40864
+ const loaderFn = this.loader();
40865
+ if (loaderFn)
40866
+ return loaderFn({
40867
+ quality: this.quality(),
40868
+ src: this.src(),
40869
+ width: this.width() ?? 0
40870
+ });
40871
+ const currentWidth = this.width();
40872
+ if (!currentWidth)
40873
+ return buildOptimizedUrl(this.src(), 0, this.quality());
40874
+ return buildOptimizedUrl(this.src(), currentWidth, this.quality());
40875
+ });
40876
+ srcSet = computed2(() => this.unoptimized() ? null : generateSrcSet(this.src(), this.width(), this.sizes(), undefined, this.loader() ?? undefined) ?? null);
40877
+ resolvedSrcSet = computed2(() => this.srcSet() ?? null);
40878
+ resolvedSizes = computed2(() => this.sizes() ?? (this.fill() ? "100vw" : null));
40879
+ resolvedCrossOrigin = computed2(() => this.crossOrigin() ?? null);
40880
+ resolvedReferrerPolicy = computed2(() => this.referrerPolicy() ?? null);
40881
+ resolvedLoading = computed2(() => this.priority() ? "eager" : this.loading());
40882
+ resolvedFetchPriority = computed2(() => this.priority() ? "high" : this.fetchPriority() ?? null);
40883
+ imgStyle = computed2(() => {
40884
+ const base = {
40885
+ ...this.style() ?? {},
40886
+ color: "transparent"
40887
+ };
40888
+ const hasBlur = !this.blurRemoved() && (this.placeholder() === "blur" || typeof this.placeholder() === "string" && this.placeholder() !== "empty" && (this.placeholder() ?? "").startsWith("data:"));
40889
+ const blurValue = hasBlur ? resolveBlurBg(this.placeholder(), this.blurDataURL()) : undefined;
40890
+ if (blurValue) {
40891
+ base["background-image"] = blurValue;
40892
+ base["background-position"] = "center";
40893
+ base["background-repeat"] = "no-repeat";
40894
+ base["background-size"] = "cover";
40895
+ }
40896
+ if (this.fill()) {
40897
+ base.height = "100%";
40898
+ base.inset = "0";
40899
+ base["object-fit"] = "cover";
40900
+ base.position = "absolute";
40901
+ base.width = "100%";
40902
+ }
40903
+ return base;
40904
+ });
40905
+ handleLoad(event) {
40906
+ this.blurRemoved.set(true);
40907
+ const callback = this.onLoad();
40908
+ if (callback)
40909
+ callback(event);
40910
+ }
40911
+ handleError(event) {
40912
+ const callback = this.onError();
40913
+ if (callback)
40914
+ callback(event);
40915
+ }
40916
+ }
40917
+ ImageComponent = __legacyDecorateClassTS([
40918
+ Component6({
40919
+ imports: [NgStyle],
40920
+ selector: "abs-image",
40921
+ standalone: true,
40922
+ template: `
40923
+ @if (fill()) {
40924
+ <span
40925
+ style="position:relative;overflow:hidden;display:block;width:100%;height:100%"
40926
+ >
40927
+ <img
40928
+ [alt]="alt()"
40929
+ [src]="resolvedSrc()"
40930
+ [attr.srcset]="resolvedSrcSet()"
40931
+ [attr.sizes]="resolvedSizes()"
40932
+ [loading]="resolvedLoading()"
40933
+ [class]="className()"
40934
+ [ngStyle]="imgStyle()"
40935
+ [attr.crossorigin]="resolvedCrossOrigin()"
40936
+ [attr.referrerpolicy]="resolvedReferrerPolicy()"
40937
+ [attr.fetchpriority]="resolvedFetchPriority()"
40938
+ decoding="async"
40939
+ (load)="handleLoad($event)"
40940
+ (error)="handleError($event)"
40941
+ />
40942
+ </span>
40943
+ } @else {
40944
+ <img
40945
+ [alt]="alt()"
40946
+ [src]="resolvedSrc()"
40947
+ [attr.srcset]="resolvedSrcSet()"
40948
+ [attr.sizes]="resolvedSizes()"
40949
+ [width]="width()"
40950
+ [height]="height()"
40951
+ [loading]="resolvedLoading()"
40952
+ [class]="className()"
40953
+ [ngStyle]="imgStyle()"
40954
+ [attr.crossorigin]="resolvedCrossOrigin()"
40955
+ [attr.referrerpolicy]="resolvedReferrerPolicy()"
40956
+ [attr.fetchpriority]="resolvedFetchPriority()"
40957
+ decoding="async"
40958
+ (load)="handleLoad($event)"
40959
+ (error)="handleError($event)"
40960
+ />
40961
+ }
40962
+ `
40963
+ })
40964
+ ], ImageComponent);
40582
40965
 
40583
40966
  // src/angular/index.ts
40584
40967
  var handleAngularPageRequest2 = wrapPageHandlerWithStreamingSlots(handleAngularPageRequest);
@@ -40587,9 +40970,12 @@ export {
40587
40970
  handleAngularPageRequest2 as handleAngularPageRequest,
40588
40971
  getCachedRouteData,
40589
40972
  createTypedIsland,
40973
+ StreamSlotComponent,
40590
40974
  IslandStore,
40591
- Island
40975
+ Island,
40976
+ ImageComponent,
40977
+ DeferSlotComponent
40592
40978
  };
40593
40979
 
40594
- //# debugId=87DA1ED8BAE8A55064756E2164756E21
40980
+ //# debugId=A6ACDE5F26CA47D664756E2164756E21
40595
40981
  //# sourceMappingURL=index.js.map