@tailorkit/react 0.1.0-beta.13 → 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.
- package/dist/components/app-view.d.ts +1 -1
- package/dist/components/context.d.ts +3 -3
- package/dist/components/root.d.ts +2 -2
- package/dist/hooks/use-apps.d.ts +6 -3
- package/dist/hooks/use-view.d.ts +6 -5
- package/dist/index.d.ts +4 -5
- package/dist/index.js +460 -101
- package/dist/preview-manager.d.ts +11 -0
- package/dist/remote-view.d.ts +6 -1
- package/dist/scope-query.d.ts +5 -0
- package/dist/store.d.ts +11 -3
- package/dist/{tailor-kit.d.ts → tailorkit.d.ts} +30 -30
- package/package.json +9 -8
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { TailorKitStore } from "../store";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TailorKitClientConfig } from "../tailorkit";
|
|
3
3
|
export interface TailorRootContextValue {
|
|
4
4
|
store: TailorKitStore;
|
|
5
|
-
client:
|
|
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,
|
|
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:
|
|
7
|
+
client: TailorKitClientConfig;
|
|
8
8
|
}
|
|
9
9
|
export declare function Root({ apps: appsProp, children, render, client, ...props }: RootProps): ReactNode;
|
package/dist/hooks/use-apps.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import type { TailorKitApp } from "../
|
|
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;
|
package/dist/hooks/use-view.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { StandardJSONSchemaV1 } from "@standard-schema/spec";
|
|
2
2
|
import type { ViewDefinition } from "@tailorkit/core/schema";
|
|
3
|
-
|
|
3
|
+
type DefaultViews = Record<`/${string}`, ViewDefinition>;
|
|
4
4
|
export type ViewName<TViews extends Record<string, ViewDefinition>> = keyof TViews & string;
|
|
5
|
-
export type ViewContext<TView> = TView extends
|
|
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>> {
|
|
7
7
|
context: ViewContext<TViews[TView]>;
|
|
8
8
|
view: TView;
|
|
@@ -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> =
|
|
22
|
-
export type ViewState<TViews extends Record<string, ViewDefinition> =
|
|
23
|
-
export
|
|
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 "./
|
|
3
|
-
export type { AppViewProps, TailorKitApp, TailorKitInstance,
|
|
2
|
+
export { components, createTailorKitClient } from "./tailorkit";
|
|
3
|
+
export type { AppViewProps, TailorKitApp, TailorKitClientConfig, TailorKitInstance, } from "./tailorkit";
|
|
4
4
|
export { Root } from "./components/root";
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
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,5 +1,6 @@
|
|
|
1
1
|
// src/components/app-view.tsx
|
|
2
|
-
import {
|
|
2
|
+
import { createSessionProvider } from "@tailorkit/app/client";
|
|
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
|
|
|
5
6
|
// src/view-registry.ts
|
|
@@ -63,25 +64,287 @@ function createViewRegistry() {
|
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
// src/preview-manager.ts
|
|
68
|
+
import {
|
|
69
|
+
createPreviewWebSocketClient,
|
|
70
|
+
previewMetadataSchema
|
|
71
|
+
} from "@tailorkit/client-platform/preview";
|
|
72
|
+
var empty = { revision: 0, source: null };
|
|
73
|
+
function viewerTokenExpiresAt(metadata) {
|
|
74
|
+
const expiresAt = Number(metadata.token.split(".", 1)[0]);
|
|
75
|
+
return Number.isSafeInteger(expiresAt) ? expiresAt : 0;
|
|
76
|
+
}
|
|
77
|
+
function bytesFromBase64(value) {
|
|
78
|
+
const raw = atob(value);
|
|
79
|
+
const bytes = new Uint8Array(raw.length);
|
|
80
|
+
for (let index = 0; index < raw.length; index++) {
|
|
81
|
+
bytes[index] = raw.codePointAt(index) ?? 0;
|
|
82
|
+
}
|
|
83
|
+
return bytes;
|
|
84
|
+
}
|
|
85
|
+
async function verify(candidate) {
|
|
86
|
+
let source = null;
|
|
87
|
+
for (const [fileIndex, file] of candidate.manifest.files.entries()) {
|
|
88
|
+
const parts = candidate.files[fileIndex];
|
|
89
|
+
if (!parts || parts.length !== file.chunks || parts.some((part) => part === null)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const bytes = new Uint8Array(file.size);
|
|
93
|
+
let offset = 0;
|
|
94
|
+
for (const part of parts) {
|
|
95
|
+
if (!part) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
bytes.set(part, offset);
|
|
99
|
+
offset += part.length;
|
|
100
|
+
}
|
|
101
|
+
if (offset !== file.size) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const digest = [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
105
|
+
if (digest !== file.sha256) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (file.path === "client.js") {
|
|
109
|
+
source = new TextDecoder().decode(bytes);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return source;
|
|
113
|
+
}
|
|
114
|
+
function createPreviewManager(baseUrl, onEnded) {
|
|
115
|
+
const entries = /* @__PURE__ */ new Map();
|
|
116
|
+
const notify = (entry) => {
|
|
117
|
+
for (const listener of entry.listeners) {
|
|
118
|
+
listener();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const close = (entry) => {
|
|
122
|
+
if (entry.reconnect) {
|
|
123
|
+
clearTimeout(entry.reconnect);
|
|
124
|
+
}
|
|
125
|
+
entry.reconnect = null;
|
|
126
|
+
const socket = entry.socket;
|
|
127
|
+
entry.socket = null;
|
|
128
|
+
socket?.close();
|
|
129
|
+
entry.connecting = false;
|
|
130
|
+
entry.candidate = null;
|
|
131
|
+
};
|
|
132
|
+
const end = (entry) => {
|
|
133
|
+
close(entry);
|
|
134
|
+
entry.metadata = null;
|
|
135
|
+
entry.snapshot = empty;
|
|
136
|
+
notify(entry);
|
|
137
|
+
onEnded();
|
|
138
|
+
};
|
|
139
|
+
const handleEvent = async (entry, event) => {
|
|
140
|
+
if (event.type === "ended") {
|
|
141
|
+
end(entry);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (event.revision <= entry.snapshot.revision) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (event.type === "begin") {
|
|
148
|
+
entry.candidate = {
|
|
149
|
+
revision: event.revision,
|
|
150
|
+
manifest: event.manifest,
|
|
151
|
+
files: event.manifest.files.map((file) => Array.from({ length: file.chunks }, () => null))
|
|
152
|
+
};
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const candidate = entry.candidate;
|
|
156
|
+
if (!candidate || candidate.revision !== event.revision) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (event.type === "chunk") {
|
|
160
|
+
const file = candidate.manifest.files[event.fileIndex];
|
|
161
|
+
const slots = candidate.files[event.fileIndex];
|
|
162
|
+
if (!file || !slots || event.chunkIndex < 0 || event.chunkIndex >= slots.length || slots[event.chunkIndex]) {
|
|
163
|
+
entry.candidate = null;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const bytes = bytesFromBase64(event.base64);
|
|
167
|
+
if (bytes.length !== Math.min(256 * 1024, file.size - event.chunkIndex * 256 * 1024)) {
|
|
168
|
+
entry.candidate = null;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
slots[event.chunkIndex] = bytes;
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (event.type === "complete") {
|
|
175
|
+
const source = await verify(candidate);
|
|
176
|
+
if (source !== null && entry.candidate === candidate && candidate.revision > entry.snapshot.revision) {
|
|
177
|
+
entry.snapshot = { revision: candidate.revision, source };
|
|
178
|
+
notify(entry);
|
|
179
|
+
}
|
|
180
|
+
if (entry.candidate === candidate) {
|
|
181
|
+
entry.candidate = null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const connect = async (entry) => {
|
|
186
|
+
if (!entry.listeners.size || !entry.app.preview || entry.socket || entry.connecting) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
entry.connecting = true;
|
|
190
|
+
const appMetadata = entry.app.preview;
|
|
191
|
+
let metadata = entry.metadata && viewerTokenExpiresAt(entry.metadata) > Math.max(Date.now(), viewerTokenExpiresAt(appMetadata)) ? entry.metadata : appMetadata;
|
|
192
|
+
try {
|
|
193
|
+
const refresh = new URL("preview/metadata", baseUrl);
|
|
194
|
+
refresh.searchParams.set("sessionId", metadata.sessionId);
|
|
195
|
+
const response = await fetch(refresh, { credentials: "same-origin" });
|
|
196
|
+
if (response.ok) {
|
|
197
|
+
metadata = previewMetadataSchema.parse(await response.json());
|
|
198
|
+
entry.metadata = metadata;
|
|
199
|
+
} else if (response.status === 404) {
|
|
200
|
+
end(entry);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
}
|
|
205
|
+
if (!entry.listeners.size) {
|
|
206
|
+
entry.connecting = false;
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const socket = new WebSocket(metadata.websocketUrl, metadata.token);
|
|
210
|
+
entry.socket = socket;
|
|
211
|
+
entry.connecting = false;
|
|
212
|
+
socket.addEventListener("open", () => {
|
|
213
|
+
entry.delay = 1e3;
|
|
214
|
+
const client = createPreviewWebSocketClient(socket);
|
|
215
|
+
void (async () => {
|
|
216
|
+
try {
|
|
217
|
+
const stream = await client.subscribe();
|
|
218
|
+
for await (const event of stream) {
|
|
219
|
+
await handleEvent(entry, event);
|
|
220
|
+
}
|
|
221
|
+
} catch {
|
|
222
|
+
} finally {
|
|
223
|
+
socket.close();
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
226
|
+
});
|
|
227
|
+
socket.addEventListener("error", () => socket.close());
|
|
228
|
+
socket.addEventListener("close", () => {
|
|
229
|
+
if (entry.socket !== socket) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
entry.socket = null;
|
|
233
|
+
entry.candidate = null;
|
|
234
|
+
if (entry.listeners.size) {
|
|
235
|
+
entry.reconnect = setTimeout(() => {
|
|
236
|
+
entry.reconnect = null;
|
|
237
|
+
void connect(entry);
|
|
238
|
+
}, entry.delay);
|
|
239
|
+
entry.delay = Math.min(entry.delay * 2, 3e4);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
return {
|
|
244
|
+
getSnapshot: (sessionId) => entries.get(sessionId)?.snapshot ?? empty,
|
|
245
|
+
updateApp: (app) => {
|
|
246
|
+
const sessionId = app.preview?.sessionId;
|
|
247
|
+
const entry = sessionId && entries.get(sessionId);
|
|
248
|
+
if (entry) {
|
|
249
|
+
entry.app = app;
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
subscribe: (app, listener) => {
|
|
253
|
+
const sessionId = app.preview?.sessionId;
|
|
254
|
+
if (!sessionId) {
|
|
255
|
+
return () => {
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
let entry = entries.get(sessionId);
|
|
259
|
+
if (!entry) {
|
|
260
|
+
entry = {
|
|
261
|
+
app,
|
|
262
|
+
metadata: null,
|
|
263
|
+
candidate: null,
|
|
264
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
265
|
+
snapshot: empty,
|
|
266
|
+
socket: null,
|
|
267
|
+
connecting: false,
|
|
268
|
+
reconnect: null,
|
|
269
|
+
delay: 1e3
|
|
270
|
+
};
|
|
271
|
+
entries.set(sessionId, entry);
|
|
272
|
+
}
|
|
273
|
+
entry.app = app;
|
|
274
|
+
entry.listeners.add(listener);
|
|
275
|
+
void connect(entry);
|
|
276
|
+
return () => {
|
|
277
|
+
entry?.listeners.delete(listener);
|
|
278
|
+
if (entry && !entry.listeners.size) {
|
|
279
|
+
close(entry);
|
|
280
|
+
entries.delete(sessionId);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
},
|
|
284
|
+
dispose: () => {
|
|
285
|
+
for (const entry of entries.values()) {
|
|
286
|
+
close(entry);
|
|
287
|
+
}
|
|
288
|
+
entries.clear();
|
|
289
|
+
}
|
|
290
|
+
};
|
|
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
|
+
|
|
66
314
|
// src/store.ts
|
|
67
|
-
function createTailorKitStore(baseUrlInput, initialApps
|
|
315
|
+
function createTailorKitStore(baseUrlInput, initialApps) {
|
|
68
316
|
const baseUrl = toBaseUrl(baseUrlInput);
|
|
69
317
|
const listeners = /* @__PURE__ */ new Set();
|
|
70
318
|
let providedApps = initialApps;
|
|
71
|
-
let
|
|
319
|
+
let unrequestedSnapshot = {
|
|
72
320
|
apps: initialApps ?? [],
|
|
73
321
|
error: null,
|
|
74
322
|
status: initialApps === void 0 ? "idle" : "ready"
|
|
75
323
|
};
|
|
324
|
+
const appsEntries = /* @__PURE__ */ new Map();
|
|
76
325
|
let metaSnapshot = {
|
|
77
326
|
assetsBaseUrl: null,
|
|
78
327
|
error: null,
|
|
79
328
|
schema: null,
|
|
80
329
|
status: "idle"
|
|
81
330
|
};
|
|
82
|
-
let fetchAppsPromise = null;
|
|
83
331
|
let fetchMetaPromise = null;
|
|
84
|
-
|
|
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
|
+
};
|
|
85
348
|
const emit = () => {
|
|
86
349
|
for (const listener of listeners) {
|
|
87
350
|
listener();
|
|
@@ -95,58 +358,65 @@ function createTailorKitStore(baseUrlInput, initialApps, previewSessionId) {
|
|
|
95
358
|
return;
|
|
96
359
|
}
|
|
97
360
|
providedApps = apps;
|
|
98
|
-
|
|
99
|
-
fetchAppsPromise = null;
|
|
100
|
-
appsSnapshot = {
|
|
361
|
+
unrequestedSnapshot = {
|
|
101
362
|
apps: apps ?? [],
|
|
102
363
|
error: null,
|
|
103
364
|
status: apps === void 0 ? "idle" : "ready"
|
|
104
365
|
};
|
|
366
|
+
for (const entry of appsEntries.values()) {
|
|
367
|
+
entry.requestId += 1;
|
|
368
|
+
entry.promise = null;
|
|
369
|
+
entry.snapshot = unrequestedSnapshot;
|
|
370
|
+
}
|
|
105
371
|
emit();
|
|
106
372
|
if (apps === void 0) {
|
|
107
|
-
|
|
373
|
+
for (const entry of appsEntries.values()) {
|
|
374
|
+
if (entry.requested && entry.subscribers > 0) {
|
|
375
|
+
void store.fetchApps({ scopes: entry.scopes });
|
|
376
|
+
}
|
|
377
|
+
}
|
|
108
378
|
}
|
|
109
379
|
},
|
|
110
380
|
fetchApps: (options = {}) => {
|
|
381
|
+
const entry = getAppsEntry(options.scopes);
|
|
382
|
+
entry.requested = true;
|
|
111
383
|
if (providedApps !== void 0) {
|
|
112
384
|
return Promise.resolve();
|
|
113
385
|
}
|
|
114
|
-
if (
|
|
115
|
-
return
|
|
386
|
+
if (entry.promise && !options.force) {
|
|
387
|
+
return entry.promise;
|
|
116
388
|
}
|
|
117
|
-
|
|
389
|
+
entry.snapshot = { ...entry.snapshot, status: "loading" };
|
|
118
390
|
emit();
|
|
119
|
-
const requestId = ++
|
|
391
|
+
const requestId = ++entry.requestId;
|
|
120
392
|
const appsUrl = new URL("apps", baseUrl);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
fetchAppsPromise = fetch(appsUrl).then(async (response) => {
|
|
393
|
+
appendScopeSelection(appsUrl, entry.scopes);
|
|
394
|
+
entry.promise = fetch(appsUrl).then(async (response) => {
|
|
125
395
|
if (!response.ok) {
|
|
126
396
|
throw new Error(`Unable to fetch TailorKit apps from ${baseUrl.toString()}.`);
|
|
127
397
|
}
|
|
128
398
|
const apps = await response.json();
|
|
129
|
-
if (requestId !==
|
|
399
|
+
if (requestId !== entry.requestId) {
|
|
130
400
|
return;
|
|
131
401
|
}
|
|
132
|
-
|
|
402
|
+
entry.snapshot = {
|
|
133
403
|
apps,
|
|
134
404
|
error: null,
|
|
135
405
|
status: "ready"
|
|
136
406
|
};
|
|
137
407
|
emit();
|
|
138
408
|
}).catch((error) => {
|
|
139
|
-
if (requestId !==
|
|
409
|
+
if (requestId !== entry.requestId) {
|
|
140
410
|
return;
|
|
141
411
|
}
|
|
142
|
-
|
|
143
|
-
...
|
|
412
|
+
entry.snapshot = {
|
|
413
|
+
...entry.snapshot,
|
|
144
414
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
145
415
|
status: "error"
|
|
146
416
|
};
|
|
147
417
|
emit();
|
|
148
418
|
});
|
|
149
|
-
return
|
|
419
|
+
return entry.promise;
|
|
150
420
|
},
|
|
151
421
|
fetchMeta: () => {
|
|
152
422
|
if (fetchMetaPromise) {
|
|
@@ -176,16 +446,39 @@ function createTailorKitStore(baseUrlInput, initialApps, previewSessionId) {
|
|
|
176
446
|
});
|
|
177
447
|
return fetchMetaPromise;
|
|
178
448
|
},
|
|
179
|
-
getAppsSnapshot: () =>
|
|
449
|
+
getAppsSnapshot: (scopes) => appsEntries.get(normalizeScopeSelection(scopes).key)?.snapshot ?? unrequestedSnapshot,
|
|
180
450
|
getMetaSnapshot: () => metaSnapshot,
|
|
181
451
|
subscribe: (listener) => {
|
|
182
452
|
listeners.add(listener);
|
|
183
453
|
return () => {
|
|
184
454
|
listeners.delete(listener);
|
|
185
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
|
+
};
|
|
186
470
|
}
|
|
187
471
|
};
|
|
188
|
-
return
|
|
472
|
+
return {
|
|
473
|
+
...store,
|
|
474
|
+
previews: createPreviewManager(baseUrl, () => {
|
|
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
|
+
}
|
|
480
|
+
})
|
|
481
|
+
};
|
|
189
482
|
}
|
|
190
483
|
function toBaseUrl(value) {
|
|
191
484
|
const url = value instanceof URL ? new URL(value) : new URL(value);
|
|
@@ -209,11 +502,16 @@ function useStableContext(value) {
|
|
|
209
502
|
// src/components/context.ts
|
|
210
503
|
import { createContext, useContext } from "react";
|
|
211
504
|
var TailorRootContext = createContext(null);
|
|
212
|
-
function useTailorRootContext(component) {
|
|
505
|
+
function useTailorRootContext(component, expectedClient) {
|
|
213
506
|
const context = useContext(TailorRootContext);
|
|
214
507
|
if (!context) {
|
|
215
508
|
throw new Error(`${component} must be rendered inside Root.`);
|
|
216
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
|
+
}
|
|
217
515
|
return context;
|
|
218
516
|
}
|
|
219
517
|
|
|
@@ -562,6 +860,8 @@ var RemoteErrorBoundary = class extends Component {
|
|
|
562
860
|
};
|
|
563
861
|
function RemoteViewHost({
|
|
564
862
|
appUrl,
|
|
863
|
+
sourceText,
|
|
864
|
+
getBackendSession,
|
|
565
865
|
components: components2,
|
|
566
866
|
createIframe,
|
|
567
867
|
props
|
|
@@ -609,6 +909,8 @@ function RemoteViewHost({
|
|
|
609
909
|
useEffect(() => {
|
|
610
910
|
const host = createIframeUiHost(appUrl, {
|
|
611
911
|
createIframe,
|
|
912
|
+
sourceText,
|
|
913
|
+
getBackendSession,
|
|
612
914
|
onError: (error2) => {
|
|
613
915
|
console.error("TailorKit remote app failed", error2);
|
|
614
916
|
setError(error2);
|
|
@@ -634,7 +936,7 @@ function RemoteViewHost({
|
|
|
634
936
|
hostRef.current = null;
|
|
635
937
|
host.destroy();
|
|
636
938
|
};
|
|
637
|
-
}, [appUrl, createIframe, store]);
|
|
939
|
+
}, [appUrl, createIframe, sourceText, getBackendSession, store]);
|
|
638
940
|
if (status === "error" && error) {
|
|
639
941
|
return createElement("div", null, formatError(error));
|
|
640
942
|
}
|
|
@@ -709,6 +1011,10 @@ var AppView = ({
|
|
|
709
1011
|
const { store, client } = useTailorRootContext("AppView");
|
|
710
1012
|
const { theme, components: wrappedComponents } = client;
|
|
711
1013
|
const reactId = useId2();
|
|
1014
|
+
const getBackendSession = useMemo3(
|
|
1015
|
+
() => createSessionProvider({ baseUrl: store.baseUrl, appId: app.id }),
|
|
1016
|
+
[store.baseUrl, app.id]
|
|
1017
|
+
);
|
|
712
1018
|
const currentView = useSyncExternalStore2(
|
|
713
1019
|
store.views.subscribe,
|
|
714
1020
|
store.views.getSnapshot,
|
|
@@ -734,6 +1040,29 @@ var AppView = ({
|
|
|
734
1040
|
return currentView === null ? void 0 : { slot, ...currentView };
|
|
735
1041
|
}, [context, currentView, view, status, slot]);
|
|
736
1042
|
const meta = useSyncExternalStore2(store.subscribe, store.getMetaSnapshot, store.getMetaSnapshot);
|
|
1043
|
+
const previewSessionId = app.preview?.sessionId ?? "";
|
|
1044
|
+
const appRef = useRef3(app);
|
|
1045
|
+
appRef.current = app;
|
|
1046
|
+
const subscribePreview = useCallback2(
|
|
1047
|
+
(listener) => {
|
|
1048
|
+
const currentApp = appRef.current;
|
|
1049
|
+
return currentApp.preview?.sessionId === previewSessionId ? store.previews.subscribe(currentApp, listener) : () => {
|
|
1050
|
+
};
|
|
1051
|
+
},
|
|
1052
|
+
[store, previewSessionId]
|
|
1053
|
+
);
|
|
1054
|
+
const getPreviewSnapshot = useCallback2(
|
|
1055
|
+
() => store.previews.getSnapshot(previewSessionId),
|
|
1056
|
+
[store, previewSessionId]
|
|
1057
|
+
);
|
|
1058
|
+
const previewSnapshot = useSyncExternalStore2(
|
|
1059
|
+
subscribePreview,
|
|
1060
|
+
getPreviewSnapshot,
|
|
1061
|
+
getPreviewSnapshot
|
|
1062
|
+
);
|
|
1063
|
+
useEffect2(() => {
|
|
1064
|
+
store.previews.updateApp(app);
|
|
1065
|
+
}, [store, app]);
|
|
737
1066
|
const runtimeProps = useMemo3(
|
|
738
1067
|
() => props === void 0 ? void 0 : {
|
|
739
1068
|
...props,
|
|
@@ -750,7 +1079,7 @@ var AppView = ({
|
|
|
750
1079
|
useEffect2(() => {
|
|
751
1080
|
void store.fetchMeta();
|
|
752
1081
|
}, [store]);
|
|
753
|
-
if (props === void 0 || appUrl === null || meta.schema === null) {
|
|
1082
|
+
if (props === void 0 || appUrl === null && previewSnapshot.source === null || meta.schema === null) {
|
|
754
1083
|
return fallback;
|
|
755
1084
|
}
|
|
756
1085
|
const viewId = `tailorkit-view-${reactId.replaceAll(":", "")}`;
|
|
@@ -759,11 +1088,14 @@ var AppView = ({
|
|
|
759
1088
|
/* @__PURE__ */ jsx3(
|
|
760
1089
|
RemoteViewHost,
|
|
761
1090
|
{
|
|
762
|
-
appUrl: appUrl.toString(),
|
|
1091
|
+
appUrl: (appUrl ?? new URL(`preview/${previewSessionId}/client.js`, store.baseUrl)).toString(),
|
|
1092
|
+
sourceText: previewSnapshot.source ?? void 0,
|
|
1093
|
+
getBackendSession,
|
|
763
1094
|
components: wrappedComponents,
|
|
764
1095
|
createIframe,
|
|
765
1096
|
props: runtimeProps
|
|
766
|
-
}
|
|
1097
|
+
},
|
|
1098
|
+
previewSnapshot.revision || appUrl?.toString()
|
|
767
1099
|
)
|
|
768
1100
|
] }) });
|
|
769
1101
|
};
|
|
@@ -775,12 +1107,77 @@ function resolveAppUrl(app, baseUrl, assetsBaseUrl) {
|
|
|
775
1107
|
return null;
|
|
776
1108
|
}
|
|
777
1109
|
return new URL(
|
|
778
|
-
`projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/
|
|
1110
|
+
`projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/client/client.js`,
|
|
779
1111
|
toBaseUrl(assetsBaseUrl)
|
|
780
1112
|
);
|
|
781
1113
|
}
|
|
782
1114
|
|
|
783
|
-
// src/
|
|
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
|
|
784
1181
|
var componentTagPrefix = "tailorkit-";
|
|
785
1182
|
var toComponentTagName = (name) => `${componentTagPrefix}${name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[\s_]+/gu, "-").toLowerCase()}`;
|
|
786
1183
|
function components(_schema, customComponents) {
|
|
@@ -807,32 +1204,51 @@ function createReactTailorKitClient(options) {
|
|
|
807
1204
|
wrappedComponents[toComponentTagName(name)] = TailorKitComponent;
|
|
808
1205
|
}
|
|
809
1206
|
}
|
|
810
|
-
|
|
1207
|
+
const clientConfig = {
|
|
811
1208
|
baseUrl: options.baseUrl,
|
|
812
1209
|
components: wrappedComponents,
|
|
813
|
-
previewSessionId: options.previewSessionId,
|
|
814
1210
|
theme
|
|
815
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;
|
|
816
1232
|
}
|
|
817
1233
|
|
|
818
1234
|
// src/components/root.tsx
|
|
819
|
-
import { useEffect as
|
|
1235
|
+
import { useEffect as useEffect5, useMemo as useMemo6, useState as useState2 } from "react";
|
|
820
1236
|
|
|
821
1237
|
// src/components/render.tsx
|
|
822
|
-
import { cloneElement, createElement as
|
|
1238
|
+
import { cloneElement, createElement as createElement3, isValidElement, useMemo as useMemo5 } from "react";
|
|
823
1239
|
function useRender({
|
|
824
1240
|
defaultTagName,
|
|
825
1241
|
props,
|
|
826
1242
|
render
|
|
827
1243
|
}) {
|
|
828
|
-
return
|
|
1244
|
+
return useMemo5(() => {
|
|
829
1245
|
if (render) {
|
|
830
1246
|
if (!isValidElement(render)) {
|
|
831
1247
|
throw new Error("The render prop must be a valid React element.");
|
|
832
1248
|
}
|
|
833
1249
|
return cloneElement(render, mergeProps(render.props, props));
|
|
834
1250
|
}
|
|
835
|
-
return
|
|
1251
|
+
return createElement3(defaultTagName, props);
|
|
836
1252
|
}, [defaultTagName, props, render]);
|
|
837
1253
|
}
|
|
838
1254
|
function mergeProps(...propSets) {
|
|
@@ -895,18 +1311,17 @@ function isEventHandler(key) {
|
|
|
895
1311
|
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
896
1312
|
function Root({ apps: appsProp, children, render, client, ...props }) {
|
|
897
1313
|
const baseUrl = toBaseUrl(client.baseUrl).toString();
|
|
898
|
-
const [previousStore, setStore] = useState2(
|
|
899
|
-
() => createTailorKitStore(baseUrl, appsProp, client.previewSessionId)
|
|
900
|
-
);
|
|
1314
|
+
const [previousStore, setStore] = useState2(() => createTailorKitStore(baseUrl, appsProp));
|
|
901
1315
|
let store = previousStore;
|
|
902
1316
|
if (previousStore.baseUrl.toString() !== baseUrl) {
|
|
903
|
-
store = createTailorKitStore(baseUrl, appsProp
|
|
1317
|
+
store = createTailorKitStore(baseUrl, appsProp);
|
|
904
1318
|
setStore(store);
|
|
905
1319
|
}
|
|
906
|
-
|
|
1320
|
+
useEffect5(() => {
|
|
907
1321
|
store.setProvidedApps(appsProp);
|
|
908
1322
|
}, [store, appsProp]);
|
|
909
|
-
|
|
1323
|
+
useEffect5(() => () => store.previews.dispose(), [store]);
|
|
1324
|
+
const context = useMemo6(
|
|
910
1325
|
() => ({
|
|
911
1326
|
store,
|
|
912
1327
|
client
|
|
@@ -920,66 +1335,10 @@ function Root({ apps: appsProp, children, render, client, ...props }) {
|
|
|
920
1335
|
}) : children;
|
|
921
1336
|
return /* @__PURE__ */ jsx4(TailorRootContext.Provider, { value: context, children: element });
|
|
922
1337
|
}
|
|
923
|
-
|
|
924
|
-
// src/hooks/use-apps.ts
|
|
925
|
-
import { useCallback as useCallback2, useEffect as useEffect4, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
926
|
-
function useApps() {
|
|
927
|
-
return useAppsStore(useTailorRootContext("useApps").store);
|
|
928
|
-
}
|
|
929
|
-
function useAppsStore(store) {
|
|
930
|
-
const snapshot = useSyncExternalStore3(
|
|
931
|
-
store.subscribe,
|
|
932
|
-
store.getAppsSnapshot,
|
|
933
|
-
store.getAppsSnapshot
|
|
934
|
-
);
|
|
935
|
-
useEffect4(() => {
|
|
936
|
-
void store.fetchApps();
|
|
937
|
-
}, [store]);
|
|
938
|
-
const refetch = useCallback2(() => store.fetchApps({ force: true }), [store]);
|
|
939
|
-
return toUseAppsResult(snapshot, refetch);
|
|
940
|
-
}
|
|
941
|
-
function toUseAppsResult(snapshot, refetch) {
|
|
942
|
-
return {
|
|
943
|
-
data: snapshot.status === "ready" ? snapshot.apps : void 0,
|
|
944
|
-
error: snapshot.error,
|
|
945
|
-
isError: snapshot.status === "error",
|
|
946
|
-
isLoading: snapshot.status === "loading",
|
|
947
|
-
isPending: snapshot.status === "idle" || snapshot.status === "loading",
|
|
948
|
-
isSuccess: snapshot.status === "ready",
|
|
949
|
-
refetch,
|
|
950
|
-
status: snapshot.status
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// src/hooks/use-view.ts
|
|
955
|
-
import { useEffect as useEffect5, useMemo as useMemo6 } from "react";
|
|
956
|
-
function useView(view, options) {
|
|
957
|
-
const { store } = useTailorRootContext("useView");
|
|
958
|
-
const id = useMemo6(() => /* @__PURE__ */ Symbol("tailorkit-current-view"), []);
|
|
959
|
-
const status = options.status ?? "ready";
|
|
960
|
-
const context = "context" in options ? options.context : void 0;
|
|
961
|
-
const contextSnapshot = useStableContext(context);
|
|
962
|
-
useEffect5(
|
|
963
|
-
() => () => {
|
|
964
|
-
store.views.unregister(id);
|
|
965
|
-
},
|
|
966
|
-
[id, store]
|
|
967
|
-
);
|
|
968
|
-
useEffect5(() => {
|
|
969
|
-
store.views.register({
|
|
970
|
-
context: contextSnapshot,
|
|
971
|
-
id,
|
|
972
|
-
view,
|
|
973
|
-
status
|
|
974
|
-
});
|
|
975
|
-
}, [contextSnapshot, id, view, status, store]);
|
|
976
|
-
}
|
|
977
1338
|
export {
|
|
978
1339
|
AppView,
|
|
979
1340
|
Root,
|
|
980
1341
|
components,
|
|
981
1342
|
createTailorKitClient,
|
|
982
|
-
primitives
|
|
983
|
-
useApps,
|
|
984
|
-
useView
|
|
1343
|
+
primitives
|
|
985
1344
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TailorKitApp } from "./tailorkit";
|
|
2
|
+
export interface PreviewSnapshot {
|
|
3
|
+
revision: number;
|
|
4
|
+
source: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function createPreviewManager(baseUrl: URL, onEnded: () => void): {
|
|
7
|
+
getSnapshot: (sessionId: string) => PreviewSnapshot;
|
|
8
|
+
updateApp: (app: TailorKitApp) => void;
|
|
9
|
+
subscribe: (app: TailorKitApp, listener: () => void) => (() => void);
|
|
10
|
+
dispose: () => void;
|
|
11
|
+
};
|
package/dist/remote-view.d.ts
CHANGED
|
@@ -1,11 +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;
|
|
5
|
+
sourceText?: string;
|
|
6
|
+
getBackendSession?: (options: {
|
|
7
|
+
refresh: boolean;
|
|
8
|
+
}) => Promise<Session>;
|
|
4
9
|
components: Record<string, unknown>;
|
|
5
10
|
createIframe?: () => HTMLIFrameElement;
|
|
6
11
|
props?: Record<string, unknown>;
|
|
7
12
|
}
|
|
8
|
-
export declare function RemoteViewHost({ appUrl, components, createIframe, props, }: RemoteViewHostProps): ReactNode;
|
|
13
|
+
export declare function RemoteViewHost({ appUrl, sourceText, getBackendSession, components, createIframe, props, }: RemoteViewHostProps): ReactNode;
|
|
9
14
|
interface RemoteViewProps {
|
|
10
15
|
nodeId: string;
|
|
11
16
|
}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TailorKitSchemaSpecType } from "@tailorkit/core/spec";
|
|
2
|
-
import type { TailorKitApp } from "./
|
|
2
|
+
import type { TailorKitApp } from "./tailorkit";
|
|
3
3
|
export interface TailorKitAppsSnapshot {
|
|
4
4
|
apps: TailorKitApp[];
|
|
5
5
|
error: Error | null;
|
|
@@ -12,7 +12,7 @@ interface TailorKitMetaSnapshot {
|
|
|
12
12
|
status: "error" | "idle" | "loading" | "ready";
|
|
13
13
|
}
|
|
14
14
|
export type TailorKitStore = ReturnType<typeof createTailorKitStore>;
|
|
15
|
-
export declare function createTailorKitStore(baseUrlInput: string | URL, initialApps?: TailorKitApp[]
|
|
15
|
+
export declare function createTailorKitStore(baseUrlInput: string | URL, initialApps?: TailorKitApp[]): {
|
|
16
16
|
baseUrl: URL;
|
|
17
17
|
views: {
|
|
18
18
|
getSnapshot: () => import("@tailorkit/core/views").ActiveView | null;
|
|
@@ -23,11 +23,19 @@ 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);
|
|
33
|
+
previews: {
|
|
34
|
+
getSnapshot: (sessionId: string) => import("./preview-manager").PreviewSnapshot;
|
|
35
|
+
updateApp: (app: TailorKitApp) => void;
|
|
36
|
+
subscribe: (app: TailorKitApp, listener: () => void) => (() => void);
|
|
37
|
+
dispose: () => void;
|
|
38
|
+
};
|
|
31
39
|
};
|
|
32
40
|
export declare function toBaseUrl(value: string | URL): URL;
|
|
33
41
|
export {};
|
|
@@ -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 {
|
|
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>;
|
|
@@ -30,7 +31,9 @@ export interface TailorKitApp {
|
|
|
30
31
|
name?: string;
|
|
31
32
|
preview?: {
|
|
32
33
|
sessionId: string;
|
|
33
|
-
|
|
34
|
+
expiresAt: string;
|
|
35
|
+
websocketUrl: string;
|
|
36
|
+
token: string;
|
|
34
37
|
};
|
|
35
38
|
}
|
|
36
39
|
interface AppViewBaseProps {
|
|
@@ -47,37 +50,26 @@ type AppViewViewProps<TViews extends Record<string, ViewDefinition>, TView exten
|
|
|
47
50
|
view?: never;
|
|
48
51
|
status?: never;
|
|
49
52
|
} | ViewOptions<TViews, TView>;
|
|
50
|
-
export type AppViewProps<TViews extends Record<string, ViewDefinition> =
|
|
51
|
-
[V in
|
|
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 & {
|
|
52
55
|
slot: V;
|
|
53
|
-
} & AppViewViewProps<TViews, Extract<TView, SlotView<V>>>;
|
|
54
|
-
}[
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
export type RegisteredViews = Register extends {
|
|
58
|
-
client: TailorKitInstance<infer S>;
|
|
59
|
-
} ? S : Record<`/${string}`, ViewDefinition>;
|
|
60
|
-
type RegisteredSlotMap = Register extends {
|
|
61
|
-
client: {
|
|
62
|
-
readonly $slots?: infer V;
|
|
63
|
-
};
|
|
64
|
-
} ? V : SlotDefinitions;
|
|
65
|
-
export type RegisteredSlots = keyof RegisteredSlotMap & string;
|
|
66
|
-
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 {
|
|
67
59
|
views: readonly (infer P)[];
|
|
68
60
|
} ? Extract<P, string> : never;
|
|
69
|
-
export interface
|
|
70
|
-
readonly $slots?: TSlots;
|
|
71
|
-
readonly $views?: TViews;
|
|
61
|
+
export interface TailorKitClientConfig {
|
|
72
62
|
readonly baseUrl: string | URL;
|
|
73
|
-
/**
|
|
74
|
-
* A host-selected, scope-authorized local-development preview session.
|
|
75
|
-
* The host handler resolves this server-side before returning app URLs.
|
|
76
|
-
*/
|
|
77
|
-
readonly previewSessionId?: string;
|
|
78
63
|
readonly components: Record<string, unknown>;
|
|
79
64
|
readonly theme: TailorKitTheme;
|
|
80
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
|
+
}
|
|
81
73
|
type PrimitiveRenderers = typeof primitives;
|
|
82
74
|
type CustomComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
|
|
83
75
|
[TName in Exclude<keyof TComponents, keyof PrimitiveRenderers>]?: ComponentRenderer<TComponents[TName]>;
|
|
@@ -87,7 +79,7 @@ interface TailorKitServerShape {
|
|
|
87
79
|
$internal: {
|
|
88
80
|
schema: {
|
|
89
81
|
components: Record<string, unknown>;
|
|
90
|
-
|
|
82
|
+
contexts: Record<string, unknown>;
|
|
91
83
|
};
|
|
92
84
|
};
|
|
93
85
|
}
|
|
@@ -95,16 +87,24 @@ type ServerComponentMap<TTailor extends TailorKitServerShape> = TTailor["$intern
|
|
|
95
87
|
type ServerComponents<TTailor extends TailorKitServerShape> = {
|
|
96
88
|
[TName in keyof ServerComponentMap<TTailor>]: ServerComponentMap<TTailor>[TName] extends AnyComponentDefinition ? ServerComponentMap<TTailor>[TName] : never;
|
|
97
89
|
};
|
|
98
|
-
type ServerViewMap<TTailor extends TailorKitServerShape> = TTailor["$internal"]["schema"]["
|
|
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;
|
|
99
100
|
type ServerViews<TTailor extends TailorKitServerShape> = {
|
|
100
101
|
[TName in keyof ServerViewMap<TTailor>]: ServerViewMap<TTailor>[TName] extends ViewDefinition ? ServerViewMap<TTailor>[TName] : never;
|
|
101
102
|
};
|
|
102
103
|
export declare function createTailorKitClient<TTailor extends TailorKitServerShape>(options: {
|
|
103
104
|
baseUrl: string | URL;
|
|
104
|
-
previewSessionId?: string;
|
|
105
105
|
components?: CompleteComponentRenderers<ServerComponents<TTailor>>;
|
|
106
106
|
theme?: TailorKitTheme;
|
|
107
107
|
}): TailorKitInstance<ServerViews<TTailor>, TTailor extends {
|
|
108
108
|
readonly $slots?: infer V extends SlotDefinitions;
|
|
109
|
-
} ? V : SlotDefinitions
|
|
109
|
+
} ? V : SlotDefinitions, ServerScopeNames<TTailor>>;
|
|
110
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.
|
|
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/
|
|
7
|
+
"url": "git+https://github.com/tailorkit-labs/TailorKit.git",
|
|
8
8
|
"directory": "packages/react"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
@@ -23,20 +23,21 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@floating-ui/react-dom": "^2.1.9",
|
|
25
25
|
"@standard-schema/spec": "^1.1.0",
|
|
26
|
-
"@tailorkit/
|
|
27
|
-
"@tailorkit/
|
|
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"
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"@tailorkit/config": "0.0.0",
|
|
31
33
|
"@testing-library/react": "^16.3.3",
|
|
32
|
-
"@types/node": "26.
|
|
34
|
+
"@types/node": "26.6.3",
|
|
33
35
|
"@types/react": "^19.3.0",
|
|
34
36
|
"happy-dom": "^20.14.5",
|
|
35
37
|
"react": "^19.3.0",
|
|
36
38
|
"react-dom": "^19.3.0",
|
|
37
39
|
"tsup": "^8.5.1",
|
|
38
|
-
"typescript": "^7.0.2"
|
|
39
|
-
"vitest": "^5.0.0"
|
|
40
|
+
"typescript": "^7.0.2"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
43
|
"react": ">=19.2.6"
|
|
@@ -44,6 +45,6 @@
|
|
|
44
45
|
"scripts": {
|
|
45
46
|
"build": "tsup src/index.ts --format esm --target node20 --clean && tsc -p tsconfig.build.json",
|
|
46
47
|
"check-types": "tsc --noEmit",
|
|
47
|
-
"test": "
|
|
48
|
+
"test": "vp test run"
|
|
48
49
|
}
|
|
49
50
|
}
|