@vincle/flow 0.10.0 → 0.11.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/CHANGELOG.md +18 -0
- package/README.md +26 -0
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/context-ByZmZGcm.js +1 -0
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -1
- package/dist/http.d.ts +1 -1
- package/dist/http.js +1 -1
- package/dist/{index-CkR-2s9P.d.ts → index-BV7bof4v.d.ts} +13 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/{jsx-augment-Da8Va85F.d.ts → jsx-augment-supbRT8y.d.ts} +2 -1
- package/dist/render-CsoOKhJw.js +5 -0
- package/dist/utils-DzVhT3Br.js +1 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/package.json +4 -4
- package/dist/context-CLpgp0eJ.js +0 -1
- package/dist/render-BkqdRPBk.js +0 -5
- package/dist/utils-yayEbXmr.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @vincle/flow
|
|
2
2
|
|
|
3
|
+
## 0.11.0 — 2026-10-11
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add optional `maxConcurrency` to streaming and static rendering options to limit active deferred fragments, including live streams. Concurrency remains unlimited by default; lazy factories start only when a slot is available, and queued factories do not start after request cancellation.
|
|
8
|
+
- Add `onObserve` and the exported `FlowObservation` type for fragment lifecycle diagnostics: start, completion, error, timeout and cancellation, with the fragment ID, elapsed milliseconds and optional error. Observer exceptions do not affect rendering, and `onError` continues to control fallback rendering.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Allow `renderToStatic` to accept the shared Flow options, including cancellation, default timeouts, error fallbacks, concurrency limits and diagnostics, and forward them to fragment emission.
|
|
13
|
+
|
|
14
|
+
## 0.10.1 — 2026-10-08
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Stop waiting for a pending shell render when the request is cancelled, allowing the stream to close and fragment storage to be cleared.
|
|
19
|
+
- Give error fallbacks a fresh render deadline using the fragment timeout or default timeout, and interrupt them on request cancellation. Preserve the original content error if fallback rendering fails or times out, without invoking the error handler again.
|
|
20
|
+
|
|
3
21
|
## 0.10.0 — 2026-10-05
|
|
4
22
|
|
|
5
23
|
### Breaking changes
|
package/README.md
CHANGED
|
@@ -56,3 +56,29 @@ Turbo, HTMX, WebPlatform (requires browser support) and ESI (static only).
|
|
|
56
56
|
[API](https://vincle.cjean.fr/api/flow/renderToStream)
|
|
57
57
|
|
|
58
58
|
MIT © Christophe Jean
|
|
59
|
+
|
|
60
|
+
### Concurrency and diagnostics
|
|
61
|
+
|
|
62
|
+
Streaming options and `renderToStatic` options accept `maxConcurrency` and
|
|
63
|
+
`onObserve`:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
const options = {
|
|
67
|
+
maxConcurrency: 8,
|
|
68
|
+
defaultTimeout: 5000,
|
|
69
|
+
onObserve(event) {
|
|
70
|
+
console.log(event.id, event.phase, event.durationMs);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The limit counts active fragments, including live streams, until their output
|
|
76
|
+
has been emitted. It defaults to unlimited concurrency. Use lazy factories to
|
|
77
|
+
limit data fetching: a promise created before rendering has already started.
|
|
78
|
+
Long-lived streams occupy a slot; set a timeout or reserve sufficient slots
|
|
79
|
+
for the other fragments. Nested fragments retain generation ordering.
|
|
80
|
+
|
|
81
|
+
Observations have phases `start`, `complete`, `error`, `timeout`, or `cancel`,
|
|
82
|
+
a fragment ID, elapsed milliseconds, and an optional error. A recovered error
|
|
83
|
+
remains an error observation. Observer exceptions are ignored. `onError` still
|
|
84
|
+
controls fallback rendering; diagnostics do not replace it.
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as withPolyfill, c as TurboAdapter, d as createAdapter, i as WebPlatformAdapter, l as Adapter, n as HtmxAdapter, o as NATIVE_POLYFILL, r as NativeAdapter, s as nativePolyfillHash, t as EsiAdapter, u as ShellContext } from "../index-
|
|
1
|
+
import { a as withPolyfill, c as TurboAdapter, d as createAdapter, i as WebPlatformAdapter, l as Adapter, n as HtmxAdapter, o as NATIVE_POLYFILL, r as NativeAdapter, s as nativePolyfillHash, t as EsiAdapter, u as ShellContext } from "../index-BV7bof4v.js";
|
|
2
2
|
export { type Adapter, EsiAdapter, HtmxAdapter, NATIVE_POLYFILL, NativeAdapter, type ShellContext, TurboAdapter, WebPlatformAdapter, createAdapter, nativePolyfillHash, withPolyfill };
|
package/dist/adapters/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e,r as t,u as n}from"../utils-
|
|
1
|
+
import{a as e,r as t,u as n}from"../utils-DzVhT3Br.js";import{raw as r,renderToString as i}from"@vincle/core";import{Fragment as a,jsx as o,jsxs as s}from"@vincle/core/jsx-runtime";import{escapeAttr as c}from"@vincle/core/html";const l={streaming:!0,merges:n};function u(t){let n={...t,capabilities:t.capabilities??l};return e(n,`createAdapter`),n}const d={replace:`replace`,append:`append`,prepend:`prepend`,before:`before`,after:`after`,morph:`replace`},f=u({Placeholder:function({id:e,src:t,children:n}){return t?/* @__PURE__ */ o(`turbo-frame`,{id:e,src:t,children:n}):/* @__PURE__ */ o(`turbo-frame`,{id:e,children:n})},Patch:({id:e,children:t,merge:n})=>/* @__PURE__ */ o(`turbo-stream`,{action:d[n],method:n===`morph`?`morph`:void 0,target:e,children:/* @__PURE__ */ o(`template`,{children:t})}),Frame:({id:e,children:t})=>/* @__PURE__ */ o(`turbo-frame`,{id:e,children:t})}),p=`(function(){
|
|
2
2
|
var ADJ={append:"beforeend",prepend:"afterbegin",before:"beforebegin",after:"afterend"};
|
|
3
3
|
var states=new WeakMap(),pending=new Set(),complete=new WeakSet();
|
|
4
4
|
var ready=document.readyState!=="loading";
|
package/dist/components/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e,t,u as n}from"../context-
|
|
1
|
+
import{l as e,t,u as n}from"../context-ByZmZGcm.js";import{Scope as r}from"@vincle/core";import{jsx as i}from"@vincle/core/jsx-runtime";const a=e=>typeof e==`function`?e():e;function o(o){let{assets:s}=r.get(t),{name:c,media:l,children:u}=o,d={};return l!=null&&(d.media=l),n(s,c,{type:`style`,content:u,attrs:d}),e(s,c)?/* @__PURE__ */ i(`style`,{"data-name":c,...d,children:a(u)}):null}function s(o){let{assets:s}=r.get(t),{name:c,src:l,module:u,defer:d,children:f}=o,p={};l!=null&&(p.src=l),u&&(p.type=`module`),d&&(p.defer=!0);let m=f??``;return n(s,c,{type:`script`,content:m,attrs:p}),e(s,c)?/* @__PURE__ */ i(`script`,{"data-name":c,...p,children:a(m)}):null}export{s as Script,o as Style};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as e,c as t,f as n,i as r,m as i,o as a,p as o,t as s}from"./utils-DzVhT3Br.js";import{Scope as c,renderToString as l}from"@vincle/core";function u(){return{entries:/* @__PURE__ */ new Map,emitted:/* @__PURE__ */ new Set,suppressed:!1}}function d(){return{entries:/* @__PURE__ */ new Map,emitted:/* @__PURE__ */ new Set,suppressed:!0}}function f(e,t,n){if(e.entries.has(t)){if(process.env.NODE_ENV!==`production`){let r=e.entries.get(t);(r.type!==n.type||JSON.stringify(r.attrs)!==JSON.stringify(n.attrs))&&console.warn(`[vincle/flow] Asset "${t}" is declared multiple times with different attributes. The first declaration wins.`)}}else e.entries.set(t,n)}function p(e,t){return e.suppressed||e.emitted.has(t)?!1:(e.emitted.add(t),!0)}function m(a){let c=/* @__PURE__ */ new Map,l=a.adapter?.capabilities.merges??[];return{register(u,d){if(s(u,`Defer`),t(d.timeout,`<Defer target="${u}">`),!a.adapter)throw e(`${r} <Defer target="${u}">: Defer requires an adapter: without one there is no placeholder to render and no patch to emit. Pass { adapter: ... } to renderToStatic, or render through renderToStream/serve with an adapter (TurboAdapter, NativeAdapter, HtmxAdapter, WebPlatformAdapter, EsiAdapter).`,i);if(!l.includes(d.merge)){let t=l.length>0?`it supports: ${l.join(`, `)}`:`it supports no merges (static output only)`;throw e(`${r} <Defer target="${u}" merge="${d.merge}">: merge="${d.merge}" is not supported by this adapter: ${t}. Pick one of those, or use an adapter that supports "${d.merge}".`,o)}if(c.has(u))throw e(`${r} <Defer target="${u}">: target is already registered: a target is registered once per render. Two <Defer> on the same target would emit two placeholders with the same DOM id, and only the second fragment would be patched. Give the second fragment a different target, or omit target to get a generated one.`,n);c.set(u,d)},get(e){return c.get(e)},outstanding(e){let t=[];for(let[n,r]of c)e.has(n)||t.push([n,r]);return t},hasOutstanding(e){for(let t of c.keys())if(!e.has(t))return!0;return!1},cursor(){let e=c.entries(),t=0;return{outstanding(){let n=[],r=c.size;for(;t<r;){let r=e.next();if(r.done)break;n.push(r.value),t++}return n},hasOutstanding:()=>t<c.size}},get size(){return c.size},clear(){c.clear()}}}const h=c.key(`@vincle/flow:flow`);function g(t,n,a){let{config:o}=c.get(h);if(!o.adapter)throw e(`${r} renderPlaceholder("${t}"): no adapter configured: a placeholder needs an adapter to emit its deferred-fragment markup. Pass { adapter: ... } to renderToStatic, or render through renderToStream() with an adapter.`,i);let s=a??(o.mode===`static`?o.generatePath(t):null);return o.adapter.Placeholder({id:t,src:s,children:n??null})}const _=()=>({names:/* @__PURE__ */ new Set,mark:void 0});function v(e,t){return e.mark??=`vincle-defer-${crypto.randomUUID()}`,[`<!--${e.mark}:${t}-->`,`<!--/${e.mark}:${t}-->`]}async function y(e){let t=await l(e);if(!t.includes(`<!--vincle-defer-`))return t;let{slots:n}=c.get(h),r=`<!--${n.mark}:`,i=[],a=[],o=0,s=t.indexOf(r,o);for(;;){let e=a.at(-1);if(e&&(s===-1||s>=e.start)){i.push(t.slice(o,e.start)),o=e.end,a.pop();continue}if(s===-1)break;let c=t.indexOf(`-->`,s),l=t.slice(s+r.length,c),u=`<!--/${n.mark}:${l}-->`,d=t.indexOf(u,c);if(d===-1)break;i.push(t.slice(o,s)),n.names.has(l)?o=d+u.length:(a.push({start:d,end:d+u.length}),o=c+3),s=t.indexOf(r,o)}return o===0?t:(i.push(t.slice(o)),i.join(``))}function b(e){a(e);let t=0,n=m(e),r=u();c.set(h,{config:e,fragments:n,assets:r,slots:_(),nextId:()=>`${e.idPrefix??`fragment-`}${++t}`,registerFragment(e,t){n.register(e,t)}})}function x(){let e=c.get(h);c.set(h,{...e,assets:u(),slots:_()})}function S(){let e=c.get(h);c.set(h,{...e,assets:d()})}function C(e,t){return c.with(async function(){return b(t),e(c.get(h))})}export{y as a,C as c,v as i,p as l,b as n,g as o,x as r,S as s,h as t,f as u};
|
package/dist/context.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { C as OnError, b as MergeType, m as FlowConfig, p as DeferContent } from "./index-BV7bof4v.js";
|
|
2
2
|
import { JSX, ScopeKey, renderToString } from "@vincle/core";
|
|
3
3
|
//#region src/assets.d.ts
|
|
4
4
|
type AssetType = "style" | "script";
|
package/dist/context.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export{t as Flow,n as initFlow,r as initFlowAssets,i as pendingMarks,a as renderFlow,o as renderPlaceholder,s as suppressFlowAssets,c as withFlow}from"./context-ByZmZGcm.js";
|
package/dist/http.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { S as Negotiation, T as StreamingAdapter, v as FlowOptions, x as Negotiate } from "./index-BV7bof4v.js";
|
|
2
2
|
import { JSX } from "@vincle/core";
|
|
3
3
|
//#region src/http.d.ts
|
|
4
4
|
/**
|
package/dist/http.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./render-
|
|
1
|
+
import{n as e}from"./render-CsoOKhJw.js";function t(e,t){let n=/* @__PURE__ */ new Set,r=[],i=e=>{if(e)for(let t of e.split(`,`)){let e=t.trim(),i=e.toLowerCase();e&&!n.has(i)&&(n.add(i),r.push(e))}};i(e.get(`vary`)),i(t),e.set(`vary`,r.join(`, `))}function n(e,n,r){let i=new Headers(e);for(let e of[n,r])for(let[n,r]of new Headers(e??{}))n===`vary`?t(i,r):i.set(n,r);return i}async function r(t,r,i,a){let o=a?.negotiate?.(t)??{},s=e(()=>r(o),i,{...a,signal:a?.signal?AbortSignal.any([t.signal,a.signal]):t.signal,mode:a?.mode??o.mode}).pipeThrough(new TextEncoderStream),c=n({"content-type":`text/html; charset=utf-8`},a?.headers,o.headers);return new Response(s,{...a,headers:c})}function i(e){return{headers:{Vary:`HX-Target`},target:e.headers.get(`HX-Target`)??void 0}}export{i as negotiateHtmx,r as serve};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./jsx-augment-
|
|
1
|
+
import "./jsx-augment-supbRT8y.js";
|
|
2
2
|
import { Awaitable, JSX, RawString } from "@vincle/core";
|
|
3
3
|
//#region src/types.d.ts
|
|
4
4
|
declare const ALL_MERGES: readonly ["replace", "append", "prepend", "before", "after", "morph"];
|
|
@@ -37,9 +37,20 @@ type FlowErrorInfo = {
|
|
|
37
37
|
kind: "fragment" | "stream";
|
|
38
38
|
};
|
|
39
39
|
type OnError = (error: unknown, info: FlowErrorInfo) => JSX.Element | void;
|
|
40
|
+
interface FlowObservation {
|
|
41
|
+
id: string;
|
|
42
|
+
phase: "start" | "complete" | "error" | "timeout" | "cancel";
|
|
43
|
+
durationMs: number;
|
|
44
|
+
error?: unknown;
|
|
45
|
+
}
|
|
40
46
|
interface FlowOptions {
|
|
47
|
+
/** Maximum active fragments, including live streams. Omit for unlimited concurrency. */
|
|
48
|
+
maxConcurrency?: number;
|
|
49
|
+
/** Diagnostic callback. Exceptions are ignored so instrumentation cannot break rendering. */
|
|
50
|
+
onObserve?: (event: FlowObservation) => void;
|
|
41
51
|
signal?: AbortSignal;
|
|
42
52
|
onError?: OnError;
|
|
53
|
+
/** Render deadline in ms; error fallbacks receive a fresh deadline of the same duration. */
|
|
43
54
|
defaultTimeout?: number;
|
|
44
55
|
}
|
|
45
56
|
type FlowConfig = {
|
|
@@ -200,4 +211,4 @@ declare const EsiAdapter: Adapter & {
|
|
|
200
211
|
};
|
|
201
212
|
};
|
|
202
213
|
//#endregion
|
|
203
|
-
export {
|
|
214
|
+
export { OnError as C, Negotiation as S, StreamingAdapter as T, FlowObservation as _, withPolyfill as a, MergeType as b, TurboAdapter as c, createAdapter as d, AdapterCapabilities as f, FlowEvent as g, FlowErrorInfo as h, WebPlatformAdapter as i, Adapter as l, FlowConfig as m, HtmxAdapter as n, NATIVE_POLYFILL as o, DeferContent as p, NativeAdapter as r, nativePolyfillHash as s, EsiAdapter as t, ShellContext as u, FlowOptions as v, Shell as w, Negotiate as x, Fragment as y };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "./jsx-augment-
|
|
2
|
-
import { C as
|
|
1
|
+
import "./jsx-augment-supbRT8y.js";
|
|
2
|
+
import { C as OnError, S as Negotiation, T as StreamingAdapter, _ as FlowObservation, b as MergeType, f as AdapterCapabilities, g as FlowEvent, h as FlowErrorInfo, l as Adapter, m as FlowConfig, p as DeferContent, v as FlowOptions, w as Shell, x as Negotiate, y as Fragment } from "./index-BV7bof4v.js";
|
|
3
3
|
import { JSX } from "@vincle/core";
|
|
4
4
|
//#region src/components/Slot.d.ts
|
|
5
5
|
interface SlotProps {
|
|
@@ -62,7 +62,7 @@ interface StaticContext extends PureStaticContext {
|
|
|
62
62
|
*/
|
|
63
63
|
emitFragments(cb: (id: string, url: string, html: string) => void | Promise<void>): Promise<void>;
|
|
64
64
|
}
|
|
65
|
-
interface StaticOptions {
|
|
65
|
+
interface StaticOptions extends FlowOptions {
|
|
66
66
|
/** Wire-format adapter for fragment framing. Required when using emitFragments. */
|
|
67
67
|
adapter: Adapter;
|
|
68
68
|
/** Fragment URL convention. Default: (id) => `/fragments/${id}.html`. */
|
|
@@ -110,4 +110,4 @@ export declare function renderFragment(id: string, content: DeferContent, opts:
|
|
|
110
110
|
html: string;
|
|
111
111
|
}>;
|
|
112
112
|
//#endregion
|
|
113
|
-
export type { AdapterCapabilities, DeferContent, DeferProps, FlowConfig, FlowErrorInfo, FlowEvent, FlowOptions, Fragment, MergeType, Negotiate, Negotiation, OnError, PureStaticContext, RenderFragmentOptions, Shell, SlotProps, StaticContext, StaticOptions, StreamingAdapter };
|
|
113
|
+
export type { AdapterCapabilities, DeferContent, DeferProps, FlowConfig, FlowErrorInfo, FlowEvent, FlowObservation, FlowOptions, Fragment, MergeType, Negotiate, Negotiation, OnError, PureStaticContext, RenderFragmentOptions, Shell, SlotProps, StaticContext, StaticOptions, StreamingAdapter };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e,c as t,i as n,o as r,r as i,s as a,t as o}from"./context-
|
|
1
|
+
import{a as e,c as t,i as n,o as r,r as i,s as a,t as o}from"./context-ByZmZGcm.js";import{_ as s,a as c,d as l,h as u,i as d,l as f,m as p,s as m}from"./utils-DzVhT3Br.js";import{n as h,r as g,t as _}from"./render-CsoOKhJw.js";import{Scope as v,raw as y,renderToString as b,snapshotContext as x}from"@vincle/core";import{Fragment as S,jsx as C,jsxs as w}from"@vincle/core/jsx-runtime";function T(e){let{name:t,children:n}=e;return v.get(o).slots.names.add(t),r(t,n)}function E(e){let{registerFragment:t,nextId:i,slots:a}=v.get(o),{children:s,merge:c,timeout:l,onError:u,fallback:d}=e,f=e.target??i();if(t(f,{content:s,treeScope:x(),merge:c??`replace`,timeout:l,onError:u,fallback:d}),a.names.has(f))return null;let p=r(f,d);if(e.target===void 0)return p;let[m,h]=n(a,f);return/* @__PURE__ */ w(S,{children:[y(m),p,y(h)]})}const D=e=>`/fragments/${e}.html`;function O(t,n,r,o){return{renderPage:r=>v.with(async()=>{i();let a=await e(r());return n?.transformShell?n.transformShell(a,t):a},v.snapshot()),emitFragments:async e=>{if(!n)throw s(`${d} emitFragments(): emitFragments requires an adapter: fragments cannot be framed into standalone files without one. Pass { adapter: ... } to renderToStatic. Example: renderToStatic(handler, { adapter: NativeAdapter })`,p);await v.with(async()=>{a(),await g(t,async t=>{if(t.type===`fragment`){let i=await b(n.Frame({id:t.id,children:y(t.html)}));await e(t.id,r(t.id),i)}},o)},v.snapshot()),t.fragments.clear()}}}async function k(e,n){let r=n?.adapter,i=n?.generatePath??D;if(c(r,`renderToStatic`),m(n,`renderToStatic`),n?.generatePath!==void 0&&typeof n.generatePath!=`function`)throw s(`${d} renderToStatic: generatePath must be a function (id) => string, got ${f(n.generatePath)}. Example: (id) => \`/fragments/\${id}.html\`.`,l);return t(t=>e(O(t,r,i,n)),{adapter:r,mode:`static`,generatePath:i})}async function A(e,t,n){if(n.adapter===void 0)throw s(`${d} renderFragment: opts.adapter is required: pass the same adapter the site's full build uses (e.g. TurboAdapter).`,p);c(n.adapter,`renderFragment`),m(n,`renderFragment`);let r;if(await k(async i=>{await i.renderPage(()=>/* @__PURE__ */ C(E,{target:e,timeout:n.timeout,onError:n.onError,children:t})),await i.emitFragments((t,n,i)=>{t===e&&(r={url:n,html:i})})},{adapter:n.adapter,generatePath:n.generatePath}),!r)throw s(`${d} renderFragment("${e}"): produced no output for this id. The fragment was never registered or rendered: its content may have thrown or timed out (check the onError/console log), or the id differs from the <Defer target> that was rendered. Verify the id matches and that the content renders.`,u);return r}export{E as Defer,T as Slot,A as renderFragment,_ as renderToFlowEvents,k as renderToStatic,h as renderToStream};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{a as e,c as t}from"./context-ByZmZGcm.js";import{_ as n,a as r,g as i,i as a,s as o}from"./utils-DzVhT3Br.js";import{raw as s,renderToString as c}from"@vincle/core";import{isAsyncIterable as l}from"@vincle/core/html";async function u(e,t){let n,r=new Promise((e,r)=>{n=()=>r(t.reason),t.aborted?n():t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e,r])}finally{t.removeEventListener(`abort`,n)}}function d(e,t){let n=new AbortController,r=t?.signal?AbortSignal.any([t.signal,n.signal]):n.signal,i,a=[],o=()=>{for(let e of a.splice(0))e()};r.addEventListener(`abort`,o,{once:!0});let s=async e=>{if(!r.aborted){for(;(i.desiredSize??1)<=0&&!r.aborted;)await new Promise(e=>a.push(e));r.aborted||i.enqueue(e)}};return new ReadableStream({start(t){i=t,e(s,r).then(()=>{try{t.close()}catch{}},e=>{try{t.error(e)}catch{}})},pull(){o()},cancel(e){n.abort(e)}})}function f(e,t,n){let r=e;if(r==null)return{signal:t??new AbortController().signal,cleanup:()=>{}};let i=new AbortController,o=setTimeout(()=>i.abort(/* @__PURE__ */ Error(`${a} Fragment "${n}" timed out after ${r}ms: its content did not finish in time. Increase the timeout (the fragment's timeout prop or defaultTimeout), or make the content finish faster, e.g. by forwarding the abort signal to fetch().`)),r);return{signal:t?AbortSignal.any([t,i.signal]):i.signal,cleanup:()=>clearTimeout(o)}}const p=e=>typeof e==`function`;function m(e,t){try{let n=p(e.content)?e.content(t):e.content;return l(n)?{kind:`stream`,iterable:n}:{kind:`value`,value:n}}catch(e){return{kind:`sync-error`,error:e}}}async function h(t,n,r,i,a,o){if(console.error(`[vincle/flow] Error rendering ${i} "${r}"`,a),o.signal?.aborted)return;let s=n?.(a,{id:r,kind:i});if(s!=null){let{signal:n,cleanup:i}=f(o.defaultTimeout,o.signal,r),a;try{a=await u(e(s),n)}finally{i()}if(n.aborted)throw n.reason;await t({type:`fragment`,id:r,html:a,merge:`replace`})}}async function g(e,t,n,r,i,a){try{await h(e,t,n,r,i,a)}catch{throw i}}function _(e,t,n,r){return t.treeScope?t.treeScope(()=>v(e,t,n,r)):v(e,t,n,r)}function v(e,t,n,r){let i=t.onError??r.onError,a={...r,defaultTimeout:t.timeout??r.defaultTimeout},o=performance.now(),s=!1,c=(t,n)=>{try{r.onObserve?.({id:e,phase:t,durationMs:performance.now()-o,error:n})}catch{}},l=(t,o)=>(s=!0,c(r.signal?.aborted?`cancel`:u.aborted?`timeout`:`error`,o),g(n,i,e,t,o,a)),{signal:u,cleanup:d}=f(t.timeout??r.defaultTimeout,r.signal,e);c(`start`);let p=e=>e.then(()=>{s||c(r.signal?.aborted?`cancel`:u.aborted?`timeout`:`complete`)},e=>{throw s||c(`error`,e),e}),h=m(t,u);switch(h.kind){case`sync-error`:return d(),{isStreaming:!1,done:p(l(`fragment`,h.error))};case`stream`:return{isStreaming:!0,done:p(b(e,h.iterable,t.merge,n,l,u).finally(d))};case`value`:return{isStreaming:!1,done:p(y(e,h.value,t.merge,n,l,u,d))}}}async function y(t,n,r,i,a,o,s){let c;try{c=await u(e(n),o)}catch(e){await a(`fragment`,e);return}finally{s()}if(o.aborted)await a(`fragment`,o.reason);else try{await i({type:`fragment`,id:t,html:c,merge:r})}catch(e){throw console.error(`[vincle/flow] Failed to emit fragment "${t}"`,e),e}}async function b(t,n,r,i,a,o){let s=n[Symbol.asyncIterator](),c=!1,l=!1;try{for(;!o.aborted;){let n=await u(Promise.resolve(s.next()),o);if(n.done){l=!0;break}let d;try{d=await u(e(n.value),o)}catch(e){try{await a(`stream`,e)}catch(e){throw c=!0,e}if(o.aborted)break;continue}try{await i({type:`fragment`,id:t,html:d,merge:r})}catch(e){throw console.error(`[vincle/flow] Failed to emit stream chunk for "${t}"`,e),c=!0,e}}}catch(e){if(c)throw e;await a(`stream`,e)}finally{if(!l)try{let e=Promise.resolve(s.return?.(void 0)).catch(()=>{});o.aborted||await e}catch{}}}async function x(e){if(e.length===0)return;let t=await Promise.allSettled(e);for(let e of t)if(e.status===`rejected`)throw e.reason}function S(e){let t=Promise.resolve();return{emit:n=>{let r=t.then(()=>e(n));return t=r.catch(()=>{}),r},drain:()=>t}}async function C(e,t,n={}){o(n,`flushFragments`);let r=/* @__PURE__ */ new Set,i=e.fragments.cursor(),a=[],s=S(t);for(;!n.signal?.aborted;){let e=i.outstanding();if(e.length>0){let t=[];for(let[i,o]of e){for(;r.size>=(n.maxConcurrency??1/0);)await Promise.race(r);if(n.signal?.aborted)break;let{isStreaming:e,done:c}=_(i,o,s.emit,n),l=c.then(()=>{r.delete(l)},()=>{r.delete(l)});r.add(l),c.catch(()=>{}),(e?a:t).push(c)}await x(t)}else if(a.length===0||(await x(a),a.length=0,!i.hasOutstanding()))break}a.length>0&&await x(a),await s.drain()}function w(e){let t=e.trimEnd();if(!t.endsWith(`</html>`))return{body:e,closingTag:``};let n=t.slice(0,-7).trimEnd(),r=n.endsWith(`</body>`)?n.length-7:n.length;return{body:e.slice(0,r),closingTag:e.slice(r)}}function T(e){let t=!1;return new TransformStream({async transform(n,r){if(n.type===`shell`){t=!0,r.enqueue(n.html);return}let i=``;if(t&&(t=!1,i=`
|
|
2
|
+
`),n.type===`fragment`){let t=await c(e.Patch({id:n.id,children:s(n.html),merge:n.merge}));r.enqueue(i+t+`
|
|
3
|
+
`)}else r.enqueue(i+n.html+`
|
|
4
|
+
`)},flush(e){t&&e.enqueue(`
|
|
5
|
+
`)}})}async function E(t,n,r){let{body:i,closingTag:a}=w(await e(t()));return{shellBody:n.transformShell?n.transformShell(i,r):i,closingTag:a}}async function D(e,n,r,i,a){await t(async t=>{let{fragments:o}=t;try{if(n.aborted)return;let s;try{s=await u(E(r,i,t),n)}catch(e){if(n.aborted)return;throw e}let{shellBody:c,closingTag:l}=s;a.mode!==`fragment`&&c!==``&&await e({type:`shell`,html:c}),await C({fragments:o},e,{...a,signal:n}),a.mode!==`fragment`&&l&&await e({type:`close`,html:l})}finally{o.clear()}},{adapter:i,mode:`streaming`})}function O(e,t,n){o(n,e),r(t,e)}function k(e,t,n={}){return O(`renderToFlowEvents`,t,n),d((r,i)=>D(r,i,e,t,n),{signal:n.signal})}function A(e,t,r){if(O(`renderToStream`,t,r),t.capabilities.streaming!==!0)throw n(`[vincle/flow] renderToStream(): this adapter does not stream: capabilities.streaming is false, so it can only produce static output. Use renderToStatic() with this adapter, or pass a streaming adapter (TurboAdapter, NativeAdapter, HtmxAdapter, WebPlatformAdapter).`,i);return k(e,t,r).pipeThrough(T(t))}export{A as n,C as r,k as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`ERR_VINCLE_FLOW_CONFIG`,t=`ERR_VINCLE_FLOW_NO_ADAPTER`,n=`ERR_VINCLE_FLOW_MERGE_UNSUPPORTED`,r=`ERR_VINCLE_FLOW_NO_FRAGMENT`,i=`ERR_VINCLE_FLOW_NO_STREAMING`,a=`ERR_VINCLE_FLOW_DUP_FRAGMENT`;function o(e,t){let n=Error(e);return n.code=t,Error.captureStackTrace?.(n,o),n}const s=[`replace`,`append`,`prepend`,`before`,`after`,`morph`],c=`[vincle/flow]`;function l(e){if(e===null)return`null`;if(typeof e==`string`)return JSON.stringify(e.length>80?e.slice(0,80)+`…`:e);if(typeof e==`number`||typeof e==`bigint`||typeof e==`boolean`)return String(e);if(typeof e==`function`)return`function ${e.name||`<anonymous>`}`;if(e instanceof Error)return`${e.name}: ${e.message}`;try{let t=JSON.stringify(e);return t===void 0?String(e):t.length>100?t.slice(0,100)+`…`:t}catch{return String(e)}}function u(e){return e instanceof AbortSignal||typeof e==`object`&&!!e&&typeof e.aborted==`boolean`&&typeof e.addEventListener==`function`&&typeof e.onabort==`object`}function d(t,n){if(t!=null){if(typeof t!=`object`)throw o(`${c} ${n}: options must be an object, got ${l(t)}. Example: { defaultTimeout: 5000, onError, signal }`,e);if(t.maxConcurrency!==void 0&&(!Number.isSafeInteger(t.maxConcurrency)||t.maxConcurrency<1))throw o(`${c} ${n}: maxConcurrency must be a positive safe integer.`,e);if(t.onObserve!==void 0&&typeof t.onObserve!=`function`)throw o(`${c} ${n}: onObserve must be a function.`,e);if(t.defaultTimeout!==void 0&&(!Number.isFinite(t.defaultTimeout)||t.defaultTimeout<0))throw o(`${c} ${n}: defaultTimeout must be a number of milliseconds >= 0, got ${l(t.defaultTimeout)}. Pass e.g. defaultTimeout: 5000, or omit it for no limit.`,e);if(t.onError!==void 0&&typeof t.onError!=`function`)throw o(`${c} ${n}: onError must be a function (error, { id, kind }) => JSX.Element | void, got ${l(t.onError)}. Omit it to log errors to the console instead.`,e);if(t.signal!==void 0&&!u(t.signal))throw o(`${c} ${n}: signal must be an AbortSignal, got ${l(t.signal)}. Example: const controller = new AbortController(); … { signal: controller.signal }`,e)}}const f=[`Placeholder`,`Patch`,`Frame`];function p(t,n){if(t===void 0)return;if(typeof t!=`object`||!t)throw o(`${c} ${n}: adapter must be an adapter object, got ${l(t)}. Use a built-in (TurboAdapter, NativeAdapter, HtmxAdapter, WebPlatformAdapter, EsiAdapter) or createAdapter().`,e);let r=t,i=f.filter(e=>typeof r[e]!=`function`);if(i.length>0)throw o(`${c} ${n}: the adapter is missing ${i.map(e=>`"${e}"`).join(`, `)}: an adapter needs Placeholder, Patch and Frame. Use createAdapter() or a built-in adapter.`,e);let a=r.capabilities;if(typeof a!=`object`||!a)throw o(`${c} ${n}: the adapter is missing capabilities: declare { streaming: boolean, merges: MergeType[] } (see createAdapter()).`,e);if(typeof a.streaming!=`boolean`)throw o(`${c} ${n}: adapter.capabilities.streaming must be a boolean, got ${l(a.streaming)}.`,e);let u=a.merges;if(!(Array.isArray(u)&&u.every(e=>typeof e==`string`&&s.includes(e))))throw o(`${c} ${n}: adapter.capabilities.merges must be an array of merge types (${s.join(`, `)}), got ${l(u)}.`,e);if(r.transformShell!==void 0&&typeof r.transformShell!=`function`)throw o(`${c} ${n}: adapter.transformShell must be a function (shell, ctx) => string, got ${l(r.transformShell)}.`,e)}function m(t){if(typeof t!=`object`||!t)throw o(`${c} FlowConfig: the flow config must be an object, got ${l(t)}. Example: { adapter: TurboAdapter, mode: "streaming" }`,e);let n=t,r=n.mode;if(r!==`streaming`&&r!==`static`)throw o(`${c} FlowConfig.mode must be "streaming" or "static", got ${l(r)}.`,e);let i=n.idPrefix;if(i!==void 0&&typeof i!=`string`)throw o(`${c} FlowConfig.idPrefix must be a string, got ${l(i)}. Default: "fragment-".`,e);let a=n.generatePath;if(a!==void 0&&typeof a!=`function`)throw o(`${c} FlowConfig.generatePath must be a function (id) => string, got ${l(a)}. Example: (id) => \`/fragments/\${id}.html\`.`,e);if(r===`static`){if(a===void 0)throw o(`${c} FlowConfig: static mode requires generatePath: (id) => string. Example: generatePath: (id) => \`/fragments/\${id}.html\`.`,e)}else if(a!==void 0)throw o(`${c} FlowConfig: generatePath is only used in static mode (renderToStatic): remove it from the streaming config, or drop mode: "streaming" if you meant static generation.`,e);p(n.adapter,`FlowConfig`)}function h(t,n){if(t!==void 0&&(!Number.isFinite(t)||t<0))throw o(`${c} ${n}: timeout must be a number of milliseconds >= 0, got ${l(t)}. Pass e.g. timeout={5000}, or omit it to use defaultTimeout.`,e)}const g=/^[a-zA-Z][a-zA-Z0-9_-]*$/;function _(e,t){if(!g.test(e))throw o(`${c} ${t}: "${e}" is not a valid fragment id: ids must start with a letter and contain only letters, digits, hyphens and underscores (e.g. "price-aapl"). Fragment ids become DOM ids and URL segments.`,`ERR_VINCLE_FLOW_FRAGMENT_ID`)}function v(e,t){let n=e.match(/<\/head\s*>/i);if(n){let r=n.index;return e.slice(0,r)+t+e.slice(r)}let r=`<head>${t}</head>`,i=e.match(/<html\b[^>]*>/i);if(i)return y(e,i.index+i[0].length,r);let a=e.match(/^\s*<!doctype\b[^>]*>/i);return a?y(e,a[0].length,r):r+e}const y=(e,t,n)=>e.slice(0,t)+n+e.slice(t);function b(...e){let t=e.filter(Boolean);return(e,n)=>t.reduce((e,t)=>t(e,n),e)}export{o as _,p as a,h as c,e as d,a as f,i as g,r as h,c as i,l,t as m,b as n,m as o,n as p,v as r,d as s,_ as t,s as u};
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export{t as assertFragmentId,n as composeShell,r as injectIntoHead}from"./utils-DzVhT3Br.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vincle/flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "HTML document orchestration layer for @vincle/core",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deferred",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"scripts": {
|
|
67
67
|
"build": "tsdown",
|
|
68
|
-
"check": "
|
|
68
|
+
"check": "bun check && bun check -p tsconfig.tests.json",
|
|
69
69
|
"coverage": "bun test --coverage --coverage-reporter=lcov",
|
|
70
70
|
"format": "oxfmt",
|
|
71
71
|
"mutation": "stryker run",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"@types/bun": "^1.4.2",
|
|
77
77
|
"@vincle/core": "0.9.1",
|
|
78
78
|
"happy-dom": "^20.14.5",
|
|
79
|
-
"oxc-parser": "^0.
|
|
80
|
-
"tsdown": "^0.23.
|
|
79
|
+
"oxc-parser": "^0.153.0",
|
|
80
|
+
"tsdown": "^0.23.1",
|
|
81
81
|
"typescript": "^6.0.3"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
package/dist/context-CLpgp0eJ.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{_ as e,c as t,f as n,i as r,m as i,o as a,p as o,t as s}from"./utils-yayEbXmr.js";import{Scope as c,renderToString as l}from"@vincle/core";function u(){return{entries:/* @__PURE__ */ new Map,emitted:/* @__PURE__ */ new Set,suppressed:!1}}function d(){return{entries:/* @__PURE__ */ new Map,emitted:/* @__PURE__ */ new Set,suppressed:!0}}function f(e,t,n){if(e.entries.has(t)){if(process.env.NODE_ENV!==`production`){let r=e.entries.get(t);(r.type!==n.type||JSON.stringify(r.attrs)!==JSON.stringify(n.attrs))&&console.warn(`[vincle/flow] Asset "${t}" is declared multiple times with different attributes. The first declaration wins.`)}return}e.entries.set(t,n)}function p(e,t){return e.suppressed||e.emitted.has(t)?!1:(e.emitted.add(t),!0)}function m(a){let c=/* @__PURE__ */ new Map,l=a.adapter?.capabilities.merges??[];return{register(u,d){if(s(u,`Defer`),t(d.timeout,`<Defer target="${u}">`),!a.adapter)throw e(`${r} <Defer target="${u}">: Defer requires an adapter: without one there is no placeholder to render and no patch to emit. Pass { adapter: ... } to renderToStatic, or render through renderToStream/serve with an adapter (TurboAdapter, NativeAdapter, HtmxAdapter, WebPlatformAdapter, EsiAdapter).`,i);if(!l.includes(d.merge)){let t=l.length>0?`it supports: ${l.join(`, `)}`:`it supports no merges (static output only)`;throw e(`${r} <Defer target="${u}" merge="${d.merge}">: merge="${d.merge}" is not supported by this adapter: ${t}. Pick one of those, or use an adapter that supports "${d.merge}".`,o)}if(c.has(u))throw e(`${r} <Defer target="${u}">: target is already registered: a target is registered once per render. Two <Defer> on the same target would emit two placeholders with the same DOM id, and only the second fragment would be patched. Give the second fragment a different target, or omit target to get a generated one.`,n);c.set(u,d)},get(e){return c.get(e)},outstanding(e){let t=[];for(let[n,r]of c)e.has(n)||t.push([n,r]);return t},hasOutstanding(e){for(let t of c.keys())if(!e.has(t))return!0;return!1},cursor(){let e=c.entries(),t=0;return{outstanding(){let n=[],r=c.size;for(;t<r;){let r=e.next();if(r.done)break;n.push(r.value),t++}return n},hasOutstanding:()=>t<c.size}},get size(){return c.size},clear(){c.clear()}}}const h=c.key(`@vincle/flow:flow`);function g(t,n,a){let{config:o}=c.get(h);if(!o.adapter)throw e(`${r} renderPlaceholder("${t}"): no adapter configured: a placeholder needs an adapter to emit its deferred-fragment markup. Pass { adapter: ... } to renderToStatic, or render through renderToStream() with an adapter.`,i);let s=a??(o.mode===`static`?o.generatePath(t):null);return o.adapter.Placeholder({id:t,src:s,children:n??null})}const _=()=>({names:/* @__PURE__ */ new Set,mark:void 0});function v(e,t){return e.mark??=`vincle-defer-${crypto.randomUUID()}`,[`<!--${e.mark}:${t}-->`,`<!--/${e.mark}:${t}-->`]}async function y(e){let t=await l(e);if(!t.includes(`<!--vincle-defer-`))return t;let{slots:n}=c.get(h),r=`<!--${n.mark}:`,i=[],a=[],o=0,s=t.indexOf(r,o);for(;;){let e=a.at(-1);if(e&&(s===-1||s>=e.start)){i.push(t.slice(o,e.start)),o=e.end,a.pop();continue}if(s===-1)break;let c=t.indexOf(`-->`,s),l=t.slice(s+r.length,c),u=`<!--/${n.mark}:${l}-->`,d=t.indexOf(u,c);if(d===-1)break;i.push(t.slice(o,s)),n.names.has(l)?o=d+u.length:(a.push({start:d,end:d+u.length}),o=c+3),s=t.indexOf(r,o)}return o===0?t:(i.push(t.slice(o)),i.join(``))}function b(e){a(e);let t=0,n=m(e),r=u();c.set(h,{config:e,fragments:n,assets:r,slots:_(),nextId:()=>`${e.idPrefix??`fragment-`}${++t}`,registerFragment(e,t){n.register(e,t)}})}function x(){let e=c.get(h);c.set(h,{...e,assets:u(),slots:_()})}function S(){let e=c.get(h);c.set(h,{...e,assets:d()})}function C(e,t){return c.with(async function(){return b(t),e(c.get(h))})}export{y as a,C as c,v as i,p as l,b as n,g as o,x as r,S as s,h as t,f as u};
|
package/dist/render-BkqdRPBk.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import{a as e,c as t}from"./context-CLpgp0eJ.js";import{_ as n,a as r,g as i,i as a,s as o}from"./utils-yayEbXmr.js";import{raw as s,renderToString as c}from"@vincle/core";import{isAsyncIterable as l}from"@vincle/core/html";function u(e,t){let n=new AbortController,r=t?.signal?AbortSignal.any([t.signal,n.signal]):n.signal,i,a=[],o=()=>{for(let e of a.splice(0))e()};r.addEventListener(`abort`,o,{once:!0});let s=async e=>{if(!r.aborted){for(;(i.desiredSize??1)<=0&&!r.aborted;)await new Promise(e=>a.push(e));r.aborted||i.enqueue(e)}};return new ReadableStream({start(t){i=t,e(s,r).then(()=>{try{t.close()}catch{}},e=>{try{t.error(e)}catch{}})},pull(){o()},cancel(e){n.abort(e)}})}function d(e,t,n){let r=e;if(r==null)return{signal:t??new AbortController().signal,cleanup:()=>{}};let i=new AbortController,o=setTimeout(()=>i.abort(/* @__PURE__ */ Error(`${a} Fragment "${n}" timed out after ${r}ms: its content did not finish in time. Increase the timeout (the fragment's timeout prop or defaultTimeout), or make the content finish faster, e.g. by forwarding the abort signal to fetch().`)),r);return{signal:t?AbortSignal.any([t,i.signal]):i.signal,cleanup:()=>clearTimeout(o)}}const f=e=>typeof e==`function`;function p(e,t){try{let n=f(e.content)?e.content(t):e.content;return l(n)?{kind:`stream`,iterable:n}:{kind:`value`,value:n}}catch(e){return{kind:`sync-error`,error:e}}}async function m(e,t){let n,r=new Promise((e,r)=>{n=()=>r(t.reason),t.aborted?n():t.addEventListener(`abort`,n,{once:!0})});try{return await Promise.race([e,r])}finally{t.removeEventListener(`abort`,n)}}async function h(t,n,r,i,a){console.error(`[vincle/flow] Error rendering ${i} "${r}"`,a);let o=n?.(a,{id:r,kind:i});o!=null&&await t({type:`fragment`,id:r,html:await e(o),merge:`replace`})}async function g(e,t,n,r,i){try{await h(e,t,n,r,i)}catch{throw i}}function _(e,t,n,r){return t.treeScope?t.treeScope(()=>v(e,t,n,r)):v(e,t,n,r)}function v(e,t,n,r){let i=t.onError??r.onError,{signal:a,cleanup:o}=d(t.timeout??r.defaultTimeout,r.signal,e),s=p(t,a);switch(s.kind){case`sync-error`:return o(),{isStreaming:!1,done:h(n,i,e,`fragment`,s.error)};case`stream`:return{isStreaming:!0,done:b(e,s.iterable,t.merge,n,i,a).finally(o)};case`value`:return{isStreaming:!1,done:y(e,s.value,t.merge,n,i,a,o)}}}async function y(t,n,r,i,a,o,s){let c;try{c=await m(e(n),o)}catch(e){await g(i,a,t,`fragment`,e);return}finally{s()}if(o.aborted){await g(i,a,t,`fragment`,o.reason);return}try{await i({type:`fragment`,id:t,html:c,merge:r})}catch(e){throw console.error(`[vincle/flow] Failed to emit fragment "${t}"`,e),e}}async function b(t,n,r,i,a,o){let s=n[Symbol.asyncIterator](),c=!1,l=!1;try{for(;!o.aborted;){let n=await m(Promise.resolve(s.next()),o);if(n.done){l=!0;break}let u;try{u=await m(e(n.value),o)}catch(e){if(await g(i,a,t,`stream`,e),o.aborted)break;continue}try{await i({type:`fragment`,id:t,html:u,merge:r})}catch(e){throw console.error(`[vincle/flow] Failed to emit stream chunk for "${t}"`,e),c=!0,e}}}catch(e){if(c)throw e;await g(i,a,t,`stream`,e)}finally{if(!l)try{let e=Promise.resolve(s.return?.(void 0)).catch(()=>{});o.aborted||await e}catch{}}}async function x(e){if(e.length===0)return;let t=await Promise.allSettled(e);for(let e of t)if(e.status===`rejected`)throw e.reason}function S(e){let t=Promise.resolve();return{emit:n=>{let r=t.then(()=>e(n));return t=r.catch(()=>{}),r},drain:()=>t}}async function C(e,t,n={}){let r=e.fragments.cursor(),i=[],a=S(t);for(;!n.signal?.aborted;){let e=r.outstanding();if(e.length>0){let t=[];for(let[r,o]of e){let{isStreaming:e,done:s}=_(r,o,a.emit,n);(e?i:t).push(s)}await x(t);continue}if(i.length===0||(await x(i),i.length=0,!r.hasOutstanding()))break}i.length>0&&await x(i),await a.drain()}function w(e){let t=e.trimEnd();if(!t.endsWith(`</html>`))return{body:e,closingTag:``};let n=t.slice(0,-7).trimEnd(),r=n.endsWith(`</body>`)?n.length-7:n.length;return{body:e.slice(0,r),closingTag:e.slice(r)}}function T(e){let t=!1;return new TransformStream({async transform(n,r){if(n.type===`shell`){t=!0,r.enqueue(n.html);return}let i=``;if(t&&(t=!1,i=`
|
|
2
|
-
`),n.type===`fragment`){let t=await c(e.Patch({id:n.id,children:s(n.html),merge:n.merge}));r.enqueue(i+t+`
|
|
3
|
-
`)}else r.enqueue(i+n.html+`
|
|
4
|
-
`)},flush(e){t&&e.enqueue(`
|
|
5
|
-
`)}})}async function E(t,n,r){let{body:i,closingTag:a}=w(await e(t()));return{shellBody:n.transformShell?n.transformShell(i,r):i,closingTag:a}}async function D(e,n,r,i,a){await t(async t=>{let{fragments:o}=t;try{if(n.aborted)return;let{shellBody:s,closingTag:c}=await E(r,i,t);a.mode!==`fragment`&&s!==``&&await e({type:`shell`,html:s}),await C({fragments:o},e,{...a,signal:n}),a.mode!==`fragment`&&c&&await e({type:`close`,html:c})}finally{o.clear()}},{adapter:i,mode:`streaming`})}function O(e,t,n){o(n,e),r(t,e)}function k(e,t,n={}){return O(`renderToFlowEvents`,t,n),u((r,i)=>D(r,i,e,t,n),{signal:n.signal})}function A(e,t,r){if(O(`renderToStream`,t,r),t.capabilities.streaming!==!0)throw n(`[vincle/flow] renderToStream(): this adapter does not stream: capabilities.streaming is false, so it can only produce static output. Use renderToStatic() with this adapter, or pass a streaming adapter (TurboAdapter, NativeAdapter, HtmxAdapter, WebPlatformAdapter).`,i);return k(e,t,r).pipeThrough(T(t))}export{A as n,C as r,k as t};
|
package/dist/utils-yayEbXmr.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=`ERR_VINCLE_FLOW_CONFIG`,t=`ERR_VINCLE_FLOW_NO_ADAPTER`,n=`ERR_VINCLE_FLOW_MERGE_UNSUPPORTED`,r=`ERR_VINCLE_FLOW_NO_FRAGMENT`,i=`ERR_VINCLE_FLOW_NO_STREAMING`,a=`ERR_VINCLE_FLOW_DUP_FRAGMENT`;function o(e,t){let n=Error(e);return n.code=t,Error.captureStackTrace?.(n,o),n}const s=[`replace`,`append`,`prepend`,`before`,`after`,`morph`],c=`[vincle/flow]`;function l(e){if(e===null)return`null`;if(typeof e==`string`)return JSON.stringify(e.length>80?e.slice(0,80)+`…`:e);if(typeof e==`number`||typeof e==`bigint`||typeof e==`boolean`)return String(e);if(typeof e==`function`)return`function ${e.name||`<anonymous>`}`;if(e instanceof Error)return`${e.name}: ${e.message}`;try{let t=JSON.stringify(e);return t===void 0?String(e):t.length>100?t.slice(0,100)+`…`:t}catch{return String(e)}}function u(e){return e instanceof AbortSignal||typeof e==`object`&&!!e&&typeof e.aborted==`boolean`&&typeof e.addEventListener==`function`&&typeof e.onabort==`object`}function d(t,n){if(t!=null){if(typeof t!=`object`)throw o(`${c} ${n}: options must be an object, got ${l(t)}. Example: { defaultTimeout: 5000, onError, signal }`,e);if(t.defaultTimeout!==void 0&&(!Number.isFinite(t.defaultTimeout)||t.defaultTimeout<0))throw o(`${c} ${n}: defaultTimeout must be a number of milliseconds >= 0, got ${l(t.defaultTimeout)}. Pass e.g. defaultTimeout: 5000, or omit it for no limit.`,e);if(t.onError!==void 0&&typeof t.onError!=`function`)throw o(`${c} ${n}: onError must be a function (error, { id, kind }) => JSX.Element | void, got ${l(t.onError)}. Omit it to log errors to the console instead.`,e);if(t.signal!==void 0&&!u(t.signal))throw o(`${c} ${n}: signal must be an AbortSignal, got ${l(t.signal)}. Example: const controller = new AbortController(); … { signal: controller.signal }`,e)}}const f=[`Placeholder`,`Patch`,`Frame`];function p(t,n){if(t===void 0)return;if(typeof t!=`object`||!t)throw o(`${c} ${n}: adapter must be an adapter object, got ${l(t)}. Use a built-in (TurboAdapter, NativeAdapter, HtmxAdapter, WebPlatformAdapter, EsiAdapter) or createAdapter().`,e);let r=t,i=f.filter(e=>typeof r[e]!=`function`);if(i.length>0)throw o(`${c} ${n}: the adapter is missing ${i.map(e=>`"${e}"`).join(`, `)}: an adapter needs Placeholder, Patch and Frame. Use createAdapter() or a built-in adapter.`,e);let a=r.capabilities;if(typeof a!=`object`||!a)throw o(`${c} ${n}: the adapter is missing capabilities: declare { streaming: boolean, merges: MergeType[] } (see createAdapter()).`,e);if(typeof a.streaming!=`boolean`)throw o(`${c} ${n}: adapter.capabilities.streaming must be a boolean, got ${l(a.streaming)}.`,e);let u=a.merges;if(!(Array.isArray(u)&&u.every(e=>typeof e==`string`&&s.includes(e))))throw o(`${c} ${n}: adapter.capabilities.merges must be an array of merge types (${s.join(`, `)}), got ${l(u)}.`,e);if(r.transformShell!==void 0&&typeof r.transformShell!=`function`)throw o(`${c} ${n}: adapter.transformShell must be a function (shell, ctx) => string, got ${l(r.transformShell)}.`,e)}function m(t){if(typeof t!=`object`||!t)throw o(`${c} FlowConfig: the flow config must be an object, got ${l(t)}. Example: { adapter: TurboAdapter, mode: "streaming" }`,e);let n=t,r=n.mode;if(r!==`streaming`&&r!==`static`)throw o(`${c} FlowConfig.mode must be "streaming" or "static", got ${l(r)}.`,e);let i=n.idPrefix;if(i!==void 0&&typeof i!=`string`)throw o(`${c} FlowConfig.idPrefix must be a string, got ${l(i)}. Default: "fragment-".`,e);let a=n.generatePath;if(a!==void 0&&typeof a!=`function`)throw o(`${c} FlowConfig.generatePath must be a function (id) => string, got ${l(a)}. Example: (id) => \`/fragments/\${id}.html\`.`,e);if(r===`static`){if(a===void 0)throw o(`${c} FlowConfig: static mode requires generatePath: (id) => string. Example: generatePath: (id) => \`/fragments/\${id}.html\`.`,e)}else if(a!==void 0)throw o(`${c} FlowConfig: generatePath is only used in static mode (renderToStatic): remove it from the streaming config, or drop mode: "streaming" if you meant static generation.`,e);p(n.adapter,`FlowConfig`)}function h(t,n){if(t!==void 0&&(!Number.isFinite(t)||t<0))throw o(`${c} ${n}: timeout must be a number of milliseconds >= 0, got ${l(t)}. Pass e.g. timeout={5000}, or omit it to use defaultTimeout.`,e)}const g=/^[a-zA-Z][a-zA-Z0-9_-]*$/;function _(e,t){if(!g.test(e))throw o(`${c} ${t}: "${e}" is not a valid fragment id: ids must start with a letter and contain only letters, digits, hyphens and underscores (e.g. "price-aapl"). Fragment ids become DOM ids and URL segments.`,`ERR_VINCLE_FLOW_FRAGMENT_ID`)}function v(e,t){let n=e.match(/<\/head\s*>/i);if(n){let r=n.index;return e.slice(0,r)+t+e.slice(r)}let r=`<head>${t}</head>`,i=e.match(/<html\b[^>]*>/i);if(i)return y(e,i.index+i[0].length,r);let a=e.match(/^\s*<!doctype\b[^>]*>/i);return a?y(e,a[0].length,r):r+e}const y=(e,t,n)=>e.slice(0,t)+n+e.slice(t);function b(...e){let t=e.filter(Boolean);return(e,n)=>t.reduce((e,t)=>t(e,n),e)}export{o as _,p as a,h as c,e as d,a as f,i as g,r as h,c as i,l,t as m,b as n,m as o,n as p,v as r,d as s,_ as t,s as u};
|