@typed/astro 1.0.0-beta.7

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 ADDED
@@ -0,0 +1,70 @@
1
+ # @typed/astro
2
+
3
+ Render Typed templates on the server and hydrate them as Astro islands. Astro owns pages, routing, asset delivery, and hydration scheduling. Typed owns each island's rendering, reactive state, and Scope.
4
+
5
+ ```ts
6
+ // astro.config.ts
7
+ import { defineConfig } from "astro/config";
8
+ import typed from "@typed/astro";
9
+
10
+ export default defineConfig({ integrations: [typed()] });
11
+ ```
12
+
13
+ Export a component from an ordinary TypeScript module:
14
+
15
+ ```ts
16
+ // Counter.ts
17
+ import { component } from "@typed/astro/Component";
18
+ import * as RefSubject from "@typed/fx/RefSubject";
19
+ import { html } from "@typed/template";
20
+
21
+ export default component(function* (props: { initial: number }) {
22
+ const count = yield* RefSubject.make(props.initial);
23
+ return html`<button @click=${RefSubject.update(count, (n) => n + 1)}>Count: ${count}</button>`;
24
+ });
25
+ ```
26
+
27
+ ```astro
28
+ ---
29
+ import Counter from "./Counter";
30
+ ---
31
+ <Counter initial={1} client:load />
32
+ ```
33
+
34
+ `component` uses the same generator and argument-forwarding conventions as `@typed/ui/Component`. A generator with no parameters produces a lazy Fx value; a generator with parameters produces a function. Return any Renderable, including strings, arrays, Effects, and templates. Optional pipeline functions receive the Fx and the original arguments, preserving errors and required services. Astro normalizes the generator return inside a template boundary, so its Fx emits `RenderEvent` values and requires `Scope | RenderTemplate` in addition to the generator and returned value’s services. This installs hydration context before nested templates run. Pipeline callbacks receive this normalized Fx; keep the final result renderable. A zero-argument pipeline that returns a primitive instead of an object throws because it cannot carry an island brand. The explicit Astro brand identifies candidates without running them; it does not validate that a final pipeline result is renderable.
35
+
36
+ The integration supplies `RenderTemplate` and `Scope`. Provide additional application services with an Fx pipeline before exporting an island. State acquired inside the generator is recreated separately for each request and browser island, including zero-argument component values.
37
+
38
+ Without a client directive, Astro emits server-rendered HTML and sends no Typed island JavaScript. `client:load`, `client:idle`, `client:visible`, and `client:media` hydrate Typed's server markers and retain matching DOM nodes. `client:only="@typed/astro"` skips SSR and replaces the fallback when mounting. Pass serializable props as required by Astro; initialize server and browser state consistently. Explicit `RefSubject.hydrate` remains available for adopting live DOM property state such as pre-hydration input edits; the integration does not serialize arbitrary Effect state.
39
+
40
+ Each SSR call closes its Scope after collecting HTML. Each browser island retains its Scope until Astro dispatches `astro:unmount`. A repeated Astro render with new props closes the previous Scope and runs the component again, resetting generator-local state. Failed initial renders reject Astro's hydration promise. Subsequent rendering failures dispatch `typed:error` on the island with the Effect Cause in `event.detail`.
41
+
42
+ ## Slots
43
+
44
+ The second argument contains default and named Astro slots as optional Typed renderables:
45
+
46
+ ```ts
47
+ import { component, type Slots } from "@typed/astro/Component";
48
+ import { html } from "@typed/template";
49
+
50
+ export default component(function* (_props: {}, slots: Slots) {
51
+ return html`<article>
52
+ ${slots.heading}
53
+ <main>${slots.default}</main>
54
+ </article>`;
55
+ });
56
+ ```
57
+
58
+ Astro supplies pre-rendered slot HTML. The integration uses `astro-slot` elements for hydrated components and `astro-static-slot` markers for static components, which Astro strips before passing their content to an ancestor. It adopts the existing live slot elements on hydration, preserving child DOM identity and listeners. Ordinary component props still pass through Typed's contextual escaping. Slot renderables are opaque, borrowed content: insert each once; Astro and nested islands retain responsibility for their contents. Do not pass unsanitized external HTML as an Astro raw-HTML slot.
59
+
60
+ ## Validation
61
+
62
+ ```sh
63
+ pnpm --filter @typed/astro build
64
+ pnpm --filter @typed/astro test
65
+ pnpm --filter @typed/astro test:integration
66
+ ```
67
+
68
+ The browser suite checks matching-node hydration, reactive updates, isolated state, repeated renders, slot ownership, and unmount cleanup. The production Astro fixture checks SSR and all five built-in hydration directives in Chromium.
69
+
70
+ The integration follows Astro's [Renderer API](https://docs.astro.build/en/reference/renderer-reference/) and [Integration API](https://docs.astro.build/en/reference/integrations-reference/).
@@ -0,0 +1,179 @@
1
+ import type * as Effect from "effect/Effect";
2
+ import * as Fx from "@typed/fx/Fx";
3
+ import { type Renderable } from "@typed/template";
4
+ import type { RenderEvent } from "@typed/template/RenderEvent";
5
+ import type { Scope } from "effect/Scope";
6
+ import type { RenderTemplate } from "@typed/template/RenderTemplate";
7
+ declare const TypeId: unique symbol;
8
+ /**
9
+ * Named Astro slots available to a component generator's second argument.
10
+ *
11
+ * Astro owns the serialized or existing slot content. Insert each present slot
12
+ * once as opaque output; a missing named slot is undefined. Re-rendering its
13
+ * descendants with Typed would give two renderers ownership of the same range.
14
+ *
15
+ * @since 1.0.0
16
+ * @category Astro slots
17
+ */
18
+ export type Slots = Readonly<Record<string, Fx.Fx<RenderEvent> | undefined>>;
19
+ /**
20
+ * Rendering services supplied by Astro for each server request or browser island.
21
+ * Application services remain visible in the component's requirements and must
22
+ * be provided by the component's pipeline or application boundary.
23
+ *
24
+ * @since 1.0.0
25
+ * @category Island services
26
+ */
27
+ export type Services = Scope | RenderTemplate;
28
+ type ComponentFx<Yield extends Effect.Effect<any, any, any>, Result extends Renderable.Any> = Fx.Fx<RenderEvent, Effect.Error<Yield> | Renderable.Error<Result>, Effect.Services<Yield> | Renderable.Services<Result> | Services>;
29
+ type AstroValue<Result> = Result extends Fx.Fx<any, any, any> ? Result & {
30
+ (_props?: {}, _slots?: Slots): Result;
31
+ readonly [TypeId]: true;
32
+ } : Result;
33
+ type ComponentResult<Args extends ReadonlyArray<any>, Result> = Args extends readonly [] ? AstroValue<Result> : (...args: Args) => Result;
34
+ /**
35
+ * Generator and argument-aware pipeline contracts for Astro components.
36
+ *
37
+ * @since 1.0.0
38
+ * @category Generator contracts
39
+ */
40
+ export declare namespace component {
41
+ /**
42
+ * Overloads preserving generator arguments, yielded errors and services, and
43
+ * the returned renderable's errors and services before optional pipelines.
44
+ *
45
+ * @since 1.0.0
46
+ * @category Generator contracts
47
+ */
48
+ type Gen = {
49
+ /**
50
+ * Creates a component directly for a zero-argument body.
51
+ *
52
+ * @since 1.0.0
53
+ * @category Zero-argument components
54
+ */
55
+ <Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any>(body: () => Generator<Yield, Result, any>): AstroValue<ComponentFx<Yield, Result>>;
56
+ /**
57
+ * Creates a component function while preserving the generator's parameters.
58
+ *
59
+ * @since 1.0.0
60
+ * @category Parameterized components
61
+ */
62
+ <Args extends readonly [any, ...ReadonlyArray<any>], Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any>(body: (...args: Args) => Generator<Yield, Result, any>): (...args: Args) => ComponentFx<Yield, Result>;
63
+ /**
64
+ * Applies one argument-aware transformation to the generated Fx.
65
+ *
66
+ * @since 1.0.0
67
+ * @category Argument-aware pipelines
68
+ */
69
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A): ComponentResult<Args, A>;
70
+ /**
71
+ * Applies two ordered argument-aware transformations to the generated Fx.
72
+ *
73
+ * @since 1.0.0
74
+ * @category Argument-aware pipelines
75
+ */
76
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B): ComponentResult<Args, B>;
77
+ /**
78
+ * Applies three ordered argument-aware transformations to the generated Fx.
79
+ *
80
+ * @since 1.0.0
81
+ * @category Argument-aware pipelines
82
+ */
83
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C): ComponentResult<Args, C>;
84
+ /**
85
+ * Applies four ordered argument-aware transformations to the generated Fx.
86
+ *
87
+ * @since 1.0.0
88
+ * @category Argument-aware pipelines
89
+ */
90
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D): ComponentResult<Args, D>;
91
+ /**
92
+ * Applies five ordered argument-aware transformations to the generated Fx.
93
+ *
94
+ * @since 1.0.0
95
+ * @category Argument-aware pipelines
96
+ */
97
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E): ComponentResult<Args, E>;
98
+ /**
99
+ * Applies six ordered argument-aware transformations to the generated Fx.
100
+ *
101
+ * @since 1.0.0
102
+ * @category Argument-aware pipelines
103
+ */
104
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F): ComponentResult<Args, F>;
105
+ /**
106
+ * Applies seven ordered argument-aware transformations to the generated Fx.
107
+ *
108
+ * @since 1.0.0
109
+ * @category Argument-aware pipelines
110
+ */
111
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G): ComponentResult<Args, G>;
112
+ /**
113
+ * Applies eight ordered argument-aware transformations to the generated Fx.
114
+ *
115
+ * @since 1.0.0
116
+ * @category Argument-aware pipelines
117
+ */
118
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G, H>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G, h: (_: G, ...args: Args) => H): ComponentResult<Args, H>;
119
+ /**
120
+ * Applies nine ordered argument-aware transformations to the generated Fx.
121
+ *
122
+ * @since 1.0.0
123
+ * @category Argument-aware pipelines
124
+ */
125
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G, H, I>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G, h: (_: G, ...args: Args) => H, i: (_: H, ...args: Args) => I): ComponentResult<Args, I>;
126
+ /**
127
+ * Applies ten ordered argument-aware transformations to the generated Fx.
128
+ *
129
+ * @since 1.0.0
130
+ * @category Argument-aware pipelines
131
+ */
132
+ <Args extends ReadonlyArray<any>, Yield extends Effect.Effect<any, any, any>, const Result extends Renderable.Any, A, B, C, D, E, F, G, H, I, J>(body: (...args: Args) => Generator<Yield, Result, any>, a: (_: ComponentFx<Yield, Result>, ...args: Args) => A, b: (_: A, ...args: Args) => B, c: (_: B, ...args: Args) => C, d: (_: C, ...args: Args) => D, e: (_: D, ...args: Args) => E, f: (_: E, ...args: Args) => F, g: (_: F, ...args: Args) => G, h: (_: G, ...args: Args) => H, i: (_: H, ...args: Args) => I, j: (_: I, ...args: Args) => J): ComponentResult<Args, J>;
133
+ };
134
+ }
135
+ /**
136
+ * Defines a Typed Astro island with the generator and pipeline semantics of
137
+ * @typed/ui/Component. Zero-argument generators produce lazy, callable Fx values
138
+ * accepted by Astro markup; calling one returns its Fx without running it.
139
+ * Parameterized generators produce functions. Each generator may return any
140
+ * Renderable, normalized to RenderEvents under Astro's hydration boundary.
141
+ * Pipes receive that Fx and the original arguments. Provide application services
142
+ * in a pipe; Astro supplies Scope and RenderTemplate. Each run owns its state.
143
+ * A zero-argument pipeline must retain an object value so it can carry the brand.
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * import { component } from "@typed/astro/Component"
148
+ * import { RefSubject } from "@typed/fx"
149
+ * import { html } from "@typed/template"
150
+ *
151
+ * export const Quantity = component(function* ({ initial }: { initial: number }) {
152
+ * const quantity = yield* RefSubject.make(initial)
153
+ * return html`<output>${quantity}</output>`
154
+ * })
155
+ * ```
156
+ *
157
+ * @since 1.0.0
158
+ * @category Island components
159
+ */
160
+ export declare const component: component.Gen;
161
+ /**
162
+ * Checks the Astro component brand without executing a candidate component.
163
+ * Used by renderer discovery so probing does not run another framework's setup.
164
+ *
165
+ * @since 1.0.0
166
+ * @category Renderer discovery
167
+ */
168
+ export declare function isComponent(value: unknown): value is Fx.Fx<RenderEvent, unknown, Services> | ((props: Record<string, unknown>, slots: Slots) => Fx.Fx<RenderEvent, unknown, Services>);
169
+ /**
170
+ * Resolves Astro's props and slots call while leaving zero-argument Fx values lazy.
171
+ * Renderer adapters use this after isComponent succeeds; application components
172
+ * normally enter through Astro markup rather than calling this helper directly.
173
+ *
174
+ * @since 1.0.0
175
+ * @category Renderer dispatch
176
+ */
177
+ export declare function view(value: Fx.Fx<RenderEvent, unknown, Services> | ((props: Record<string, unknown>, slots: Slots) => Fx.Fx<RenderEvent, unknown, Services>), props: Record<string, unknown>, slots: Slots): Fx.Fx<RenderEvent, unknown, Services>;
178
+ export {};
179
+ //# sourceMappingURL=Component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Component.d.ts","sourceRoot":"","sources":["../src/Component.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import { pipeArguments } from "effect/Pipeable";
2
+ import { component as uiComponent } from "@typed/ui/Component";
3
+ import * as Fx from "@typed/fx/Fx";
4
+ import { html } from "@typed/template";
5
+ const TypeId = Symbol.for("@typed/astro/Component");
6
+ /**
7
+ * Defines a Typed Astro island with the generator and pipeline semantics of
8
+ * @typed/ui/Component. Zero-argument generators produce lazy, callable Fx values
9
+ * accepted by Astro markup; calling one returns its Fx without running it.
10
+ * Parameterized generators produce functions. Each generator may return any
11
+ * Renderable, normalized to RenderEvents under Astro's hydration boundary.
12
+ * Pipes receive that Fx and the original arguments. Provide application services
13
+ * in a pipe; Astro supplies Scope and RenderTemplate. Each run owns its state.
14
+ * A zero-argument pipeline must retain an object value so it can carry the brand.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import { component } from "@typed/astro/Component"
19
+ * import { RefSubject } from "@typed/fx"
20
+ * import { html } from "@typed/template"
21
+ *
22
+ * export const Quantity = component(function* ({ initial }: { initial: number }) {
23
+ * const quantity = yield* RefSubject.make(initial)
24
+ * return html`<output>${quantity}</output>`
25
+ * })
26
+ * ```
27
+ *
28
+ * @since 1.0.0
29
+ * @category Island components
30
+ */
31
+ export const component = (body, ...pipes) => {
32
+ const normalized = function* (...args) {
33
+ return html `${yield* body(...args)}`;
34
+ };
35
+ // Fx.fn selects by generator arity, so the parameterized wrapper must retain it.
36
+ Object.defineProperty(normalized, "length", { value: body.length });
37
+ const result = uiComponent(normalized, ...pipes);
38
+ if ((typeof result !== "object" || result === null) && typeof result !== "function") {
39
+ throw new TypeError("An Astro component pipeline must produce a Renderable object or function");
40
+ }
41
+ if (body.length === 0 && Fx.isFx(result)) {
42
+ // Astro's JSX checker requires a callable component. Preserve the Fx protocol
43
+ // on that callable; neither calling it nor using run/pipe evaluates the body.
44
+ const callable = Object.assign(() => result, result, {
45
+ [Fx.FxTypeId]: result[Fx.FxTypeId],
46
+ run: result.run.bind(result),
47
+ pipe() {
48
+ return pipeArguments(this, arguments);
49
+ },
50
+ [TypeId]: true,
51
+ });
52
+ return callable;
53
+ }
54
+ return Object.assign(result, { [TypeId]: true });
55
+ };
56
+ /**
57
+ * Checks the Astro component brand without executing a candidate component.
58
+ * Used by renderer discovery so probing does not run another framework's setup.
59
+ *
60
+ * @since 1.0.0
61
+ * @category Renderer discovery
62
+ */
63
+ export function isComponent(value) {
64
+ return (((typeof value === "object" && value !== null) || typeof value === "function") &&
65
+ TypeId in value &&
66
+ value[TypeId] === true);
67
+ }
68
+ /**
69
+ * Resolves Astro's props and slots call while leaving zero-argument Fx values lazy.
70
+ * Renderer adapters use this after isComponent succeeds; application components
71
+ * normally enter through Astro markup rather than calling this helper directly.
72
+ *
73
+ * @since 1.0.0
74
+ * @category Renderer dispatch
75
+ */
76
+ export function view(value, props, slots) {
77
+ return !Fx.isFx(value) && typeof value === "function" ? value(props, slots) : value;
78
+ }
@@ -0,0 +1,12 @@
1
+ export default _default;
2
+ /**
3
+ * Creates Astro's native hydration entry. Each island owns its reactive render
4
+ * lifetime; replacement and unmount close the previous render before proceeding.
5
+ *
6
+ * @since 1.0.0
7
+ * @category Hydration and lifecycle
8
+ */
9
+ declare function _default(element: HTMLElement): (component: unknown, props: Record<string, unknown>, slots?: Record<string, string>, { client }?: {
10
+ client: string;
11
+ }) => Promise<void>;
12
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";AA+CA;;;;;;GAMG;0BACa,OAAO,EAAE,WAAW,eAErB,OAAO,SACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,UACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,eACjB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,KAC7B,OAAO,CAAC,IAAI,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,97 @@
1
+ import * as Cause from "effect/Cause";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Fiber from "effect/Fiber";
4
+ import * as Fx from "@typed/fx/Fx";
5
+ import * as Subject from "@typed/fx/Subject";
6
+ import { DomRenderTemplate, render } from "@typed/template/Render";
7
+ import { DomRenderEvent } from "@typed/template/RenderEvent";
8
+ import * as Component from "./Component.js";
9
+ const islands = new WeakMap();
10
+ /** Adopt only this island's slots; nested islands keep their own renderer ownership. */
11
+ function slotsFromAstro(element, slots, initialized) {
12
+ const existing = Array.from(element.querySelectorAll("astro-slot")).filter((slot) => slot.closest("astro-island") === element);
13
+ return Object.fromEntries(Object.entries(slots).map(([name, content]) => {
14
+ let slot = existing.find((node) => (node.getAttribute("name") ?? "default") === name);
15
+ if (!slot) {
16
+ slot = element.ownerDocument.createElement("astro-slot");
17
+ if (name !== "default")
18
+ slot.setAttribute("name", name);
19
+ // Only Astro's trusted slot transport crosses this HTML boundary, never component props.
20
+ slot.innerHTML = content;
21
+ }
22
+ else if (initialized && slot.innerHTML !== content) {
23
+ // Astro reserializes live child DOM on parent updates. Only replace content that differs.
24
+ slot.innerHTML = content;
25
+ }
26
+ return [name, Fx.succeed(DomRenderEvent(slot))];
27
+ }));
28
+ }
29
+ /**
30
+ * Creates Astro's native hydration entry. Each island owns its reactive render
31
+ * lifetime; replacement and unmount close the previous render before proceeding.
32
+ *
33
+ * @since 1.0.0
34
+ * @category Hydration and lifecycle
35
+ */
36
+ export default (element) => (component, props, slots = {}, { client } = { client: "load" }) => {
37
+ if (!Component.isComponent(component)) {
38
+ return Promise.reject(new TypeError("@typed/astro requires a component created with component"));
39
+ }
40
+ const { promise, resolve, reject } = Promise.withResolvers();
41
+ const submit = islands.get(element) ?? mount(element);
42
+ submit({ component, props, slots, client, resolve, reject });
43
+ return promise;
44
+ };
45
+ function mount(element) {
46
+ const requests = Subject.unsafeMake();
47
+ let current = null;
48
+ let initialized = false;
49
+ const rendering = requests.pipe(Fx.switchMap((request) => {
50
+ let ready = false;
51
+ return Fx.unwrapScoped(Effect.sync(() => {
52
+ if (request === null || request !== current)
53
+ return Fx.empty;
54
+ if (request.client === "only" && !initialized)
55
+ element.replaceChildren();
56
+ const children = slotsFromAstro(element, request.slots, initialized);
57
+ initialized = true;
58
+ return render(Component.view(request.component, request.props, children), element).pipe(Fx.provide(DomRenderTemplate.using(element.ownerDocument)), Fx.tap(() => {
59
+ ready = true;
60
+ request.resolve();
61
+ }), Fx.continueWith(() => ready ? Fx.never : Fx.die(new Error("Typed component completed without rendering"))));
62
+ })).pipe(Fx.catchCause((cause) => {
63
+ if (request !== null && request === current && !Cause.hasInterruptsOnly(cause)) {
64
+ if (!ready)
65
+ request.reject(cause);
66
+ else
67
+ element.dispatchEvent(new CustomEvent("typed:error", { detail: cause }));
68
+ }
69
+ return Fx.empty;
70
+ }));
71
+ }), Fx.ensuring(Effect.sync(() => {
72
+ if (islands.get(element) === submit)
73
+ islands.delete(element);
74
+ element.removeEventListener("astro:unmount", unmount);
75
+ })));
76
+ const fiber = Effect.runFork(Effect.scoped(Fx.drain(rendering)));
77
+ function submit(request) {
78
+ current?.resolve();
79
+ current = request;
80
+ Effect.runFork(requests.onSuccess(request));
81
+ }
82
+ function unmount() {
83
+ current?.resolve();
84
+ current = null;
85
+ initialized = false;
86
+ Effect.runFork(requests.onSuccess(null).pipe(Effect.andThen(() => {
87
+ if (current !== null)
88
+ return Effect.void;
89
+ if (islands.get(element) === submit)
90
+ islands.delete(element);
91
+ return Fiber.interrupt(fiber);
92
+ })));
93
+ }
94
+ islands.set(element, submit);
95
+ element.addEventListener("astro:unmount", unmount);
96
+ return submit;
97
+ }
@@ -0,0 +1,19 @@
1
+ import type { AstroIntegration } from "astro";
2
+ /**
3
+ * Registers the Typed server and browser renderers with Astro.
4
+ * Add one instance to astro.config's integrations list. Client directives then
5
+ * select whether and when each branded Typed component starts in the browser.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { defineConfig } from "astro/config"
10
+ * import typed from "@typed/astro"
11
+ *
12
+ * export default defineConfig({ integrations: [typed()] })
13
+ * ```
14
+ *
15
+ * @since 1.0.0
16
+ * @category Integration setup
17
+ */
18
+ export default function typed(): AstroIntegration;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAE9C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,OAAO,UAAU,KAAK,IAAI,gBAAgB,CAahD"}
package/dist/index.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Registers the Typed server and browser renderers with Astro.
3
+ * Add one instance to astro.config's integrations list. Client directives then
4
+ * select whether and when each branded Typed component starts in the browser.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { defineConfig } from "astro/config"
9
+ * import typed from "@typed/astro"
10
+ *
11
+ * export default defineConfig({ integrations: [typed()] })
12
+ * ```
13
+ *
14
+ * @since 1.0.0
15
+ * @category Integration setup
16
+ */
17
+ export default function typed() {
18
+ return {
19
+ name: "@typed/astro",
20
+ hooks: {
21
+ "astro:config:setup": ({ addRenderer }) => {
22
+ addRenderer({
23
+ name: "@typed/astro",
24
+ serverEntrypoint: "@typed/astro/server",
25
+ clientEntrypoint: "@typed/astro/client",
26
+ });
27
+ },
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,19 @@
1
+ import type { AstroComponentMetadata } from "astro";
2
+ /**
3
+ * Astro renderer protocol for branded Typed components and trusted Astro slots.
4
+ * Each render owns an HTML rendering scope that closes after serialization.
5
+ * Astro loads this entry through the integration; it is not a raw-HTML API.
6
+ *
7
+ * @since 1.0.0
8
+ * @category Server rendering
9
+ */
10
+ declare const renderer: {
11
+ name: string;
12
+ supportsAstroStaticSlot: true;
13
+ check(component: unknown): Promise<boolean>;
14
+ renderToStaticMarkup(component: unknown, props: Record<string, unknown>, slots?: Record<string, string>, metadata?: AstroComponentMetadata): Promise<{
15
+ html: string;
16
+ }>;
17
+ };
18
+ export default renderer;
19
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAA+B,MAAM,OAAO,CAAC;AA2CjF;;;;;;;GAOG;AACH,QAAA,MAAM,QAAQ;;;qBAGW,OAAO;oCAIjB,OAAO,SACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,UACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,aAClB,sBAAsB;;;CAYE,CAAC;eAEzB,QAAQ"}
package/dist/server.js ADDED
@@ -0,0 +1,52 @@
1
+ import * as Fx from "@typed/fx/Fx";
2
+ import * as Effect from "effect/Effect";
3
+ import { HtmlRenderTemplate, renderToHtmlString } from "@typed/template/Html";
4
+ import { HtmlRenderEvent } from "@typed/template/RenderEvent";
5
+ import * as Component from "./Component.js";
6
+ /** Astro supplies already-rendered slot HTML. This boundary is not a public raw HTML helper. */
7
+ function slotsFromAstro(slots, metadata) {
8
+ const tag = metadata?.hydrate ? "astro-slot" : "astro-static-slot";
9
+ return Object.fromEntries(Object.entries(slots).map(([name, content]) => [
10
+ name,
11
+ Fx.succeed(HtmlRenderEvent(`<${tag}${name === "default" ? "" : ` name="${escapeAttribute(name)}"`}>${content}</${tag}>`, true)),
12
+ ]));
13
+ }
14
+ function escapeAttribute(value) {
15
+ return value.replace(/[&"<>']/g, (character) => {
16
+ switch (character) {
17
+ case "&":
18
+ return "&amp;";
19
+ case '"':
20
+ return "&quot;";
21
+ case "'":
22
+ return "&#39;";
23
+ case "<":
24
+ return "&lt;";
25
+ default:
26
+ return "&gt;";
27
+ }
28
+ });
29
+ }
30
+ /**
31
+ * Astro renderer protocol for branded Typed components and trusted Astro slots.
32
+ * Each render owns an HTML rendering scope that closes after serialization.
33
+ * Astro loads this entry through the integration; it is not a raw-HTML API.
34
+ *
35
+ * @since 1.0.0
36
+ * @category Server rendering
37
+ */
38
+ const renderer = {
39
+ name: "@typed/astro",
40
+ supportsAstroStaticSlot: true,
41
+ async check(component) {
42
+ return Component.isComponent(component);
43
+ },
44
+ async renderToStaticMarkup(component, props, slots = {}, metadata) {
45
+ if (!Component.isComponent(component)) {
46
+ throw new TypeError("@typed/astro requires a component created with component");
47
+ }
48
+ const html = await Effect.runPromise(Effect.suspend(() => renderToHtmlString(Component.view(component, props, slotsFromAstro(slots, metadata)))).pipe(Effect.provide(HtmlRenderTemplate), Effect.scoped));
49
+ return { html };
50
+ },
51
+ };
52
+ export default renderer;
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@typed/astro",
3
+ "version": "1.0.0-beta.7",
4
+ "description": "Astro integration for Typed HTML templates and hydrated islands.",
5
+ "keywords": [
6
+ "astro-integration",
7
+ "effect",
8
+ "typed"
9
+ ],
10
+ "homepage": "https://github.com/TylorS/typed-smol/tree/main/packages/astro#readme",
11
+ "bugs": "https://github.com/TylorS/typed-smol/issues",
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/TylorS/typed-smol.git",
16
+ "directory": "packages/astro"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "type": "module",
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
27
+ },
28
+ "./Component": {
29
+ "types": "./dist/Component.d.ts",
30
+ "import": "./dist/Component.js"
31
+ },
32
+ "./server": {
33
+ "types": "./dist/server.d.ts",
34
+ "import": "./dist/server.js"
35
+ },
36
+ "./client": {
37
+ "types": "./dist/client.d.ts",
38
+ "import": "./dist/client.js"
39
+ }
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "@typed/fx": "2.0.0-beta.7",
46
+ "@typed/template": "1.0.0-beta.7",
47
+ "@typed/ui": "1.0.0-beta.7",
48
+ "effect": "4.0.0-rc.112"
49
+ },
50
+ "devDependencies": {
51
+ "@vitest/browser-playwright": "5.0.0",
52
+ "astro": "^7.3.1",
53
+ "happy-dom": "20.13.2",
54
+ "playwright": "^1.62.1",
55
+ "typescript": "7.0.2",
56
+ "vite": "8.2.2",
57
+ "vitest": "5.0.0"
58
+ },
59
+ "peerDependencies": {
60
+ "astro": "^7.3.1"
61
+ },
62
+ "scripts": {
63
+ "build": "rm -rf dist tsconfig.tsbuildinfo && tsc",
64
+ "test": "pnpm run test:node && pnpm run test:browser && pnpm run test:types",
65
+ "test:node": "vitest run --config vitest.config.ts",
66
+ "test:browser": "vitest run --config vitest.browser.config.ts",
67
+ "test:types": "tsc -p tsconfig.tests.json",
68
+ "test:integration": "pnpm run build && node test.integration.mjs"
69
+ }
70
+ }