@cogenta/render 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/astro/integration.d.ts +57 -0
- package/dist/astro/integration.d.ts.map +1 -0
- package/dist/astro/integration.js +72 -0
- package/dist/astro/integration.js.map +1 -0
- package/dist/build/index.d.ts +7 -0
- package/dist/build/index.d.ts.map +1 -0
- package/dist/build/index.js +7 -0
- package/dist/build/index.js.map +1 -0
- package/dist/build/integration.d.ts +25 -0
- package/dist/build/integration.d.ts.map +1 -0
- package/dist/build/integration.js +41 -0
- package/dist/build/integration.js.map +1 -0
- package/dist/build/plan.d.ts +41 -0
- package/dist/build/plan.d.ts.map +1 -0
- package/dist/build/plan.js +48 -0
- package/dist/build/plan.js.map +1 -0
- package/dist/build/refusal.d.ts +14 -0
- package/dist/build/refusal.d.ts.map +1 -0
- package/dist/build/refusal.js +110 -0
- package/dist/build/refusal.js.map +1 -0
- package/dist/build/requirements.d.ts +59 -0
- package/dist/build/requirements.d.ts.map +1 -0
- package/dist/build/requirements.js +74 -0
- package/dist/build/requirements.js.map +1 -0
- package/dist/build/run-build.d.ts +43 -0
- package/dist/build/run-build.d.ts.map +1 -0
- package/dist/build/run-build.js +73 -0
- package/dist/build/run-build.js.map +1 -0
- package/dist/build/targets.d.ts +47 -0
- package/dist/build/targets.d.ts.map +1 -0
- package/dist/build/targets.js +69 -0
- package/dist/build/targets.js.map +1 -0
- package/dist/cache/index.d.ts +7 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +4 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/page-cache.d.ts +64 -0
- package/dist/cache/page-cache.d.ts.map +1 -0
- package/dist/cache/page-cache.js +128 -0
- package/dist/cache/page-cache.js.map +1 -0
- package/dist/cache/recorder.d.ts +49 -0
- package/dist/cache/recorder.d.ts.map +1 -0
- package/dist/cache/recorder.js +77 -0
- package/dist/cache/recorder.js.map +1 -0
- package/dist/cache/tags.d.ts +55 -0
- package/dist/cache/tags.d.ts.map +1 -0
- package/dist/cache/tags.js +62 -0
- package/dist/cache/tags.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/content/client.d.ts +35 -0
- package/dist/content/client.d.ts.map +1 -0
- package/dist/content/client.js +155 -0
- package/dist/content/client.js.map +1 -0
- package/dist/content/types.d.ts +104 -0
- package/dist/content/types.d.ts.map +1 -0
- package/dist/content/types.js +16 -0
- package/dist/content/types.js.map +1 -0
- package/dist/context/render-context.d.ts +31 -0
- package/dist/context/render-context.d.ts.map +1 -0
- package/dist/context/render-context.js +152 -0
- package/dist/context/render-context.js.map +1 -0
- package/dist/context/types.d.ts +49 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/context/types.js +2 -0
- package/dist/context/types.js.map +1 -0
- package/dist/images/errors.d.ts +20 -0
- package/dist/images/errors.d.ts.map +1 -0
- package/dist/images/errors.js +58 -0
- package/dist/images/errors.js.map +1 -0
- package/dist/images/format.d.ts +18 -0
- package/dist/images/format.d.ts.map +1 -0
- package/dist/images/format.js +34 -0
- package/dist/images/format.js.map +1 -0
- package/dist/images/geometry.d.ts +46 -0
- package/dist/images/geometry.d.ts.map +1 -0
- package/dist/images/geometry.js +127 -0
- package/dist/images/geometry.js.map +1 -0
- package/dist/images/index.d.ts +29 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +30 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/pipeline.d.ts +51 -0
- package/dist/images/pipeline.d.ts.map +1 -0
- package/dist/images/pipeline.js +105 -0
- package/dist/images/pipeline.js.map +1 -0
- package/dist/images/sharp.d.ts +57 -0
- package/dist/images/sharp.d.ts.map +1 -0
- package/dist/images/sharp.js +139 -0
- package/dist/images/sharp.js.map +1 -0
- package/dist/images/srcset.d.ts +33 -0
- package/dist/images/srcset.d.ts.map +1 -0
- package/dist/images/srcset.js +94 -0
- package/dist/images/srcset.js.map +1 -0
- package/dist/images/store.d.ts +32 -0
- package/dist/images/store.d.ts.map +1 -0
- package/dist/images/store.js +54 -0
- package/dist/images/store.js.map +1 -0
- package/dist/images/types.d.ts +128 -0
- package/dist/images/types.d.ts.map +1 -0
- package/dist/images/types.js +22 -0
- package/dist/images/types.js.map +1 -0
- package/dist/images/wasm.d.ts +43 -0
- package/dist/images/wasm.d.ts.map +1 -0
- package/dist/images/wasm.js +128 -0
- package/dist/images/wasm.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/pwa/build.d.ts +39 -0
- package/dist/pwa/build.d.ts.map +1 -0
- package/dist/pwa/build.js +38 -0
- package/dist/pwa/build.js.map +1 -0
- package/dist/pwa/client.d.ts +90 -0
- package/dist/pwa/client.d.ts.map +1 -0
- package/dist/pwa/client.js +76 -0
- package/dist/pwa/client.js.map +1 -0
- package/dist/pwa/index.d.ts +29 -0
- package/dist/pwa/index.d.ts.map +1 -0
- package/dist/pwa/index.js +22 -0
- package/dist/pwa/index.js.map +1 -0
- package/dist/pwa/manifest.d.ts +69 -0
- package/dist/pwa/manifest.d.ts.map +1 -0
- package/dist/pwa/manifest.js +108 -0
- package/dist/pwa/manifest.js.map +1 -0
- package/dist/pwa/offline.d.ts +44 -0
- package/dist/pwa/offline.d.ts.map +1 -0
- package/dist/pwa/offline.js +94 -0
- package/dist/pwa/offline.js.map +1 -0
- package/dist/pwa/service-worker.d.ts +71 -0
- package/dist/pwa/service-worker.d.ts.map +1 -0
- package/dist/pwa/service-worker.js +119 -0
- package/dist/pwa/service-worker.js.map +1 -0
- package/dist/pwa/strategy.d.ts +36 -0
- package/dist/pwa/strategy.d.ts.map +1 -0
- package/dist/pwa/strategy.js +180 -0
- package/dist/pwa/strategy.js.map +1 -0
- package/dist/pwa/sw-runtime.d.ts +109 -0
- package/dist/pwa/sw-runtime.d.ts.map +1 -0
- package/dist/pwa/sw-runtime.js +214 -0
- package/dist/pwa/sw-runtime.js.map +1 -0
- package/dist/pwa/types.d.ts +107 -0
- package/dist/pwa/types.d.ts.map +1 -0
- package/dist/pwa/types.js +18 -0
- package/dist/pwa/types.js.map +1 -0
- package/dist/pwa/version.d.ts +45 -0
- package/dist/pwa/version.d.ts.map +1 -0
- package/dist/pwa/version.js +89 -0
- package/dist/pwa/version.js.map +1 -0
- package/dist/skin/color.d.ts +50 -0
- package/dist/skin/color.d.ts.map +1 -0
- package/dist/skin/color.js +176 -0
- package/dist/skin/color.js.map +1 -0
- package/dist/skin/css.d.ts +19 -0
- package/dist/skin/css.d.ts.map +1 -0
- package/dist/skin/css.js +69 -0
- package/dist/skin/css.js.map +1 -0
- package/dist/skin/errors.d.ts +22 -0
- package/dist/skin/errors.d.ts.map +1 -0
- package/dist/skin/errors.js +59 -0
- package/dist/skin/errors.js.map +1 -0
- package/dist/skin/index.d.ts +15 -0
- package/dist/skin/index.d.ts.map +1 -0
- package/dist/skin/index.js +11 -0
- package/dist/skin/index.js.map +1 -0
- package/dist/skin/sheet.d.ts +42 -0
- package/dist/skin/sheet.d.ts.map +1 -0
- package/dist/skin/sheet.js +36 -0
- package/dist/skin/sheet.js.map +1 -0
- package/dist/skin/tokens.d.ts +90 -0
- package/dist/skin/tokens.d.ts.map +1 -0
- package/dist/skin/tokens.js +56 -0
- package/dist/skin/tokens.js.map +1 -0
- package/dist/skin/validate.d.ts +14 -0
- package/dist/skin/validate.d.ts.map +1 -0
- package/dist/skin/validate.js +272 -0
- package/dist/skin/validate.js.map +1 -0
- package/dist/theme/load-theme.d.ts +37 -0
- package/dist/theme/load-theme.d.ts.map +1 -0
- package/dist/theme/load-theme.js +102 -0
- package/dist/theme/load-theme.js.map +1 -0
- package/dist/theme/manifest.d.ts +48 -0
- package/dist/theme/manifest.d.ts.map +1 -0
- package/dist/theme/manifest.js +36 -0
- package/dist/theme/manifest.js.map +1 -0
- package/dist/theme/verify/forbidden.d.ts +39 -0
- package/dist/theme/verify/forbidden.d.ts.map +1 -0
- package/dist/theme/verify/forbidden.js +107 -0
- package/dist/theme/verify/forbidden.js.map +1 -0
- package/dist/theme/verify/scan-file.d.ts +15 -0
- package/dist/theme/verify/scan-file.d.ts.map +1 -0
- package/dist/theme/verify/scan-file.js +151 -0
- package/dist/theme/verify/scan-file.js.map +1 -0
- package/dist/theme/verify/scanner.d.ts +52 -0
- package/dist/theme/verify/scanner.d.ts.map +1 -0
- package/dist/theme/verify/scanner.js +304 -0
- package/dist/theme/verify/scanner.js.map +1 -0
- package/dist/theme/verify/verify-theme.d.ts +44 -0
- package/dist/theme/verify/verify-theme.d.ts.map +1 -0
- package/dist/theme/verify/verify-theme.js +179 -0
- package/dist/theme/verify/verify-theme.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type { DriverTier } from '@cogenta/core';
|
|
2
|
+
/**
|
|
3
|
+
* The image pipeline, as a Cogenta driver.
|
|
4
|
+
*
|
|
5
|
+
* One interface, an optimal tier (`sharp`, native libvips) and a degraded tier
|
|
6
|
+
* (WebAssembly libvips), and a single contract suite played against both — the
|
|
7
|
+
* shape rule R1 asks of every infrastructure need. `sharp` is the fastest thing
|
|
8
|
+
* available on a machine that can install it, and it cannot be installed on a
|
|
9
|
+
* good number of the machines Cogenta targets: ARM, musl, shared hosting. Rule
|
|
10
|
+
* R10 is explicit about it, so the native tier is an *optional* peer and the
|
|
11
|
+
* WASM tier is the one that always exists.
|
|
12
|
+
*
|
|
13
|
+
* What a driver does is deliberately small: decode a header, and execute a
|
|
14
|
+
* transform that has **already been decided**. Every geometric decision — which
|
|
15
|
+
* crop rectangle a focal point implies, which widths a `srcset` offers, whether
|
|
16
|
+
* a request would upscale — lives in `geometry.ts` and `srcset.ts`, shared by
|
|
17
|
+
* both tiers. That is what makes "the same image, on any host, byte-for-byte
|
|
18
|
+
* comparable output" achievable: the two tiers cannot disagree about geometry
|
|
19
|
+
* because only one of them computes it.
|
|
20
|
+
*/
|
|
21
|
+
/** Output formats contract D allows a theme to ask for. */
|
|
22
|
+
export declare const IMAGE_FORMATS: readonly ['avif', 'webp', 'jpeg', 'png'];
|
|
23
|
+
export type ImageFormat = (typeof IMAGE_FORMATS)[number];
|
|
24
|
+
export type ImageFit = 'cover' | 'contain';
|
|
25
|
+
/** Normalised coordinates, `0..1`, of the point a crop must keep in frame. */
|
|
26
|
+
export interface FocalPoint {
|
|
27
|
+
readonly x: number;
|
|
28
|
+
readonly y: number;
|
|
29
|
+
}
|
|
30
|
+
/** Contract D — `ImageOptions`, frozen at `theme@1.1`. */
|
|
31
|
+
export interface ImageOptions {
|
|
32
|
+
readonly width?: number | undefined;
|
|
33
|
+
readonly height?: number | undefined;
|
|
34
|
+
readonly format?: ImageFormat | undefined;
|
|
35
|
+
readonly fit?: ImageFit | undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Contract D — `ImageSource`, frozen at `theme@1.1`. */
|
|
38
|
+
export interface ImageSource {
|
|
39
|
+
/**
|
|
40
|
+
* What this media actually is. `hero.media` and `mediaFigure.media` accept an
|
|
41
|
+
* image **or** a video (contract B), and a theme that cannot tell them apart
|
|
42
|
+
* renders every video as a broken `<img>`.
|
|
43
|
+
*/
|
|
44
|
+
readonly kind: 'image' | 'video';
|
|
45
|
+
readonly src: string;
|
|
46
|
+
/** Empty for a video: there is no responsive source set to offer. */
|
|
47
|
+
readonly srcset: string;
|
|
48
|
+
readonly width: number;
|
|
49
|
+
readonly height: number;
|
|
50
|
+
/** Alt text and focal point come from the media entity, never invented here. */
|
|
51
|
+
readonly alt: string;
|
|
52
|
+
readonly focal: FocalPoint | null;
|
|
53
|
+
/** Video only: the still shown before playback. */
|
|
54
|
+
readonly poster?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A media entity as the delivery plane sees it.
|
|
58
|
+
*
|
|
59
|
+
* A superset of `MediaReference` with the two fields a video needs. It is
|
|
60
|
+
* declared here rather than imported for the reason ADR-0016 gives about every
|
|
61
|
+
* other wire type: what crosses the boundary is JSON, and the pipeline must not
|
|
62
|
+
* be able to reach the content engine's declarations.
|
|
63
|
+
*/
|
|
64
|
+
export interface MediaAsset {
|
|
65
|
+
readonly id: string;
|
|
66
|
+
readonly kind: 'image' | 'video';
|
|
67
|
+
readonly alt?: string | undefined;
|
|
68
|
+
readonly width?: number | undefined;
|
|
69
|
+
readonly height?: number | undefined;
|
|
70
|
+
readonly focal?: FocalPoint | null | undefined;
|
|
71
|
+
/** Delivery URL of the original. Absent means "derive it from the id". */
|
|
72
|
+
readonly url?: string | undefined;
|
|
73
|
+
/** Video only: the still shown before playback. */
|
|
74
|
+
readonly poster?: string | undefined;
|
|
75
|
+
}
|
|
76
|
+
export interface Size {
|
|
77
|
+
readonly width: number;
|
|
78
|
+
readonly height: number;
|
|
79
|
+
}
|
|
80
|
+
/** A rectangle in **source** pixels. */
|
|
81
|
+
export interface Rect extends Size {
|
|
82
|
+
readonly left: number;
|
|
83
|
+
readonly top: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A fully decided transform. Crop first, then resize, then encode — in that
|
|
87
|
+
* order, on every tier. No option here is a hint: a driver that reinterprets one
|
|
88
|
+
* makes the two tiers disagree.
|
|
89
|
+
*/
|
|
90
|
+
export interface TransformOperation {
|
|
91
|
+
readonly crop: Rect | null;
|
|
92
|
+
readonly resize: Size | null;
|
|
93
|
+
readonly format: ImageFormat;
|
|
94
|
+
/** `1..100`. Ignored by `png`, which is lossless. */
|
|
95
|
+
readonly quality: number;
|
|
96
|
+
}
|
|
97
|
+
export interface ImageMetadata {
|
|
98
|
+
readonly width: number;
|
|
99
|
+
readonly height: number;
|
|
100
|
+
readonly format: ImageFormat;
|
|
101
|
+
}
|
|
102
|
+
export interface RenderedVariant {
|
|
103
|
+
readonly bytes: Uint8Array;
|
|
104
|
+
readonly width: number;
|
|
105
|
+
readonly height: number;
|
|
106
|
+
readonly format: ImageFormat;
|
|
107
|
+
/** Ready to serve as `Content-Type`. */
|
|
108
|
+
readonly contentType: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* One implementation of the pipeline. Stateless between calls, so that a
|
|
112
|
+
* selection can be shared by every request without a lock.
|
|
113
|
+
*/
|
|
114
|
+
export interface ImageTransformer {
|
|
115
|
+
readonly name: string;
|
|
116
|
+
readonly tier: DriverTier;
|
|
117
|
+
/** Intrinsic size and container format. Must not decode the pixels. */
|
|
118
|
+
metadata(bytes: Uint8Array): Promise<ImageMetadata>;
|
|
119
|
+
transform(bytes: Uint8Array, operation: TransformOperation): Promise<RenderedVariant>;
|
|
120
|
+
dispose(): Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
/** The resolved `images` section of the configuration. */
|
|
123
|
+
export interface ImageConfig {
|
|
124
|
+
readonly driver?: string;
|
|
125
|
+
/** Default encoder quality, `1..100`. */
|
|
126
|
+
readonly quality?: number;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/images/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The image pipeline, as a Cogenta driver.
|
|
3
|
+
*
|
|
4
|
+
* One interface, an optimal tier (`sharp`, native libvips) and a degraded tier
|
|
5
|
+
* (WebAssembly libvips), and a single contract suite played against both — the
|
|
6
|
+
* shape rule R1 asks of every infrastructure need. `sharp` is the fastest thing
|
|
7
|
+
* available on a machine that can install it, and it cannot be installed on a
|
|
8
|
+
* good number of the machines Cogenta targets: ARM, musl, shared hosting. Rule
|
|
9
|
+
* R10 is explicit about it, so the native tier is an *optional* peer and the
|
|
10
|
+
* WASM tier is the one that always exists.
|
|
11
|
+
*
|
|
12
|
+
* What a driver does is deliberately small: decode a header, and execute a
|
|
13
|
+
* transform that has **already been decided**. Every geometric decision — which
|
|
14
|
+
* crop rectangle a focal point implies, which widths a `srcset` offers, whether
|
|
15
|
+
* a request would upscale — lives in `geometry.ts` and `srcset.ts`, shared by
|
|
16
|
+
* both tiers. That is what makes "the same image, on any host, byte-for-byte
|
|
17
|
+
* comparable output" achievable: the two tiers cannot disagree about geometry
|
|
18
|
+
* because only one of them computes it.
|
|
19
|
+
*/
|
|
20
|
+
/** Output formats contract D allows a theme to ask for. */
|
|
21
|
+
export const IMAGE_FORMATS = ['avif', 'webp', 'jpeg', 'png'];
|
|
22
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/images/types.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAU,CAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Driver } from '@cogenta/core';
|
|
2
|
+
import type { ImageConfig, ImageTransformer } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The degraded tier: libvips compiled to WebAssembly.
|
|
5
|
+
*
|
|
6
|
+
* This is the implementation that must always work, and the reason the pipeline
|
|
7
|
+
* has two tiers at all. `sharp` links a native libvips, and rule R10 names the
|
|
8
|
+
* hosts where that does not install — ARM, musl, shared hosting. A CMS whose
|
|
9
|
+
* images stop working on those hosts is not a CMS that runs anywhere, so the
|
|
10
|
+
* WASM tier is a plain dependency while the native one is an optional peer.
|
|
11
|
+
*
|
|
12
|
+
* It is slower — roughly a small multiple of native for a typical variant — and
|
|
13
|
+
* that is an acceptable price for something that runs once per variant and is
|
|
14
|
+
* then cached forever.
|
|
15
|
+
*
|
|
16
|
+
* Its types are declared structurally rather than imported, exactly as the Redis
|
|
17
|
+
* cache driver does: a 12 MB WebAssembly package has no business appearing in
|
|
18
|
+
* the published type declarations of `@cogenta/render`.
|
|
19
|
+
*/
|
|
20
|
+
interface VipsImageLike {
|
|
21
|
+
readonly width: number;
|
|
22
|
+
readonly height: number;
|
|
23
|
+
hasAlpha(): boolean;
|
|
24
|
+
crop(left: number, top: number, width: number, height: number): VipsImageLike;
|
|
25
|
+
resize(scale: number, options?: {
|
|
26
|
+
vscale?: number;
|
|
27
|
+
}): VipsImageLike;
|
|
28
|
+
flatten(options?: {
|
|
29
|
+
background?: readonly number[];
|
|
30
|
+
}): VipsImageLike;
|
|
31
|
+
writeToBuffer(suffix: string, options?: Readonly<Record<string, unknown>>): Uint8Array;
|
|
32
|
+
delete(): void;
|
|
33
|
+
}
|
|
34
|
+
interface VipsLike {
|
|
35
|
+
readonly Image: {
|
|
36
|
+
newFromBuffer(data: Uint8Array): VipsImageLike;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export declare function loadVips(): Promise<VipsLike>;
|
|
40
|
+
export declare function createWasmTransformer(vips: VipsLike): ImageTransformer;
|
|
41
|
+
export declare function wasmImageDriver(): Driver<ImageTransformer, ImageConfig>;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=wasm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm.d.ts","sourceRoot":"","sources":["../../src/images/wasm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAgB,MAAM,eAAe,CAAA;AAGzD,OAAO,KAAK,EACV,WAAW,EAEX,gBAAgB,EAGjB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;;;;GAgBG;AAEH,UAAU,aAAa;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,IAAI,OAAO,CAAA;IACnB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAA;IAC7E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa,CAAA;IACnE,OAAO,CAAC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,GAAG,aAAa,CAAA;IACpE,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,UAAU,CAAA;IACtF,MAAM,IAAI,IAAI,CAAA;CACf;AAED,UAAU,QAAQ;IAChB,QAAQ,CAAC,KAAK,EAAE;QACd,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,aAAa,CAAA;KAC/C,CAAA;CACF;AAaD,wBAAsB,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,CASlD;AAOD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,gBAAgB,CAoFtE;AAED,wBAAgB,eAAe,IAAI,MAAM,CAAC,gBAAgB,EAAE,WAAW,CAAC,CAyBvE"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { decodeFailedError, encodeFailedError, unsupportedFormatError } from './errors.js';
|
|
2
|
+
import { contentTypeOf, sniffImageFormat, suffixOf } from './format.js';
|
|
3
|
+
/**
|
|
4
|
+
* One libvips instance for the process.
|
|
5
|
+
*
|
|
6
|
+
* Booting the WebAssembly module costs about a second, and it is stateless once
|
|
7
|
+
* up. Paying that per request would make the first render of every page slow
|
|
8
|
+
* enough to look broken.
|
|
9
|
+
*/
|
|
10
|
+
let booting;
|
|
11
|
+
export async function loadVips() {
|
|
12
|
+
booting ??= (async () => {
|
|
13
|
+
const module = (await import('wasm-vips'));
|
|
14
|
+
// Silenced on purpose: libvips writes warnings to stdout, and rule "no
|
|
15
|
+
// console" applies to what a dependency prints through us as much as to
|
|
16
|
+
// what we print ourselves.
|
|
17
|
+
return module.default({ print: () => { }, printErr: () => { } });
|
|
18
|
+
})();
|
|
19
|
+
return booting;
|
|
20
|
+
}
|
|
21
|
+
/** Encoder options per format. `Q` means nothing to a lossless PNG. */
|
|
22
|
+
function encoderOptions(format, quality) {
|
|
23
|
+
return format === 'png' ? { compression: 9 } : { Q: quality };
|
|
24
|
+
}
|
|
25
|
+
export function createWasmTransformer(vips) {
|
|
26
|
+
function load(bytes, mediaId) {
|
|
27
|
+
if (sniffImageFormat(bytes) === null)
|
|
28
|
+
throw unsupportedFormatError(bytes, mediaId);
|
|
29
|
+
try {
|
|
30
|
+
return vips.Image.newFromBuffer(bytes);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
// libvips reports a bad file as a WebAssembly exception, which is not an
|
|
34
|
+
// Error and would cross the whole call stack unrecognised.
|
|
35
|
+
throw decodeFailedError(mediaId, error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
name: 'wasm',
|
|
40
|
+
tier: 'degraded',
|
|
41
|
+
metadata: async (bytes) => {
|
|
42
|
+
const format = sniffImageFormat(bytes);
|
|
43
|
+
if (format === null)
|
|
44
|
+
throw unsupportedFormatError(bytes, 'this file');
|
|
45
|
+
const image = load(bytes, 'this file');
|
|
46
|
+
try {
|
|
47
|
+
return { width: image.width, height: image.height, format };
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
image.delete();
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
transform: async (bytes, operation) => {
|
|
54
|
+
const handles = [];
|
|
55
|
+
const keep = (image) => {
|
|
56
|
+
handles.push(image);
|
|
57
|
+
return image;
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
let image = keep(load(bytes, 'this file'));
|
|
61
|
+
const { crop, resize } = operation;
|
|
62
|
+
if (crop !== null)
|
|
63
|
+
image = keep(image.crop(crop.left, crop.top, crop.width, crop.height));
|
|
64
|
+
if (resize !== null) {
|
|
65
|
+
image = keep(image.resize(resize.width / image.width, { vscale: resize.height / image.height }));
|
|
66
|
+
}
|
|
67
|
+
// JPEG has no alpha channel. libvips refuses the write rather than
|
|
68
|
+
// guessing a background, so the guess is made here, once, and the same
|
|
69
|
+
// way the native tier makes it.
|
|
70
|
+
if (operation.format === 'jpeg' && image.hasAlpha()) {
|
|
71
|
+
image = keep(image.flatten({ background: [255, 255, 255] }));
|
|
72
|
+
}
|
|
73
|
+
// The dimensions reported are the ones libvips actually produced, not
|
|
74
|
+
// the ones the plan asked for. They agree, and saying so from the
|
|
75
|
+
// handle is what lets the contract suite prove it on both tiers.
|
|
76
|
+
const size = { width: image.width, height: image.height };
|
|
77
|
+
try {
|
|
78
|
+
const written = image.writeToBuffer(suffixOf(operation.format), encoderOptions(operation.format, operation.quality));
|
|
79
|
+
return {
|
|
80
|
+
// Copied out of the WebAssembly heap: the view libvips returns
|
|
81
|
+
// points into memory it is free to reuse.
|
|
82
|
+
bytes: Uint8Array.from(written),
|
|
83
|
+
width: size.width,
|
|
84
|
+
height: size.height,
|
|
85
|
+
format: operation.format,
|
|
86
|
+
contentType: contentTypeOf(operation.format),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
throw encodeFailedError('this file', operation.format, 'wasm', error);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
for (const handle of handles)
|
|
95
|
+
handle.delete();
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
dispose: async () => {
|
|
99
|
+
// Nothing to release: the libvips instance is process-wide and shared by
|
|
100
|
+
// every selection, so disposing one caller must not take it down.
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function wasmImageDriver() {
|
|
105
|
+
let instance;
|
|
106
|
+
return {
|
|
107
|
+
name: 'wasm',
|
|
108
|
+
tier: 'degraded',
|
|
109
|
+
// Always true, and that is the point of this driver: it is what rule R1
|
|
110
|
+
// means by "at least one implementation with no external service".
|
|
111
|
+
available: async () => true,
|
|
112
|
+
init: async () => {
|
|
113
|
+
instance ??= createWasmTransformer(await loadVips());
|
|
114
|
+
return instance;
|
|
115
|
+
},
|
|
116
|
+
dispose: async () => {
|
|
117
|
+
await instance?.dispose();
|
|
118
|
+
instance = undefined;
|
|
119
|
+
},
|
|
120
|
+
health: async () => ({
|
|
121
|
+
status: 'degraded',
|
|
122
|
+
driver: 'wasm',
|
|
123
|
+
tier: 'degraded',
|
|
124
|
+
message: 'Images are resized by libvips compiled to WebAssembly. Correct everywhere, several times slower than sharp. Install sharp for the native path.',
|
|
125
|
+
}),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=wasm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm.js","sourceRoot":"","sources":["../../src/images/wasm.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cogenta/render` — the delivery plane.
|
|
3
|
+
*
|
|
4
|
+
* Astro integration, the frozen `RenderContext`, theme loading and the install
|
|
5
|
+
* check that makes theme isolation real, skins, images, the three build targets,
|
|
6
|
+
* the tag-invalidated page cache and the PWA.
|
|
7
|
+
*
|
|
8
|
+
* Nothing here holds a database connection or a secret: a theme reads content
|
|
9
|
+
* over HTTP with a read-only token (ADR-0016), which is what turns the two-plane
|
|
10
|
+
* architecture into a sandbox rather than a promise.
|
|
11
|
+
*/
|
|
12
|
+
export * from './astro/integration.js';
|
|
13
|
+
export * from './build/index.js';
|
|
14
|
+
export * from './cache/index.js';
|
|
15
|
+
export * from './config.js';
|
|
16
|
+
export * from './content/client.js';
|
|
17
|
+
export * from './content/types.js';
|
|
18
|
+
export * from './context/render-context.js';
|
|
19
|
+
export * from './context/types.js';
|
|
20
|
+
export * from './images/index.js';
|
|
21
|
+
export * from './pwa/index.js';
|
|
22
|
+
export * from './skin/index.js';
|
|
23
|
+
export * from './theme/load-theme.js';
|
|
24
|
+
export * from './theme/manifest.js';
|
|
25
|
+
export * from './theme/verify/forbidden.js';
|
|
26
|
+
export * from './theme/verify/scan-file.js';
|
|
27
|
+
export * from './theme/verify/scanner.js';
|
|
28
|
+
export * from './theme/verify/verify-theme.js';
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,gCAAgC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cogenta/render` — the delivery plane.
|
|
3
|
+
*
|
|
4
|
+
* Astro integration, the frozen `RenderContext`, theme loading and the install
|
|
5
|
+
* check that makes theme isolation real, skins, images, the three build targets,
|
|
6
|
+
* the tag-invalidated page cache and the PWA.
|
|
7
|
+
*
|
|
8
|
+
* Nothing here holds a database connection or a secret: a theme reads content
|
|
9
|
+
* over HTTP with a read-only token (ADR-0016), which is what turns the two-plane
|
|
10
|
+
* architecture into a sandbox rather than a promise.
|
|
11
|
+
*/
|
|
12
|
+
export * from './astro/integration.js';
|
|
13
|
+
export * from './build/index.js';
|
|
14
|
+
export * from './cache/index.js';
|
|
15
|
+
export * from './config.js';
|
|
16
|
+
export * from './content/client.js';
|
|
17
|
+
export * from './content/types.js';
|
|
18
|
+
export * from './context/render-context.js';
|
|
19
|
+
export * from './context/types.js';
|
|
20
|
+
export * from './images/index.js';
|
|
21
|
+
export * from './pwa/index.js';
|
|
22
|
+
export * from './skin/index.js';
|
|
23
|
+
export * from './theme/load-theme.js';
|
|
24
|
+
export * from './theme/manifest.js';
|
|
25
|
+
export * from './theme/verify/forbidden.js';
|
|
26
|
+
export * from './theme/verify/scan-file.js';
|
|
27
|
+
export * from './theme/verify/scanner.js';
|
|
28
|
+
export * from './theme/verify/verify-theme.js';
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,wBAAwB,CAAA;AACtC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,aAAa,CAAA;AAC3B,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,oBAAoB,CAAA;AAClC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,gCAAgC,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ManifestInput, WebAppManifest } from './manifest.js';
|
|
2
|
+
import type { OfflinePageOptions } from './offline.js';
|
|
3
|
+
import type { PwaConfig, RouteRule } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* One call that produces every PWA artefact a build has to emit, so that the
|
|
6
|
+
* manifest, the service worker and the offline page cannot disagree about the
|
|
7
|
+
* offline URL, the cache prefix or the generation. They disagreed in every
|
|
8
|
+
* hand-wired PWA any of us has debugged.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_OFFLINE_URL = "/offline.html";
|
|
11
|
+
export interface PwaBuildInput {
|
|
12
|
+
readonly manifest: ManifestInput;
|
|
13
|
+
readonly offline: OfflinePageOptions;
|
|
14
|
+
/** Commit sha, build timestamp — anything that changes when content does. */
|
|
15
|
+
readonly buildId: string;
|
|
16
|
+
readonly offlineUrl?: string;
|
|
17
|
+
readonly cachePrefix?: string;
|
|
18
|
+
/** Extra URLs to precache. Keep this list short: it is downloaded on install. */
|
|
19
|
+
readonly precache?: readonly string[];
|
|
20
|
+
readonly routes?: readonly RouteRule[];
|
|
21
|
+
}
|
|
22
|
+
export interface PwaArtefacts {
|
|
23
|
+
readonly config: PwaConfig;
|
|
24
|
+
readonly manifest: WebAppManifest;
|
|
25
|
+
/** Contents of `manifest.webmanifest`. */
|
|
26
|
+
readonly manifestJson: string;
|
|
27
|
+
/** Contents of the service worker script. Serve with SERVICE_WORKER_HEADERS. */
|
|
28
|
+
readonly serviceWorker: string;
|
|
29
|
+
/**
|
|
30
|
+
* The worker that removes the PWA. Emitted on every build, never served by
|
|
31
|
+
* default: an exit path you have to write during an incident is an exit path
|
|
32
|
+
* you do not have.
|
|
33
|
+
*/
|
|
34
|
+
readonly tombstoneServiceWorker: string;
|
|
35
|
+
/** Contents of the offline page, at `config.offlineUrl`. */
|
|
36
|
+
readonly offlineHtml: string;
|
|
37
|
+
}
|
|
38
|
+
export declare function buildPwaAssets(input: PwaBuildInput): PwaArtefacts;
|
|
39
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/pwa/build.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAElE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAItD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAGtD;;;;;GAKG;AAEH,eAAO,MAAM,mBAAmB,kBAAkB,CAAA;AAElD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAA;IAChC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAA;IACpC,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;IAC1B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAA;IACjC,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAA;IACvC,4DAA4D;IAC5D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CA2BjE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { assertInstallable, buildManifest, renderManifest } from './manifest.js';
|
|
2
|
+
import { renderOfflinePage } from './offline.js';
|
|
3
|
+
import { renderServiceWorker, renderTombstoneServiceWorker } from './service-worker.js';
|
|
4
|
+
import { DEFAULT_ROUTES } from './strategy.js';
|
|
5
|
+
import { computeCacheVersion, DEFAULT_CACHE_PREFIX } from './version.js';
|
|
6
|
+
/**
|
|
7
|
+
* One call that produces every PWA artefact a build has to emit, so that the
|
|
8
|
+
* manifest, the service worker and the offline page cannot disagree about the
|
|
9
|
+
* offline URL, the cache prefix or the generation. They disagreed in every
|
|
10
|
+
* hand-wired PWA any of us has debugged.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_OFFLINE_URL = '/offline.html';
|
|
13
|
+
export function buildPwaAssets(input) {
|
|
14
|
+
const manifest = buildManifest(input.manifest);
|
|
15
|
+
// Refuse at build time. A manifest that silently fails to install is
|
|
16
|
+
// discovered weeks later by someone reading a Lighthouse report.
|
|
17
|
+
assertInstallable(manifest);
|
|
18
|
+
const offlineUrl = input.offlineUrl ?? DEFAULT_OFFLINE_URL;
|
|
19
|
+
const routes = input.routes ?? DEFAULT_ROUTES;
|
|
20
|
+
const precache = [offlineUrl, ...(input.precache ?? [])];
|
|
21
|
+
const version = computeCacheVersion({ offlineUrl, precache, routes, buildId: input.buildId });
|
|
22
|
+
const config = {
|
|
23
|
+
cachePrefix: input.cachePrefix ?? DEFAULT_CACHE_PREFIX,
|
|
24
|
+
version,
|
|
25
|
+
offlineUrl,
|
|
26
|
+
precache,
|
|
27
|
+
routes,
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
config,
|
|
31
|
+
manifest,
|
|
32
|
+
manifestJson: renderManifest(manifest),
|
|
33
|
+
serviceWorker: renderServiceWorker(config),
|
|
34
|
+
tombstoneServiceWorker: renderTombstoneServiceWorker(config.cachePrefix),
|
|
35
|
+
offlineHtml: renderOfflinePage(input.offline),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/pwa/build.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEhF,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,mBAAmB,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAA;AACvF,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAE9C,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAExE;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,eAAe,CAAA;AA+BlD,MAAM,UAAU,cAAc,CAAC,KAAoB;IACjD,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IAC9C,qEAAqE;IACrE,iEAAiE;IACjE,iBAAiB,CAAC,QAAQ,CAAC,CAAA;IAE3B,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,mBAAmB,CAAA;IAC1D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,cAAc,CAAA;IAC7C,MAAM,QAAQ,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAA;IACxD,MAAM,OAAO,GAAG,mBAAmB,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAA;IAE7F,MAAM,MAAM,GAAc;QACxB,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,oBAAoB;QACtD,OAAO;QACP,UAAU;QACV,QAAQ;QACR,MAAM;KACP,CAAA;IAED,OAAO;QACL,MAAM;QACN,QAAQ;QACR,YAAY,EAAE,cAAc,CAAC,QAAQ,CAAC;QACtC,aAAa,EAAE,mBAAmB,CAAC,MAAM,CAAC;QAC1C,sBAAsB,EAAE,4BAA4B,CAAC,MAAM,CAAC,WAAW,CAAC;QACxE,WAAW,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC;KAC9C,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page side of the PWA: registration, and the way out.
|
|
3
|
+
*
|
|
4
|
+
* These functions take the browser objects as arguments instead of reaching for
|
|
5
|
+
* `navigator` and `caches`. That is what lets them be unit-tested at all — the
|
|
6
|
+
* package compiles without the DOM library — and it keeps the surface the code
|
|
7
|
+
* depends on down to the six methods declared here. A caller in the admin (L2)
|
|
8
|
+
* passes `navigator.serviceWorker` and `caches`; the real objects satisfy these
|
|
9
|
+
* interfaces structurally, with no cast.
|
|
10
|
+
*/
|
|
11
|
+
export interface ClientCacheStorage {
|
|
12
|
+
keys(): Promise<string[]>;
|
|
13
|
+
delete(name: string): Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
export interface ClientWorker {
|
|
16
|
+
postMessage(message: unknown): void;
|
|
17
|
+
}
|
|
18
|
+
export interface ClientRegistration {
|
|
19
|
+
unregister(): Promise<boolean>;
|
|
20
|
+
update(): Promise<void>;
|
|
21
|
+
readonly active: ClientWorker | null;
|
|
22
|
+
}
|
|
23
|
+
export interface ClientRegisterOptions {
|
|
24
|
+
readonly scope?: string;
|
|
25
|
+
/**
|
|
26
|
+
* `'none'` means the *script* is never answered from the HTTP cache, only its
|
|
27
|
+
* imports may be. Without it, a worker script cached by an over-eager CDN
|
|
28
|
+
* cannot be updated: the daily update check compares the new script against a
|
|
29
|
+
* stale copy and concludes nothing changed.
|
|
30
|
+
*/
|
|
31
|
+
readonly updateViaCache?: 'none' | 'imports' | 'all';
|
|
32
|
+
}
|
|
33
|
+
export interface ClientServiceWorkerContainer {
|
|
34
|
+
register(url: string, options?: ClientRegisterOptions): Promise<ClientRegistration>;
|
|
35
|
+
getRegistrations(): Promise<readonly ClientRegistration[]>;
|
|
36
|
+
readonly controller: ClientWorker | null;
|
|
37
|
+
}
|
|
38
|
+
/** What a page hands in. Both entries are absent on unsupporting browsers. */
|
|
39
|
+
export interface PwaClientEnvironment {
|
|
40
|
+
readonly serviceWorker?: ClientServiceWorkerContainer | undefined;
|
|
41
|
+
readonly caches?: ClientCacheStorage | undefined;
|
|
42
|
+
}
|
|
43
|
+
export declare const DEFAULT_SERVICE_WORKER_URL = "/sw.js";
|
|
44
|
+
/**
|
|
45
|
+
* Registers the worker at the root scope.
|
|
46
|
+
*
|
|
47
|
+
* Returns `null` rather than throwing when the browser has no service worker
|
|
48
|
+
* support, or when registration fails: a site that renders on the server does
|
|
49
|
+
* not stop working because an offline nicety could not be installed (rule R1
|
|
50
|
+
* applied to the browser).
|
|
51
|
+
*/
|
|
52
|
+
export declare function registerServiceWorker(container: ClientServiceWorkerContainer | undefined, options?: {
|
|
53
|
+
readonly scriptUrl?: string;
|
|
54
|
+
readonly scope?: string;
|
|
55
|
+
}): Promise<ClientRegistration | null>;
|
|
56
|
+
export interface PwaResetReport {
|
|
57
|
+
/** Registrations that reported themselves removed. */
|
|
58
|
+
readonly registrationsRemoved: number;
|
|
59
|
+
readonly cachesDeleted: readonly string[];
|
|
60
|
+
/**
|
|
61
|
+
* True when a worker was controlling the page. An unregistered worker keeps
|
|
62
|
+
* controlling already-loaded pages until they are reloaded, so the caller
|
|
63
|
+
* must reload for the reset to be visible. Saying so is the difference
|
|
64
|
+
* between a button that works and a button that appears not to.
|
|
65
|
+
*/
|
|
66
|
+
readonly reloadRequired: boolean;
|
|
67
|
+
/** False when the browser has no service worker or no Cache Storage. */
|
|
68
|
+
readonly supported: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Deletes the caches this site owns. Names outside `cachePrefix` are left
|
|
72
|
+
* alone: another application may share the origin, and deleting a neighbour's
|
|
73
|
+
* cache is a worse incident than the stale content this is fixing.
|
|
74
|
+
*/
|
|
75
|
+
export declare function purgeSiteCaches(cacheStorage: ClientCacheStorage | undefined, cachePrefix: string): Promise<string[]>;
|
|
76
|
+
/**
|
|
77
|
+
* The "clear client cache" button of the admin, as a function.
|
|
78
|
+
*
|
|
79
|
+
* The order is deliberate. The active worker is asked to purge and unregister
|
|
80
|
+
* itself first, because it is the only party that can empty its caches while it
|
|
81
|
+
* is still running. Then the registrations are unregistered from the page side,
|
|
82
|
+
* which also covers a worker that is broken, hung, or from a build that never
|
|
83
|
+
* understood the message. Only then are the remaining caches deleted directly —
|
|
84
|
+
* doing that first would simply let the still-running worker refill them.
|
|
85
|
+
*
|
|
86
|
+
* Even after all of this the current page is still controlled by the old
|
|
87
|
+
* worker, which is why the report insists on a reload.
|
|
88
|
+
*/
|
|
89
|
+
export declare function resetPwaClient(environment: PwaClientEnvironment, cachePrefix: string): Promise<PwaResetReport>;
|
|
90
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/pwa/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,kBAAkB;IACjC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACzB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACvC;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,CAAA;CACrD;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACnF,gBAAgB,IAAI,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAA;IAC1D,QAAQ,CAAC,UAAU,EAAE,YAAY,GAAG,IAAI,CAAA;CACzC;AAED,8EAA8E;AAC9E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,4BAA4B,GAAG,SAAS,CAAA;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;CACjD;AAED,eAAO,MAAM,0BAA0B,WAAW,CAAA;AAElD;;;;;;;GAOG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,4BAA4B,GAAG,SAAS,EACnD,OAAO,GAAE;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACrE,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAUpC;AAED,MAAM,WAAW,cAAc;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAA;IACrC,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAA;IAChC,wEAAwE;IACxE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,kBAAkB,GAAG,SAAS,EAC5C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAMnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,oBAAoB,EACjC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,cAAc,CAAC,CAqBzB"}
|