@tailorkit/react 0.1.0-beta.22 → 0.1.0-beta.24

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.
@@ -1,51 +1,9 @@
1
- import type { ViewInstance } from "@tailorkit/app/client";
1
+ import type { ControlledSlotProps, SlotProps } from "@tailorkit/client-core";
2
2
  import type { SlotDefinitions, ViewDefinition } from "@tailorkit/core/schema";
3
3
  import type { ReactNode } from "react";
4
- import type { ViewContext, ViewName } from "../hooks/use-register-view";
5
- import type { TailorKitApp } from "../tailorkit";
4
+ export type { ControlledSlotProps, SlotProps, SlotContext } from "@tailorkit/client-core";
6
5
  type DefaultViews = Record<`/${string}`, ViewDefinition>;
7
- type ContextPaths<TViews extends Record<string, ViewDefinition>, TView extends ViewName<TViews>> = {
8
- [P in ViewName<TViews>]: P extends "/" | TView ? P : TView extends `${P}/${string}` ? P : never;
9
- }[ViewName<TViews>];
10
- type ContextFields<TView> = undefined extends ViewContext<TView> ? Partial<Exclude<ViewContext<TView>, undefined>> : ViewContext<TView>;
11
- export type SlotContext<TViews extends Record<string, ViewDefinition>, TView extends ViewName<TViews>> = {
12
- [P in ContextPaths<TViews, TView>]: (context: ContextFields<TViews[P]>) => void;
13
- }[ContextPaths<TViews, TView>] extends (context: infer TContext) => void ? TContext : never;
14
- type InstanceProps<TSlot, TKey extends string, TValue> = boolean extends (TSlot extends {
15
- multiple?: infer T;
16
- } ? T : false) ? {
17
- [K in TKey]?: TValue;
18
- } : TSlot extends {
19
- multiple: true;
20
- } ? {
21
- [K in TKey]: TValue;
22
- } : {
23
- [K in TKey]?: never;
24
- };
25
- export type SlotProps<TSlots extends SlotDefinitions = SlotDefinitions> = {
26
- [TSlot in keyof TSlots & string]: {
27
- app: TailorKitApp;
28
- name: TSlot;
29
- } & InstanceProps<TSlots[TSlot], "instanceKey", string>;
30
- }[keyof TSlots & string];
31
- export type ControlledSlotProps<TViews extends Record<string, ViewDefinition> = DefaultViews, TSlots extends SlotDefinitions = SlotDefinitions> = {
32
- [TSlot in keyof TSlots & string]: {
33
- [TView in Extract<ViewName<TViews>, TSlots[TSlot]["views"][number]>]: {
34
- app: TailorKitApp;
35
- name: TSlot;
36
- view: TView;
37
- } & (({
38
- context: SlotContext<TViews, TView>;
39
- status: "ready";
40
- } & InstanceProps<TSlots[TSlot], "instance", ViewInstance>) | {
41
- context?: never;
42
- status: "loading" | "error";
43
- instance?: never;
44
- });
45
- }[Extract<ViewName<TViews>, TSlots[TSlot]["views"][number]>];
46
- }[keyof TSlots & string];
47
6
  export type SlotComponent<TViews extends Record<string, ViewDefinition> = DefaultViews, TSlots extends SlotDefinitions = SlotDefinitions> = ((props: SlotProps<TSlots>) => ReactNode) & {
48
7
  Controlled: (props: ControlledSlotProps<TViews, TSlots>) => ReactNode;
49
8
  };
50
9
  export declare const Slot: SlotComponent;
51
- export {};
@@ -1,22 +1,5 @@
1
- import type { TailorKitApp } from "../tailorkit";
2
- import type { TailorKitAppsSnapshot, TailorKitStore } from "@tailorkit/client-core";
3
- export interface UseAppsOptions<TScopeNames extends string = string> {
4
- scopes?: readonly TScopeNames[];
5
- appIds?: readonly string[];
6
- }
7
- export interface UseAppsResult {
8
- data: TailorKitApp[] | undefined;
9
- error: Error | null;
10
- isFetching: boolean;
11
- isError: boolean;
12
- isLoading: boolean;
13
- isPending: boolean;
14
- isSuccess: boolean;
15
- refetch: () => Promise<void>;
16
- status: "error" | "idle" | "loading" | "ready";
17
- }
1
+ import type { AppsQueryOptions, QueryResult, TailorKitApp, TailorKitStore } from "@tailorkit/client-core";
2
+ export type UseAppsOptions<TScopeNames extends string = string> = AppsQueryOptions<TScopeNames>;
3
+ export type UseAppsResult = QueryResult<TailorKitApp[]>;
18
4
  export declare function useApps(options?: UseAppsOptions): UseAppsResult;
19
5
  export declare function useAppsStore(store: TailorKitStore, options?: UseAppsOptions): UseAppsResult;
20
- export { matchesApp } from "@tailorkit/client-core";
21
- export declare function useAppsSnapshot(store: TailorKitStore): TailorKitAppsSnapshot;
22
- export declare function toUseAppsResult(snapshot: TailorKitAppsSnapshot, refetch: () => Promise<void>): UseAppsResult;
@@ -0,0 +1,6 @@
1
+ import type { ViewDefinition } from "@tailorkit/core/schema";
2
+ import type { ViewName, ViewState } from "@tailorkit/client-core";
3
+ export type { ViewName, ViewContext, ViewOptions, ViewState } from "@tailorkit/client-core";
4
+ type DefaultViews = Record<`/${string}`, ViewDefinition>;
5
+ export type UseViewContext<TViews extends Record<string, ViewDefinition>> = <TView extends ViewName<TViews>>(view: TView, options: ViewState<TViews, NoInfer<TView>>) => void;
6
+ export declare function useViewContext<TViews extends Record<string, ViewDefinition> = DefaultViews, TView extends ViewName<TViews> = ViewName<TViews>>(view: TView, options: ViewState<TViews, NoInfer<TView>>): void;
@@ -0,0 +1,6 @@
1
+ import type { ViewsQueryOptions, ViewsQueryResult } from "@tailorkit/client-core";
2
+ export type { SlotItem } from "@tailorkit/client-core";
3
+ export type UseViewsOptions<TScopeNames extends string = string, TSlot extends string = string> = ViewsQueryOptions<TScopeNames, TSlot>;
4
+ export type UseViewsResult<TMultiple extends boolean = boolean> = ViewsQueryResult<TMultiple>;
5
+ /** Subscribe to the shared views query using React's lifecycle. */
6
+ export declare function useViews(options: UseViewsOptions): UseViewsResult;
package/dist/index.d.ts CHANGED
@@ -4,6 +4,6 @@ export { components, createTailorKitClient } from "./tailorkit";
4
4
  export type { TailorKitApp, TailorKitClientConfig, TailorKitInstance } from "./tailorkit";
5
5
  export { Root } from "./components/root";
6
6
  export type { UseAppsOptions, UseAppsResult } from "./hooks/use-apps";
7
- export type { SlotItem, UseSlotOptions, UseSlotResult } from "./hooks/use-slot";
8
- export type { UseRegisterView, ViewOptions, ViewState } from "./hooks/use-register-view";
7
+ export type { SlotItem, UseViewsOptions, UseViewsResult } from "./hooks/use-views";
8
+ export type { UseViewContext, ViewOptions, ViewState } from "./hooks/use-view-context";
9
9
  export { primitives } from "./primitives";
package/dist/index.js CHANGED
@@ -1,326 +1,16 @@
1
1
  // src/components/slot.tsx
2
- import { useCallback as useCallback4, useEffect as useEffect4, useId as useId2, useMemo as useMemo5, useRef as useRef3, useSyncExternalStore as useSyncExternalStore4 } from "react";
3
-
4
- // src/hooks/use-stable-context.ts
5
- import { useRef } from "react";
6
- function useStableContext(value) {
7
- const key = JSON.stringify(value);
8
- const ref = useRef({ key, value });
9
- if (ref.current.key !== key) {
10
- ref.current = { key, value };
11
- }
12
- return ref.current.value;
13
- }
14
-
15
- // src/hooks/use-slot.ts
16
- import { createSlotStore, matchesApp as matchesApp3, serializeCacheKey } from "@tailorkit/client-core";
17
- import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useSyncExternalStore as useSyncExternalStore2 } from "react";
18
-
19
- // src/components/context.ts
20
- import { createContext, useContext } from "react";
21
- var TailorRootContext = createContext(null);
22
- function useTailorRootContext(component, expectedClient) {
23
- const context = useContext(TailorRootContext);
24
- if (!context) {
25
- throw new Error(`${component} must be rendered inside Root.`);
26
- }
27
- if (expectedClient && context.client !== expectedClient) {
28
- throw new Error(
29
- `${component} was created for a different TailorKit client than the one passed to Root.`
30
- );
31
- }
32
- return context;
33
- }
34
-
35
- // src/hooks/use-apps.ts
36
- import { useCallback, useEffect, useMemo, useSyncExternalStore } from "react";
37
- import { matchesApp } from "@tailorkit/client-core";
38
-
39
- // src/scope-query.ts
40
- import { appendScopeSelection, normalizeScopeSelection } from "@tailorkit/client-core";
41
-
42
- // src/hooks/use-apps.ts
43
- import { matchesApp as matchesApp2 } from "@tailorkit/client-core";
44
- function useApps(options = {}) {
45
- return useAppsStore(useTailorRootContext("useApps").store, options);
46
- }
47
- function useAppsStore(store, options = {}) {
48
- const snapshot = useAppsSnapshot(store);
49
- const scopes = normalizeScopeSelection(options.scopes);
50
- const appIds = normalizeScopeSelection(options.appIds);
51
- const data = useMemo(
52
- () => snapshot.status === "ready" ? snapshot.apps.filter((app) => matchesApp(app, scopes.scopes, appIds.scopes)) : void 0,
53
- [snapshot, scopes.key, appIds.key]
54
- );
55
- const refetch = useCallback(() => store.fetchApps({ force: true }), [store]);
56
- return { ...toUseAppsResult(snapshot, refetch), data };
57
- }
58
- function useAppsSnapshot(store) {
59
- const snapshot = useSyncExternalStore(
60
- store.subscribeApps,
61
- store.getAppsSnapshot,
62
- store.getAppsSnapshot
63
- );
64
- useEffect(() => {
65
- void store.fetchApps();
66
- }, [store]);
67
- return snapshot;
68
- }
69
- function toUseAppsResult(snapshot, refetch) {
70
- return {
71
- data: snapshot.status === "ready" ? snapshot.apps : void 0,
72
- error: snapshot.error,
73
- isFetching: snapshot.isFetching,
74
- isError: snapshot.status === "error",
75
- isLoading: snapshot.status === "loading",
76
- isPending: snapshot.status === "idle" || snapshot.status === "loading",
77
- isSuccess: snapshot.status === "ready",
78
- refetch,
79
- status: snapshot.status
80
- };
81
- }
82
-
83
- // src/hooks/use-slot.ts
84
- function useSlot({ slot, scopes, appIds }) {
85
- const { store } = useTailorRootContext("useSlot");
86
- const apps = useApps({ scopes, appIds });
87
- const items = useResolvedSlot(slot, apps.data, apps.status, apps.error);
88
- const refetch = useCallback2(async () => {
89
- const multiple = store.client.meta().getSnapshot().data?.schema.slots[slot]?.multiple;
90
- if (apps.isSuccess && multiple === true) {
91
- await items.refetch();
92
- return;
93
- }
94
- await apps.refetch();
95
- let previousKey;
96
- while (true) {
97
- const discovery = store.getAppsSnapshot();
98
- if (discovery.status !== "ready") return;
99
- const schema = store.getMetaSnapshot().schema;
100
- const options = {
101
- apps: discovery.apps.filter((app) => matchesApp3(app, scopes, appIds)),
102
- slot,
103
- activeView: schema === null || schema.slots[slot]?.multiple === true ? store.views.getSnapshot() : null
104
- };
105
- const key = serializeCacheKey([options.apps, slot, options.activeView]);
106
- if (key === previousKey) return;
107
- previousKey = key;
108
- await createSlotStore(store.client, options).fetch();
109
- }
110
- }, [store, slot, scopes, appIds, apps.isSuccess, apps.refetch, items.refetch]);
111
- return { ...items, isFetching: apps.isFetching || items.isFetching, refetch };
112
- }
113
- function useAppSlot(app, slot) {
114
- return useResolvedSlot(slot, [app], "ready", null);
115
- }
116
- function useResolvedSlot(slot, apps, appsStatus, appsError) {
117
- const { store } = useTailorRootContext("useSlot");
118
- const activeView = useSyncExternalStore2(
119
- store.views.subscribe,
120
- store.views.getSnapshot,
121
- store.views.getSnapshot
122
- );
123
- const meta = useSyncExternalStore2(store.subscribe, store.getMetaSnapshot, store.getMetaSnapshot);
124
- const options = useStableContext({
125
- apps: apps ?? [],
126
- slot,
127
- activeView: meta.schema === null || meta.schema.slots[slot]?.multiple === true ? activeView : null
128
- });
129
- const query = useMemo2(
130
- () => createSlotStore(store.client, {
131
- ...options,
132
- appsStatus,
133
- appsError
134
- }),
135
- [store.client, options, appsStatus, appsError]
136
- );
137
- const snapshot = useSyncExternalStore2(query.subscribe, query.getSnapshot, query.getSnapshot);
138
- useEffect2(() => {
139
- void query.fetch();
140
- }, [query]);
141
- const refetch = useCallback2(() => query.fetch({ force: true }), [query]);
142
- return {
143
- ...snapshot,
144
- isError: snapshot.status === "error",
145
- isPending: snapshot.status === "idle" || snapshot.status === "loading",
146
- isLoading: snapshot.status === "loading",
147
- isSuccess: snapshot.status === "ready",
148
- refetch
149
- };
150
- }
151
-
152
- // src/slot-view.ts
153
- import { resolveSlotView, selectSlotView } from "@tailorkit/client-core";
2
+ import { useStore as useStore2 } from "@nanostores/react";
3
+ import { createSlotRuntime, buildThemeCss } from "@tailorkit/client-core";
4
+ import { useEffect as useEffect2, useId as useId2, useMemo as useMemo3 } from "react";
154
5
 
155
6
  // src/primitives.tsx
156
- import { createContext as createContext2, useContext as useContext2, useId, useMemo as useMemo3 } from "react";
157
- import { resolveTheme } from "@tailorkit/core/primitives/theme";
7
+ import { createContext, useContext, useId, useMemo } from "react";
8
+ import { buildPrimitiveCss } from "@tailorkit/client-core";
158
9
  import { jsx, jsxs } from "react/jsx-runtime";
159
- var PrimitiveThemeContext = createContext2(null);
160
- var tokenGroups = [
161
- "space",
162
- "radius",
163
- "background",
164
- "border",
165
- "borderColor",
166
- "textColor"
167
- ];
168
- var cssEscape = (value) => {
169
- const css = globalThis.CSS;
170
- if (typeof css?.escape === "function") {
171
- return css.escape(value);
172
- }
173
- return value.replaceAll(/[^A-Za-z0-9_-]/gu, "\\$&");
174
- };
175
- var toCssValue = (group, value) => {
176
- if (typeof value === "number") {
177
- return `${value}px`;
178
- }
179
- if (group === "size" || group === "raw") {
180
- return value;
181
- }
182
- return `var(--tailorkit-${group}-${cssEscape(value)})`;
183
- };
184
- var toResponsiveEntries = (value) => {
185
- if (value === void 0) {
186
- return [];
187
- }
188
- if (!(value && typeof value === "object") || Array.isArray(value)) {
189
- return [["base", value]];
190
- }
191
- return Object.entries(value).filter(
192
- (entry) => entry[1] !== void 0
193
- );
194
- };
195
- var declarationsForProp = (prop, value) => {
196
- if (prop === "columns") {
197
- return [`grid-template-columns: repeat(${String(value)}, minmax(0, 1fr));`];
198
- }
199
- const tokenDeclarations = {
200
- background: ["background", "background"],
201
- border: ["border", "border"],
202
- borderColor: ["borderColor", "border-color"],
203
- textColor: ["textColor", "color"]
204
- };
205
- const tokenDeclaration = tokenDeclarations[prop];
206
- if (tokenDeclaration) {
207
- const [group, property] = tokenDeclaration;
208
- if (prop === "border") {
209
- return [`border-style: ${toCssValue(group, value)};`, "border-width: 1px;"];
210
- }
211
- return [`${property}: ${toCssValue(group, value)};`];
212
- }
213
- if (prop === "gap" || prop === "margin" || prop === "padding") {
214
- return [`${prop}: ${toCssValue("space", value)};`];
215
- }
216
- if (prop === "height" || prop === "width") {
217
- return [`${prop}: ${toCssValue("size", value)};`];
218
- }
219
- if (prop === "radius") {
220
- return [`border-radius: ${toCssValue("radius", value)};`];
221
- }
222
- if (prop === "align") {
223
- return [`align-items: ${toFlexAlignment(value)};`];
224
- }
225
- if (prop === "justify") {
226
- return [`justify-content: ${toFlexJustification(value)};`];
227
- }
228
- if (prop === "direction") {
229
- return [`flex-direction: ${toCssValue("raw", value)};`];
230
- }
231
- if (prop === "overflow") {
232
- return [`overflow: ${toCssValue("raw", value)};`];
233
- }
234
- if (prop === "grow") {
235
- return [`flex-grow: ${String(value)};`];
236
- }
237
- if (prop === "shrink") {
238
- return [`flex-shrink: ${String(value)};`];
239
- }
240
- if (prop === "minHeight") {
241
- return [`min-height: ${toCssValue("size", value)};`];
242
- }
243
- if (prop === "minWidth") {
244
- return [`min-width: ${toCssValue("size", value)};`];
245
- }
246
- if (prop === "basis") {
247
- return [`flex-basis: ${toCssValue("size", value)};`];
248
- }
249
- if (prop === "wrap") {
250
- return [`flex-wrap: ${toCssValue("raw", value)};`];
251
- }
252
- return [];
253
- };
254
- var toFlexAlignment = (value) => {
255
- if (value === "start") {
256
- return "flex-start";
257
- }
258
- if (value === "end") {
259
- return "flex-end";
260
- }
261
- return String(value);
262
- };
263
- var toFlexJustification = (value) => {
264
- if (value === "start") {
265
- return "flex-start";
266
- }
267
- if (value === "end") {
268
- return "flex-end";
269
- }
270
- if (value === "between") {
271
- return "space-between";
272
- }
273
- return String(value);
274
- };
275
- var buildPrimitiveCss = ({
276
- display,
277
- nodeId,
278
- props,
279
- viewId,
280
- theme
281
- }) => {
282
- const selector = `[data-tailorkit-view="${cssEscape(viewId)}"] [data-tailorkit-node="${cssEscape(
283
- nodeId
284
- )}"]`;
285
- const base = [`display: ${display};`];
286
- const byBreakpoint = /* @__PURE__ */ new Map();
287
- for (const [prop, rawValue] of Object.entries(props)) {
288
- for (const [breakpoint, value] of toResponsiveEntries(rawValue)) {
289
- const declarations = declarationsForProp(prop, value);
290
- if (declarations.length === 0) {
291
- continue;
292
- }
293
- const target = breakpoint === "base" ? base : byBreakpoint.get(breakpoint) ?? [];
294
- target.push(...declarations);
295
- if (breakpoint !== "base") {
296
- byBreakpoint.set(breakpoint, target);
297
- }
298
- }
299
- }
300
- const rules = [`${selector} { ${base.join(" ")} }`];
301
- for (const [breakpoint, declarations] of byBreakpoint) {
302
- const minWidth = theme.breakpoints?.[breakpoint];
303
- if (!minWidth) {
304
- continue;
305
- }
306
- rules.push(`@media (min-width: ${minWidth}) { ${selector} { ${declarations.join(" ")} } }`);
307
- }
308
- return rules.join("\n");
309
- };
310
- var buildThemeCss = (viewId, theme) => {
311
- const selector = `[data-tailorkit-view="${cssEscape(viewId)}"]`;
312
- const declarations = [];
313
- const resolvedTheme = resolveTheme(theme);
314
- for (const group of tokenGroups) {
315
- for (const [name, value] of Object.entries(resolvedTheme.tokens?.[group] ?? {})) {
316
- declarations.push(`--tailorkit-${group}-${cssEscape(name)}: ${value};`);
317
- }
318
- }
319
- return `${selector} { ${declarations.join(" ")} }`;
320
- };
10
+ var PrimitiveThemeContext = createContext(null);
321
11
  var usePrimitiveNodeId = () => {
322
12
  const id = useId();
323
- return useMemo3(() => `tailorkit-${id.replaceAll(":", "")}`, [id]);
13
+ return useMemo(() => `tailorkit-${id.replaceAll(":", "")}`, [id]);
324
14
  };
325
15
  var Primitive = ({
326
16
  children,
@@ -328,7 +18,7 @@ var Primitive = ({
328
18
  element,
329
19
  props
330
20
  }) => {
331
- const context = useContext2(PrimitiveThemeContext);
21
+ const context = useContext(PrimitiveThemeContext);
332
22
  const nodeId = usePrimitiveNodeId();
333
23
  const Element = element;
334
24
  const css = context ? buildPrimitiveCss({
@@ -351,28 +41,20 @@ var primitives = {
351
41
  };
352
42
 
353
43
  // src/remote-view.tsx
44
+ import { useStore } from "@nanostores/react";
354
45
  import {
355
46
  Component,
356
47
  Fragment,
357
48
  createElement,
49
+ createContext as createContext2,
358
50
  memo,
359
- useCallback as useCallback3,
360
- useContext as useContext3,
361
- useEffect as useEffect3,
362
- useMemo as useMemo4,
363
- useRef as useRef2,
364
- useState,
365
- useSyncExternalStore as useSyncExternalStore3
51
+ useContext as useContext2,
52
+ useEffect,
53
+ useMemo as useMemo2
366
54
  } from "react";
367
- import { createIframeUiHost } from "@tailorkit/sandbox/host";
368
- import { NodeStore } from "@tailorkit/client-core";
369
-
370
- // src/remote-context.ts
371
- import { createContext as createContext3 } from "react";
372
- var RemoteUIContext = createContext3(null);
373
-
374
- // src/remote-view.tsx
55
+ import { createRemoteViewStore, createRemoteElementProps } from "@tailorkit/client-core";
375
56
  import { jsx as jsx2 } from "react/jsx-runtime";
57
+ var RemoteUIContext = createContext2(null);
376
58
  var formatError = (error) => error.message;
377
59
  var RemoteErrorBoundary = class extends Component {
378
60
  state = { error: null };
@@ -398,124 +80,44 @@ function RemoteViewHost({
398
80
  props
399
81
  }) {
400
82
  const appKey = appUrl.toString();
401
- const storeRef = useRef2(null);
402
- if (storeRef.current === null) {
403
- storeRef.current = new NodeStore();
404
- }
405
- const store = storeRef.current;
406
- const appKeyRef = useRef2(appKey);
407
- if (appKeyRef.current !== appKey) {
408
- appKeyRef.current = appKey;
409
- store.clear();
410
- }
411
- const hostRef = useRef2(null);
412
- const propsRef = useRef2(props);
413
- useEffect3(() => {
414
- propsRef.current = props;
415
- hostRef.current?.setProps(props);
416
- }, [props]);
417
- const dispatchRef = useRef2(null);
418
- const [error, setError] = useState(null);
419
- const [status, setStatus] = useState("starting");
420
- const ctxRef = useRef2({
421
- components: components2,
422
- dispatch: (payload) => dispatchRef.current?.(payload),
423
- store
424
- });
425
- useEffect3(() => {
426
- ctxRef.current.components = components2;
427
- }, [components2]);
428
- const ui = useMemo4(() => {
429
- const ctx = ctxRef.current;
430
- return createElement(
431
- RemoteUIContext.Provider,
432
- { value: ctx },
433
- createElement(
434
- RemoteErrorBoundary,
435
- { key: appKey },
436
- createElement(RemoteRoot, { store: ctx.store })
437
- )
438
- );
439
- }, [appKey]);
440
- useEffect3(() => {
441
- const host = createIframeUiHost(appUrl, {
442
- createIframe,
443
- sourceText,
444
- getBackendSession,
445
- onError: (error2) => {
446
- console.error("TailorKit remote app failed", error2);
447
- setError(error2);
448
- setStatus("error");
449
- },
450
- props: propsRef.current
451
- });
452
- hostRef.current = host;
453
- dispatchRef.current = (payload) => host.dispatch(payload);
454
- const unsubscribe = host.subscribe(() => {
455
- const tree = host.getSnapshot();
456
- if (tree !== null) {
457
- store.setSnapshot(tree);
458
- setStatus("ready");
459
- }
460
- });
461
- setStatus("starting");
462
- setError(null);
463
- host.mount();
464
- return () => {
465
- unsubscribe();
466
- dispatchRef.current = null;
467
- hostRef.current = null;
468
- host.destroy();
469
- };
470
- }, [appUrl, createIframe, sourceText, getBackendSession, store]);
471
- if (status === "error" && error) {
472
- return createElement("div", null, formatError(error));
83
+ const store = useMemo2(() => createRemoteViewStore({ components: components2, props }), [appKey]);
84
+ const snapshot = useStore(store.state);
85
+ useEffect(() => {
86
+ store.setComponents(components2);
87
+ }, [components2, store]);
88
+ useEffect(() => {
89
+ store.setProps(props);
90
+ }, [props, store]);
91
+ useEffect(
92
+ () => store.mount({ appUrl, sourceText, getBackendSession, createIframe }),
93
+ [appUrl, sourceText, getBackendSession, createIframe, store]
94
+ );
95
+ if (snapshot.status === "error" && snapshot.error) {
96
+ return createElement("div", null, formatError(snapshot.error));
473
97
  }
474
- return ui;
98
+ return /* @__PURE__ */ jsx2(RemoteUIContext.Provider, { value: store, children: /* @__PURE__ */ jsx2(RemoteErrorBoundary, { children: /* @__PURE__ */ jsx2(RemoteRoot, { store }) }, `${appKey}:${snapshot.generation}`) });
475
99
  }
476
100
  function RemoteRoot({ store }) {
477
- const subscribe = useCallback3((listener) => store.subscribeRoot(listener), [store]);
478
- const getSnapshot = useCallback3(() => store.getRootId(), [store]);
479
- const rootId = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
480
- if (rootId === null) {
481
- return null;
482
- }
483
- return /* @__PURE__ */ jsx2(RemoteView, { nodeId: rootId });
101
+ const rootId = useStore(store.nodes.root);
102
+ return rootId === null ? null : /* @__PURE__ */ jsx2(RemoteView, { nodeId: rootId });
484
103
  }
485
104
  var RemoteElementView = memo(({ ctx, node }) => {
486
- const { dispatch, components: components2 } = ctx;
105
+ const componentStore = useMemo2(() => ctx.selectComponent(node.type), [ctx, node.type]);
106
+ const component = useStore(componentStore);
487
107
  const children = node.children.map((child) => /* @__PURE__ */ jsx2(RemoteView, { nodeId: child.id }, child.id));
488
- const component = components2[node.type];
489
108
  if (component === void 0) {
490
109
  throw new Error(`TailorKit component "${node.type}" is not registered.`);
491
110
  }
492
- const props = { ...node.props };
493
- for (const binding of node.callbacks ?? []) {
494
- props[binding.callback] = (...args) => {
495
- dispatch({
496
- data: {
497
- args: args.slice(0, binding.inputCount),
498
- event: binding.event,
499
- nodeId: node.id
500
- },
501
- type: "dispatchCallback"
502
- });
503
- };
504
- }
111
+ const props = createRemoteElementProps(node, ctx.dispatch);
505
112
  return createElement(component, props, ...children);
506
113
  });
507
114
  function RemoteViewComponent({ nodeId }) {
508
- const ctx = useContext3(RemoteUIContext);
509
- if (ctx === null) {
510
- return null;
511
- }
512
- const { store } = ctx;
513
- const subscribe = useCallback3(
514
- (listener) => store.subscribe(nodeId, listener),
515
- [store, nodeId]
516
- );
517
- const getSnapshot = useCallback3(() => store.getNode(nodeId), [store, nodeId]);
518
- const node = useSyncExternalStore3(subscribe, getSnapshot, getSnapshot);
115
+ const ctx = useContext2(RemoteUIContext);
116
+ return ctx === null ? null : /* @__PURE__ */ jsx2(RemoteNodeView, { ctx, nodeId });
117
+ }
118
+ function RemoteNodeView({ ctx, nodeId }) {
119
+ const nodeStore = useMemo2(() => ctx.nodes.selectNode(nodeId), [ctx, nodeId]);
120
+ const node = useStore(nodeStore);
519
121
  if (node === null) {
520
122
  return null;
521
123
  }
@@ -530,210 +132,105 @@ function RemoteViewComponent({ nodeId }) {
530
132
  }
531
133
  var RemoteView = memo(RemoteViewComponent);
532
134
 
135
+ // src/components/context.ts
136
+ import { createContext as createContext3, useContext as useContext3 } from "react";
137
+ var TailorRootContext = createContext3(null);
138
+ function useTailorRootContext(component, expectedClient) {
139
+ const context = useContext3(TailorRootContext);
140
+ if (!context) {
141
+ throw new Error(`${component} must be rendered inside Root.`);
142
+ }
143
+ if (expectedClient && context.client !== expectedClient) {
144
+ throw new Error(
145
+ `${component} was created for a different TailorKit client than the one passed to Root.`
146
+ );
147
+ }
148
+ return context;
149
+ }
150
+
533
151
  // src/components/slot.tsx
534
- import { toBaseUrl } from "@tailorkit/client-core";
535
152
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
536
- function ManagedSlot({ app, name, instanceKey }) {
537
- const { store } = useTailorRootContext("Slot");
538
- const state = useSyncExternalStore4(
539
- store.views.subscribe,
540
- store.views.getSnapshot,
541
- store.views.getSnapshot
542
- );
543
- if (state === null) return null;
544
- return instanceKey === void 0 ? /* @__PURE__ */ jsx3(SlotRenderer, { app, name, state }) : /* @__PURE__ */ jsx3(InstanceSlot, { app, name, instanceKey, state });
153
+ function ManagedSlot(props) {
154
+ return /* @__PURE__ */ jsx3(SlotRenderer, { mode: "managed", ...props });
545
155
  }
546
- function InstanceSlot({
547
- app,
548
- name,
549
- instanceKey,
550
- state
551
- }) {
552
- const { store } = useTailorRootContext("Slot");
553
- const instances = useAppSlot(app, name);
554
- const meta = useSyncExternalStore4(store.subscribe, store.getMetaSnapshot, store.getMetaSnapshot);
555
- const instance = instances.data?.find(
556
- (item) => "key" in item && item.key === instanceKey
557
- );
558
- const resolved = instances.isSuccess && instance && meta.schema ? resolveSlotView(app.views ?? [], name, state, meta.schema) : null;
559
- const view = resolved?.status === "ready" ? resolved.view : void 0;
560
- const stableContext = useStableContext(resolved?.context);
561
- const stableInstance = useStableContext(
562
- instance ? { key: instance.key, metadata: instance.metadata, data: instance.data } : void 0
563
- );
564
- const readyState = useMemo5(
565
- () => view === void 0 || !stableInstance ? null : {
566
- controlled: true,
567
- view,
568
- context: stableContext,
569
- status: "ready",
570
- instance: stableInstance
571
- },
572
- [view, stableContext, stableInstance]
573
- );
574
- if (instances.isPending) return /* @__PURE__ */ jsx3("div", { role: "status", children: "Loading view\u2026" });
575
- if (instances.error) return /* @__PURE__ */ jsx3("div", { role: "alert", children: instances.error.message });
576
- if (meta.schema && meta.schema.slots[name]?.multiple !== true)
577
- return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
578
- 'Slot "',
579
- name,
580
- '" does not support instances.'
581
- ] });
582
- if (!instance) return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
583
- 'View instance "',
584
- instanceKey,
585
- '" is unavailable.'
586
- ] });
587
- if (!readyState) return null;
588
- return /* @__PURE__ */ jsx3(SlotRenderer, { app, name, state: readyState });
589
- }
590
- function ControlledSlot({
591
- app,
592
- name,
593
- view,
594
- context,
595
- status,
596
- instance
597
- }) {
598
- useTailorRootContext("Slot.Controlled");
599
- const stableContext = useStableContext(context);
600
- const stableInstance = useStableContext(instance);
601
- const state = useMemo5(
602
- () => ({
603
- controlled: true,
604
- view,
605
- context: stableContext,
606
- status,
607
- ...status === "ready" && stableInstance ? { instance: stableInstance } : {}
608
- }),
609
- [view, stableContext, status, stableInstance]
610
- );
611
- if (status === "ready" && !instance && app.views?.some(
612
- (entry) => entry.slot === name && entry.path === view && entry.instances && !entry.disabled
613
- )) {
614
- return /* @__PURE__ */ jsx3("div", { role: "alert", children: "A view instance is required. Pass instance to Slot.Controlled." });
615
- }
616
- return /* @__PURE__ */ jsx3(SlotRenderer, { app, name, state });
156
+ function ControlledSlot(props) {
157
+ return /* @__PURE__ */ jsx3(SlotRenderer, { mode: "controlled", ...props });
617
158
  }
618
159
  var Slot = Object.assign(ManagedSlot, { Controlled: ControlledSlot });
619
- function SlotRenderer({ app, name, state }) {
620
- const { store, client } = useTailorRootContext("Slot");
621
- const reactId = useId2();
622
- const getBackendSession = useMemo5(
623
- () => store.client.endpoints.getSessionProvider(app),
624
- [store.client, app.id, app.currentDeployment?.id, app.preview?.sessionId]
625
- );
626
- const meta = useSyncExternalStore4(store.subscribe, store.getMetaSnapshot, store.getMetaSnapshot);
627
- const previewSessionId = app.preview?.sessionId ?? "";
628
- const appRef = useRef3(app);
629
- appRef.current = app;
630
- const subscribePreview = useCallback4(
631
- (listener) => {
632
- const currentApp = appRef.current;
633
- return currentApp.preview?.sessionId === previewSessionId ? store.previews.subscribe(currentApp, listener) : () => {
634
- };
635
- },
636
- [store, previewSessionId]
637
- );
638
- const getPreviewSnapshot = useCallback4(
639
- () => store.previews.getSnapshot(previewSessionId),
640
- [store, previewSessionId]
160
+ function SlotRenderer(options) {
161
+ const { store, client } = useTailorRootContext(
162
+ options.mode === "managed" ? "Slot" : "Slot.Controlled"
641
163
  );
642
- const preview = useSyncExternalStore4(subscribePreview, getPreviewSnapshot, getPreviewSnapshot);
643
- const props = useMemo5(
644
- () => ({
645
- ...state,
646
- slot: name,
647
- declaredViews: Object.keys(meta.schema?.views ?? {}),
648
- supportedViews: meta.schema?.slots[name]?.views ?? []
649
- }),
650
- [state, name, meta.schema]
651
- );
652
- const appUrl = useMemo5(() => {
653
- if (app.clientPath) return new URL(app.clientPath, store.baseUrl);
654
- if (!meta.assetsBaseUrl || !app.projectId || !app.currentDeployment?.id) return null;
655
- return new URL(
656
- `projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/client/client.js`,
657
- toBaseUrl(meta.assetsBaseUrl)
658
- );
659
- }, [app, store.baseUrl, meta.assetsBaseUrl]);
660
- useEffect4(() => {
661
- void store.fetchMeta();
662
- }, [store]);
663
- useEffect4(() => {
664
- store.previews.updateApp(app);
665
- }, [store, app]);
666
- if (meta.schema === null || appUrl === null && preview.source === null) return null;
667
- const multiple = meta.schema.slots[name]?.multiple === true;
668
- if ("controlled" in state && state.status === "ready") {
669
- if (multiple && !state.instance)
670
- return /* @__PURE__ */ jsx3("div", { role: "alert", children: "A view instance is required. Pass instance to Slot.Controlled." });
671
- if (!multiple && state.instance)
672
- return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
673
- 'Slot "',
674
- name,
675
- '" does not support instances.'
676
- ] });
677
- }
678
- if (!("controlled" in state)) {
679
- const selected = selectSlotView(app.views ?? [], name, state.view, meta.schema);
680
- if (selected?.instances && !multiple)
681
- return /* @__PURE__ */ jsxs2("div", { role: "alert", children: [
682
- 'Slot "',
683
- name,
684
- '" does not support instances.'
685
- ] });
686
- if (multiple)
687
- return /* @__PURE__ */ jsx3("div", { role: "alert", children: "A view instance key is required. Pass instanceKey to Slot." });
688
- }
164
+ const reactId = useId2();
165
+ const runtime = useMemo3(() => createSlotRuntime(store, options), [store]);
166
+ useEffect2(() => runtime.setInput(options), [runtime, options]);
167
+ const snapshot = useStore2(runtime.state);
168
+ if (snapshot.status === "hidden") return null;
169
+ if (snapshot.status === "loading") return /* @__PURE__ */ jsx3("div", { role: "status", children: snapshot.message });
170
+ if (snapshot.status === "error") return /* @__PURE__ */ jsx3("div", { role: "alert", children: snapshot.error.message });
689
171
  const viewId = `tailorkit-view-${reactId.replaceAll(":", "")}`;
690
172
  return /* @__PURE__ */ jsx3(PrimitiveThemeContext.Provider, { value: { viewId, theme: client.theme }, children: /* @__PURE__ */ jsxs2("div", { "data-tailorkit-view": viewId, children: [
691
173
  /* @__PURE__ */ jsx3("style", { "data-tailorkit-theme-style": viewId, children: buildThemeCss(viewId, client.theme) }),
692
174
  /* @__PURE__ */ jsx3(
693
175
  RemoteViewHost,
694
176
  {
695
- appUrl: (appUrl ?? new URL(`preview/${previewSessionId}/client.js`, store.baseUrl)).toString(),
696
- sourceText: preview.source ?? void 0,
697
- getBackendSession,
177
+ appUrl: snapshot.appUrl,
178
+ sourceText: snapshot.sourceText,
179
+ getBackendSession: snapshot.getBackendSession,
698
180
  components: client.components,
699
- props
181
+ props: snapshot.props
700
182
  },
701
- preview.revision || appUrl?.toString()
183
+ snapshot.hostKey
702
184
  )
703
185
  ] }) });
704
186
  }
705
187
 
706
188
  // src/tailorkit.tsx
707
- import { createTailorKitFetchClient } from "@tailorkit/client-core";
189
+ import { createTailorKitClientConfig, createComponentRegistry } from "@tailorkit/client-core";
708
190
  import { createElement as createElement2 } from "react";
709
191
 
710
- // src/hooks/use-register-view.ts
711
- import { useEffect as useEffect5, useMemo as useMemo6 } from "react";
712
- function useRegisterView(view, options) {
713
- const { store } = useTailorRootContext("useRegisterView");
714
- const id = useMemo6(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
715
- const status = options.status ?? "ready";
716
- const context = "context" in options ? options.context : void 0;
717
- const contextSnapshot = useStableContext(context);
718
- useEffect5(
192
+ // src/hooks/use-view-context.ts
193
+ import { useEffect as useEffect3, useMemo as useMemo4 } from "react";
194
+ function useViewContext(view, options) {
195
+ const { store } = useTailorRootContext("useViewContext");
196
+ const id = useMemo4(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
197
+ const { context, loading, error } = options;
198
+ useEffect3(
719
199
  () => () => {
720
200
  store.views.unregister(id);
721
201
  },
722
202
  [id, store]
723
203
  );
724
- useEffect5(() => {
725
- store.views.register({
726
- context: contextSnapshot,
727
- id,
728
- view,
729
- status
730
- });
731
- }, [contextSnapshot, id, view, status, store]);
204
+ useEffect3(() => {
205
+ store.views.register({ id, view, context, loading, error });
206
+ }, [id, view, context, loading, error, store]);
207
+ }
208
+
209
+ // src/hooks/use-apps.ts
210
+ import { useStore as useStore3 } from "@nanostores/react";
211
+ import { useMemo as useMemo5 } from "react";
212
+ import { appsQueryKey, createAppsQuery } from "@tailorkit/client-core";
213
+ function useApps(options = {}) {
214
+ return useAppsStore(useTailorRootContext("useApps").store, options);
215
+ }
216
+ function useAppsStore(store, options = {}) {
217
+ const key = appsQueryKey(options);
218
+ const query = useMemo5(() => createAppsQuery(store, options), [store, key]);
219
+ return useStore3(query.state);
220
+ }
221
+
222
+ // src/hooks/use-views.ts
223
+ import { useStore as useStore4 } from "@nanostores/react";
224
+ import { useMemo as useMemo6 } from "react";
225
+ import { appsQueryKey as appsQueryKey2, createViewsQuery } from "@tailorkit/client-core";
226
+ function useViews(options) {
227
+ const { store } = useTailorRootContext("useViews");
228
+ const key = appsQueryKey2(options);
229
+ const query = useMemo6(() => createViewsQuery(store, options), [store, key, options.slot]);
230
+ return useStore4(query.state);
732
231
  }
733
232
 
734
233
  // src/tailorkit.tsx
735
- var componentTagPrefix = "tailorkit-";
736
- var toComponentTagName = (name) => `${componentTagPrefix}${name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[\s_]+/gu, "-").toLowerCase()}`;
737
234
  function components(_schema, customComponents) {
738
235
  return customComponents;
739
236
  }
@@ -741,33 +238,18 @@ function createTailorKitClient(options) {
741
238
  return createReactTailorKitClient(options);
742
239
  }
743
240
  function createReactTailorKitClient(options) {
744
- const wrappedComponents = {};
745
- const theme = options.theme ?? {};
746
- for (const [name, renderer] of Object.entries(options.components ?? {})) {
747
- if (renderer) {
748
- const TailorKitComponent = function TailorKitComponent2({
749
- children,
750
- ...props
751
- }) {
752
- return renderer({
753
- props,
754
- children
755
- });
756
- };
757
- wrappedComponents[name] = TailorKitComponent;
758
- wrappedComponents[toComponentTagName(name)] = TailorKitComponent;
759
- }
760
- }
761
- const clientConfig = {
762
- baseUrl: options.baseUrl,
763
- fetchClient: createTailorKitFetchClient({
764
- baseUrl: options.baseUrl,
765
- cache: options.cache,
766
- fetch: options.fetch
767
- }),
768
- components: wrappedComponents,
769
- theme
770
- };
241
+ const wrappedComponents = createComponentRegistry(options.components ?? {}, (renderer) => {
242
+ return function TailorKitComponent({
243
+ children,
244
+ ...props
245
+ }) {
246
+ return renderer({
247
+ props,
248
+ children
249
+ });
250
+ };
251
+ });
252
+ const clientConfig = createTailorKitClientConfig({ ...options, components: wrappedComponents });
771
253
  const client = {
772
254
  ...clientConfig,
773
255
  Slot: Object.assign(
@@ -786,21 +268,21 @@ function createReactTailorKitClient(options) {
786
268
  useTailorRootContext("useApps", client);
787
269
  return useApps(options2);
788
270
  },
789
- useSlot: function useClientSlot(options2) {
790
- useTailorRootContext("useSlot", client);
791
- return useSlot(options2);
271
+ useViews: function useClientViews(options2) {
272
+ useTailorRootContext("useViews", client);
273
+ return useViews(options2);
792
274
  },
793
- useRegisterView: function useClientRegisterView(view, options2) {
794
- useTailorRootContext("useRegisterView", client);
795
- useRegisterView(view, options2);
275
+ useViewContext: function useClientViewContext(view, options2) {
276
+ useTailorRootContext("useViewContext", client);
277
+ useViewContext(view, options2);
796
278
  }
797
279
  };
798
280
  return client;
799
281
  }
800
282
 
801
283
  // src/components/root.tsx
802
- import { useEffect as useEffect6, useMemo as useMemo8, useState as useState2 } from "react";
803
- import { createTailorKitStore, toBaseUrl as toBaseUrl2 } from "@tailorkit/client-core";
284
+ import { useEffect as useEffect4, useMemo as useMemo8, useState } from "react";
285
+ import { createTailorKitStore, toBaseUrl } from "@tailorkit/client-core";
804
286
 
805
287
  // src/components/render.tsx
806
288
  import { cloneElement, createElement as createElement3, isValidElement, useMemo as useMemo7 } from "react";
@@ -878,8 +360,8 @@ function isEventHandler(key) {
878
360
  // src/components/root.tsx
879
361
  import { jsx as jsx4 } from "react/jsx-runtime";
880
362
  function Root({ apps: appsProp, children, render, client, ...props }) {
881
- const baseUrl = toBaseUrl2(client.baseUrl).toString();
882
- const [previous, setStore] = useState2(() => ({
363
+ const baseUrl = toBaseUrl(client.baseUrl).toString();
364
+ const [previous, setStore] = useState(() => ({
883
365
  store: createTailorKitStore(baseUrl, appsProp, client.fetchClient),
884
366
  fetchClient: client.fetchClient
885
367
  }));
@@ -889,10 +371,10 @@ function Root({ apps: appsProp, children, render, client, ...props }) {
889
371
  store = createTailorKitStore(baseUrl, appsProp, client.fetchClient);
890
372
  setStore({ store, fetchClient: client.fetchClient });
891
373
  }
892
- useEffect6(() => {
374
+ useEffect4(() => {
893
375
  store.setProvidedApps(appsProp);
894
376
  }, [store, appsProp]);
895
- useEffect6(() => () => store.previews.dispose(), [store]);
377
+ useEffect4(() => () => store.previews.dispose(), [store]);
896
378
  const context = useMemo8(
897
379
  () => ({
898
380
  store,
@@ -1,12 +1,11 @@
1
1
  import type { CSSProperties, ReactNode } from "react";
2
2
  import type { TailorKitTheme } from "@tailorkit/core/schema";
3
- type PrimitiveProps = Record<string, unknown>;
3
+ import type { PrimitiveProps } from "@tailorkit/client-core";
4
4
  interface PrimitiveThemeContextValue {
5
5
  viewId: string;
6
6
  theme: TailorKitTheme;
7
7
  }
8
8
  export declare const PrimitiveThemeContext: import("react").Context<PrimitiveThemeContextValue | null>;
9
- export declare const buildThemeCss: (viewId: string, theme: TailorKitTheme) => string;
10
9
  export declare const primitives: {
11
10
  readonly Box: ({ props, children }: {
12
11
  props: PrimitiveProps;
@@ -1,81 +1,32 @@
1
- import type { TailorKitFetchClient, TailorKitCacheOptions } from "@tailorkit/client-core";
1
+ import type { AnyComponentDefinition, ComponentRenderer, ComponentRenderers, CompleteComponentRenderers, TailorKitClientConfig, TailorKitServerShape, ServerComponents, ServerViews, ServerSlots, ServerScopeNames, SlotMultiple } from "@tailorkit/client-core";
2
+ export type { TailorKitClientConfig } from "@tailorkit/client-core";
3
+ import type { TailorKitCacheOptions } from "@tailorkit/client-core";
2
4
  import type { ReactNode } from "react";
3
- import type { TailorKitTheme, CallbackMap, ComponentDefinition, ComponentProps, Schema, ViewDefinition, SlotDefinitions, TailorKitSchema } from "@tailorkit/core/schema";
5
+ import type { TailorKitTheme, ViewDefinition, SlotDefinitions, TailorKitSchema } from "@tailorkit/core/schema";
4
6
  import type { primitives } from "./primitives";
5
- import type { UseRegisterView } from "./hooks/use-register-view";
7
+ import type { UseViewContext } from "./hooks/use-view-context";
6
8
  import type { UseAppsOptions, UseAppsResult } from "./hooks/use-apps";
7
- import type { UseSlotOptions, UseSlotResult } from "./hooks/use-slot";
9
+ import type { UseViewsOptions, UseViewsResult } from "./hooks/use-views";
8
10
  import type { SlotComponent } from "./components/slot";
9
- type AnyComponentDefinition = ComponentDefinition<Schema | undefined, CallbackMap, boolean | undefined>;
10
- type ComponentRenderer<TComponent extends AnyComponentDefinition> = (args: {
11
- props: ComponentProps<TComponent>;
12
- children?: TComponent extends {
13
- children: true;
14
- } ? ReactNode : never;
15
- }) => ReactNode;
16
- type ComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
17
- [TName in keyof TComponents]?: ComponentRenderer<TComponents[TName]>;
18
- };
19
- type CompleteComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
20
- [TName in keyof TComponents]-?: ComponentRenderer<TComponents[TName]>;
21
- };
22
11
  export type { TailorKitApp } from "@tailorkit/client-core";
23
- export interface TailorKitClientConfig {
24
- readonly baseUrl: string | URL;
25
- readonly fetchClient?: TailorKitFetchClient;
26
- readonly components: Record<string, unknown>;
27
- readonly theme: TailorKitTheme;
28
- }
29
12
  export interface TailorKitInstance<TViews extends Record<string, ViewDefinition> = Record<string, ViewDefinition>, TSlots extends SlotDefinitions = SlotDefinitions, TScopeNames extends string = string> extends TailorKitClientConfig {
30
13
  readonly $slots?: TSlots;
31
14
  readonly $views?: TViews;
32
15
  readonly Slot: SlotComponent<TViews, TSlots>;
33
16
  readonly useApps: (options?: UseAppsOptions<TScopeNames>) => UseAppsResult;
34
- readonly useSlot: <TSlot extends keyof TSlots & string>(options: UseSlotOptions<TScopeNames, TSlot>) => UseSlotResult<SlotMultiple<TSlots[TSlot]>>;
35
- readonly useRegisterView: UseRegisterView<TViews>;
17
+ readonly useViews: <TSlot extends keyof TSlots & string>(options: UseViewsOptions<TScopeNames, TSlot>) => UseViewsResult<SlotMultiple<TSlots[TSlot]>>;
18
+ readonly useViewContext: UseViewContext<TViews>;
36
19
  }
37
- type SlotMultiple<TSlot> = TSlot extends {
38
- multiple: infer TMultiple extends boolean;
39
- } ? TMultiple : TSlot extends {
40
- multiple?: infer TMultiple extends boolean;
41
- } ? TMultiple | false : false;
42
20
  type PrimitiveRenderers = typeof primitives;
43
21
  type CustomComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
44
- [TName in Exclude<keyof TComponents, keyof PrimitiveRenderers>]?: ComponentRenderer<TComponents[TName]>;
45
- };
46
- export declare function components<TComponents extends Record<string, AnyComponentDefinition>>(_schema: TailorKitSchema<TComponents, Record<string, ViewDefinition>>, customComponents: CustomComponentRenderers<TComponents>): ComponentRenderers<TComponents>;
47
- interface TailorKitServerShape {
48
- $internal: {
49
- schema: {
50
- components: Record<string, unknown>;
51
- views: Record<string, unknown>;
52
- };
53
- };
54
- }
55
- type ServerComponentMap<TTailor extends TailorKitServerShape> = TTailor["$internal"]["schema"]["components"];
56
- type ServerComponents<TTailor extends TailorKitServerShape> = {
57
- [TName in keyof ServerComponentMap<TTailor>]: ServerComponentMap<TTailor>[TName] extends AnyComponentDefinition ? ServerComponentMap<TTailor>[TName] : never;
58
- };
59
- type ServerViewMap<TTailor extends TailorKitServerShape> = TTailor["$internal"]["schema"]["views"];
60
- type ServerScopeNames<TTailor extends TailorKitServerShape> = TTailor extends {
61
- handler: (request: Request, options: infer TOptions) => unknown;
62
- } ? TOptions extends {
63
- authenticate: infer TAuthenticate;
64
- } ? TAuthenticate extends (...args: infer _TArgs) => infer TResult ? Extract<Awaited<TResult>, {
65
- scopes: unknown;
66
- }> extends {
67
- scopes: infer TScopes;
68
- } ? keyof TScopes & string : never : never : never : never;
69
- type ServerViews<TTailor extends TailorKitServerShape> = {
70
- [TName in keyof ServerViewMap<TTailor>]: ServerViewMap<TTailor>[TName] extends ViewDefinition ? ServerViewMap<TTailor>[TName] : never;
22
+ [TName in Exclude<keyof TComponents, keyof PrimitiveRenderers>]?: ComponentRenderer<TComponents[TName], ReactNode>;
71
23
  };
24
+ export declare function components<TComponents extends Record<string, AnyComponentDefinition>>(_schema: TailorKitSchema<TComponents, Record<string, ViewDefinition>>, customComponents: CustomComponentRenderers<TComponents>): ComponentRenderers<TComponents, ReactNode>;
72
25
  export declare function createTailorKitClient<TTailor extends TailorKitServerShape>(options: {
73
26
  baseUrl: string | URL;
74
- components?: CompleteComponentRenderers<ServerComponents<TTailor>>;
27
+ components?: CompleteComponentRenderers<ServerComponents<TTailor>, ReactNode>;
75
28
  theme?: TailorKitTheme;
76
29
  cache?: TailorKitCacheOptions;
77
30
  fetch?: typeof fetch;
78
- }): TailorKitInstance<ServerViews<TTailor>, TTailor extends {
79
- readonly $slots?: infer V extends SlotDefinitions;
80
- } ? V : SlotDefinitions, ServerScopeNames<TTailor>>;
81
- export type { ViewOptions } from "./hooks/use-register-view";
31
+ }): TailorKitInstance<ServerViews<TTailor>, ServerSlots<TTailor>, ServerScopeNames<TTailor>>;
32
+ export type { ViewOptions } from "./hooks/use-view-context";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailorkit/react",
3
- "version": "0.1.0-beta.22",
3
+ "version": "0.1.0-beta.24",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,12 +22,14 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@floating-ui/react-dom": "^2.1.9",
25
+ "@nanostores/react": "2.0.1",
25
26
  "@standard-schema/spec": "^1.1.0",
26
- "@tailorkit/app": "0.1.0-beta.22",
27
- "@tailorkit/client-core": "0.1.0-beta.22",
28
- "@tailorkit/client-platform": "0.1.0-beta.22",
29
- "@tailorkit/core": "0.1.0-beta.22",
30
- "@tailorkit/sandbox": "0.1.0-beta.22"
27
+ "@tailorkit/app": "0.1.0-beta.24",
28
+ "@tailorkit/client-core": "0.1.0-beta.24",
29
+ "@tailorkit/client-platform": "0.1.0-beta.24",
30
+ "@tailorkit/core": "0.1.0-beta.24",
31
+ "@tailorkit/sandbox": "0.1.0-beta.24",
32
+ "nanostores": "1.5.4"
31
33
  },
32
34
  "devDependencies": {
33
35
  "@tailorkit/config": "0.0.0",
@@ -1,25 +0,0 @@
1
- import type { StandardJSONSchemaV1 } from "@standard-schema/spec";
2
- import type { ViewDefinition } from "@tailorkit/core/schema";
3
- type DefaultViews = Record<`/${string}`, ViewDefinition>;
4
- export type ViewName<TViews extends Record<string, ViewDefinition>> = keyof TViews & string;
5
- export type ViewContext<TView> = TView extends StandardJSONSchemaV1 ? StandardJSONSchemaV1.InferOutput<TView> : Record<string, never>;
6
- interface ReadyViewOptions<TViews extends Record<string, ViewDefinition>, TView extends ViewName<TViews>> {
7
- context: ViewContext<TViews[TView]>;
8
- view: TView;
9
- status?: "ready";
10
- }
11
- interface LoadingViewOptions<TView extends string> {
12
- context?: never;
13
- view: TView;
14
- status: "loading";
15
- }
16
- interface ErrorViewOptions<TView extends string> {
17
- context?: never;
18
- view: TView;
19
- status: "error";
20
- }
21
- export type ViewOptions<TViews extends Record<string, ViewDefinition> = DefaultViews, TView extends ViewName<TViews> = ViewName<TViews>> = TView extends ViewName<TViews> ? ReadyViewOptions<TViews, TView> | LoadingViewOptions<TView> | ErrorViewOptions<TView> : never;
22
- export type ViewState<TViews extends Record<string, ViewDefinition> = DefaultViews, TView extends ViewName<TViews> = ViewName<TViews>> = Omit<ReadyViewOptions<TViews, TView>, "view"> | Omit<LoadingViewOptions<TView>, "view"> | Omit<ErrorViewOptions<TView>, "view">;
23
- export type UseRegisterView<TViews extends Record<string, ViewDefinition>> = <TView extends ViewName<TViews>>(view: TView, options: ViewState<TViews, NoInfer<TView>>) => void;
24
- export declare function useRegisterView<TViews extends Record<string, ViewDefinition> = DefaultViews, TView extends ViewName<TViews> = ViewName<TViews>>(view: TView, options: ViewState<TViews, NoInfer<TView>>): void;
25
- export {};
@@ -1,13 +0,0 @@
1
- import type { TailorKitApp, SlotItem } from "@tailorkit/client-core";
2
- import type { UseAppsOptions, UseAppsResult } from "./use-apps";
3
- export type { SlotItem } from "@tailorkit/client-core";
4
- export interface UseSlotOptions<TScopeNames extends string = string, TSlot extends string = string> extends UseAppsOptions<TScopeNames> {
5
- slot: TSlot;
6
- }
7
- export interface UseSlotResult<TMultiple extends boolean = boolean> extends Omit<UseAppsResult, "data"> {
8
- data: SlotItem<TMultiple>[] | undefined;
9
- }
10
- /** List apps for single-view slots or resolve instances for multiple-view slots. */
11
- export declare function useSlot({ slot, scopes, appIds }: UseSlotOptions): UseSlotResult;
12
- /** Managed Slot resolves only its explicitly supplied app. */
13
- export declare function useAppSlot(app: TailorKitApp, slot: string): UseSlotResult;
@@ -1,2 +0,0 @@
1
- /** Keep equivalent JSON context values stable without requiring callers to memoize. */
2
- export declare function useStableContext<T>(value: T): T;
@@ -1,8 +0,0 @@
1
- import type { HostToIframePayload } from "@tailorkit/sandbox/protocol";
2
- import type { NodeStore } from "@tailorkit/client-core";
3
- export interface RemoteViewContext {
4
- components: Record<string, unknown>;
5
- dispatch: (payload: HostToIframePayload) => void;
6
- store: NodeStore;
7
- }
8
- export declare const RemoteUIContext: import("react").Context<RemoteViewContext | null>;
@@ -1 +0,0 @@
1
- export { appendScopeSelection, normalizeScopeSelection } from "@tailorkit/client-core";
@@ -1 +0,0 @@
1
- export { resolveSlotView, selectSlotView } from "@tailorkit/client-core";