@invisibleloop/pulse 0.1.21
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/.claude/commands/build-page.md +59 -0
- package/.claude/commands/new-doc-page.md +45 -0
- package/.claude/commands/verify.md +52 -0
- package/.claude/pulse-checklist.md +111 -0
- package/.claude/settings.local.json +102 -0
- package/.github/workflows/ci.yml +22 -0
- package/.github/workflows/publish.yml +41 -0
- package/.pulse/load-reports/home/1773432711417.json +22 -0
- package/CLAUDE.md +383 -0
- package/README.md +95 -0
- package/docs/.claude/pulse-checklist.md +111 -0
- package/docs/public/.pulse-ui-version +1 -0
- package/docs/public/dist/accessibility.boot-5DVTARJU.js +115 -0
- package/docs/public/dist/actions.boot-P66HKQEM.js +164 -0
- package/docs/public/dist/auth.boot-IMAJAUPH.js +140 -0
- package/docs/public/dist/caching.boot-DVR6KDE7.js +53 -0
- package/docs/public/dist/components--accordion.boot-3HVKMNWC.js +11 -0
- package/docs/public/dist/components--alert.boot-GCEXOZAC.js +6 -0
- package/docs/public/dist/components--app-badge.boot-DVT3GCHJ.js +6 -0
- package/docs/public/dist/components--avatar.boot-PSW24EVA.js +5 -0
- package/docs/public/dist/components--badge.boot-TYDY2RMK.js +7 -0
- package/docs/public/dist/components--banner.boot-EI5PZSZK.js +7 -0
- package/docs/public/dist/components--breadcrumbs.boot-SMA2E2GO.js +34 -0
- package/docs/public/dist/components--button.boot-J54BQM2E.js +23 -0
- package/docs/public/dist/components--card.boot-PZGNDIB6.js +138 -0
- package/docs/public/dist/components--carousel.boot-TP6LPFZZ.js +12 -0
- package/docs/public/dist/components--charts.boot-2EOYQWKL.js +108 -0
- package/docs/public/dist/components--checkbox.boot-DS5BSL6T.js +54 -0
- package/docs/public/dist/components--cluster.boot-HHVIBBJG.js +9 -0
- package/docs/public/dist/components--code-window.boot-2GR2DV33.js +20 -0
- package/docs/public/dist/components--container.boot-7LOOGK2K.js +5 -0
- package/docs/public/dist/components--cta.boot-FSNZ5YRT.js +11 -0
- package/docs/public/dist/components--divider.boot-3NI2C3QG.js +6 -0
- package/docs/public/dist/components--empty.boot-YX2UR3PV.js +7 -0
- package/docs/public/dist/components--feature.boot-MUD7NSUO.js +13 -0
- package/docs/public/dist/components--fieldset.boot-J7BYHMKF.js +19 -0
- package/docs/public/dist/components--fileupload.boot-NIKVTTPD.js +52 -0
- package/docs/public/dist/components--footer.boot-EYUK5FRG.js +14 -0
- package/docs/public/dist/components--grid.boot-URDQVDDR.js +59 -0
- package/docs/public/dist/components--heading.boot-BPQKU43E.js +44 -0
- package/docs/public/dist/components--hero.boot-4RAPRGAB.js +17 -0
- package/docs/public/dist/components--icons.boot-ZITNU5JP.js +68 -0
- package/docs/public/dist/components--image.boot-XEEGHQZF.js +19 -0
- package/docs/public/dist/components--input.boot-SGASZG5K.js +7 -0
- package/docs/public/dist/components--list.boot-W3XC5MHD.js +55 -0
- package/docs/public/dist/components--media.boot-5VFIETZO.js +13 -0
- package/docs/public/dist/components--modal.boot-RZUYXBN2.js +47 -0
- package/docs/public/dist/components--nav.boot-ODBOHU7O.js +33 -0
- package/docs/public/dist/components--pricing.boot-4AQ4ZVBY.js +21 -0
- package/docs/public/dist/components--progress.boot-GHAGYZOK.js +30 -0
- package/docs/public/dist/components--prose.boot-QANJL6JI.js +67 -0
- package/docs/public/dist/components--pullquote.boot-Q2WMNAZU.js +22 -0
- package/docs/public/dist/components--radio.boot-TJRDQ2OL.js +75 -0
- package/docs/public/dist/components--rating.boot-QBAN6DEL.js +38 -0
- package/docs/public/dist/components--search.boot-PXH5O5AG.js +17 -0
- package/docs/public/dist/components--section.boot-AQGIYHWW.js +12 -0
- package/docs/public/dist/components--segmented.boot-BEVTKEJO.js +33 -0
- package/docs/public/dist/components--select.boot-47X5RHOC.js +10 -0
- package/docs/public/dist/components--slider.boot-PSRRX7XL.js +47 -0
- package/docs/public/dist/components--spinner.boot-MZ5MO2OH.js +22 -0
- package/docs/public/dist/components--stack.boot-DI4NJXBF.js +9 -0
- package/docs/public/dist/components--stat.boot-QMFUWBQT.js +9 -0
- package/docs/public/dist/components--stepper.boot-34PP2NEV.js +22 -0
- package/docs/public/dist/components--table.boot-FCQGSFIQ.js +11 -0
- package/docs/public/dist/components--testimonial.boot-DWQPDKYG.js +11 -0
- package/docs/public/dist/components--textarea.boot-QVXLBOJ5.js +4 -0
- package/docs/public/dist/components--timeline.boot-26LN52P2.js +95 -0
- package/docs/public/dist/components--toggle.boot-IQQEI76S.js +29 -0
- package/docs/public/dist/components--tooltip.boot-LGHCO6NN.js +9 -0
- package/docs/public/dist/components.boot-SE6PQ4P7.js +103 -0
- package/docs/public/dist/config.boot-DTRRWUE6.js +126 -0
- package/docs/public/dist/constraints.boot-DUHDZBMC.js +71 -0
- package/docs/public/dist/deploy.boot-SLAD3NI2.js +163 -0
- package/docs/public/dist/docs-8e3d4b5c.css +1 -0
- package/docs/public/dist/extending.boot-UA3CN243.js +159 -0
- package/docs/public/dist/faq.boot-6EQAWLQR.js +43 -0
- package/docs/public/dist/getting-started.boot-TDKIFL5U.js +86 -0
- package/docs/public/dist/guard.boot-AUHAWTG4.js +80 -0
- package/docs/public/dist/home.boot-BVQXRH32.js +383 -0
- package/docs/public/dist/how-it-works.boot-LTWAKWKW.js +104 -0
- package/docs/public/dist/hydration.boot-JRM6IPJL.js +78 -0
- package/docs/public/dist/images.boot-M6ZVKTZS.js +80 -0
- package/docs/public/dist/manifest.json +94 -0
- package/docs/public/dist/meta.boot-7NXGPHR4.js +79 -0
- package/docs/public/dist/mutations.boot-F6F43UDX.js +79 -0
- package/docs/public/dist/navigation.boot-AOXWS3ZF.js +57 -0
- package/docs/public/dist/performance.boot-C3UPCOBK.js +98 -0
- package/docs/public/dist/persist.boot-WT32PQOQ.js +61 -0
- package/docs/public/dist/project-structure.boot-FB3LRVJ4.js +63 -0
- package/docs/public/dist/prompt-examples.boot-YKR4VDK4.js +31 -0
- package/docs/public/dist/pulse-ui-81a85c03.css +1 -0
- package/docs/public/dist/raw-responses.boot-M4KA5YXL.js +104 -0
- package/docs/public/dist/routing.boot-FNX5FDGH.js +70 -0
- package/docs/public/dist/runtime-B73WLANC.js +1 -0
- package/docs/public/dist/runtime-KO4BHUQ3.js +49 -0
- package/docs/public/dist/runtime-L2HNXIHW.js +59 -0
- package/docs/public/dist/runtime-QFURDKA2.js +5 -0
- package/docs/public/dist/runtime-UVPXO4IR.js +375 -0
- package/docs/public/dist/runtime-VMJA3Z4N.js +10 -0
- package/docs/public/dist/runtime-ZJ4FXT5O.js +11 -0
- package/docs/public/dist/server-api.boot-K7X3LCFB.js +219 -0
- package/docs/public/dist/server-data.boot-Y7HQYC4R.js +157 -0
- package/docs/public/dist/slash-commands.boot-V2UV7OW2.js +26 -0
- package/docs/public/dist/spec.boot-2WU7ZHCV.js +159 -0
- package/docs/public/dist/state.boot-B24GUE3R.js +73 -0
- package/docs/public/dist/store.boot-TLIB4XHH.js +150 -0
- package/docs/public/dist/streaming.boot-W2DZSMW4.js +80 -0
- package/docs/public/dist/stripe.boot-QN3C2GEL.js +164 -0
- package/docs/public/dist/supabase.boot-BG4XXLZE.js +303 -0
- package/docs/public/dist/testing.boot-6U4WKMTE.js +130 -0
- package/docs/public/dist/validation.boot-PQHYGW5B.js +100 -0
- package/docs/public/docs.css +2020 -0
- package/docs/public/menu.js +83 -0
- package/docs/public/pulse-ui.css +2739 -0
- package/docs/public/pulse-ui.js +236 -0
- package/docs/server.js +192 -0
- package/docs/src/lib/component-page.js +47 -0
- package/docs/src/lib/highlight.js +255 -0
- package/docs/src/lib/layout.js +131 -0
- package/docs/src/lib/metrics-store.js +6 -0
- package/docs/src/lib/nav.js +159 -0
- package/docs/src/lib/stats.js +81 -0
- package/docs/src/pages/accessibility.js +157 -0
- package/docs/src/pages/actions.js +191 -0
- package/docs/src/pages/auth.js +177 -0
- package/docs/src/pages/caching.js +95 -0
- package/docs/src/pages/components/accordion.js +48 -0
- package/docs/src/pages/components/alert.js +35 -0
- package/docs/src/pages/components/app-badge.js +41 -0
- package/docs/src/pages/components/avatar.js +35 -0
- package/docs/src/pages/components/badge.js +36 -0
- package/docs/src/pages/components/banner.js +45 -0
- package/docs/src/pages/components/breadcrumbs.js +94 -0
- package/docs/src/pages/components/button.js +84 -0
- package/docs/src/pages/components/card.js +225 -0
- package/docs/src/pages/components/carousel.js +72 -0
- package/docs/src/pages/components/charts.js +278 -0
- package/docs/src/pages/components/checkbox.js +129 -0
- package/docs/src/pages/components/cluster.js +47 -0
- package/docs/src/pages/components/code-window.js +57 -0
- package/docs/src/pages/components/container.js +40 -0
- package/docs/src/pages/components/cta.js +53 -0
- package/docs/src/pages/components/divider.js +37 -0
- package/docs/src/pages/components/empty.js +36 -0
- package/docs/src/pages/components/feature.js +60 -0
- package/docs/src/pages/components/fieldset.js +65 -0
- package/docs/src/pages/components/fileupload.js +127 -0
- package/docs/src/pages/components/footer.js +58 -0
- package/docs/src/pages/components/grid.js +165 -0
- package/docs/src/pages/components/heading.js +107 -0
- package/docs/src/pages/components/hero.js +65 -0
- package/docs/src/pages/components/icons.js +285 -0
- package/docs/src/pages/components/image.js +71 -0
- package/docs/src/pages/components/input.js +51 -0
- package/docs/src/pages/components/list.js +112 -0
- package/docs/src/pages/components/media.js +51 -0
- package/docs/src/pages/components/modal.js +111 -0
- package/docs/src/pages/components/nav.js +86 -0
- package/docs/src/pages/components/pricing.js +68 -0
- package/docs/src/pages/components/progress.js +102 -0
- package/docs/src/pages/components/prose.js +111 -0
- package/docs/src/pages/components/pullquote.js +71 -0
- package/docs/src/pages/components/radio.js +194 -0
- package/docs/src/pages/components/rating.js +106 -0
- package/docs/src/pages/components/search.js +61 -0
- package/docs/src/pages/components/section.js +59 -0
- package/docs/src/pages/components/segmented.js +121 -0
- package/docs/src/pages/components/select.js +45 -0
- package/docs/src/pages/components/slider.js +114 -0
- package/docs/src/pages/components/spinner.js +73 -0
- package/docs/src/pages/components/stack.js +48 -0
- package/docs/src/pages/components/stat.js +55 -0
- package/docs/src/pages/components/stepper.js +66 -0
- package/docs/src/pages/components/table.js +45 -0
- package/docs/src/pages/components/testimonial.js +49 -0
- package/docs/src/pages/components/textarea.js +31 -0
- package/docs/src/pages/components/timeline.js +227 -0
- package/docs/src/pages/components/toggle.js +84 -0
- package/docs/src/pages/components/tooltip.js +48 -0
- package/docs/src/pages/components.js +204 -0
- package/docs/src/pages/config.js +193 -0
- package/docs/src/pages/constraints.js +99 -0
- package/docs/src/pages/deploy.js +233 -0
- package/docs/src/pages/extending.js +198 -0
- package/docs/src/pages/faq.js +96 -0
- package/docs/src/pages/getting-started.js +106 -0
- package/docs/src/pages/guard.js +121 -0
- package/docs/src/pages/home.js +401 -0
- package/docs/src/pages/how-it-works.js +183 -0
- package/docs/src/pages/hydration.js +98 -0
- package/docs/src/pages/images.js +121 -0
- package/docs/src/pages/meta.js +120 -0
- package/docs/src/pages/mutations.js +106 -0
- package/docs/src/pages/navigation.js +85 -0
- package/docs/src/pages/performance.js +157 -0
- package/docs/src/pages/persist.js +88 -0
- package/docs/src/pages/project-structure.js +90 -0
- package/docs/src/pages/prompt-examples.js +186 -0
- package/docs/src/pages/raw-responses.js +124 -0
- package/docs/src/pages/routing.js +99 -0
- package/docs/src/pages/server-api.js +281 -0
- package/docs/src/pages/server-data.js +185 -0
- package/docs/src/pages/slash-commands.js +55 -0
- package/docs/src/pages/spec.js +207 -0
- package/docs/src/pages/state.js +101 -0
- package/docs/src/pages/store.js +181 -0
- package/docs/src/pages/streaming.js +108 -0
- package/docs/src/pages/stripe.js +193 -0
- package/docs/src/pages/supabase.js +323 -0
- package/docs/src/pages/testing.js +198 -0
- package/docs/src/pages/validation.js +138 -0
- package/examples/contact.js +166 -0
- package/examples/counter.js +94 -0
- package/examples/dev.server.js +91 -0
- package/examples/examples.test.js +394 -0
- package/examples/pricing.js +244 -0
- package/examples/products.js +191 -0
- package/examples/quiz.js +208 -0
- package/examples/shared.js +78 -0
- package/examples/todos.js +162 -0
- package/package.json +75 -0
- package/public/.pulse-ui-version +1 -0
- package/public/chippy-bird.css +246 -0
- package/public/examples/contact.css +119 -0
- package/public/examples/counter.css +79 -0
- package/public/examples/pricing.css +132 -0
- package/public/examples/products.css +100 -0
- package/public/examples/quiz.css +200 -0
- package/public/examples/todos.css +137 -0
- package/public/favicon.ico +0 -0
- package/public/log-dashboard.css +383 -0
- package/public/pulse-ui.css +2740 -0
- package/public/pulse-ui.js +236 -0
- package/public/pulse.css +149 -0
- package/scripts/build.js +411 -0
- package/src/agent/checklist.md +111 -0
- package/src/agent/coverage-check.js +66 -0
- package/src/agent/guide-components.md +274 -0
- package/src/agent/guide-examples.md +54 -0
- package/src/agent/guide-routing.md +36 -0
- package/src/agent/guide-server.md +258 -0
- package/src/agent/guide-spec.md +103 -0
- package/src/agent/guide-styles.md +191 -0
- package/src/agent/guide.md +979 -0
- package/src/agent/identity.md +106 -0
- package/src/agent/workflow.md +108 -0
- package/src/cli/cli.test.js +82 -0
- package/src/cli/dev.js +195 -0
- package/src/cli/discover.js +113 -0
- package/src/cli/index.js +361 -0
- package/src/cli/load-report.js +91 -0
- package/src/cli/load-runner.js +121 -0
- package/src/cli/report-server.js +723 -0
- package/src/cli/report.js +116 -0
- package/src/cli/scaffold.archive.js +1371 -0
- package/src/cli/scaffold.js +349 -0
- package/src/cli/start.js +74 -0
- package/src/html.js +19 -0
- package/src/mcp/server.js +884 -0
- package/src/mcp/validate-worker.js +110 -0
- package/src/runtime/image.js +74 -0
- package/src/runtime/image.test.js +111 -0
- package/src/runtime/index.js +621 -0
- package/src/runtime/navigate.js +146 -0
- package/src/runtime/runtime.test.js +773 -0
- package/src/runtime/ssr.js +464 -0
- package/src/runtime/ssr.test.js +421 -0
- package/src/runtime/store.js +92 -0
- package/src/runtime/toast.js +163 -0
- package/src/server/index.js +1386 -0
- package/src/server/server.test.js +1248 -0
- package/src/spec/schema.js +428 -0
- package/src/spec/schema.test.js +291 -0
- package/src/store/index.js +102 -0
- package/src/store/store.test.js +210 -0
- package/src/testing/html.js +283 -0
- package/src/testing/index.js +249 -0
- package/src/testing/testing.test.js +450 -0
- package/src/ui/accordion.js +28 -0
- package/src/ui/alert.js +43 -0
- package/src/ui/app-badge.js +48 -0
- package/src/ui/avatar.js +47 -0
- package/src/ui/badge.js +24 -0
- package/src/ui/banner.js +26 -0
- package/src/ui/breadcrumbs.js +38 -0
- package/src/ui/button.js +66 -0
- package/src/ui/card.js +34 -0
- package/src/ui/carousel.js +59 -0
- package/src/ui/charts.js +321 -0
- package/src/ui/checkbox.js +65 -0
- package/src/ui/cluster.js +44 -0
- package/src/ui/code-window.js +39 -0
- package/src/ui/container.js +24 -0
- package/src/ui/cta.js +37 -0
- package/src/ui/divider.js +29 -0
- package/src/ui/empty.js +33 -0
- package/src/ui/feature.js +33 -0
- package/src/ui/fieldset.js +37 -0
- package/src/ui/fileupload.js +89 -0
- package/src/ui/footer.js +38 -0
- package/src/ui/grid.js +36 -0
- package/src/ui/heading.js +45 -0
- package/src/ui/hero.js +37 -0
- package/src/ui/icons.js +161 -0
- package/src/ui/index.js +89 -0
- package/src/ui/input.js +74 -0
- package/src/ui/list.js +36 -0
- package/src/ui/media.js +44 -0
- package/src/ui/modal.js +80 -0
- package/src/ui/nav.js +61 -0
- package/src/ui/pricing.js +56 -0
- package/src/ui/progress.js +62 -0
- package/src/ui/prose.js +29 -0
- package/src/ui/pullquote.js +34 -0
- package/src/ui/radio.js +102 -0
- package/src/ui/rating.js +93 -0
- package/src/ui/search.js +77 -0
- package/src/ui/section.js +69 -0
- package/src/ui/segmented.js +50 -0
- package/src/ui/select.js +77 -0
- package/src/ui/slider.js +84 -0
- package/src/ui/spinner.js +34 -0
- package/src/ui/stack.js +36 -0
- package/src/ui/stat.js +52 -0
- package/src/ui/stepper.js +46 -0
- package/src/ui/switch.js +57 -0
- package/src/ui/table.js +45 -0
- package/src/ui/testimonial.js +48 -0
- package/src/ui/textarea.js +72 -0
- package/src/ui/timeline.js +72 -0
- package/src/ui/tooltip.js +28 -0
- package/src/ui/ui.test.js +1241 -0
- package/src/ui/uiimage.js +65 -0
- package/tsconfig.json +13 -0
- package/types/html.d.ts +17 -0
- package/types/image.d.ts +70 -0
- package/types/index.d.ts +7 -0
- package/types/navigate.d.ts +38 -0
- package/types/runtime.d.ts +63 -0
- package/types/schema.d.ts +243 -0
- package/types/server.d.ts +145 -0
- package/types/ssr.d.ts +110 -0
- package/types/testing.d.ts +154 -0
- package/types/ui.d.ts +704 -0
package/types/ssr.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse — SSR renderer types
|
|
3
|
+
* @invisibleloop/pulse/ssr
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PulseSpec } from './schema.js'
|
|
7
|
+
import type { RequestContext } from './schema.js'
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// renderToString
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
export interface RenderToStringResult {
|
|
14
|
+
/** Complete rendered HTML fragment (without the outer document wrapper) */
|
|
15
|
+
html: string
|
|
16
|
+
/** Resolved server state from all fetchers */
|
|
17
|
+
serverState: Record<string, unknown>
|
|
18
|
+
/** Render timings in milliseconds */
|
|
19
|
+
timing: {
|
|
20
|
+
data: number
|
|
21
|
+
render: number
|
|
22
|
+
total: number
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Render a spec to a complete HTML string.
|
|
28
|
+
* Resolves all server data before rendering — no streaming.
|
|
29
|
+
*
|
|
30
|
+
* @param spec The page spec to render
|
|
31
|
+
* @param ctx Request context passed to server data fetchers
|
|
32
|
+
* @param pageState Optional additional client state
|
|
33
|
+
*/
|
|
34
|
+
export function renderToString(
|
|
35
|
+
spec: PulseSpec,
|
|
36
|
+
ctx?: Partial<RequestContext> & Record<string, unknown>,
|
|
37
|
+
pageState?: Record<string, unknown>
|
|
38
|
+
): Promise<RenderToStringResult>
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// renderToStream
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Render a spec as a Web ReadableStream.
|
|
46
|
+
* Shell segments are flushed immediately; deferred segments follow as their
|
|
47
|
+
* server data resolves. Falls back to non-streaming for simple specs.
|
|
48
|
+
*
|
|
49
|
+
* Returns a ReadableStream of Uint8Array chunks (HTML).
|
|
50
|
+
*
|
|
51
|
+
* @param spec The page spec to render
|
|
52
|
+
* @param ctx Request context passed to server data fetchers
|
|
53
|
+
*/
|
|
54
|
+
export function renderToStream(
|
|
55
|
+
spec: PulseSpec,
|
|
56
|
+
ctx?: Partial<RequestContext> & Record<string, unknown>
|
|
57
|
+
): ReadableStream<Uint8Array>
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// wrapDocument
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
export interface WrapDocumentOptions {
|
|
64
|
+
/** Rendered HTML content (body of the page) */
|
|
65
|
+
content: string
|
|
66
|
+
/** The spec (for meta, hydration script) */
|
|
67
|
+
spec?: PulseSpec
|
|
68
|
+
/** Resolved server state — serialised as window.__PULSE_SERVER__ */
|
|
69
|
+
serverState?: Record<string, unknown>
|
|
70
|
+
/** Timing values from renderToString */
|
|
71
|
+
timing?: { data: number; render: number; total: number }
|
|
72
|
+
/** CSP nonce for the hydration script */
|
|
73
|
+
nonce?: string
|
|
74
|
+
/** Canonical URL for <link rel="canonical"> */
|
|
75
|
+
canonicalUrl?: string
|
|
76
|
+
/** Extra HTML injected before </body> */
|
|
77
|
+
extraBody?: string
|
|
78
|
+
/** Path to the runtime bundle for <link rel="modulepreload"> */
|
|
79
|
+
runtimeBundle?: string
|
|
80
|
+
/** Hydrate path resolved from manifest (overrides spec.hydrate) */
|
|
81
|
+
resolvedHydrate?: string
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface WrapDocumentResult {
|
|
85
|
+
/** Complete HTML document string */
|
|
86
|
+
html: string
|
|
87
|
+
/** Server-Timing header value (null if no timing provided) */
|
|
88
|
+
serverTimingValue: string | null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wrap a rendered HTML fragment in a full HTML document.
|
|
93
|
+
* Adds DOCTYPE, <head> (meta, styles, hydration script), and </html>.
|
|
94
|
+
*/
|
|
95
|
+
export function wrapDocument(options: WrapDocumentOptions): WrapDocumentResult
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// withTimeout
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Race a promise against a timeout.
|
|
103
|
+
* Rejects with a descriptive error if the promise does not resolve within ms.
|
|
104
|
+
* Pass null or 0 for ms to disable the timeout (returns the original promise).
|
|
105
|
+
*
|
|
106
|
+
* @param promise The promise to race
|
|
107
|
+
* @param ms Timeout in milliseconds (null or 0 = disabled)
|
|
108
|
+
* @param name Fetcher name used in the rejection error message
|
|
109
|
+
*/
|
|
110
|
+
export function withTimeout<T>(promise: Promise<T>, ms: number | null, name: string): Promise<T>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse — View testing helper types
|
|
3
|
+
* @invisibleloop/pulse/testing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { PulseSpec, RequestContext } from './schema.js'
|
|
7
|
+
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Element
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
export interface Element {
|
|
13
|
+
/** Tag name (lowercase) */
|
|
14
|
+
tag: string
|
|
15
|
+
/** Parsed attributes. Boolean attrs (e.g. disabled) have value true. */
|
|
16
|
+
attrs: Record<string, string | true>
|
|
17
|
+
/** All text content within the element, whitespace-collapsed. */
|
|
18
|
+
text: string
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get an attribute value. Returns '' for boolean attrs, null if absent.
|
|
22
|
+
* Mirrors DOM getAttribute() behaviour.
|
|
23
|
+
*/
|
|
24
|
+
attr(name: string): string | null
|
|
25
|
+
|
|
26
|
+
/** Find the first descendant matching selector. Returns null if not found. */
|
|
27
|
+
find(selector: string): Element | null
|
|
28
|
+
|
|
29
|
+
/** Find all descendants matching selector. */
|
|
30
|
+
findAll(selector: string): Element[]
|
|
31
|
+
|
|
32
|
+
/** True if any descendant matches selector. */
|
|
33
|
+
has(selector: string): boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// RenderResult
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export interface RenderResult<S extends object = Record<string, unknown>> {
|
|
41
|
+
/** Raw HTML string returned by the view function(s). */
|
|
42
|
+
html: string
|
|
43
|
+
/** Client state used for rendering. */
|
|
44
|
+
state: S
|
|
45
|
+
/** Server state used for rendering (resolved fetchers or provided mock). */
|
|
46
|
+
server: Record<string, unknown>
|
|
47
|
+
|
|
48
|
+
/** All text content in the output, whitespace-collapsed, tags stripped. */
|
|
49
|
+
text(): string
|
|
50
|
+
|
|
51
|
+
/** True if any element in the output matches selector. */
|
|
52
|
+
has(selector: string): boolean
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Find the first element matching selector.
|
|
56
|
+
* Returns null if not found.
|
|
57
|
+
*/
|
|
58
|
+
find(selector: string): Element | null
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Find the first element matching selector.
|
|
62
|
+
* Throws with a clear message if not found.
|
|
63
|
+
* Use this when the element must exist — failures are immediately obvious.
|
|
64
|
+
*/
|
|
65
|
+
get(selector: string): Element
|
|
66
|
+
|
|
67
|
+
/** Find all elements matching selector. */
|
|
68
|
+
findAll(selector: string): Element[]
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Get an attribute value from the first element matching selector.
|
|
72
|
+
* Returns null if the element or attribute is not present.
|
|
73
|
+
*/
|
|
74
|
+
attr(selector: string, name: string): string | null
|
|
75
|
+
|
|
76
|
+
/** Count elements matching selector. */
|
|
77
|
+
count(selector: string): number
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// renderSync
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
export interface RenderSyncOptions<S extends object = Record<string, unknown>> {
|
|
85
|
+
/** State overrides merged with spec.state. */
|
|
86
|
+
state?: Partial<S>
|
|
87
|
+
/** Server state passed directly to the view (bypasses spec.server fetchers). */
|
|
88
|
+
server?: Record<string, unknown>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Render a spec's view synchronously.
|
|
93
|
+
*
|
|
94
|
+
* Calls the view function directly — no server fetcher resolution.
|
|
95
|
+
* Pass mock server data via `options.server`.
|
|
96
|
+
*
|
|
97
|
+
* Ideal for fast unit tests of pure view functions.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* import { renderSync } from '@invisibleloop/pulse/testing'
|
|
101
|
+
*
|
|
102
|
+
* const result = renderSync(counterSpec, { state: { count: 5 } })
|
|
103
|
+
* assert(result.get('#count').text === '5')
|
|
104
|
+
*/
|
|
105
|
+
export function renderSync<S extends object = Record<string, unknown>>(
|
|
106
|
+
spec: PulseSpec<S>,
|
|
107
|
+
options?: RenderSyncOptions<S>
|
|
108
|
+
): RenderResult<S>
|
|
109
|
+
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// render
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
export interface RenderOptions<S extends object = Record<string, unknown>> {
|
|
115
|
+
/** State overrides merged with spec.state. */
|
|
116
|
+
state?: Partial<S>
|
|
117
|
+
/**
|
|
118
|
+
* Server state passed directly to the view.
|
|
119
|
+
* When provided, spec.server fetchers are NOT called — use for unit tests.
|
|
120
|
+
* When omitted, spec.server fetchers are called with `ctx` — use for integration tests.
|
|
121
|
+
*/
|
|
122
|
+
server?: Record<string, unknown>
|
|
123
|
+
/**
|
|
124
|
+
* Request context passed to spec.server fetchers.
|
|
125
|
+
* Only relevant when server is not provided (integration mode).
|
|
126
|
+
*/
|
|
127
|
+
ctx?: Partial<RequestContext> & Record<string, unknown>
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Render a spec's view asynchronously.
|
|
132
|
+
*
|
|
133
|
+
* Two modes:
|
|
134
|
+
* - **Mock mode** — pass `server` in options to bypass fetchers entirely.
|
|
135
|
+
* Fast, deterministic, no I/O.
|
|
136
|
+
* - **Integration mode** — omit `server` to run real spec.server fetchers.
|
|
137
|
+
* Use `ctx` to set params, cookies, headers, etc.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* // Mock server data
|
|
141
|
+
* const result = await render(productSpec, {
|
|
142
|
+
* server: { product: { id: 1, name: 'Widget' } }
|
|
143
|
+
* })
|
|
144
|
+
* assert.equal(result.get('h1').text, 'Widget')
|
|
145
|
+
*
|
|
146
|
+
* // Integration — real fetchers
|
|
147
|
+
* const result = await render(productSpec, {
|
|
148
|
+
* ctx: { params: { id: '42' } }
|
|
149
|
+
* })
|
|
150
|
+
*/
|
|
151
|
+
export function render<S extends object = Record<string, unknown>>(
|
|
152
|
+
spec: PulseSpec<S>,
|
|
153
|
+
options?: RenderOptions<S>
|
|
154
|
+
): Promise<RenderResult<S>>
|