@tailorkit/react 0.1.0-beta.14 → 0.1.0-beta.16

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,3 +1,3 @@
1
1
  import type { ReactNode } from "react";
2
- import type { AppViewProps } from "../tailor-kit";
2
+ import type { AppViewProps } from "../tailorkit";
3
3
  export declare const AppView: ({ app, slot, createIframe, fallback, ...viewProps }: AppViewProps) => ReactNode;
@@ -1,8 +1,8 @@
1
1
  import type { TailorKitStore } from "../store";
2
- import type { TailorKitInstance } from "../tailor-kit";
2
+ import type { TailorKitClientConfig } from "../tailorkit";
3
3
  export interface TailorRootContextValue {
4
4
  store: TailorKitStore;
5
- client: TailorKitInstance;
5
+ client: TailorKitClientConfig;
6
6
  }
7
7
  export declare const TailorRootContext: import("react").Context<TailorRootContextValue | null>;
8
- export declare function useTailorRootContext(component: string): TailorRootContextValue;
8
+ export declare function useTailorRootContext(component: string, expectedClient?: TailorKitClientConfig): TailorRootContextValue;
@@ -1,9 +1,9 @@
1
1
  import type { ReactNode } from "react";
2
- import type { TailorKitApp, TailorKitInstance } from "../tailor-kit";
2
+ import type { TailorKitApp, TailorKitClientConfig } from "../tailorkit";
3
3
  import type { ComponentProps } from "./render";
4
4
  export interface RootProps extends ComponentProps<"div"> {
5
5
  apps?: TailorKitApp[];
6
6
  children?: ReactNode;
7
- client: TailorKitInstance;
7
+ client: TailorKitClientConfig;
8
8
  }
9
9
  export declare function Root({ apps: appsProp, children, render, client, ...props }: RootProps): ReactNode;
@@ -1,5 +1,8 @@
1
- import type { TailorKitApp } from "../tailor-kit";
1
+ import type { TailorKitApp } from "../tailorkit";
2
2
  import type { TailorKitStore } from "../store";
3
+ export interface UseAppsOptions<TScopeNames extends string = string> {
4
+ scopes?: readonly TScopeNames[];
5
+ }
3
6
  export interface UseAppsResult {
4
7
  data: TailorKitApp[] | undefined;
5
8
  error: Error | null;
@@ -10,5 +13,5 @@ export interface UseAppsResult {
10
13
  refetch: () => Promise<void>;
11
14
  status: "error" | "idle" | "loading" | "ready";
12
15
  }
13
- export declare function useApps(): UseAppsResult;
14
- export declare function useAppsStore(store: TailorKitStore): UseAppsResult;
16
+ export declare function useApps(options?: UseAppsOptions): UseAppsResult;
17
+ export declare function useAppsStore(store: TailorKitStore, options?: UseAppsOptions): UseAppsResult;
@@ -1,6 +1,6 @@
1
1
  import type { StandardJSONSchemaV1 } from "@standard-schema/spec";
2
2
  import type { ViewDefinition } from "@tailorkit/core/schema";
3
- import type { RegisteredViews } from "../tailor-kit";
3
+ type DefaultViews = Record<`/${string}`, ViewDefinition>;
4
4
  export type ViewName<TViews extends Record<string, ViewDefinition>> = keyof TViews & string;
5
5
  export type ViewContext<TView> = TView extends StandardJSONSchemaV1 ? StandardJSONSchemaV1.InferOutput<TView> : Record<string, never>;
6
6
  interface ReadyViewOptions<TViews extends Record<string, ViewDefinition>, TView extends ViewName<TViews>> {
@@ -18,7 +18,8 @@ interface ErrorViewOptions<TView extends string> {
18
18
  view: TView;
19
19
  status: "error";
20
20
  }
21
- export type ViewOptions<TViews extends Record<string, ViewDefinition> = RegisteredViews, 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> = RegisteredViews, TView extends ViewName<TViews> = ViewName<TViews>> = Omit<ReadyViewOptions<TViews, TView>, "view"> | Omit<LoadingViewOptions<TView>, "view"> | Omit<ErrorViewOptions<TView>, "view">;
23
- export declare function useView<TView extends ViewName<RegisteredViews>>(view: TView, options: ViewState<RegisteredViews, NoInfer<TView>>): void;
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 UseView<TViews extends Record<string, ViewDefinition>> = <TView extends ViewName<TViews>>(view: TView, options: ViewState<TViews, NoInfer<TView>>) => void;
24
+ export declare function useView<TViews extends Record<string, ViewDefinition> = DefaultViews, TView extends ViewName<TViews> = ViewName<TViews>>(view: TView, options: ViewState<TViews, NoInfer<TView>>): void;
24
25
  export {};
package/dist/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  export { AppView } from "./components/app-view";
2
- export { components, createTailorKitClient } from "./tailor-kit";
3
- export type { AppViewProps, TailorKitApp, TailorKitInstance, Register } from "./tailor-kit";
2
+ export { components, createTailorKitClient } from "./tailorkit";
3
+ export type { AppViewProps, TailorKitApp, TailorKitClientConfig, TailorKitInstance, } from "./tailorkit";
4
4
  export { Root } from "./components/root";
5
- export { useApps } from "./hooks/use-apps";
6
- export { useView } from "./hooks/use-view";
7
- export type { ViewOptions, ViewState } from "./hooks/use-view";
5
+ export type { UseAppsOptions, UseAppsResult } from "./hooks/use-apps";
6
+ export type { UseView, ViewOptions, ViewState } from "./hooks/use-view";
8
7
  export { primitives } from "./primitives";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/components/app-view.tsx
2
+ import { createSessionProvider } from "@tailorkit/app/client";
2
3
  import { useCallback as useCallback2, useEffect as useEffect2, useId as useId2, useMemo as useMemo3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore2 } from "react";
3
4
  import { isViewAncestor as isViewAncestor2 } from "@tailorkit/core/views";
4
5
 
@@ -289,25 +290,61 @@ function createPreviewManager(baseUrl, onEnded) {
289
290
  };
290
291
  }
291
292
 
293
+ // src/scope-query.ts
294
+ function appendScopeSelection(url, scopes) {
295
+ if (scopes === void 0) {
296
+ return;
297
+ }
298
+ if (scopes.length === 0) {
299
+ url.searchParams.append("scopes", "");
300
+ return;
301
+ }
302
+ for (const scope of scopes) {
303
+ url.searchParams.append("scopes", scope);
304
+ }
305
+ }
306
+ function normalizeScopeSelection(scopes) {
307
+ if (scopes === void 0) {
308
+ return { key: "*" };
309
+ }
310
+ const names = [...new Set(scopes)].sort();
311
+ return { key: JSON.stringify(names), scopes: names };
312
+ }
313
+
292
314
  // src/store.ts
293
315
  function createTailorKitStore(baseUrlInput, initialApps) {
294
316
  const baseUrl = toBaseUrl(baseUrlInput);
295
317
  const listeners = /* @__PURE__ */ new Set();
296
318
  let providedApps = initialApps;
297
- let appsSnapshot = {
319
+ let unrequestedSnapshot = {
298
320
  apps: initialApps ?? [],
299
321
  error: null,
300
322
  status: initialApps === void 0 ? "idle" : "ready"
301
323
  };
324
+ const appsEntries = /* @__PURE__ */ new Map();
302
325
  let metaSnapshot = {
303
326
  assetsBaseUrl: null,
304
327
  error: null,
305
328
  schema: null,
306
329
  status: "idle"
307
330
  };
308
- let fetchAppsPromise = null;
309
331
  let fetchMetaPromise = null;
310
- let fetchAppsRequestId = 0;
332
+ const getAppsEntry = (scopes) => {
333
+ const selection = normalizeScopeSelection(scopes);
334
+ let entry = appsEntries.get(selection.key);
335
+ if (!entry) {
336
+ entry = {
337
+ scopes: selection.scopes,
338
+ snapshot: unrequestedSnapshot,
339
+ promise: null,
340
+ requestId: 0,
341
+ requested: false,
342
+ subscribers: 0
343
+ };
344
+ appsEntries.set(selection.key, entry);
345
+ }
346
+ return entry;
347
+ };
311
348
  const emit = () => {
312
349
  for (const listener of listeners) {
313
350
  listener();
@@ -321,55 +358,65 @@ function createTailorKitStore(baseUrlInput, initialApps) {
321
358
  return;
322
359
  }
323
360
  providedApps = apps;
324
- fetchAppsRequestId += 1;
325
- fetchAppsPromise = null;
326
- appsSnapshot = {
361
+ unrequestedSnapshot = {
327
362
  apps: apps ?? [],
328
363
  error: null,
329
364
  status: apps === void 0 ? "idle" : "ready"
330
365
  };
366
+ for (const entry of appsEntries.values()) {
367
+ entry.requestId += 1;
368
+ entry.promise = null;
369
+ entry.snapshot = unrequestedSnapshot;
370
+ }
331
371
  emit();
332
372
  if (apps === void 0) {
333
- void store.fetchApps();
373
+ for (const entry of appsEntries.values()) {
374
+ if (entry.requested && entry.subscribers > 0) {
375
+ void store.fetchApps({ scopes: entry.scopes });
376
+ }
377
+ }
334
378
  }
335
379
  },
336
380
  fetchApps: (options = {}) => {
381
+ const entry = getAppsEntry(options.scopes);
382
+ entry.requested = true;
337
383
  if (providedApps !== void 0) {
338
384
  return Promise.resolve();
339
385
  }
340
- if (fetchAppsPromise && !options.force) {
341
- return fetchAppsPromise;
386
+ if (entry.promise && !options.force) {
387
+ return entry.promise;
342
388
  }
343
- appsSnapshot = { ...appsSnapshot, status: "loading" };
389
+ entry.snapshot = { ...entry.snapshot, status: "loading" };
344
390
  emit();
345
- const requestId = ++fetchAppsRequestId;
391
+ const requestId = ++entry.requestId;
346
392
  const appsUrl = new URL("apps", baseUrl);
347
- fetchAppsPromise = fetch(appsUrl).then(async (response) => {
393
+ appendScopeSelection(appsUrl, entry.scopes);
394
+ entry.promise = fetch(appsUrl).then(async (response) => {
348
395
  if (!response.ok) {
349
396
  throw new Error(`Unable to fetch TailorKit apps from ${baseUrl.toString()}.`);
350
397
  }
351
398
  const apps = await response.json();
352
- if (requestId !== fetchAppsRequestId) {
399
+ if (requestId !== entry.requestId) {
353
400
  return;
354
401
  }
355
- appsSnapshot = {
402
+ entry.snapshot = {
356
403
  apps,
357
404
  error: null,
358
405
  status: "ready"
359
406
  };
360
407
  emit();
361
408
  }).catch((error) => {
362
- if (requestId !== fetchAppsRequestId) {
409
+ if (requestId !== entry.requestId) {
363
410
  return;
364
411
  }
365
- appsSnapshot = {
366
- ...appsSnapshot,
412
+ entry.snapshot = {
413
+ ...entry.snapshot,
367
414
  error: error instanceof Error ? error : new Error(String(error)),
368
415
  status: "error"
369
416
  };
370
417
  emit();
371
418
  });
372
- return fetchAppsPromise;
419
+ return entry.promise;
373
420
  },
374
421
  fetchMeta: () => {
375
422
  if (fetchMetaPromise) {
@@ -399,19 +446,37 @@ function createTailorKitStore(baseUrlInput, initialApps) {
399
446
  });
400
447
  return fetchMetaPromise;
401
448
  },
402
- getAppsSnapshot: () => appsSnapshot,
449
+ getAppsSnapshot: (scopes) => appsEntries.get(normalizeScopeSelection(scopes).key)?.snapshot ?? unrequestedSnapshot,
403
450
  getMetaSnapshot: () => metaSnapshot,
404
451
  subscribe: (listener) => {
405
452
  listeners.add(listener);
406
453
  return () => {
407
454
  listeners.delete(listener);
408
455
  };
456
+ },
457
+ subscribeApps: (scopes, listener) => {
458
+ const selection = normalizeScopeSelection(scopes);
459
+ const entry = getAppsEntry(selection.scopes);
460
+ entry.subscribers += 1;
461
+ listeners.add(listener);
462
+ return () => {
463
+ listeners.delete(listener);
464
+ entry.subscribers -= 1;
465
+ if (entry.subscribers === 0) {
466
+ entry.requestId += 1;
467
+ appsEntries.delete(selection.key);
468
+ }
469
+ };
409
470
  }
410
471
  };
411
472
  return {
412
473
  ...store,
413
474
  previews: createPreviewManager(baseUrl, () => {
414
- void store.fetchApps({ force: true });
475
+ for (const entry of appsEntries.values()) {
476
+ if (entry.requested && entry.subscribers > 0) {
477
+ void store.fetchApps({ force: true, scopes: entry.scopes });
478
+ }
479
+ }
415
480
  })
416
481
  };
417
482
  }
@@ -437,11 +502,16 @@ function useStableContext(value) {
437
502
  // src/components/context.ts
438
503
  import { createContext, useContext } from "react";
439
504
  var TailorRootContext = createContext(null);
440
- function useTailorRootContext(component) {
505
+ function useTailorRootContext(component, expectedClient) {
441
506
  const context = useContext(TailorRootContext);
442
507
  if (!context) {
443
508
  throw new Error(`${component} must be rendered inside Root.`);
444
509
  }
510
+ if (expectedClient && context.client !== expectedClient) {
511
+ throw new Error(
512
+ `${component} was created for a different TailorKit client than the one passed to Root.`
513
+ );
514
+ }
445
515
  return context;
446
516
  }
447
517
 
@@ -791,6 +861,7 @@ var RemoteErrorBoundary = class extends Component {
791
861
  function RemoteViewHost({
792
862
  appUrl,
793
863
  sourceText,
864
+ getBackendSession,
794
865
  components: components2,
795
866
  createIframe,
796
867
  props
@@ -839,6 +910,7 @@ function RemoteViewHost({
839
910
  const host = createIframeUiHost(appUrl, {
840
911
  createIframe,
841
912
  sourceText,
913
+ getBackendSession,
842
914
  onError: (error2) => {
843
915
  console.error("TailorKit remote app failed", error2);
844
916
  setError(error2);
@@ -864,7 +936,7 @@ function RemoteViewHost({
864
936
  hostRef.current = null;
865
937
  host.destroy();
866
938
  };
867
- }, [appUrl, createIframe, sourceText, store]);
939
+ }, [appUrl, createIframe, sourceText, getBackendSession, store]);
868
940
  if (status === "error" && error) {
869
941
  return createElement("div", null, formatError(error));
870
942
  }
@@ -939,6 +1011,10 @@ var AppView = ({
939
1011
  const { store, client } = useTailorRootContext("AppView");
940
1012
  const { theme, components: wrappedComponents } = client;
941
1013
  const reactId = useId2();
1014
+ const getBackendSession = useMemo3(
1015
+ () => createSessionProvider({ baseUrl: store.baseUrl, appId: app.id }),
1016
+ [store.baseUrl, app.id]
1017
+ );
942
1018
  const currentView = useSyncExternalStore2(
943
1019
  store.views.subscribe,
944
1020
  store.views.getSnapshot,
@@ -1014,6 +1090,7 @@ var AppView = ({
1014
1090
  {
1015
1091
  appUrl: (appUrl ?? new URL(`preview/${previewSessionId}/client.js`, store.baseUrl)).toString(),
1016
1092
  sourceText: previewSnapshot.source ?? void 0,
1093
+ getBackendSession,
1017
1094
  components: wrappedComponents,
1018
1095
  createIframe,
1019
1096
  props: runtimeProps
@@ -1030,12 +1107,77 @@ function resolveAppUrl(app, baseUrl, assetsBaseUrl) {
1030
1107
  return null;
1031
1108
  }
1032
1109
  return new URL(
1033
- `projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/files/client.js`,
1110
+ `projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/client/client.js`,
1034
1111
  toBaseUrl(assetsBaseUrl)
1035
1112
  );
1036
1113
  }
1037
1114
 
1038
- // src/tailor-kit.tsx
1115
+ // src/tailorkit.tsx
1116
+ import { createElement as createElement2 } from "react";
1117
+
1118
+ // src/hooks/use-view.ts
1119
+ import { useEffect as useEffect3, useMemo as useMemo4 } from "react";
1120
+ function useView(view, options) {
1121
+ const { store } = useTailorRootContext("useView");
1122
+ const id = useMemo4(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
1123
+ const status = options.status ?? "ready";
1124
+ const context = "context" in options ? options.context : void 0;
1125
+ const contextSnapshot = useStableContext(context);
1126
+ useEffect3(
1127
+ () => () => {
1128
+ store.views.unregister(id);
1129
+ },
1130
+ [id, store]
1131
+ );
1132
+ useEffect3(() => {
1133
+ store.views.register({
1134
+ context: contextSnapshot,
1135
+ id,
1136
+ view,
1137
+ status
1138
+ });
1139
+ }, [contextSnapshot, id, view, status, store]);
1140
+ }
1141
+
1142
+ // src/hooks/use-apps.ts
1143
+ import { useCallback as useCallback3, useEffect as useEffect4, useSyncExternalStore as useSyncExternalStore3 } from "react";
1144
+ function useApps(options = {}) {
1145
+ return useAppsStore(useTailorRootContext("useApps").store, options);
1146
+ }
1147
+ function useAppsStore(store, options = {}) {
1148
+ const selection = normalizeScopeSelection(options.scopes);
1149
+ const subscribe = useCallback3(
1150
+ (listener) => store.subscribeApps(selection.scopes, listener),
1151
+ [store, selection.key]
1152
+ );
1153
+ const snapshot = useSyncExternalStore3(
1154
+ subscribe,
1155
+ () => store.getAppsSnapshot(selection.scopes),
1156
+ () => store.getAppsSnapshot(selection.scopes)
1157
+ );
1158
+ useEffect4(() => {
1159
+ void store.fetchApps({ scopes: selection.scopes });
1160
+ }, [store, selection.key]);
1161
+ const refetch = useCallback3(
1162
+ () => store.fetchApps({ force: true, scopes: selection.scopes }),
1163
+ [store, selection.key]
1164
+ );
1165
+ return toUseAppsResult(snapshot, refetch);
1166
+ }
1167
+ function toUseAppsResult(snapshot, refetch) {
1168
+ return {
1169
+ data: snapshot.status === "ready" ? snapshot.apps : void 0,
1170
+ error: snapshot.error,
1171
+ isError: snapshot.status === "error",
1172
+ isLoading: snapshot.status === "loading",
1173
+ isPending: snapshot.status === "idle" || snapshot.status === "loading",
1174
+ isSuccess: snapshot.status === "ready",
1175
+ refetch,
1176
+ status: snapshot.status
1177
+ };
1178
+ }
1179
+
1180
+ // src/tailorkit.tsx
1039
1181
  var componentTagPrefix = "tailorkit-";
1040
1182
  var toComponentTagName = (name) => `${componentTagPrefix}${name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[\s_]+/gu, "-").toLowerCase()}`;
1041
1183
  function components(_schema, customComponents) {
@@ -1062,31 +1204,51 @@ function createReactTailorKitClient(options) {
1062
1204
  wrappedComponents[toComponentTagName(name)] = TailorKitComponent;
1063
1205
  }
1064
1206
  }
1065
- return {
1207
+ const clientConfig = {
1066
1208
  baseUrl: options.baseUrl,
1067
1209
  components: wrappedComponents,
1068
1210
  theme
1069
1211
  };
1212
+ const client = {
1213
+ ...clientConfig,
1214
+ AppView: function ClientAppView(props) {
1215
+ useTailorRootContext("AppView", client);
1216
+ const TypedReactAppView = AppView;
1217
+ return createElement2(
1218
+ TypedReactAppView,
1219
+ props
1220
+ );
1221
+ },
1222
+ useApps: function useClientApps(options2) {
1223
+ useTailorRootContext("useApps", client);
1224
+ return useApps(options2);
1225
+ },
1226
+ useView: function useClientView(view, options2) {
1227
+ useTailorRootContext("useView", client);
1228
+ useView(view, options2);
1229
+ }
1230
+ };
1231
+ return client;
1070
1232
  }
1071
1233
 
1072
1234
  // src/components/root.tsx
1073
- import { useEffect as useEffect3, useMemo as useMemo5, useState as useState2 } from "react";
1235
+ import { useEffect as useEffect5, useMemo as useMemo6, useState as useState2 } from "react";
1074
1236
 
1075
1237
  // src/components/render.tsx
1076
- import { cloneElement, createElement as createElement2, isValidElement, useMemo as useMemo4 } from "react";
1238
+ import { cloneElement, createElement as createElement3, isValidElement, useMemo as useMemo5 } from "react";
1077
1239
  function useRender({
1078
1240
  defaultTagName,
1079
1241
  props,
1080
1242
  render
1081
1243
  }) {
1082
- return useMemo4(() => {
1244
+ return useMemo5(() => {
1083
1245
  if (render) {
1084
1246
  if (!isValidElement(render)) {
1085
1247
  throw new Error("The render prop must be a valid React element.");
1086
1248
  }
1087
1249
  return cloneElement(render, mergeProps(render.props, props));
1088
1250
  }
1089
- return createElement2(defaultTagName, props);
1251
+ return createElement3(defaultTagName, props);
1090
1252
  }, [defaultTagName, props, render]);
1091
1253
  }
1092
1254
  function mergeProps(...propSets) {
@@ -1155,11 +1317,11 @@ function Root({ apps: appsProp, children, render, client, ...props }) {
1155
1317
  store = createTailorKitStore(baseUrl, appsProp);
1156
1318
  setStore(store);
1157
1319
  }
1158
- useEffect3(() => {
1320
+ useEffect5(() => {
1159
1321
  store.setProvidedApps(appsProp);
1160
1322
  }, [store, appsProp]);
1161
- useEffect3(() => () => store.previews.dispose(), [store]);
1162
- const context = useMemo5(
1323
+ useEffect5(() => () => store.previews.dispose(), [store]);
1324
+ const context = useMemo6(
1163
1325
  () => ({
1164
1326
  store,
1165
1327
  client
@@ -1173,66 +1335,10 @@ function Root({ apps: appsProp, children, render, client, ...props }) {
1173
1335
  }) : children;
1174
1336
  return /* @__PURE__ */ jsx4(TailorRootContext.Provider, { value: context, children: element });
1175
1337
  }
1176
-
1177
- // src/hooks/use-apps.ts
1178
- import { useCallback as useCallback3, useEffect as useEffect4, useSyncExternalStore as useSyncExternalStore3 } from "react";
1179
- function useApps() {
1180
- return useAppsStore(useTailorRootContext("useApps").store);
1181
- }
1182
- function useAppsStore(store) {
1183
- const snapshot = useSyncExternalStore3(
1184
- store.subscribe,
1185
- store.getAppsSnapshot,
1186
- store.getAppsSnapshot
1187
- );
1188
- useEffect4(() => {
1189
- void store.fetchApps();
1190
- }, [store]);
1191
- const refetch = useCallback3(() => store.fetchApps({ force: true }), [store]);
1192
- return toUseAppsResult(snapshot, refetch);
1193
- }
1194
- function toUseAppsResult(snapshot, refetch) {
1195
- return {
1196
- data: snapshot.status === "ready" ? snapshot.apps : void 0,
1197
- error: snapshot.error,
1198
- isError: snapshot.status === "error",
1199
- isLoading: snapshot.status === "loading",
1200
- isPending: snapshot.status === "idle" || snapshot.status === "loading",
1201
- isSuccess: snapshot.status === "ready",
1202
- refetch,
1203
- status: snapshot.status
1204
- };
1205
- }
1206
-
1207
- // src/hooks/use-view.ts
1208
- import { useEffect as useEffect5, useMemo as useMemo6 } from "react";
1209
- function useView(view, options) {
1210
- const { store } = useTailorRootContext("useView");
1211
- const id = useMemo6(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
1212
- const status = options.status ?? "ready";
1213
- const context = "context" in options ? options.context : void 0;
1214
- const contextSnapshot = useStableContext(context);
1215
- useEffect5(
1216
- () => () => {
1217
- store.views.unregister(id);
1218
- },
1219
- [id, store]
1220
- );
1221
- useEffect5(() => {
1222
- store.views.register({
1223
- context: contextSnapshot,
1224
- id,
1225
- view,
1226
- status
1227
- });
1228
- }, [contextSnapshot, id, view, status, store]);
1229
- }
1230
1338
  export {
1231
1339
  AppView,
1232
1340
  Root,
1233
1341
  components,
1234
1342
  createTailorKitClient,
1235
- primitives,
1236
- useApps,
1237
- useView
1343
+ primitives
1238
1344
  };
@@ -1,4 +1,4 @@
1
- import type { TailorKitApp } from "./tailor-kit";
1
+ import type { TailorKitApp } from "./tailorkit";
2
2
  export interface PreviewSnapshot {
3
3
  revision: number;
4
4
  source: string | null;
@@ -1,12 +1,16 @@
1
+ import type { Session } from "@tailorkit/app/client";
1
2
  import type { ReactNode } from "react";
2
3
  interface RemoteViewHostProps {
3
4
  appUrl: string | URL;
4
5
  sourceText?: string;
6
+ getBackendSession?: (options: {
7
+ refresh: boolean;
8
+ }) => Promise<Session>;
5
9
  components: Record<string, unknown>;
6
10
  createIframe?: () => HTMLIFrameElement;
7
11
  props?: Record<string, unknown>;
8
12
  }
9
- export declare function RemoteViewHost({ appUrl, sourceText, components, createIframe, props, }: RemoteViewHostProps): ReactNode;
13
+ export declare function RemoteViewHost({ appUrl, sourceText, getBackendSession, components, createIframe, props, }: RemoteViewHostProps): ReactNode;
10
14
  interface RemoteViewProps {
11
15
  nodeId: string;
12
16
  }
@@ -0,0 +1,5 @@
1
+ export declare function appendScopeSelection(url: URL, scopes?: readonly string[]): void;
2
+ export declare function normalizeScopeSelection(scopes?: readonly string[]): {
3
+ key: string;
4
+ scopes?: readonly string[];
5
+ };
package/dist/store.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { TailorKitSchemaSpecType } from "@tailorkit/core/spec";
2
- import type { TailorKitApp } from "./tailor-kit";
2
+ import type { TailorKitApp } from "./tailorkit";
3
3
  export interface TailorKitAppsSnapshot {
4
4
  apps: TailorKitApp[];
5
5
  error: Error | null;
@@ -23,11 +23,13 @@ export declare function createTailorKitStore(baseUrlInput: string | URL, initial
23
23
  setProvidedApps(apps: TailorKitApp[] | undefined): void;
24
24
  fetchApps: (options?: {
25
25
  force?: boolean;
26
+ scopes?: readonly string[];
26
27
  }) => Promise<void>;
27
28
  fetchMeta: () => Promise<void>;
28
- getAppsSnapshot: () => TailorKitAppsSnapshot;
29
+ getAppsSnapshot: (scopes?: readonly string[]) => TailorKitAppsSnapshot;
29
30
  getMetaSnapshot: () => TailorKitMetaSnapshot;
30
31
  subscribe: (listener: () => void) => (() => void);
32
+ subscribeApps: (scopes: readonly string[] | undefined, listener: () => void) => (() => void);
31
33
  previews: {
32
34
  getSnapshot: (sessionId: string) => import("./preview-manager").PreviewSnapshot;
33
35
  updateApp: (app: TailorKitApp) => void;
@@ -1,7 +1,8 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { TailorKitTheme, CallbackMap, ComponentDefinition, ComponentProps, Schema, ViewDefinition, SlotDefinitions, TailorKitSchema } from "@tailorkit/core/schema";
3
3
  import type { primitives } from "./primitives";
4
- import type { ViewOptions, ViewName } from "./hooks/use-view";
4
+ import type { UseView, ViewName, ViewOptions } from "./hooks/use-view";
5
+ import type { UseAppsOptions, UseAppsResult } from "./hooks/use-apps";
5
6
  type AnyComponentDefinition = ComponentDefinition<Schema | undefined, CallbackMap, boolean | undefined>;
6
7
  type ComponentRenderer<TComponent extends AnyComponentDefinition> = (args: {
7
8
  props: ComponentProps<TComponent>;
@@ -49,32 +50,26 @@ type AppViewViewProps<TViews extends Record<string, ViewDefinition>, TView exten
49
50
  view?: never;
50
51
  status?: never;
51
52
  } | ViewOptions<TViews, TView>;
52
- export type AppViewProps<TViews extends Record<string, ViewDefinition> = RegisteredViews, TView extends ViewName<TViews> = ViewName<TViews>> = {
53
- [V in RegisteredSlots]: AppViewBaseProps & {
53
+ export type AppViewProps<TViews extends Record<string, ViewDefinition> = Record<`/${string}`, ViewDefinition>, TView extends ViewName<TViews> = ViewName<TViews>, TSlots extends SlotDefinitions = SlotDefinitions> = {
54
+ [V in keyof TSlots & string]: AppViewBaseProps & {
54
55
  slot: V;
55
- } & AppViewViewProps<TViews, Extract<TView, SlotView<V>>>;
56
- }[RegisteredSlots];
57
- export interface Register {
58
- }
59
- export type RegisteredViews = Register extends {
60
- client: TailorKitInstance<infer S>;
61
- } ? S : Record<`/${string}`, ViewDefinition>;
62
- type RegisteredSlotMap = Register extends {
63
- client: {
64
- readonly $slots?: infer V;
65
- };
66
- } ? V : SlotDefinitions;
67
- export type RegisteredSlots = keyof RegisteredSlotMap & string;
68
- type SlotView<V extends RegisteredSlots> = RegisteredSlotMap[V] extends {
56
+ } & AppViewViewProps<TViews, Extract<TView, SlotView<TSlots, V>>>;
57
+ }[keyof TSlots & string];
58
+ type SlotView<TSlots extends SlotDefinitions, V extends keyof TSlots & string> = TSlots[V] extends {
69
59
  views: readonly (infer P)[];
70
60
  } ? Extract<P, string> : never;
71
- export interface TailorKitInstance<TViews extends Record<string, ViewDefinition> = Record<string, ViewDefinition>, TSlots extends SlotDefinitions = SlotDefinitions> {
72
- readonly $slots?: TSlots;
73
- readonly $views?: TViews;
61
+ export interface TailorKitClientConfig {
74
62
  readonly baseUrl: string | URL;
75
63
  readonly components: Record<string, unknown>;
76
64
  readonly theme: TailorKitTheme;
77
65
  }
66
+ export interface TailorKitInstance<TViews extends Record<string, ViewDefinition> = Record<string, ViewDefinition>, TSlots extends SlotDefinitions = SlotDefinitions, TScopeNames extends string = string> extends TailorKitClientConfig {
67
+ readonly $slots?: TSlots;
68
+ readonly $views?: TViews;
69
+ readonly AppView: (props: AppViewProps<TViews, ViewName<TViews>, TSlots>) => ReactNode;
70
+ readonly useApps: (options?: UseAppsOptions<TScopeNames>) => UseAppsResult;
71
+ readonly useView: UseView<TViews>;
72
+ }
78
73
  type PrimitiveRenderers = typeof primitives;
79
74
  type CustomComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
80
75
  [TName in Exclude<keyof TComponents, keyof PrimitiveRenderers>]?: ComponentRenderer<TComponents[TName]>;
@@ -93,6 +88,15 @@ type ServerComponents<TTailor extends TailorKitServerShape> = {
93
88
  [TName in keyof ServerComponentMap<TTailor>]: ServerComponentMap<TTailor>[TName] extends AnyComponentDefinition ? ServerComponentMap<TTailor>[TName] : never;
94
89
  };
95
90
  type ServerViewMap<TTailor extends TailorKitServerShape> = TTailor["$internal"]["schema"]["contexts"];
91
+ type ServerScopeNames<TTailor extends TailorKitServerShape> = TTailor extends {
92
+ handler: (request: Request, options: infer TOptions) => unknown;
93
+ } ? TOptions extends {
94
+ authenticate: infer TAuthenticate;
95
+ } ? TAuthenticate extends (...args: infer _TArgs) => infer TResult ? Extract<Awaited<TResult>, {
96
+ scopes: unknown;
97
+ }> extends {
98
+ scopes: infer TScopes;
99
+ } ? keyof TScopes & string : never : never : never : never;
96
100
  type ServerViews<TTailor extends TailorKitServerShape> = {
97
101
  [TName in keyof ServerViewMap<TTailor>]: ServerViewMap<TTailor>[TName] extends ViewDefinition ? ServerViewMap<TTailor>[TName] : never;
98
102
  };
@@ -102,5 +106,5 @@ export declare function createTailorKitClient<TTailor extends TailorKitServerSha
102
106
  theme?: TailorKitTheme;
103
107
  }): TailorKitInstance<ServerViews<TTailor>, TTailor extends {
104
108
  readonly $slots?: infer V extends SlotDefinitions;
105
- } ? V : SlotDefinitions>;
109
+ } ? V : SlotDefinitions, ServerScopeNames<TTailor>>;
106
110
  export type { ViewOptions } from "./hooks/use-view";
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@tailorkit/react",
3
- "version": "0.1.0-beta.14",
3
+ "version": "0.1.0-beta.16",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/AlfieJones/tailorkit.git",
7
+ "url": "git+https://github.com/tailorkit-labs/TailorKit.git",
8
8
  "directory": "packages/react"
9
9
  },
10
10
  "files": [
@@ -23,21 +23,21 @@
23
23
  "dependencies": {
24
24
  "@floating-ui/react-dom": "^2.1.9",
25
25
  "@standard-schema/spec": "^1.1.0",
26
- "@tailorkit/client-platform": "0.1.0-beta.14",
27
- "@tailorkit/core": "0.1.0-beta.14",
28
- "@tailorkit/sandbox": "0.1.0-beta.14"
26
+ "@tailorkit/app": "0.1.0-beta.16",
27
+ "@tailorkit/client-platform": "0.1.0-beta.16",
28
+ "@tailorkit/core": "0.1.0-beta.16",
29
+ "@tailorkit/sandbox": "0.1.0-beta.16"
29
30
  },
30
31
  "devDependencies": {
31
32
  "@tailorkit/config": "0.0.0",
32
33
  "@testing-library/react": "^16.3.3",
33
- "@types/node": "26.2.0",
34
+ "@types/node": "26.6.3",
34
35
  "@types/react": "^19.3.0",
35
36
  "happy-dom": "^20.14.5",
36
37
  "react": "^19.3.0",
37
38
  "react-dom": "^19.3.0",
38
39
  "tsup": "^8.5.1",
39
- "typescript": "^7.0.2",
40
- "vitest": "^5.0.0"
40
+ "typescript": "^7.0.2"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "react": ">=19.2.6"
@@ -45,6 +45,6 @@
45
45
  "scripts": {
46
46
  "build": "tsup src/index.ts --format esm --target node20 --clean && tsc -p tsconfig.build.json",
47
47
  "check-types": "tsc --noEmit",
48
- "test": "vitest run"
48
+ "test": "vp test run"
49
49
  }
50
50
  }