@tailorkit/app 0.1.0-beta.23 → 0.1.0-beta.25

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.
@@ -0,0 +1,437 @@
1
+ import {
2
+ createClient,
3
+ reference
4
+ } from "./chunk-LIPODQ7B.js";
5
+ import {
6
+ appError
7
+ } from "./chunk-IJBQ77AH.js";
8
+
9
+ // src/views.ts
10
+ import { createContext, h } from "preact";
11
+ import { useContext } from "preact/hooks";
12
+ function defineView(options) {
13
+ if (!options.slot || !options.view) {
14
+ throw new Error(
15
+ "Views without a slot or path must be compiled from a .view.tsx file by the TailorKit app build."
16
+ );
17
+ }
18
+ const path = options.view;
19
+ const Context2 = createContext(
20
+ null
21
+ );
22
+ const instances = options.instances;
23
+ if (instances && !instances.resolver) {
24
+ throw new Error("View instance resolvers must be compiled with the TailorKit app build.");
25
+ }
26
+ const View = (props) => {
27
+ if (props.status !== "ready") {
28
+ return null;
29
+ }
30
+ if (instances && !props.instance) {
31
+ throw new Error(
32
+ `View "${path}" requires a selected instance. Pass instanceKey to RenderSlot or instance to RenderSlot.Controlled.`
33
+ );
34
+ }
35
+ return h(
36
+ Context2.Provider,
37
+ { value: { context: props.context, instance: props.instance } },
38
+ h(options.component, { key: props.instance?.key })
39
+ );
40
+ };
41
+ return {
42
+ component: View,
43
+ path,
44
+ slot: options.slot,
45
+ ...instances?.resolver ? { instances: { resolver: instances.resolver } } : {},
46
+ useContext: () => {
47
+ const context = useContext(Context2);
48
+ if (context === null) {
49
+ throw new Error(`View context is only available while rendering "${path}".`);
50
+ }
51
+ return context.context;
52
+ },
53
+ useInstance: () => {
54
+ const value = useContext(Context2);
55
+ if (!value?.instance)
56
+ throw new Error(
57
+ `View instance is only available while rendering an instance of "${path}".`
58
+ );
59
+ return value.instance;
60
+ }
61
+ };
62
+ }
63
+ var componentTagPrefix = "tailorkit-";
64
+ var toComponentTagName = (name) => `${componentTagPrefix}${name.replaceAll(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[\s_]+/gu, "-").toLowerCase()}`;
65
+ var toCallbackEventName = (name) => `tailorkitcallback${name.replaceAll(/[^A-Za-z0-9_$]/gu, "").toLowerCase()}`;
66
+ var toEventProp = (event) => `on${event}`;
67
+ var createRemoteComponent = (name, options = {}) => {
68
+ const tagName = toComponentTagName(name);
69
+ const callbacks = options.callbacks ?? {};
70
+ return function RemoteComponent({ children, ...props }) {
71
+ const nextProps = { ...props };
72
+ const callbackMap = {};
73
+ for (const [key, inputCount] of Object.entries(callbacks)) {
74
+ const callback = nextProps[key];
75
+ Reflect.deleteProperty(nextProps, key);
76
+ if (typeof callback !== "function") {
77
+ continue;
78
+ }
79
+ const eventName = toCallbackEventName(key);
80
+ callbackMap[eventName] = { callback: key, inputCount };
81
+ nextProps[toEventProp(eventName)] = (event) => {
82
+ callback(...(event.detail ?? []).slice(0, inputCount));
83
+ };
84
+ }
85
+ const serializedProps = Object.fromEntries(
86
+ Object.entries(nextProps).filter(([, value]) => typeof value !== "function")
87
+ );
88
+ nextProps["data-tailorkit-props"] = JSON.stringify(serializedProps);
89
+ if (Object.keys(callbackMap).length > 0) {
90
+ nextProps["data-tailorkit-callbacks"] = JSON.stringify(callbackMap);
91
+ }
92
+ return h(tagName, nextProps, options.children ? children : void 0);
93
+ };
94
+ };
95
+
96
+ // src/preact.ts
97
+ import { atom as atom2, computed } from "nanostores";
98
+ import { useStore } from "@nanostores/preact";
99
+ import { createContext as createContext2, h as h2 } from "preact";
100
+ import { useCallback, useContext as useContext2, useEffect, useLayoutEffect, useMemo, useRef } from "preact/hooks";
101
+
102
+ // src/client/query-store.ts
103
+ import { atom } from "nanostores";
104
+ var pendingQuery = { data: void 0, error: null, status: "pending" };
105
+ function serializeInput(input) {
106
+ return JSON.stringify({ input, defined: input !== void 0 }, (_key, value) => {
107
+ if (value && typeof value === "object" && !Array.isArray(value)) {
108
+ return Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)));
109
+ }
110
+ return value;
111
+ });
112
+ }
113
+ var QueryStore = class {
114
+ state = atom(/* @__PURE__ */ new Map());
115
+ entries = /* @__PURE__ */ new Map();
116
+ disposed = false;
117
+ client;
118
+ ownsClient;
119
+ errorHandler;
120
+ constructor(client, ownsClient, errorHandler = () => {
121
+ }) {
122
+ this.client = client;
123
+ this.ownsClient = ownsClient;
124
+ this.errorHandler = errorHandler;
125
+ }
126
+ watch(key, ref, input) {
127
+ let entry = this.entries.get(key);
128
+ const first = !entry;
129
+ if (!entry) {
130
+ entry = { observers: 0 };
131
+ this.entries.set(key, entry);
132
+ }
133
+ const current = entry;
134
+ current.observers += 1;
135
+ const publish = (state) => {
136
+ if (!this.disposed && this.entries.get(key) === current) {
137
+ this.state.set(new Map(this.state.get()).set(key, state));
138
+ }
139
+ };
140
+ if (first) {
141
+ const onError = (error) => {
142
+ if (this.disposed || this.entries.get(key) !== current) {
143
+ return;
144
+ }
145
+ publish({ data: this.state.get().get(key)?.data, error, status: "error" });
146
+ this.onError(error);
147
+ };
148
+ try {
149
+ current.stop = this.client.subscribe(
150
+ ref,
151
+ input,
152
+ (data) => publish({ data, error: null, status: "success" }),
153
+ { onError }
154
+ );
155
+ } catch (error) {
156
+ onError(appError(error));
157
+ }
158
+ }
159
+ let active = true;
160
+ return () => {
161
+ if (!active) {
162
+ return;
163
+ }
164
+ active = false;
165
+ current.observers -= 1;
166
+ if (current.observers === 0 && this.entries.get(key) === current) {
167
+ this.entries.delete(key);
168
+ current.stop?.();
169
+ const next = new Map(this.state.get());
170
+ next.delete(key);
171
+ this.state.set(next);
172
+ }
173
+ };
174
+ }
175
+ dispose() {
176
+ this.disposed = true;
177
+ const entries = [...this.entries.values()];
178
+ this.entries.clear();
179
+ for (const entry of entries) {
180
+ entry.stop?.();
181
+ entry.stop = void 0;
182
+ }
183
+ this.state.set(/* @__PURE__ */ new Map());
184
+ if (this.ownsClient) {
185
+ this.client.close();
186
+ }
187
+ }
188
+ onError(error) {
189
+ if (!this.disposed) {
190
+ this.errorHandler(error);
191
+ }
192
+ }
193
+ };
194
+
195
+ // src/preact.ts
196
+ var Context = createContext2(null);
197
+ function ClientProvider({ children, client, onError }) {
198
+ const errorHandler = useRef(onError);
199
+ errorHandler.current = onError;
200
+ const store = useMemo(
201
+ () => new QueryStore(client ?? createClient(), !client, (error) => errorHandler.current?.(error)),
202
+ [client]
203
+ );
204
+ useLayoutEffect(() => () => store.dispose(), [store]);
205
+ return h2(Context.Provider, { value: store }, children);
206
+ }
207
+ function useQueryStore() {
208
+ const store = useContext2(Context);
209
+ if (!store) {
210
+ throw new Error("TailorKit backend hooks require a ClientProvider at the app root.");
211
+ }
212
+ return store;
213
+ }
214
+ function useQuery(ref, ...[input, options]) {
215
+ const store = useQueryStore();
216
+ const enabled = options?.enabled ?? true;
217
+ const name = ref.name;
218
+ const serialized = serializeInput(input);
219
+ const key = `${name}\0${serialized}`;
220
+ const stableInput = useMemo(() => JSON.parse(serialized).input, [serialized]);
221
+ const stableReference = useMemo(() => reference(name, "query"), [name]);
222
+ const query = useMemo(
223
+ () => computed(
224
+ store.state,
225
+ (queries) => enabled ? queries.get(key) ?? pendingQuery : pendingQuery
226
+ ),
227
+ [store, key, enabled]
228
+ );
229
+ const state = useStore(query);
230
+ useEffect(() => {
231
+ if (enabled) {
232
+ return store.watch(key, stableReference, stableInput);
233
+ }
234
+ }, [store, key, enabled, stableInput, stableReference]);
235
+ return {
236
+ ...state,
237
+ isPending: state.status === "pending",
238
+ isLoading: enabled && state.status === "pending",
239
+ isSuccess: state.status === "success",
240
+ isError: state.status === "error"
241
+ };
242
+ }
243
+ var idleCall = { data: void 0, error: null, status: "idle" };
244
+ function useCall(kind, ref, options) {
245
+ const store = useQueryStore();
246
+ const name = ref.name;
247
+ const identity = useMemo(
248
+ () => ({
249
+ store,
250
+ name,
251
+ kind,
252
+ state: atom2(idleCall)
253
+ }),
254
+ [store, name, kind]
255
+ );
256
+ const state = useStore(identity.state);
257
+ const current = useRef({ identity, sequence: 0, mounted: true });
258
+ if (current.current.identity !== identity) {
259
+ current.current = { identity, sequence: current.current.sequence + 1, mounted: true };
260
+ }
261
+ const callbacks = useRef(options);
262
+ callbacks.current = options;
263
+ useLayoutEffect(() => {
264
+ current.current.mounted = true;
265
+ return () => {
266
+ current.current.mounted = false;
267
+ current.current.sequence += 1;
268
+ };
269
+ }, []);
270
+ const runAsync = useCallback(
271
+ async (...[input]) => {
272
+ const sequence = ++current.current.sequence;
273
+ const settings = callbacks.current;
274
+ const args = input;
275
+ const isCurrent = () => current.current.mounted && current.current.identity === identity && current.current.sequence === sequence;
276
+ identity.state.set({ ...idleCall, status: "pending" });
277
+ let data;
278
+ try {
279
+ data = kind === "mutation" ? await store.client.mutate(reference(name, "mutation"), args) : await store.client.action(reference(name, "action"), args);
280
+ } catch (error) {
281
+ const failure = appError(error);
282
+ if (isCurrent()) {
283
+ identity.state.set({ data: void 0, error: failure, status: "error" });
284
+ settings.onError?.(failure, args);
285
+ }
286
+ store.onError(failure);
287
+ throw failure;
288
+ }
289
+ if (isCurrent()) {
290
+ identity.state.set({ data, error: null, status: "success" });
291
+ settings.onSuccess?.(data, args);
292
+ }
293
+ return data;
294
+ },
295
+ [identity, kind, store, name]
296
+ );
297
+ const run = useCallback(
298
+ (...args) => {
299
+ void runAsync(...args).catch(() => {
300
+ });
301
+ },
302
+ [runAsync]
303
+ );
304
+ const reset = useCallback(() => {
305
+ current.current.sequence += 1;
306
+ identity.state.set(idleCall);
307
+ }, [identity]);
308
+ return {
309
+ ...state,
310
+ isPending: state.status === "pending",
311
+ isSuccess: state.status === "success",
312
+ isError: state.status === "error",
313
+ reset,
314
+ run,
315
+ runAsync
316
+ };
317
+ }
318
+ function useMutation(ref, options = {}) {
319
+ const { run, runAsync, ...state } = useCall("mutation", ref, options);
320
+ return { ...state, mutate: run, mutateAsync: runAsync };
321
+ }
322
+ function useAction(ref, options = {}) {
323
+ const { run, runAsync, ...state } = useCall("action", ref, options);
324
+ return { ...state, execute: run, executeAsync: runAsync };
325
+ }
326
+
327
+ // src/routing.ts
328
+ import { createContext as createContext3, h as h3, render } from "preact";
329
+ import { useContext as useContext3 } from "preact/hooks";
330
+ function defineRoute(options) {
331
+ if (typeof options.shellComponent !== "function") {
332
+ throw new TypeError("defineRoute requires a shellComponent.");
333
+ }
334
+ return Object.freeze({ shellComponent: options.shellComponent });
335
+ }
336
+ var Outlet = createContext3(void 0);
337
+ function Route() {
338
+ const children = useContext3(Outlet);
339
+ if (children === void 0) {
340
+ throw new Error("Route must be rendered inside a root or layout.");
341
+ }
342
+ return children;
343
+ }
344
+ function Shell({ shell, children }) {
345
+ return h3(Outlet.Provider, { value: children }, h3(shell, { children }));
346
+ }
347
+ function shellComponent(module, root) {
348
+ const definition = module.definition;
349
+ if (!root && typeof definition === "function") {
350
+ return definition;
351
+ }
352
+ if (definition && typeof definition === "object" && "shellComponent" in definition && typeof definition.shellComponent === "function") {
353
+ return definition.shellComponent;
354
+ }
355
+ throw new TypeError(
356
+ `${module.filename} must default-export ${root ? "defineRoute({ shellComponent })" : "a layout component or defineRoute({ shellComponent })"}.`
357
+ );
358
+ }
359
+ function createFileClient(options) {
360
+ const rootShell = options.root ? shellComponent(options.root, true) : void 0;
361
+ const slotShells = new Map(options.roots.map((root) => [root.slot, shellComponent(root, true)]));
362
+ const layouts = options.layouts.map((layout) => ({
363
+ ...layout,
364
+ shell: shellComponent(layout, false)
365
+ }));
366
+ const definitions = /* @__PURE__ */ Object.create(null);
367
+ for (const module of options.views) {
368
+ const view = module.definition;
369
+ if (!view || typeof view.component !== "function" || typeof view.path !== "string" || !view.path.startsWith("/")) {
370
+ throw new TypeError(`${module.filename} must default-export defineView(...).`);
371
+ }
372
+ if (view.slot !== module.slot) {
373
+ throw new Error(
374
+ `${module.filename} declares slot "${view.slot}" but is in "${module.slot}".`
375
+ );
376
+ }
377
+ const slot = definitions[module.slot] ??= /* @__PURE__ */ Object.create(null);
378
+ if (Object.hasOwn(slot, view.path)) {
379
+ throw new Error(
380
+ `Duplicate view "${view.path}" in slot "${module.slot}" (${module.filename}).`
381
+ );
382
+ }
383
+ slot[view.path] = view;
384
+ }
385
+ const slots = /* @__PURE__ */ Object.create(null);
386
+ for (const [slot, views] of Object.entries(definitions)) {
387
+ const slotLayouts = layouts.filter((layout) => layout.slot === slot).toSorted(
388
+ (a, b) => a.path.split("/").filter(Boolean).length - b.path.split("/").filter(Boolean).length
389
+ );
390
+ const paths = /* @__PURE__ */ new Set();
391
+ for (const layout of slotLayouts) {
392
+ if (paths.has(layout.path)) {
393
+ throw new Error(`Duplicate layout "${layout.path}" in slot "${slot}".`);
394
+ }
395
+ paths.add(layout.path);
396
+ }
397
+ const SlotRoute = (props) => {
398
+ const view = views[props.view];
399
+ if (!view) {
400
+ return null;
401
+ }
402
+ let child = h3(Outlet.Provider, { value: null }, h3(view.component, props));
403
+ const matching = slotLayouts.filter(
404
+ (layout) => layout.path === "/" || props.view === layout.path || props.view.startsWith(`${layout.path}/`)
405
+ );
406
+ for (const layout of matching.toReversed()) {
407
+ child = h3(Shell, { key: layout.path, shell: layout.shell, children: child });
408
+ }
409
+ const shell = slotShells.get(slot);
410
+ return shell ? h3(Shell, { shell, children: child }) : child;
411
+ };
412
+ slots[slot] = Object.fromEntries(
413
+ Object.entries(views).map(([path, view]) => [path, { ...view, component: SlotRoute }])
414
+ );
415
+ }
416
+ const Root = rootShell ? ({ children }) => h3(Shell, { shell: rootShell, children }) : void 0;
417
+ return {
418
+ $meta: { preactVersion: "11.0.0" },
419
+ $runtime: {
420
+ h: h3,
421
+ render: (vnode, parent) => render(vnode && Root ? h3(Root, { children: vnode }) : vnode, parent)
422
+ },
423
+ slots
424
+ };
425
+ }
426
+
427
+ export {
428
+ defineView,
429
+ createRemoteComponent,
430
+ ClientProvider,
431
+ useQuery,
432
+ useMutation,
433
+ useAction,
434
+ defineRoute,
435
+ Route,
436
+ createFileClient
437
+ };
@@ -0,0 +1,14 @@
1
+ import {
2
+ createClient,
3
+ createSessionProvider,
4
+ reference
5
+ } from "./chunk-LIPODQ7B.js";
6
+ import {
7
+ AppError
8
+ } from "./chunk-IJBQ77AH.js";
9
+ export {
10
+ AppError,
11
+ createClient,
12
+ createSessionProvider,
13
+ reference
14
+ };
package/dist/client.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from "./views";
2
2
  export * from "./preact";
3
+ export { defineRoute, Route, createFileClient } from "./routing";
4
+ export type { RouteDefinition, ShellProps } from "./routing";
3
5
  export { createClient, createSessionProvider, reference } from "./client/connection";
4
6
  export { createApi } from "./client/reference";
5
7
  export type { Client, Session, Reference, References } from "./client/connection";
package/dist/client.js CHANGED
@@ -1,16 +1,20 @@
1
1
  import {
2
2
  ClientProvider,
3
- createApi,
4
- createClient,
3
+ Route,
4
+ createFileClient,
5
5
  createRemoteComponent,
6
- createSessionProvider,
7
- defineClient,
6
+ defineRoute,
8
7
  defineView,
9
- reference,
10
8
  useAction,
11
9
  useMutation,
12
10
  useQuery
13
- } from "./chunk-LQJEPSUZ.js";
11
+ } from "./chunk-O3M4EOCQ.js";
12
+ import {
13
+ createApi,
14
+ createClient,
15
+ createSessionProvider,
16
+ reference
17
+ } from "./chunk-LIPODQ7B.js";
14
18
  import {
15
19
  AppError,
16
20
  appError
@@ -18,12 +22,14 @@ import {
18
22
  export {
19
23
  AppError,
20
24
  ClientProvider,
25
+ Route,
21
26
  appError,
22
27
  createApi,
23
28
  createClient,
29
+ createFileClient,
24
30
  createRemoteComponent,
25
31
  createSessionProvider,
26
- defineClient,
32
+ defineRoute,
27
33
  defineView,
28
34
  reference,
29
35
  useAction,
@@ -4,9 +4,6 @@ declare const tailorkitConfigSchema: z.ZodObject<{
4
4
  build: z.ZodOptional<z.ZodObject<{
5
5
  outDir: z.ZodDefault<z.ZodString>;
6
6
  }, z.core.$strip>>;
7
- client: z.ZodOptional<z.ZodObject<{
8
- entry: z.ZodDefault<z.ZodString>;
9
- }, z.core.$strict>>;
10
7
  host: z.ZodString;
11
8
  logos: z.ZodOptional<z.ZodObject<{
12
9
  dark: z.ZodOptional<z.ZodString>;
@@ -42,17 +39,6 @@ export interface TailorKitConfig extends z.input<typeof tailorkitConfigSchema> {
42
39
  */
43
40
  outDir?: string;
44
41
  };
45
- /**
46
- * Browser client build settings. Omit to use the default client entry point.
47
- * @default undefined
48
- */
49
- client?: {
50
- /**
51
- * Source entry point for the app's browser bundle.
52
- * @default "src/client.ts"
53
- */
54
- entry?: string;
55
- };
56
42
  /**
57
43
  * URL of the TailorKit host used for authentication, deployment, remote preview,
58
44
  * and fetching the host schema for generated types. Required; has no default.
@@ -88,9 +74,9 @@ export interface TailorKitConfig extends z.input<typeof tailorkitConfigSchema> {
88
74
  entry?: string;
89
75
  /**
90
76
  * Directory containing Drizzle migrations, used for generation and bundling.
91
- * When omitted, uses `./migrations` and allows that directory to be absent.
77
+ * When omitted, uses `./src/db/migrations` and allows that directory to be absent.
92
78
  * An explicitly configured directory must exist when building the backend.
93
- * @default "./migrations"
79
+ * @default "./src/db/migrations"
94
80
  */
95
81
  migrations?: string;
96
82
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadTailorKitConfig
3
- } from "./chunk-DEU6RITM.js";
4
- import "./chunk-GVB2PAK7.js";
3
+ } from "./chunk-L5ECQGR7.js";
4
+ import "./chunk-DA2TE2FW.js";
5
5
  export {
6
6
  loadTailorKitConfig
7
7
  };
package/dist/config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  defineConfig,
3
3
  defineTailorKitConfig
4
- } from "./chunk-GVB2PAK7.js";
4
+ } from "./chunk-DA2TE2FW.js";
5
5
  export {
6
6
  defineConfig,
7
7
  defineTailorKitConfig
@@ -0,0 +1,8 @@
1
+ import type { AnyRelations } from "drizzle-orm/relations";
2
+ export interface DatabaseDefinition<R extends AnyRelations = AnyRelations> {
3
+ readonly relations: R;
4
+ }
5
+ /** Configure the installation database with Drizzle relations, which also contain its tables. */
6
+ export declare function defineDatabase<const R extends AnyRelations>(definition: DatabaseDefinition<R>): Readonly<{
7
+ relations: R;
8
+ }>;
@@ -1,7 +1,8 @@
1
+ import type { AnyRelations, EmptyRelations } from "drizzle-orm/relations";
1
2
  import type { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core";
2
- type Database = BaseSQLiteDatabase<"sync", {
3
+ type Database<R extends AnyRelations> = BaseSQLiteDatabase<"sync", {
3
4
  changes: number;
4
- }>;
5
- export type QueryDatabase = Pick<Database, "select" | "selectDistinct">;
6
- export type MutationDatabase = QueryDatabase & Pick<Database, "insert" | "update" | "delete">;
5
+ }, Record<string, never>, R>;
6
+ export type QueryDatabase<R extends AnyRelations = EmptyRelations> = Pick<Database<R>, "select" | "selectDistinct" | "query">;
7
+ export type MutationDatabase<R extends AnyRelations = EmptyRelations> = QueryDatabase<R> & Pick<Database<R>, "insert" | "update" | "delete">;
7
8
  export {};
package/dist/index.js CHANGED
@@ -1,22 +1,27 @@
1
1
  import {
2
2
  ClientProvider,
3
- createApi,
4
- createClient,
3
+ Route,
4
+ createFileClient,
5
5
  createRemoteComponent,
6
- createSessionProvider,
7
- defineClient,
6
+ defineRoute,
8
7
  defineView,
9
- reference,
10
8
  useAction,
11
9
  useMutation,
12
10
  useQuery
13
- } from "./chunk-LQJEPSUZ.js";
11
+ } from "./chunk-O3M4EOCQ.js";
12
+ import {
13
+ createApi,
14
+ createClient,
15
+ createSessionProvider,
16
+ reference
17
+ } from "./chunk-LIPODQ7B.js";
14
18
  import {
15
19
  action,
16
20
  and,
17
21
  asc,
18
22
  boolean,
19
23
  count,
24
+ defineDatabase,
20
25
  defineServer,
21
26
  desc,
22
27
  eq,
@@ -37,7 +42,7 @@ import {
37
42
  text,
38
43
  tk,
39
44
  withInstanceResolvers
40
- } from "./chunk-7NOEYJOB.js";
45
+ } from "./chunk-ANTVMHDZ.js";
41
46
  import {
42
47
  AppError,
43
48
  appError
@@ -45,6 +50,7 @@ import {
45
50
  export {
46
51
  AppError,
47
52
  ClientProvider,
53
+ Route,
48
54
  action,
49
55
  and,
50
56
  appError,
@@ -53,9 +59,11 @@ export {
53
59
  count,
54
60
  createApi,
55
61
  createClient,
62
+ createFileClient,
56
63
  createRemoteComponent,
57
64
  createSessionProvider,
58
- defineClient,
65
+ defineDatabase,
66
+ defineRoute,
59
67
  defineServer,
60
68
  defineView,
61
69
  desc,
@@ -0,0 +1,34 @@
1
+ import type { ComponentChildren, ComponentType } from "preact";
2
+ import type { TailorKitClientWithMeta } from "./views";
3
+ export interface ShellProps {
4
+ children?: ComponentChildren;
5
+ }
6
+ export interface RouteDefinition {
7
+ readonly shellComponent: ComponentType<ShellProps>;
8
+ }
9
+ /** Define the shell in an app or slot root.tsx. */
10
+ export declare function defineRoute(options: RouteDefinition): RouteDefinition;
11
+ /** Render the next layout or selected view inside a root shell or layout. */
12
+ export declare function Route(): string | number | bigint | boolean | object | import("preact").ComponentChild[] | import("preact").VNode<any> | null;
13
+ export interface RouteModule {
14
+ filename: string;
15
+ definition: unknown;
16
+ }
17
+ export interface FileViewModule extends RouteModule {
18
+ slot: string;
19
+ }
20
+ export interface FileLayoutModule extends RouteModule {
21
+ slot: string;
22
+ path: string;
23
+ }
24
+ /** The app builder generates this registration; apps only export their files.
25
+ * @internal
26
+ */
27
+ export declare function createFileClient(options: {
28
+ root?: RouteModule;
29
+ roots: (RouteModule & {
30
+ slot: string;
31
+ })[];
32
+ layouts: FileLayoutModule[];
33
+ views: FileViewModule[];
34
+ }): TailorKitClientWithMeta;