@camp.dev/bones 0.4.0 → 0.5.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/README.md +61 -106
- package/package.json +4 -36
- package/src/css/bones.css +1008 -134
- package/dist/core/attributes.d.mts +0 -25
- package/dist/core/attributes.mjs +0 -36
- package/dist/css/auto.css +0 -1105
- package/dist/css/bones.css +0 -197
- package/dist/element/boundary.d.mts +0 -26
- package/dist/element/boundary.mjs +0 -220
- package/dist/element/index.d.mts +0 -14
- package/dist/element/index.mjs +0 -6
- package/dist/element/measure.mjs +0 -99
- package/dist/element/overlay.mjs +0 -184
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -2
- package/dist/react/boundary.d.mts +0 -40
- package/dist/react/boundary.mjs +0 -21
- package/dist/react/create-bones.d.mts +0 -23
- package/dist/react/create-bones.mjs +0 -69
- package/dist/react/index.d.mts +0 -4
- package/dist/react/index.mjs +0 -4
- package/dist/server/bootstrap.d.mts +0 -4
- package/dist/server/bootstrap.mjs +0 -3
- package/dist/server/index.d.mts +0 -17
- package/dist/server/index.mjs +0 -57
- package/src/css/auto.css +0 -1105
package/dist/element/overlay.mjs
DELETED
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
import { measureBones } from "./measure.mjs";
|
|
2
|
-
//#region src/element/overlay.ts
|
|
3
|
-
const OVERLAY_CSS = `
|
|
4
|
-
:host([precision="measured"]) {
|
|
5
|
-
display: block;
|
|
6
|
-
position: relative;
|
|
7
|
-
}
|
|
8
|
-
/* Hidden content still lays out, so re-measurement stays valid. Deliberately
|
|
9
|
-
not !important: outer-tree rules beat ::slotted, which is what lets the
|
|
10
|
-
auto.css opt-out rule re-show exempt subtrees (and lets an author
|
|
11
|
-
visibility rule on a direct child win — a documented edge). */
|
|
12
|
-
:host([data-bones-measured]) ::slotted(*) {
|
|
13
|
-
visibility: hidden;
|
|
14
|
-
}
|
|
15
|
-
[part~="overlay"] {
|
|
16
|
-
position: absolute;
|
|
17
|
-
inset: 0;
|
|
18
|
-
pointer-events: none;
|
|
19
|
-
}
|
|
20
|
-
[part~="bone"] {
|
|
21
|
-
position: absolute;
|
|
22
|
-
background: var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent));
|
|
23
|
-
border-radius: var(--bone-radius, 4px);
|
|
24
|
-
}
|
|
25
|
-
@keyframes bone-shimmer {
|
|
26
|
-
0% { background-position: 200% 0; }
|
|
27
|
-
100% { background-position: -200% 0; }
|
|
28
|
-
}
|
|
29
|
-
@keyframes bone-pulse {
|
|
30
|
-
0%, 100% { opacity: 1; }
|
|
31
|
-
50% { opacity: 0.5; }
|
|
32
|
-
}
|
|
33
|
-
[part~="overlay"]:not([data-bone-animate]) [part~="bone"],
|
|
34
|
-
[part~="overlay"][data-bone-animate="shimmer"] [part~="bone"] {
|
|
35
|
-
animation: bone-shimmer var(--bone-duration, 1.5s) ease-in-out infinite;
|
|
36
|
-
background: linear-gradient(
|
|
37
|
-
90deg,
|
|
38
|
-
var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent)) 25%,
|
|
39
|
-
var(--bone-highlight, color-mix(in srgb, rgb(from currentcolor r g b / 1) 6%, transparent)) 50%,
|
|
40
|
-
var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent)) 75%
|
|
41
|
-
);
|
|
42
|
-
background-size: 200% 100%;
|
|
43
|
-
}
|
|
44
|
-
[part~="overlay"][data-bone-animate="pulse"] [part~="bone"] {
|
|
45
|
-
animation: bone-pulse var(--bone-duration, 1.5s) ease-in-out infinite;
|
|
46
|
-
}
|
|
47
|
-
[part~="overlay"][data-bone-animate="none"] [part~="bone"] {
|
|
48
|
-
animation: none;
|
|
49
|
-
}
|
|
50
|
-
/* Matches the shimmer/pulse selectors above at equal (0,3,0) specificity so
|
|
51
|
-
this override always wins the cascade instead of losing to source order.
|
|
52
|
-
data-bone-animate="none" is deliberately excluded: none still means none. */
|
|
53
|
-
@media (prefers-reduced-motion: reduce) {
|
|
54
|
-
[part~="overlay"]:not([data-bone-animate]) [part~="bone"],
|
|
55
|
-
[part~="overlay"][data-bone-animate="shimmer"] [part~="bone"],
|
|
56
|
-
[part~="overlay"][data-bone-animate="pulse"] [part~="bone"] {
|
|
57
|
-
animation: bone-pulse 2s ease-in-out infinite;
|
|
58
|
-
background: var(--bone-base, color-mix(in srgb, rgb(from currentcolor r g b / 1) 12%, transparent));
|
|
59
|
-
background-size: auto;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
`;
|
|
63
|
-
let sharedSheet;
|
|
64
|
-
function applyStyles(root) {
|
|
65
|
-
if ("adoptedStyleSheets" in root && typeof CSSStyleSheet !== "undefined" && "replaceSync" in CSSStyleSheet.prototype) {
|
|
66
|
-
if (sharedSheet === void 0) {
|
|
67
|
-
sharedSheet = new CSSStyleSheet();
|
|
68
|
-
sharedSheet.replaceSync(OVERLAY_CSS);
|
|
69
|
-
}
|
|
70
|
-
root.adoptedStyleSheets = [...root.adoptedStyleSheets, sharedSheet];
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
const style = root.ownerDocument.createElement("style");
|
|
74
|
-
style.textContent = OVERLAY_CSS;
|
|
75
|
-
root.append(style);
|
|
76
|
-
}
|
|
77
|
-
var MeasuredOverlay = class {
|
|
78
|
-
#host;
|
|
79
|
-
#container;
|
|
80
|
-
#observer;
|
|
81
|
-
#mutations;
|
|
82
|
-
#active = false;
|
|
83
|
-
#ownsAutoOff = false;
|
|
84
|
-
constructor(host) {
|
|
85
|
-
this.#host = host;
|
|
86
|
-
}
|
|
87
|
-
get active() {
|
|
88
|
-
return this.#active;
|
|
89
|
-
}
|
|
90
|
-
prepare() {
|
|
91
|
-
if (this.#host.shadowRoot) return;
|
|
92
|
-
const root = this.#host.attachShadow({ mode: "open" });
|
|
93
|
-
root.append(this.#host.ownerDocument.createElement("slot"));
|
|
94
|
-
applyStyles(root);
|
|
95
|
-
}
|
|
96
|
-
activate() {
|
|
97
|
-
this.prepare();
|
|
98
|
-
if (!this.#active) {
|
|
99
|
-
this.#active = true;
|
|
100
|
-
if (!this.#host.hasAttribute("data-bones-auto")) {
|
|
101
|
-
this.#host.setAttribute("data-bones-auto", "off");
|
|
102
|
-
this.#ownsAutoOff = true;
|
|
103
|
-
}
|
|
104
|
-
this.#host.setAttribute("data-bones-measured", "");
|
|
105
|
-
}
|
|
106
|
-
if (!this.#renderBars()) {
|
|
107
|
-
this.deactivate();
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
this.#observe();
|
|
111
|
-
}
|
|
112
|
-
deactivate() {
|
|
113
|
-
this.#unobserve();
|
|
114
|
-
this.#container?.remove();
|
|
115
|
-
this.#container = void 0;
|
|
116
|
-
if (!this.#active) return;
|
|
117
|
-
this.#active = false;
|
|
118
|
-
this.#host.removeAttribute("data-bones-measured");
|
|
119
|
-
if (this.#ownsAutoOff) {
|
|
120
|
-
this.#host.removeAttribute("data-bones-auto");
|
|
121
|
-
this.#ownsAutoOff = false;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
pause() {
|
|
125
|
-
this.#unobserve();
|
|
126
|
-
}
|
|
127
|
-
#observe() {
|
|
128
|
-
if (typeof ResizeObserver !== "undefined" && !this.#observer) {
|
|
129
|
-
this.#observer = new ResizeObserver(() => this.#invalidate());
|
|
130
|
-
this.#observer.observe(this.#host);
|
|
131
|
-
}
|
|
132
|
-
if (typeof MutationObserver !== "undefined" && !this.#mutations) {
|
|
133
|
-
this.#mutations = new MutationObserver(() => this.#invalidate());
|
|
134
|
-
this.#mutations.observe(this.#host, {
|
|
135
|
-
childList: true,
|
|
136
|
-
subtree: true,
|
|
137
|
-
characterData: true
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
#unobserve() {
|
|
142
|
-
this.#observer?.disconnect();
|
|
143
|
-
this.#observer = void 0;
|
|
144
|
-
this.#mutations?.disconnect();
|
|
145
|
-
this.#mutations = void 0;
|
|
146
|
-
}
|
|
147
|
-
#invalidate() {
|
|
148
|
-
if (this.#active && !this.#renderBars()) this.deactivate();
|
|
149
|
-
}
|
|
150
|
-
#renderBars() {
|
|
151
|
-
const root = this.#host.shadowRoot;
|
|
152
|
-
if (!root) return false;
|
|
153
|
-
const bones = measureBones(this.#host);
|
|
154
|
-
if (bones.length === 0) return false;
|
|
155
|
-
const doc = this.#host.ownerDocument;
|
|
156
|
-
if (!this.#container) {
|
|
157
|
-
this.#container = doc.createElement("div");
|
|
158
|
-
this.#container.setAttribute("part", "overlay");
|
|
159
|
-
this.#container.setAttribute("aria-hidden", "true");
|
|
160
|
-
root.append(this.#container);
|
|
161
|
-
}
|
|
162
|
-
const animate = this.#host.closest("[data-bone-animate]")?.getAttribute("data-bone-animate");
|
|
163
|
-
if (animate) this.#container.setAttribute("data-bone-animate", animate);
|
|
164
|
-
else this.#container.removeAttribute("data-bone-animate");
|
|
165
|
-
const origin = this.#container.getBoundingClientRect();
|
|
166
|
-
const style = getComputedStyle(this.#container);
|
|
167
|
-
const layoutWidth = Number.parseFloat(style.width);
|
|
168
|
-
const layoutHeight = Number.parseFloat(style.height);
|
|
169
|
-
const scaleX = layoutWidth > 0 ? origin.width / layoutWidth : 1;
|
|
170
|
-
const scaleY = layoutHeight > 0 ? origin.height / layoutHeight : 1;
|
|
171
|
-
this.#container.replaceChildren(...bones.map((bone) => {
|
|
172
|
-
const bar = doc.createElement("div");
|
|
173
|
-
bar.setAttribute("part", `bone bone-${bone.kind}`);
|
|
174
|
-
bar.style.left = `${(bone.left - origin.left) / scaleX}px`;
|
|
175
|
-
bar.style.top = `${(bone.top - origin.top) / scaleY}px`;
|
|
176
|
-
bar.style.width = `${bone.width / scaleX}px`;
|
|
177
|
-
bar.style.height = `${bone.height / scaleY}px`;
|
|
178
|
-
return bar;
|
|
179
|
-
}));
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
//#endregion
|
|
184
|
-
export { MeasuredOverlay };
|
package/dist/index.d.mts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { BoneAttributes, BoneOptions, BoneType, MinMax, TRANSPARENT_PIXEL, boneAttributes, isMinMax, minMax, resolveLength } from "./core/attributes.mjs";
|
|
2
|
-
export { type BoneAttributes, type BoneOptions, type BoneType, type MinMax, TRANSPARENT_PIXEL, boneAttributes, isMinMax, minMax, resolveLength };
|
package/dist/index.mjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { BonesBoundary as BonesBoundary$1 } from "../element/boundary.mjs";
|
|
2
|
-
import { HTMLAttributes, ReactNode, Ref } from "react";
|
|
3
|
-
|
|
4
|
-
//#region src/react/boundary.d.ts
|
|
5
|
-
interface ElementAttributes extends HTMLAttributes<HTMLElement> {
|
|
6
|
-
busy?: boolean;
|
|
7
|
-
force?: boolean;
|
|
8
|
-
delay?: number;
|
|
9
|
-
"min-duration"?: number;
|
|
10
|
-
transition?: "auto" | "none";
|
|
11
|
-
precision?: "css" | "measured";
|
|
12
|
-
class?: string;
|
|
13
|
-
ref?: Ref<BonesBoundary$1>;
|
|
14
|
-
[dataAttribute: `data-${string}`]: string | number | boolean | undefined;
|
|
15
|
-
}
|
|
16
|
-
declare module "react" {
|
|
17
|
-
namespace JSX {
|
|
18
|
-
interface IntrinsicElements {
|
|
19
|
-
"bones-boundary": ElementAttributes;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
interface BonesBoundaryProps extends Omit<ElementAttributes, "min-duration" | "inert" | "aria-busy" | "class"> {
|
|
24
|
-
minDuration?: number;
|
|
25
|
-
onShow?: (event: CustomEvent) => void;
|
|
26
|
-
onHide?: (event: CustomEvent) => void;
|
|
27
|
-
}
|
|
28
|
-
declare function BonesBoundary({
|
|
29
|
-
busy,
|
|
30
|
-
force,
|
|
31
|
-
delay,
|
|
32
|
-
minDuration,
|
|
33
|
-
transition,
|
|
34
|
-
onShow,
|
|
35
|
-
onHide,
|
|
36
|
-
children,
|
|
37
|
-
...rest
|
|
38
|
-
}: BonesBoundaryProps): ReactNode;
|
|
39
|
-
//#endregion
|
|
40
|
-
export { BonesBoundary, BonesBoundaryProps };
|
package/dist/react/boundary.mjs
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { createElement } from "react";
|
|
2
|
-
//#region src/react/boundary.ts
|
|
3
|
-
function BonesBoundary({ busy, force, delay, minDuration, transition, onShow, onHide, children, ...rest }) {
|
|
4
|
-
return createElement("bones-boundary", {
|
|
5
|
-
...rest,
|
|
6
|
-
busy: busy ? true : void 0,
|
|
7
|
-
force: force ? true : void 0,
|
|
8
|
-
delay,
|
|
9
|
-
"min-duration": minDuration,
|
|
10
|
-
transition,
|
|
11
|
-
...force ? {
|
|
12
|
-
"aria-busy": "true",
|
|
13
|
-
inert: true
|
|
14
|
-
} : {},
|
|
15
|
-
suppressHydrationWarning: true,
|
|
16
|
-
"onbones:show": onShow,
|
|
17
|
-
"onbones:hide": onHide
|
|
18
|
-
}, children);
|
|
19
|
-
}
|
|
20
|
-
//#endregion
|
|
21
|
-
export { BonesBoundary };
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { BoneOptions, BoneType, MinMax, isMinMax, minMax } from "../core/attributes.mjs";
|
|
2
|
-
import { ReactNode } from "react";
|
|
3
|
-
|
|
4
|
-
//#region src/react/create-bones.d.ts
|
|
5
|
-
declare function readPromise<T>(promise: Promise<T>): T;
|
|
6
|
-
type BoneProps = Record<string, unknown>;
|
|
7
|
-
declare const forceBones: Promise<never>;
|
|
8
|
-
interface CreateBonesOptions {
|
|
9
|
-
loading?: boolean;
|
|
10
|
-
}
|
|
11
|
-
interface CreateBonesReturn<T> {
|
|
12
|
-
bone: {
|
|
13
|
-
(type: "text", options?: BoneOptions): BoneProps;
|
|
14
|
-
(type: "block" | "container"): BoneProps;
|
|
15
|
-
};
|
|
16
|
-
data: T | null | undefined;
|
|
17
|
-
repeat: <U>(arr: U[] | undefined | null, count: number, render: (item: U | undefined, index: number) => ReactNode) => ReactNode[];
|
|
18
|
-
lines: <V>(value: V | null | undefined, count: number, render: (item: V | ReactNode) => ReactNode) => ReactNode[];
|
|
19
|
-
}
|
|
20
|
-
declare function createBones(options: CreateBonesOptions): CreateBonesReturn<never>;
|
|
21
|
-
declare function createBones<T>(data: T | Promise<T> | undefined | null, options?: CreateBonesOptions): CreateBonesReturn<T>;
|
|
22
|
-
//#endregion
|
|
23
|
-
export { CreateBonesOptions, CreateBonesReturn, createBones, forceBones, readPromise };
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { boneAttributes } from "../core/attributes.mjs";
|
|
2
|
-
import { cloneElement, createElement, isValidElement } from "react";
|
|
3
|
-
//#region src/react/create-bones.ts
|
|
4
|
-
function withKey(node, key) {
|
|
5
|
-
return isValidElement(node) ? cloneElement(node, { key }) : node;
|
|
6
|
-
}
|
|
7
|
-
function readPromise(promise) {
|
|
8
|
-
const tracked = promise;
|
|
9
|
-
if (tracked._status === void 0) {
|
|
10
|
-
tracked._status = "pending";
|
|
11
|
-
promise.then((result) => {
|
|
12
|
-
tracked._status = "fulfilled";
|
|
13
|
-
tracked._result = result;
|
|
14
|
-
}, (error) => {
|
|
15
|
-
tracked._status = "rejected";
|
|
16
|
-
tracked._error = error;
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
if (tracked._status === "fulfilled") return tracked._result;
|
|
20
|
-
if (tracked._status === "rejected") throw tracked._error;
|
|
21
|
-
throw promise;
|
|
22
|
-
}
|
|
23
|
-
const forceBones = Object.freeze({});
|
|
24
|
-
function createBones(dataOrOptions, maybeOptions) {
|
|
25
|
-
let data;
|
|
26
|
-
let options;
|
|
27
|
-
if (maybeOptions === void 0 && dataOrOptions !== null && dataOrOptions !== void 0 && typeof dataOrOptions === "object" && !(dataOrOptions instanceof Promise) && "loading" in dataOrOptions) {
|
|
28
|
-
data = void 0;
|
|
29
|
-
options = dataOrOptions;
|
|
30
|
-
} else {
|
|
31
|
-
data = dataOrOptions;
|
|
32
|
-
options = maybeOptions;
|
|
33
|
-
}
|
|
34
|
-
let resolved;
|
|
35
|
-
let isLoading = false;
|
|
36
|
-
if (options?.loading) {
|
|
37
|
-
isLoading = true;
|
|
38
|
-
resolved = void 0;
|
|
39
|
-
} else if (data != null && data === forceBones) {
|
|
40
|
-
isLoading = true;
|
|
41
|
-
resolved = void 0;
|
|
42
|
-
} else if (data != null && data instanceof Promise) resolved = readPromise(data);
|
|
43
|
-
else resolved = data;
|
|
44
|
-
let boneCallIndex = 0;
|
|
45
|
-
const bone = (type, options) => {
|
|
46
|
-
if (!isLoading) return {};
|
|
47
|
-
return { ...boneAttributes(type, options, boneCallIndex++) };
|
|
48
|
-
};
|
|
49
|
-
function repeat(arr, count, render) {
|
|
50
|
-
return (isLoading ? Array.from({ length: count }) : arr ?? []).map(render);
|
|
51
|
-
}
|
|
52
|
-
function lines(value, count, render) {
|
|
53
|
-
if (isLoading) return [withKey(render(Array.from({ length: count }, (_, i) => createElement("span", {
|
|
54
|
-
key: i,
|
|
55
|
-
"data-bone-line": true,
|
|
56
|
-
...bone("text")
|
|
57
|
-
}))), 0)];
|
|
58
|
-
if (value == null) return [];
|
|
59
|
-
return [withKey(render(value), 0)];
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
bone,
|
|
63
|
-
data: isLoading ? void 0 : resolved,
|
|
64
|
-
repeat,
|
|
65
|
-
lines
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
//#endregion
|
|
69
|
-
export { createBones, forceBones, readPromise };
|
package/dist/react/index.d.mts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { BoneOptions, BoneType, MinMax, isMinMax, minMax } from "../core/attributes.mjs";
|
|
2
|
-
import { CreateBonesOptions, CreateBonesReturn, createBones, forceBones, readPromise } from "./create-bones.mjs";
|
|
3
|
-
import { BonesBoundary, BonesBoundaryProps } from "./boundary.mjs";
|
|
4
|
-
export { type BoneOptions, type BoneType, BonesBoundary, type BonesBoundaryProps, type CreateBonesOptions, type CreateBonesReturn, type MinMax, createBones, forceBones, isMinMax, minMax, readPromise };
|
package/dist/react/index.mjs
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
//#region src/server/bootstrap.d.ts
|
|
2
|
-
declare const BOOTSTRAP_SCRIPT = "<script>function __bonesSwap(e,r){var t=document.querySelector('template[data-bones-chunk=\"'+e+'\"]'),n=document.querySelector('[data-bones-slot=\"'+e+'\"]');n?(t&&(n.replaceChildren(t.content),t.remove()),r&&n.setAttribute(\"data-bones-error\",\"\"),\"boolean\"==typeof n.busy?n.busy=!1:(n.removeAttribute(\"busy\"),n.removeAttribute(\"aria-busy\"),n.removeAttribute(\"inert\"))):t&&t.remove()}</script>";
|
|
3
|
-
//#endregion
|
|
4
|
-
export { BOOTSTRAP_SCRIPT };
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
const BOOTSTRAP_SCRIPT = `<script>function __bonesSwap(e,r){var t=document.querySelector('template[data-bones-chunk="'+e+'"]'),n=document.querySelector('[data-bones-slot="'+e+'"]');n?(t&&(n.replaceChildren(t.content),t.remove()),r&&n.setAttribute("data-bones-error",""),"boolean"==typeof n.busy?n.busy=!1:(n.removeAttribute("busy"),n.removeAttribute("aria-busy"),n.removeAttribute("inert"))):t&&t.remove()}<\/script>`;
|
|
2
|
-
//#endregion
|
|
3
|
-
export { BOOTSTRAP_SCRIPT };
|
package/dist/server/index.d.mts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { BOOTSTRAP_SCRIPT } from "./bootstrap.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/server/index.d.ts
|
|
4
|
-
declare function renderBoundary(id: string, fallbackHtml: string, attrs?: string): string;
|
|
5
|
-
declare function renderChunk(id: string, html: string): string;
|
|
6
|
-
declare function renderErrorChunk(id: string, html?: string): string;
|
|
7
|
-
interface StreamBonesOptions {
|
|
8
|
-
/**
|
|
9
|
-
* Renders the error HTML for a rejected slot. Returning undefined (and
|
|
10
|
-
* throwing) falls back to the bare error chunk, which keeps the boundary's
|
|
11
|
-
* fallback children.
|
|
12
|
-
*/
|
|
13
|
-
onError?: (id: string, error: unknown) => string | undefined;
|
|
14
|
-
}
|
|
15
|
-
declare function streamBones(shell: string, slots: Record<string, Promise<string>>, options?: StreamBonesOptions): ReadableStream<Uint8Array>;
|
|
16
|
-
//#endregion
|
|
17
|
-
export { BOOTSTRAP_SCRIPT, StreamBonesOptions, renderBoundary, renderChunk, renderErrorChunk, streamBones };
|
package/dist/server/index.mjs
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { BOOTSTRAP_SCRIPT } from "./bootstrap.mjs";
|
|
2
|
-
//#region src/server/index.ts
|
|
3
|
-
const ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
4
|
-
function assertId(id) {
|
|
5
|
-
if (!ID_PATTERN.test(id)) throw new Error(`bones slot id ${JSON.stringify(id)} must match [A-Za-z0-9_-]+`);
|
|
6
|
-
}
|
|
7
|
-
function renderBoundary(id, fallbackHtml, attrs) {
|
|
8
|
-
assertId(id);
|
|
9
|
-
return `<bones-boundary busy aria-busy="true" inert data-bones-slot="${id}"${attrs === void 0 || attrs === "" ? "" : ` ${attrs}`}>${fallbackHtml}</bones-boundary>`;
|
|
10
|
-
}
|
|
11
|
-
function renderChunk(id, html) {
|
|
12
|
-
assertId(id);
|
|
13
|
-
return `<template data-bones-chunk="${id}">${html}</template><script>__bonesSwap("${id}")<\/script>`;
|
|
14
|
-
}
|
|
15
|
-
function renderErrorChunk(id, html) {
|
|
16
|
-
assertId(id);
|
|
17
|
-
if (html === void 0) return `<script>__bonesSwap("${id}",1)<\/script>`;
|
|
18
|
-
return `<template data-bones-chunk="${id}">${html}</template><script>__bonesSwap("${id}",1)<\/script>`;
|
|
19
|
-
}
|
|
20
|
-
function streamBones(shell, slots, options = {}) {
|
|
21
|
-
const ids = Object.keys(slots);
|
|
22
|
-
for (const id of ids) assertId(id);
|
|
23
|
-
const encoder = new TextEncoder();
|
|
24
|
-
let cancelled = false;
|
|
25
|
-
return new ReadableStream({
|
|
26
|
-
start(controller) {
|
|
27
|
-
const send = (html) => {
|
|
28
|
-
if (!cancelled) controller.enqueue(encoder.encode(html));
|
|
29
|
-
};
|
|
30
|
-
send(shell + BOOTSTRAP_SCRIPT);
|
|
31
|
-
if (ids.length === 0) {
|
|
32
|
-
controller.close();
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
let pending = ids.length;
|
|
36
|
-
const settle = (chunk) => {
|
|
37
|
-
send(chunk);
|
|
38
|
-
pending -= 1;
|
|
39
|
-
if (pending === 0 && !cancelled) controller.close();
|
|
40
|
-
};
|
|
41
|
-
for (const id of ids) slots[id].then((html) => settle(renderChunk(id, html)), (error) => {
|
|
42
|
-
let html;
|
|
43
|
-
try {
|
|
44
|
-
html = options.onError?.(id, error);
|
|
45
|
-
} catch {
|
|
46
|
-
html = void 0;
|
|
47
|
-
}
|
|
48
|
-
settle(renderErrorChunk(id, html));
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
cancel() {
|
|
52
|
-
cancelled = true;
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
//#endregion
|
|
57
|
-
export { BOOTSTRAP_SCRIPT, renderBoundary, renderChunk, renderErrorChunk, streamBones };
|