@tailorkit/react 0.1.0-beta.3 → 0.1.0-beta.33
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/LICENSE.md +74 -0
- package/README.md +3 -0
- package/dist/components/context.d.ts +7 -10
- package/dist/components/index.d.ts +1 -2
- package/dist/components/render-slot.d.ts +9 -0
- package/dist/hooks/use-apps.d.ts +6 -13
- package/dist/hooks/use-query-result.d.ts +9 -0
- package/dist/hooks/use-view-context.d.ts +6 -0
- package/dist/hooks/use-views.d.ts +7 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +362 -839
- package/dist/primitives.d.ts +11 -21
- package/dist/remote-view.d.ts +12 -6
- package/dist/tailorkit.d.ts +45 -0
- package/package.json +42 -24
- package/LICENSE +0 -674
- package/dist/components/context.d.ts.map +0 -1
- package/dist/components/index.d.ts.map +0 -1
- package/dist/components/render.d.ts +0 -20
- package/dist/components/render.d.ts.map +0 -1
- package/dist/components/root.d.ts +0 -11
- package/dist/components/root.d.ts.map +0 -1
- package/dist/hooks/use-apps.d.ts.map +0 -1
- package/dist/hooks/use-current-screen.d.ts +0 -24
- package/dist/hooks/use-current-screen.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/node-store.d.ts +0 -17
- package/dist/node-store.d.ts.map +0 -1
- package/dist/primitives.d.ts.map +0 -1
- package/dist/remote-context.d.ts +0 -9
- package/dist/remote-context.d.ts.map +0 -1
- package/dist/remote-view.d.ts.map +0 -1
- package/dist/tailor-kit.d.ts +0 -117
- package/dist/tailor-kit.d.ts.map +0 -1
- package/src/__tests__/node-store.test.ts +0 -206
- package/src/__tests__/primitives.test.tsx +0 -85
- package/src/__tests__/tailor-kit.test.tsx +0 -424
- package/src/__tests__/tailor-kit.types.tsx +0 -151
- package/src/components/context.ts +0 -21
- package/src/components/index.ts +0 -1
- package/src/components/render.tsx +0 -116
- package/src/components/root.tsx +0 -37
- package/src/hooks/use-apps.ts +0 -47
- package/src/hooks/use-current-screen.ts +0 -81
- package/src/index.ts +0 -8
- package/src/node-store.ts +0 -131
- package/src/primitives.tsx +0 -261
- package/src/remote-context.ts +0 -11
- package/src/remote-view.tsx +0 -232
- package/src/tailor-kit.tsx +0 -501
package/src/tailor-kit.tsx
DELETED
|
@@ -1,501 +0,0 @@
|
|
|
1
|
-
import { useEffect, useId, useMemo, useSyncExternalStore } from "react";
|
|
2
|
-
import type { ReactNode } from "react";
|
|
3
|
-
import type { TailorKitSchemaSpecType } from "@tailorkit/core/spec";
|
|
4
|
-
import type {
|
|
5
|
-
TailorKitTheme,
|
|
6
|
-
CallbackMap,
|
|
7
|
-
ComponentDefinition,
|
|
8
|
-
ComponentProps,
|
|
9
|
-
ComponentSlots,
|
|
10
|
-
Schema,
|
|
11
|
-
ScreenDefinition,
|
|
12
|
-
TailorKitSchema,
|
|
13
|
-
} from "@tailorkit/core/schema";
|
|
14
|
-
import type { primitives } from "./primitives";
|
|
15
|
-
import { Root } from "./components";
|
|
16
|
-
import { createUseApps } from "./hooks/use-apps";
|
|
17
|
-
import type { UseAppsResult } from "./hooks/use-apps";
|
|
18
|
-
import { createUseCurrentScreen } from "./hooks/use-current-screen";
|
|
19
|
-
import type { CurrentScreenOptions, ScreenName } from "./hooks/use-current-screen";
|
|
20
|
-
import { buildThemeCss, PrimitiveThemeContext } from "./primitives";
|
|
21
|
-
import { RemoteViewHost } from "./remote-view";
|
|
22
|
-
|
|
23
|
-
type ReactComponentSlots<TComponent> = {
|
|
24
|
-
[TSlot in keyof ComponentSlots<TComponent>]: ReactNode;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
type AnyComponentDefinition = ComponentDefinition<
|
|
28
|
-
Schema | undefined,
|
|
29
|
-
CallbackMap,
|
|
30
|
-
readonly string[] | undefined
|
|
31
|
-
>;
|
|
32
|
-
|
|
33
|
-
type ComponentRenderer<TComponent extends AnyComponentDefinition> = (args: {
|
|
34
|
-
props: ComponentProps<TComponent>;
|
|
35
|
-
slots: ReactComponentSlots<TComponent>;
|
|
36
|
-
}) => ReactNode;
|
|
37
|
-
|
|
38
|
-
type ComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
|
|
39
|
-
[TName in keyof TComponents]?: ComponentRenderer<TComponents[TName]>;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
type CompleteComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
|
|
43
|
-
[TName in keyof TComponents]-?: ComponentRenderer<TComponents[TName]>;
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export interface TailorKitApp {
|
|
47
|
-
clientPath?: string;
|
|
48
|
-
description?: string;
|
|
49
|
-
id: string;
|
|
50
|
-
projectId?: string;
|
|
51
|
-
currentDeployment?: {
|
|
52
|
-
id: string;
|
|
53
|
-
} | null;
|
|
54
|
-
name?: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface TailorKitAppsSnapshot {
|
|
58
|
-
apps: TailorKitApp[];
|
|
59
|
-
error: Error | null;
|
|
60
|
-
status: "error" | "idle" | "loading" | "ready";
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
interface TailorKitMetaSnapshot {
|
|
64
|
-
assetsBaseUrl: string | null;
|
|
65
|
-
error: Error | null;
|
|
66
|
-
schema: TailorKitSchemaSpecType | null;
|
|
67
|
-
status: "error" | "idle" | "loading" | "ready";
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export interface CurrentScreenEntry {
|
|
71
|
-
context: unknown;
|
|
72
|
-
id: symbol;
|
|
73
|
-
order: number;
|
|
74
|
-
screen: string;
|
|
75
|
-
status: "error" | "loading" | "ready";
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
interface AppViewBaseProps {
|
|
79
|
-
app: TailorKitApp;
|
|
80
|
-
createIframe?: () => HTMLIFrameElement;
|
|
81
|
-
fallback?: ReactNode;
|
|
82
|
-
runtimeUrl?: string | URL;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
type AppViewScreenProps<
|
|
86
|
-
TScreens extends Record<string, ScreenDefinition>,
|
|
87
|
-
TScreen extends ScreenName<TScreens> = ScreenName<TScreens>,
|
|
88
|
-
> = [ScreenName<TScreens>] extends [never]
|
|
89
|
-
? {
|
|
90
|
-
context?: never;
|
|
91
|
-
screen?: never;
|
|
92
|
-
status?: never;
|
|
93
|
-
}
|
|
94
|
-
:
|
|
95
|
-
| {
|
|
96
|
-
context?: never;
|
|
97
|
-
screen?: never;
|
|
98
|
-
status?: never;
|
|
99
|
-
}
|
|
100
|
-
| CurrentScreenOptions<TScreens, TScreen>;
|
|
101
|
-
|
|
102
|
-
export type AppViewProps<
|
|
103
|
-
TScreens extends Record<string, ScreenDefinition>,
|
|
104
|
-
TScreen extends ScreenName<TScreens> = ScreenName<TScreens>,
|
|
105
|
-
> = AppViewBaseProps & AppViewScreenProps<TScreens, TScreen>;
|
|
106
|
-
|
|
107
|
-
const componentTagPrefix = "tailorkit-";
|
|
108
|
-
|
|
109
|
-
const toComponentTagName = (name: string): string =>
|
|
110
|
-
`${componentTagPrefix}${name
|
|
111
|
-
.replaceAll(/([a-z0-9])([A-Z])/gu, "$1-$2")
|
|
112
|
-
.replaceAll(/[\s_]+/gu, "-")
|
|
113
|
-
.toLowerCase()}`;
|
|
114
|
-
|
|
115
|
-
export interface TailorKitInstance<
|
|
116
|
-
TScreens extends Record<string, ScreenDefinition> = Record<string, never>,
|
|
117
|
-
> {
|
|
118
|
-
AppView: (props: AppViewProps<TScreens>) => ReactNode;
|
|
119
|
-
Root: (props: Omit<Parameters<typeof Root>[0], "tailor">) => ReactNode;
|
|
120
|
-
getApp: (id: string) => TailorKitApp | undefined;
|
|
121
|
-
getApps: () => TailorKitApp[];
|
|
122
|
-
useApps: () => UseAppsResult;
|
|
123
|
-
useCurrentScreen: <TScreen extends ScreenName<TScreens>>(
|
|
124
|
-
options: CurrentScreenOptions<TScreens, TScreen>,
|
|
125
|
-
) => void;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
type PrimitiveRenderers = typeof primitives;
|
|
129
|
-
type CustomComponentRenderers<TComponents extends Record<string, AnyComponentDefinition>> = {
|
|
130
|
-
[TName in Exclude<keyof TComponents, keyof PrimitiveRenderers>]?: ComponentRenderer<
|
|
131
|
-
TComponents[TName]
|
|
132
|
-
>;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
export function components<TComponents extends Record<string, AnyComponentDefinition>>(
|
|
136
|
-
_schema: TailorKitSchema<TComponents, Record<string, ScreenDefinition>>,
|
|
137
|
-
customComponents: CustomComponentRenderers<TComponents>,
|
|
138
|
-
): ComponentRenderers<TComponents> {
|
|
139
|
-
return customComponents as ComponentRenderers<TComponents>;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
interface TailorKitServerShape {
|
|
143
|
-
$internal: {
|
|
144
|
-
schema: {
|
|
145
|
-
components: Record<string, unknown>;
|
|
146
|
-
screens: Record<string, unknown>;
|
|
147
|
-
};
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
type ServerComponentMap<TTailor extends TailorKitServerShape> =
|
|
152
|
-
TTailor["$internal"]["schema"]["components"];
|
|
153
|
-
|
|
154
|
-
type ServerComponents<TTailor extends TailorKitServerShape> = {
|
|
155
|
-
[
|
|
156
|
-
TName in keyof ServerComponentMap<TTailor>
|
|
157
|
-
]: ServerComponentMap<TTailor>[TName] extends AnyComponentDefinition
|
|
158
|
-
? ServerComponentMap<TTailor>[TName]
|
|
159
|
-
: never;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
type ServerScreenMap<TTailor extends TailorKitServerShape> =
|
|
163
|
-
TTailor["$internal"]["schema"]["screens"];
|
|
164
|
-
|
|
165
|
-
type ServerScreens<TTailor extends TailorKitServerShape> = {
|
|
166
|
-
[
|
|
167
|
-
TName in keyof ServerScreenMap<TTailor>
|
|
168
|
-
]: ServerScreenMap<TTailor>[TName] extends ScreenDefinition
|
|
169
|
-
? ServerScreenMap<TTailor>[TName]
|
|
170
|
-
: never;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
export function createTailorKitClient<TTailor extends TailorKitServerShape>(options: {
|
|
174
|
-
baseUrl: string | URL;
|
|
175
|
-
components?: CompleteComponentRenderers<ServerComponents<TTailor>>;
|
|
176
|
-
theme?: TailorKitTheme;
|
|
177
|
-
}): TailorKitInstance<ServerScreens<TTailor>> {
|
|
178
|
-
return createReactTailorKitClient<ServerComponents<TTailor>, ServerScreens<TTailor>>(options);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function createReactTailorKitClient<
|
|
182
|
-
TComponents extends Record<string, AnyComponentDefinition>,
|
|
183
|
-
TScreens extends Record<string, ScreenDefinition> = Record<string, never>,
|
|
184
|
-
>(options: {
|
|
185
|
-
baseUrl: string | URL;
|
|
186
|
-
components?: ComponentRenderers<TComponents>;
|
|
187
|
-
theme?: TailorKitTheme;
|
|
188
|
-
}): TailorKitInstance<TScreens> {
|
|
189
|
-
const wrappedComponents: Record<string, unknown> = {};
|
|
190
|
-
const store = createTailorKitStore(options.baseUrl);
|
|
191
|
-
const theme = options.theme ?? {};
|
|
192
|
-
|
|
193
|
-
for (const [name, renderer] of Object.entries(options.components ?? {})) {
|
|
194
|
-
if (renderer) {
|
|
195
|
-
const TailorKitComponent = function TailorKitComponent({
|
|
196
|
-
children,
|
|
197
|
-
...props
|
|
198
|
-
}: Record<string, unknown> & { children?: ReactNode }) {
|
|
199
|
-
return (renderer as ComponentRenderer<ComponentDefinition>)({
|
|
200
|
-
props: props as ComponentProps<ComponentDefinition>,
|
|
201
|
-
slots: { default: children } as ReactComponentSlots<ComponentDefinition>,
|
|
202
|
-
});
|
|
203
|
-
};
|
|
204
|
-
wrappedComponents[name] = TailorKitComponent;
|
|
205
|
-
wrappedComponents[toComponentTagName(name)] = TailorKitComponent;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const useApps = createUseApps(store);
|
|
210
|
-
const useCurrentScreen = createUseCurrentScreen<TScreens>(store);
|
|
211
|
-
|
|
212
|
-
const AppView = ({
|
|
213
|
-
app,
|
|
214
|
-
createIframe,
|
|
215
|
-
fallback = null,
|
|
216
|
-
runtimeUrl,
|
|
217
|
-
...screenProps
|
|
218
|
-
}: AppViewProps<TScreens>): ReactNode => {
|
|
219
|
-
const reactId = useId();
|
|
220
|
-
const currentScreen = useSyncExternalStore(
|
|
221
|
-
store.subscribe,
|
|
222
|
-
store.getCurrentScreen,
|
|
223
|
-
store.getCurrentScreen,
|
|
224
|
-
);
|
|
225
|
-
const screen = (screenProps as { screen?: string }).screen;
|
|
226
|
-
const context = (screenProps as { context?: unknown }).context;
|
|
227
|
-
const status = (screenProps as { status?: "error" | "loading" | "ready" }).status ?? "ready";
|
|
228
|
-
const props = useMemo(() => {
|
|
229
|
-
if (screen !== undefined) {
|
|
230
|
-
return {
|
|
231
|
-
screen: {
|
|
232
|
-
context,
|
|
233
|
-
path: screen,
|
|
234
|
-
status,
|
|
235
|
-
},
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return currentScreen === null
|
|
240
|
-
? undefined
|
|
241
|
-
: {
|
|
242
|
-
screen: {
|
|
243
|
-
context: currentScreen.context,
|
|
244
|
-
path: currentScreen.screen,
|
|
245
|
-
status: currentScreen.status,
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
}, [context, currentScreen, screen, status]);
|
|
249
|
-
const meta = useSyncExternalStore(
|
|
250
|
-
store.subscribe,
|
|
251
|
-
store.getMetaSnapshot,
|
|
252
|
-
store.getMetaSnapshot,
|
|
253
|
-
);
|
|
254
|
-
const assetsBaseUrl = app.clientPath ? null : meta.assetsBaseUrl;
|
|
255
|
-
const appUrl = useMemo(
|
|
256
|
-
() => resolveAppUrl(app, store.baseUrl, assetsBaseUrl),
|
|
257
|
-
[app, assetsBaseUrl],
|
|
258
|
-
);
|
|
259
|
-
|
|
260
|
-
useEffect(() => {
|
|
261
|
-
void store.fetchMeta();
|
|
262
|
-
}, []);
|
|
263
|
-
|
|
264
|
-
if (props === undefined || appUrl === null) {
|
|
265
|
-
return fallback;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
const screenId = `tailorkit-screen-${reactId.replaceAll(":", "")}`;
|
|
269
|
-
|
|
270
|
-
return (
|
|
271
|
-
<PrimitiveThemeContext.Provider value={{ screenId, theme }}>
|
|
272
|
-
<div data-tailorkit-screen={screenId}>
|
|
273
|
-
<style data-tailorkit-theme-style={screenId}>{buildThemeCss(screenId, theme)}</style>
|
|
274
|
-
<RemoteViewHost
|
|
275
|
-
appUrl={appUrl}
|
|
276
|
-
components={wrappedComponents}
|
|
277
|
-
createIframe={createIframe}
|
|
278
|
-
props={props}
|
|
279
|
-
runtimeUrl={runtimeUrl}
|
|
280
|
-
/>
|
|
281
|
-
</div>
|
|
282
|
-
</PrimitiveThemeContext.Provider>
|
|
283
|
-
);
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const client = {
|
|
287
|
-
AppView,
|
|
288
|
-
Root: (props: Omit<Parameters<typeof Root>[0], "tailor">) => (
|
|
289
|
-
<Root {...props} tailor={client} />
|
|
290
|
-
),
|
|
291
|
-
getApp: store.getApp,
|
|
292
|
-
getApps: store.getApps,
|
|
293
|
-
useApps,
|
|
294
|
-
useCurrentScreen,
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
return {
|
|
298
|
-
...client,
|
|
299
|
-
} as TailorKitInstance<TScreens>;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export type TailorKitStore = ReturnType<typeof createTailorKitStore>;
|
|
303
|
-
|
|
304
|
-
function createTailorKitStore(baseUrlInput: string | URL) {
|
|
305
|
-
const baseUrl = toBaseUrl(baseUrlInput);
|
|
306
|
-
const listeners = new Set<() => void>();
|
|
307
|
-
const screens = new Map<symbol, CurrentScreenEntry>();
|
|
308
|
-
let appsSnapshot: TailorKitAppsSnapshot = {
|
|
309
|
-
apps: [],
|
|
310
|
-
error: null,
|
|
311
|
-
status: "idle",
|
|
312
|
-
};
|
|
313
|
-
let metaSnapshot: TailorKitMetaSnapshot = {
|
|
314
|
-
assetsBaseUrl: null,
|
|
315
|
-
error: null,
|
|
316
|
-
schema: null,
|
|
317
|
-
status: "idle",
|
|
318
|
-
};
|
|
319
|
-
let currentScreen: CurrentScreenEntry | null = null;
|
|
320
|
-
let fetchAppsPromise: Promise<void> | null = null;
|
|
321
|
-
let fetchMetaPromise: Promise<void> | null = null;
|
|
322
|
-
let fetchAppsRequestId = 0;
|
|
323
|
-
let nextOrder = 0;
|
|
324
|
-
|
|
325
|
-
const emit = (): void => {
|
|
326
|
-
for (const listener of listeners) {
|
|
327
|
-
listener();
|
|
328
|
-
}
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
const selectCurrentScreen = (): void => {
|
|
332
|
-
let selected: CurrentScreenEntry | null = null;
|
|
333
|
-
|
|
334
|
-
for (const screen of screens.values()) {
|
|
335
|
-
const depth = getScreenDepth(screen.screen);
|
|
336
|
-
const selectedDepth = selected === null ? -1 : getScreenDepth(selected.screen);
|
|
337
|
-
|
|
338
|
-
if (selected === null || depth > selectedDepth) {
|
|
339
|
-
selected = screen;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (selected !== null && depth === selectedDepth && screen.order > selected.order) {
|
|
343
|
-
selected = screen;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const deepestScreens =
|
|
348
|
-
selected === null
|
|
349
|
-
? []
|
|
350
|
-
: [...screens.values()].filter(
|
|
351
|
-
(screen) => getScreenDepth(screen.screen) === getScreenDepth(selected.screen),
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
if (typeof console !== "undefined" && selected !== null && deepestScreens.length > 1) {
|
|
355
|
-
const screenNames = deepestScreens.map((screen) => `"${screen.screen}"`).join(", ");
|
|
356
|
-
console.warn(
|
|
357
|
-
`TailorKit found multiple active screens at the same hierarchy depth: ${screenNames}. TailorKit selected "${selected.screen}" by mount order. Only one route at a hierarchy depth should call useCurrentScreen.`,
|
|
358
|
-
);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
currentScreen = selected;
|
|
362
|
-
};
|
|
363
|
-
|
|
364
|
-
return {
|
|
365
|
-
baseUrl,
|
|
366
|
-
fetchApps: (options: { force?: boolean } = {}): Promise<void> => {
|
|
367
|
-
if (fetchAppsPromise && !options.force) {
|
|
368
|
-
return fetchAppsPromise;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
appsSnapshot = { ...appsSnapshot, status: "loading" };
|
|
372
|
-
emit();
|
|
373
|
-
|
|
374
|
-
const requestId = ++fetchAppsRequestId;
|
|
375
|
-
|
|
376
|
-
fetchAppsPromise = fetch(new URL("apps", baseUrl))
|
|
377
|
-
.then(async (response) => {
|
|
378
|
-
if (!response.ok) {
|
|
379
|
-
throw new Error(`Unable to fetch TailorKit apps from ${baseUrl.toString()}.`);
|
|
380
|
-
}
|
|
381
|
-
const apps = (await response.json()) as TailorKitApp[];
|
|
382
|
-
if (requestId !== fetchAppsRequestId) {
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
appsSnapshot = {
|
|
386
|
-
apps,
|
|
387
|
-
error: null,
|
|
388
|
-
status: "ready",
|
|
389
|
-
};
|
|
390
|
-
emit();
|
|
391
|
-
})
|
|
392
|
-
.catch((error: unknown) => {
|
|
393
|
-
if (requestId !== fetchAppsRequestId) {
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
appsSnapshot = {
|
|
397
|
-
...appsSnapshot,
|
|
398
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
399
|
-
status: "error",
|
|
400
|
-
};
|
|
401
|
-
emit();
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
return fetchAppsPromise;
|
|
405
|
-
},
|
|
406
|
-
fetchMeta: (): Promise<void> => {
|
|
407
|
-
if (fetchMetaPromise) {
|
|
408
|
-
return fetchMetaPromise;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
metaSnapshot = { ...metaSnapshot, status: "loading" };
|
|
412
|
-
emit();
|
|
413
|
-
|
|
414
|
-
fetchMetaPromise = fetch(new URL("meta", baseUrl))
|
|
415
|
-
.then(async (response) => {
|
|
416
|
-
if (!response.ok) {
|
|
417
|
-
throw new Error(`Unable to fetch TailorKit metadata from ${baseUrl.toString()}.`);
|
|
418
|
-
}
|
|
419
|
-
const meta = (await response.json()) as {
|
|
420
|
-
assetsBaseUrl?: string | null;
|
|
421
|
-
schema: TailorKitSchemaSpecType;
|
|
422
|
-
};
|
|
423
|
-
metaSnapshot = {
|
|
424
|
-
assetsBaseUrl: meta.assetsBaseUrl ?? null,
|
|
425
|
-
error: null,
|
|
426
|
-
schema: meta.schema,
|
|
427
|
-
status: "ready",
|
|
428
|
-
};
|
|
429
|
-
emit();
|
|
430
|
-
})
|
|
431
|
-
.catch((error: unknown) => {
|
|
432
|
-
metaSnapshot = {
|
|
433
|
-
...metaSnapshot,
|
|
434
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
435
|
-
status: "error",
|
|
436
|
-
};
|
|
437
|
-
emit();
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
return fetchMetaPromise;
|
|
441
|
-
},
|
|
442
|
-
getApp: (id: string): TailorKitApp | undefined =>
|
|
443
|
-
appsSnapshot.apps.find((app) => app.id === id),
|
|
444
|
-
getApps: (): TailorKitApp[] => appsSnapshot.apps,
|
|
445
|
-
getAppsSnapshot: (): TailorKitAppsSnapshot => appsSnapshot,
|
|
446
|
-
getCurrentScreen: (): CurrentScreenEntry | null => currentScreen,
|
|
447
|
-
getMetaSnapshot: (): TailorKitMetaSnapshot => metaSnapshot,
|
|
448
|
-
registerScreen: (entry: Omit<CurrentScreenEntry, "order">): void => {
|
|
449
|
-
const existing = screens.get(entry.id);
|
|
450
|
-
screens.set(entry.id, {
|
|
451
|
-
...entry,
|
|
452
|
-
order: existing?.order ?? nextOrder,
|
|
453
|
-
});
|
|
454
|
-
if (!existing) {
|
|
455
|
-
nextOrder += 1;
|
|
456
|
-
}
|
|
457
|
-
selectCurrentScreen();
|
|
458
|
-
emit();
|
|
459
|
-
},
|
|
460
|
-
subscribe: (listener: () => void): (() => void) => {
|
|
461
|
-
listeners.add(listener);
|
|
462
|
-
return () => {
|
|
463
|
-
listeners.delete(listener);
|
|
464
|
-
};
|
|
465
|
-
},
|
|
466
|
-
unregisterScreen: (id: symbol): void => {
|
|
467
|
-
screens.delete(id);
|
|
468
|
-
selectCurrentScreen();
|
|
469
|
-
emit();
|
|
470
|
-
},
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
function getScreenDepth(screen: string): number {
|
|
475
|
-
return screen === "/" ? 0 : screen.split("/").filter(Boolean).length;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
export type { CurrentScreenOptions } from "./hooks/use-current-screen";
|
|
479
|
-
|
|
480
|
-
function resolveAppUrl(app: TailorKitApp, baseUrl: URL, assetsBaseUrl: string | null): URL | null {
|
|
481
|
-
if (app.clientPath) {
|
|
482
|
-
return new URL(app.clientPath, baseUrl);
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (!assetsBaseUrl || !app.projectId || !app.currentDeployment?.id) {
|
|
486
|
-
return null;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return new URL(
|
|
490
|
-
`projects/${app.projectId}/apps/${app.id}/deployments/${app.currentDeployment.id}/files/client.js`,
|
|
491
|
-
toBaseUrl(assetsBaseUrl),
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function toBaseUrl(value: string | URL): URL {
|
|
496
|
-
const url = value instanceof URL ? new URL(value) : new URL(value);
|
|
497
|
-
if (!url.pathname.endsWith("/")) {
|
|
498
|
-
url.pathname = `${url.pathname}/`;
|
|
499
|
-
}
|
|
500
|
-
return url;
|
|
501
|
-
}
|