@tailorkit/app 0.1.0-beta.8 → 1.0.0-beta.44
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 +123 -0
- package/dist/chunk-7ZQPXG7O.js +34 -0
- package/dist/chunk-MYK3WL3S.js +428 -0
- package/dist/chunk-QDZXHVTU.js +277 -0
- package/dist/chunk-YFKW5FBP.js +623 -0
- package/dist/client/connection.d.ts +31 -0
- package/dist/client/query-store.d.ts +23 -0
- package/dist/client/reference.d.ts +20 -0
- package/dist/client/session.d.ts +20 -0
- package/dist/client/tools.d.ts +16 -0
- package/dist/client-connection.js +16 -0
- package/dist/client.d.ts +11 -0
- package/dist/client.js +40 -0
- package/dist/database/definition.d.ts +8 -0
- package/dist/database/schema.d.ts +78 -0
- package/dist/database/types.d.ts +8 -0
- package/dist/errors.d.ts +6 -0
- package/dist/index.d.ts +2 -71
- package/dist/index.js +91 -75
- package/dist/preact.d.ts +52 -0
- package/dist/protocol.d.ts +74 -0
- package/dist/protocol.js +39 -0
- package/dist/routing.d.ts +35 -0
- package/dist/server/builder.d.ts +46 -0
- package/dist/server/bundle.d.ts +12 -0
- package/dist/server/functions.d.ts +113 -0
- package/dist/server/instances.d.ts +17 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +60 -0
- package/dist/validation.d.ts +5 -0
- package/dist/views.d.ts +126 -0
- package/package.json +59 -22
- package/LICENSE +0 -674
- package/dist/builder/index.d.ts +0 -10
- package/dist/builder/upload-manifest.d.ts +0 -13
- package/dist/builder.js +0 -224
- package/dist/chunk-EKCUETAV.js +0 -26
- package/dist/chunk-ZFYKVXDO.js +0 -27
- package/dist/config/config.d.ts +0 -20
- package/dist/config/index.d.ts +0 -1
- package/dist/config/loader.d.ts +0 -7
- package/dist/config-loader.js +0 -7
- package/dist/config.js +0 -8
- package/dist/preact-version.d.ts +0 -7
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createClient,
|
|
3
|
+
reference
|
|
4
|
+
} from "./chunk-MYK3WL3S.js";
|
|
5
|
+
import {
|
|
6
|
+
AppError,
|
|
7
|
+
appError
|
|
8
|
+
} from "./chunk-7ZQPXG7O.js";
|
|
9
|
+
|
|
10
|
+
// src/views.ts
|
|
11
|
+
import { createContext, h } from "preact";
|
|
12
|
+
import { useContext } from "preact/hooks";
|
|
13
|
+
var isFunctionValue = (value) => typeof value === "function";
|
|
14
|
+
function defineView(options) {
|
|
15
|
+
if (!options.slot || !options.view) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
"Views without a slot or path must be compiled from a .view.tsx file by the TailorKit app build."
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
const path = options.view;
|
|
21
|
+
const Context2 = createContext(null);
|
|
22
|
+
const instances = (
|
|
23
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- Generated bindings associate this view path with its context and instance schema; the adapter retains that relationship while erasing renderer generics. */
|
|
24
|
+
options.instances
|
|
25
|
+
);
|
|
26
|
+
if (instances && !(instances.resolver !== void 0 && instances.resolver !== null && instances.resolver !== "")) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"View instance resolvers must be compiled with the TailorKit app build."
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const View = (props) => {
|
|
32
|
+
if (props.status !== "ready") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (instances && !props.instance) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`View "${path}" requires a selected instance. Pass instanceKey to RenderSlot or instance to RenderSlot.Controlled.`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return h(
|
|
41
|
+
Context2.Provider,
|
|
42
|
+
{
|
|
43
|
+
value: {
|
|
44
|
+
context: props.context,
|
|
45
|
+
instance: props.instance
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
h(
|
|
49
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- Generated bindings associate this view path with its context and instance schema; the adapter retains that relationship while erasing renderer generics. */
|
|
50
|
+
options.component,
|
|
51
|
+
/* oxlint-enable typescript/no-unsafe-type-assertion */
|
|
52
|
+
{
|
|
53
|
+
key: props.instance?.key
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
const instanceMetadata = {};
|
|
59
|
+
if (instances?.resolver !== void 0 && instances?.resolver !== null && instances?.resolver !== "") {
|
|
60
|
+
instanceMetadata.instances = { resolver: instances.resolver };
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...instanceMetadata,
|
|
64
|
+
component: View,
|
|
65
|
+
path,
|
|
66
|
+
slot: options.slot,
|
|
67
|
+
useContext: () => {
|
|
68
|
+
const context = useContext(Context2);
|
|
69
|
+
if (context === null) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`View context is only available while rendering "${path}".`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return context.context;
|
|
75
|
+
},
|
|
76
|
+
useInstance: () => {
|
|
77
|
+
const value = useContext(Context2);
|
|
78
|
+
if (!value?.instance) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`View instance is only available while rendering an instance of "${path}".`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return (
|
|
84
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- Generated bindings associate this view path with its context and instance schema; the adapter retains that relationship while erasing renderer generics. */
|
|
85
|
+
value.instance
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
var componentTagPrefix = "tailorkit-";
|
|
91
|
+
var toComponentTagName = (name) => `${componentTagPrefix}${name.replaceAll(
|
|
92
|
+
/(?<lowercase>[a-z0-9])(?<uppercase>[A-Z])/gu,
|
|
93
|
+
"$<lowercase>-$<uppercase>"
|
|
94
|
+
).replaceAll(/[\s_]+/gu, "-").toLowerCase()}`;
|
|
95
|
+
var toCallbackEventName = (name) => `tailorkitcallback${name.replaceAll(/[^A-Za-z0-9_$]/gu, "").toLowerCase()}`;
|
|
96
|
+
var toEventProp = (event) => `on${event}`;
|
|
97
|
+
var createRemoteComponent = (name, options = {}) => {
|
|
98
|
+
const tagName = toComponentTagName(name);
|
|
99
|
+
const callbacks = options.callbacks ?? {};
|
|
100
|
+
return function RemoteComponent({ children, ...props }) {
|
|
101
|
+
const nextProps = { ...props };
|
|
102
|
+
const callbackMap = {};
|
|
103
|
+
for (const [key, inputCount] of Object.entries(callbacks)) {
|
|
104
|
+
const operation = nextProps[key];
|
|
105
|
+
Reflect.deleteProperty(nextProps, key);
|
|
106
|
+
if (!isFunctionValue(operation)) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
const eventName = toCallbackEventName(key);
|
|
110
|
+
callbackMap[eventName] = { callback: key, inputCount };
|
|
111
|
+
nextProps[toEventProp(eventName)] = (event) => {
|
|
112
|
+
operation(...(event.detail ?? []).slice(0, inputCount));
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const serializedProps = Object.fromEntries(
|
|
116
|
+
Object.entries(nextProps).filter(([, value]) => !isFunctionValue(value))
|
|
117
|
+
);
|
|
118
|
+
nextProps["data-tailorkit-props"] = JSON.stringify(serializedProps);
|
|
119
|
+
if (Object.keys(callbackMap).length > 0) {
|
|
120
|
+
nextProps["data-tailorkit-callbacks"] = JSON.stringify(callbackMap);
|
|
121
|
+
}
|
|
122
|
+
return h(
|
|
123
|
+
tagName,
|
|
124
|
+
nextProps,
|
|
125
|
+
options.children === true ? children : void 0
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// src/preact.ts
|
|
131
|
+
import { useStore } from "@nanostores/preact";
|
|
132
|
+
import { atom as atom2, computed } from "nanostores";
|
|
133
|
+
import { createContext as createContext2, h as h2 } from "preact";
|
|
134
|
+
import {
|
|
135
|
+
useCallback,
|
|
136
|
+
useContext as useContext2,
|
|
137
|
+
useEffect,
|
|
138
|
+
useLayoutEffect,
|
|
139
|
+
useMemo,
|
|
140
|
+
useRef
|
|
141
|
+
} from "preact/hooks";
|
|
142
|
+
|
|
143
|
+
// src/client/query-store.ts
|
|
144
|
+
import { atom } from "nanostores";
|
|
145
|
+
var isObjectValue = (value) => typeof value === "object" && value !== null;
|
|
146
|
+
var pendingQuery = {
|
|
147
|
+
data: void 0,
|
|
148
|
+
error: null,
|
|
149
|
+
status: "pending"
|
|
150
|
+
};
|
|
151
|
+
var serializeInput = (input) => JSON.stringify(
|
|
152
|
+
{ defined: input !== void 0, input },
|
|
153
|
+
// Native JSON serialization supplies arbitrary values; this boundary only normalizes object key order.
|
|
154
|
+
// oxlint-disable-next-line anti-slop/no-unknown-parameters -- These are serializer inputs, not unparsed domain-handler arguments.
|
|
155
|
+
(_key, value) => {
|
|
156
|
+
if (isObjectValue(value) && !Array.isArray(value)) {
|
|
157
|
+
return Object.fromEntries(
|
|
158
|
+
Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b))
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
var QueryStore = class {
|
|
165
|
+
state = atom(/* @__PURE__ */ new Map());
|
|
166
|
+
entries = /* @__PURE__ */ new Map();
|
|
167
|
+
disposed = false;
|
|
168
|
+
client;
|
|
169
|
+
ownsClient;
|
|
170
|
+
errorHandler;
|
|
171
|
+
constructor(client, ownsClient, errorHandler = () => {
|
|
172
|
+
}) {
|
|
173
|
+
this.client = client;
|
|
174
|
+
this.ownsClient = ownsClient;
|
|
175
|
+
this.errorHandler = errorHandler;
|
|
176
|
+
}
|
|
177
|
+
watch(key, ref, input) {
|
|
178
|
+
let entry = this.entries.get(key);
|
|
179
|
+
const first = !entry;
|
|
180
|
+
if (!entry) {
|
|
181
|
+
entry = { observers: 0 };
|
|
182
|
+
this.entries.set(key, entry);
|
|
183
|
+
}
|
|
184
|
+
const current = entry;
|
|
185
|
+
current.observers += 1;
|
|
186
|
+
const publish = (state) => {
|
|
187
|
+
if (!this.disposed && this.entries.get(key) === current) {
|
|
188
|
+
this.state.set(new Map(this.state.get()).set(key, state));
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
if (first) {
|
|
192
|
+
const onError = (error) => {
|
|
193
|
+
if (this.disposed || this.entries.get(key) !== current) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
publish({
|
|
197
|
+
data: this.state.get().get(key)?.data,
|
|
198
|
+
error,
|
|
199
|
+
status: "error"
|
|
200
|
+
});
|
|
201
|
+
this.onError(error);
|
|
202
|
+
};
|
|
203
|
+
try {
|
|
204
|
+
current.stop = this.client.subscribe(
|
|
205
|
+
ref,
|
|
206
|
+
input,
|
|
207
|
+
(data) => {
|
|
208
|
+
publish({ data, error: null, status: "success" });
|
|
209
|
+
},
|
|
210
|
+
{ onError }
|
|
211
|
+
);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
onError(appError(error));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
let active = true;
|
|
217
|
+
return () => {
|
|
218
|
+
if (!active) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
active = false;
|
|
222
|
+
current.observers -= 1;
|
|
223
|
+
if (current.observers === 0 && this.entries.get(key) === current) {
|
|
224
|
+
this.entries.delete(key);
|
|
225
|
+
current.stop?.();
|
|
226
|
+
const next = new Map(this.state.get());
|
|
227
|
+
next.delete(key);
|
|
228
|
+
this.state.set(next);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
dispose() {
|
|
233
|
+
this.disposed = true;
|
|
234
|
+
const entries = [...this.entries.values()];
|
|
235
|
+
this.entries.clear();
|
|
236
|
+
for (const entry of entries) {
|
|
237
|
+
entry.stop?.();
|
|
238
|
+
entry.stop = void 0;
|
|
239
|
+
}
|
|
240
|
+
this.state.set(/* @__PURE__ */ new Map());
|
|
241
|
+
if (this.ownsClient) {
|
|
242
|
+
this.client.close();
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
onError(error) {
|
|
246
|
+
if (!this.disposed) {
|
|
247
|
+
this.errorHandler(error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// src/preact.ts
|
|
253
|
+
var Context = createContext2(null);
|
|
254
|
+
var ClientProvider = ({
|
|
255
|
+
children,
|
|
256
|
+
client,
|
|
257
|
+
onError
|
|
258
|
+
}) => {
|
|
259
|
+
const errorHandler = useRef(onError);
|
|
260
|
+
errorHandler.current = onError;
|
|
261
|
+
const store = useMemo(
|
|
262
|
+
() => new QueryStore(
|
|
263
|
+
client ?? createClient(),
|
|
264
|
+
!client,
|
|
265
|
+
(error) => errorHandler.current?.(error)
|
|
266
|
+
),
|
|
267
|
+
[client]
|
|
268
|
+
);
|
|
269
|
+
useLayoutEffect(
|
|
270
|
+
() => () => {
|
|
271
|
+
store.dispose();
|
|
272
|
+
},
|
|
273
|
+
[store]
|
|
274
|
+
);
|
|
275
|
+
return h2(Context.Provider, { value: store }, children);
|
|
276
|
+
};
|
|
277
|
+
var useQueryStore = () => {
|
|
278
|
+
const store = useContext2(Context);
|
|
279
|
+
if (!store) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
"TailorKit backend hooks require a ClientProvider at the app root."
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
return store;
|
|
285
|
+
};
|
|
286
|
+
var useQuery = (ref, ...[input, options]) => {
|
|
287
|
+
const store = useQueryStore();
|
|
288
|
+
const enabled = options?.enabled ?? true;
|
|
289
|
+
const { name } = ref;
|
|
290
|
+
const serialized = serializeInput(input);
|
|
291
|
+
const key = `${name}\0${serialized}`;
|
|
292
|
+
const stableInput = useMemo(
|
|
293
|
+
() => (
|
|
294
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- serializeInput produced serialized from this reference's own input; this parses local transport-normalized input rather than external JSON. */
|
|
295
|
+
JSON.parse(serialized).input
|
|
296
|
+
),
|
|
297
|
+
[serialized]
|
|
298
|
+
);
|
|
299
|
+
const stableReference = useMemo(
|
|
300
|
+
() => reference(name, "query"),
|
|
301
|
+
[name]
|
|
302
|
+
);
|
|
303
|
+
const query = useMemo(
|
|
304
|
+
() => computed(
|
|
305
|
+
store.state,
|
|
306
|
+
(queries) => (
|
|
307
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- This cache key identifies the same reference and serialized input, so its state is populated only by that reference's O-typed subscription. */
|
|
308
|
+
enabled ? queries.get(key) ?? pendingQuery : pendingQuery
|
|
309
|
+
)
|
|
310
|
+
/* oxlint-enable typescript/no-unsafe-type-assertion */
|
|
311
|
+
),
|
|
312
|
+
[store, key, enabled]
|
|
313
|
+
);
|
|
314
|
+
const state = useStore(query);
|
|
315
|
+
useEffect(
|
|
316
|
+
() => enabled ? store.watch(key, stableReference, stableInput) : void 0,
|
|
317
|
+
[store, key, enabled, stableInput, stableReference]
|
|
318
|
+
);
|
|
319
|
+
return {
|
|
320
|
+
...state,
|
|
321
|
+
isError: state.status === "error",
|
|
322
|
+
isLoading: enabled && state.status === "pending",
|
|
323
|
+
isPending: state.status === "pending",
|
|
324
|
+
isSuccess: state.status === "success"
|
|
325
|
+
};
|
|
326
|
+
};
|
|
327
|
+
var idleCall = { data: void 0, error: null, status: "idle" };
|
|
328
|
+
var useCall = (kind, ref, options) => {
|
|
329
|
+
const store = useQueryStore();
|
|
330
|
+
const { name } = ref;
|
|
331
|
+
const identity = useMemo(
|
|
332
|
+
() => ({
|
|
333
|
+
kind,
|
|
334
|
+
name,
|
|
335
|
+
state: atom2(idleCall),
|
|
336
|
+
store
|
|
337
|
+
}),
|
|
338
|
+
[store, name, kind]
|
|
339
|
+
);
|
|
340
|
+
const state = useStore(identity.state);
|
|
341
|
+
const current = useRef({ identity, mounted: true, sequence: 0 });
|
|
342
|
+
if (current.current.identity !== identity) {
|
|
343
|
+
current.current = {
|
|
344
|
+
identity,
|
|
345
|
+
mounted: true,
|
|
346
|
+
sequence: current.current.sequence + 1
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
const callbacks = useRef(options);
|
|
350
|
+
callbacks.current = options;
|
|
351
|
+
useLayoutEffect(() => {
|
|
352
|
+
current.current.mounted = true;
|
|
353
|
+
return () => {
|
|
354
|
+
current.current.mounted = false;
|
|
355
|
+
current.current.sequence += 1;
|
|
356
|
+
};
|
|
357
|
+
}, []);
|
|
358
|
+
const runAsync = useCallback(
|
|
359
|
+
async (...[input]) => {
|
|
360
|
+
current.current.sequence += 1;
|
|
361
|
+
const { sequence } = current.current;
|
|
362
|
+
const settings = callbacks.current;
|
|
363
|
+
const args = (
|
|
364
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- InputArguments requires an I value unless I includes undefined; destructuring its conditional tuple loses that relationship. */
|
|
365
|
+
input
|
|
366
|
+
);
|
|
367
|
+
const isCurrent = () => current.current.mounted && current.current.identity === identity && current.current.sequence === sequence;
|
|
368
|
+
identity.state.set({ ...idleCall, status: "pending" });
|
|
369
|
+
let data;
|
|
370
|
+
try {
|
|
371
|
+
data = kind === "mutation" ? await store.client.mutate(
|
|
372
|
+
reference(name, "mutation"),
|
|
373
|
+
args
|
|
374
|
+
) : await store.client.action(
|
|
375
|
+
reference(name, "action"),
|
|
376
|
+
args
|
|
377
|
+
);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
const failure = appError(error);
|
|
380
|
+
if (isCurrent()) {
|
|
381
|
+
identity.state.set({
|
|
382
|
+
data: void 0,
|
|
383
|
+
error: failure,
|
|
384
|
+
status: "error"
|
|
385
|
+
});
|
|
386
|
+
settings.onError?.(failure, args);
|
|
387
|
+
}
|
|
388
|
+
store.onError(failure);
|
|
389
|
+
throw failure;
|
|
390
|
+
}
|
|
391
|
+
if (isCurrent()) {
|
|
392
|
+
identity.state.set({ data, error: null, status: "success" });
|
|
393
|
+
settings.onSuccess?.(data, args);
|
|
394
|
+
}
|
|
395
|
+
return data;
|
|
396
|
+
},
|
|
397
|
+
[identity, kind, store, name]
|
|
398
|
+
);
|
|
399
|
+
const run = useCallback(
|
|
400
|
+
(...args) => {
|
|
401
|
+
void (async () => {
|
|
402
|
+
try {
|
|
403
|
+
await runAsync(...args);
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
})();
|
|
407
|
+
},
|
|
408
|
+
[runAsync]
|
|
409
|
+
);
|
|
410
|
+
const reset = useCallback(() => {
|
|
411
|
+
current.current.sequence += 1;
|
|
412
|
+
identity.state.set(idleCall);
|
|
413
|
+
}, [identity]);
|
|
414
|
+
return {
|
|
415
|
+
...state,
|
|
416
|
+
isError: state.status === "error",
|
|
417
|
+
isPending: state.status === "pending",
|
|
418
|
+
isSuccess: state.status === "success",
|
|
419
|
+
reset,
|
|
420
|
+
run,
|
|
421
|
+
runAsync
|
|
422
|
+
};
|
|
423
|
+
};
|
|
424
|
+
var useMutation = (ref, options = {}) => {
|
|
425
|
+
const { run, runAsync, ...state } = useCall("mutation", ref, options);
|
|
426
|
+
return { ...state, mutate: run, mutateAsync: runAsync };
|
|
427
|
+
};
|
|
428
|
+
var useAction = (ref, options = {}) => {
|
|
429
|
+
const { run, runAsync, ...state } = useCall("action", ref, options);
|
|
430
|
+
return { ...state, execute: run, executeAsync: runAsync };
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// src/routing.ts
|
|
434
|
+
import { createContext as createContext3, h as h3, render } from "preact";
|
|
435
|
+
import { useContext as useContext3 } from "preact/hooks";
|
|
436
|
+
var isFunctionValue2 = (value) => typeof value === "function";
|
|
437
|
+
var isObjectValue2 = (value) => typeof value === "object" && value !== null;
|
|
438
|
+
var isStringValue = (value) => typeof value === "string";
|
|
439
|
+
var defineRoute = (options) => {
|
|
440
|
+
if (!isFunctionValue2(options.shellComponent)) {
|
|
441
|
+
throw new TypeError("defineRoute requires a shellComponent.");
|
|
442
|
+
}
|
|
443
|
+
return Object.freeze({ shellComponent: options.shellComponent });
|
|
444
|
+
};
|
|
445
|
+
var Outlet = createContext3(void 0);
|
|
446
|
+
var Route = () => {
|
|
447
|
+
const children = useContext3(Outlet);
|
|
448
|
+
if (children === void 0) {
|
|
449
|
+
throw new Error("Route must be rendered inside a root or layout.");
|
|
450
|
+
}
|
|
451
|
+
return children;
|
|
452
|
+
};
|
|
453
|
+
var Shell = ({
|
|
454
|
+
shell,
|
|
455
|
+
children
|
|
456
|
+
}) => h3(Outlet.Provider, { value: children }, h3(shell, { children }));
|
|
457
|
+
var shellComponent = (module, root) => {
|
|
458
|
+
const { definition } = module;
|
|
459
|
+
if (!root && isFunctionValue2(definition)) {
|
|
460
|
+
return definition;
|
|
461
|
+
}
|
|
462
|
+
if (isObjectValue2(definition) && "shellComponent" in definition && isFunctionValue2(definition.shellComponent)) {
|
|
463
|
+
return definition.shellComponent;
|
|
464
|
+
}
|
|
465
|
+
throw new TypeError(
|
|
466
|
+
`${module.filename} must default-export ${root ? "defineRoute({ shellComponent })" : "a layout component or defineRoute({ shellComponent })"}.`
|
|
467
|
+
);
|
|
468
|
+
};
|
|
469
|
+
var createFileClient = (options) => {
|
|
470
|
+
const rootShell = options.root ? shellComponent(options.root, true) : void 0;
|
|
471
|
+
const slotShells = new Map(
|
|
472
|
+
options.roots.map((root) => [root.slot, shellComponent(root, true)])
|
|
473
|
+
);
|
|
474
|
+
const layouts = options.layouts.map((layout) => ({
|
|
475
|
+
...layout,
|
|
476
|
+
shell: shellComponent(layout, false)
|
|
477
|
+
}));
|
|
478
|
+
const definitions = {};
|
|
479
|
+
Object.setPrototypeOf(definitions, null);
|
|
480
|
+
for (const module of options.views) {
|
|
481
|
+
const view = (
|
|
482
|
+
/* oxlint-disable typescript/no-unsafe-type-assertion -- The generated module registry owns these component and view declarations; the runtime adapter preserves their exported implementations. */
|
|
483
|
+
module.definition
|
|
484
|
+
);
|
|
485
|
+
if (!view || !isFunctionValue2(view.component) || !isStringValue(view.path) || !view.path.startsWith("/")) {
|
|
486
|
+
throw new TypeError(
|
|
487
|
+
`${module.filename} must default-export defineView(...).`
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
if (view.slot !== module.slot) {
|
|
491
|
+
throw new Error(
|
|
492
|
+
`${module.filename} declares slot "${view.slot}" but is in "${module.slot}".`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
if (view.path !== module.path) {
|
|
496
|
+
throw new Error(
|
|
497
|
+
`${module.filename} declares view "${view.path}" but its file location requires "${module.path}".`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
definitions[module.slot] ??= /* @__PURE__ */ Object.create(null);
|
|
501
|
+
const slot = definitions[module.slot];
|
|
502
|
+
if (slot === void 0) {
|
|
503
|
+
throw new Error("Missing initialized slot registry.");
|
|
504
|
+
}
|
|
505
|
+
if (Object.hasOwn(slot, view.path)) {
|
|
506
|
+
throw new Error(
|
|
507
|
+
`Duplicate view "${view.path}" in slot "${module.slot}" (${module.filename}).`
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
slot[view.path] = view;
|
|
511
|
+
}
|
|
512
|
+
const slots = {};
|
|
513
|
+
Object.setPrototypeOf(slots, null);
|
|
514
|
+
for (const [slot, views] of Object.entries(definitions)) {
|
|
515
|
+
const slotLayouts = layouts.filter((layout) => layout.slot === slot).toSorted(
|
|
516
|
+
(a, b) => a.path.split("/").filter(Boolean).length - b.path.split("/").filter(Boolean).length
|
|
517
|
+
);
|
|
518
|
+
const paths = /* @__PURE__ */ new Set();
|
|
519
|
+
for (const layout of slotLayouts) {
|
|
520
|
+
if (paths.has(layout.path)) {
|
|
521
|
+
throw new Error(`Duplicate layout "${layout.path}" in slot "${slot}".`);
|
|
522
|
+
}
|
|
523
|
+
paths.add(layout.path);
|
|
524
|
+
}
|
|
525
|
+
const SlotRoute = (props) => {
|
|
526
|
+
const view = views[props.view];
|
|
527
|
+
if (!view) {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
530
|
+
let child = h3(
|
|
531
|
+
Outlet.Provider,
|
|
532
|
+
{ value: null },
|
|
533
|
+
h3(view.component, props)
|
|
534
|
+
);
|
|
535
|
+
const matching = slotLayouts.filter(
|
|
536
|
+
(layout) => layout.path === "/" || props.view === layout.path || props.view.startsWith(`${layout.path}/`)
|
|
537
|
+
);
|
|
538
|
+
for (const layout of matching.toReversed()) {
|
|
539
|
+
child = h3(Shell, {
|
|
540
|
+
children: child,
|
|
541
|
+
key: layout.path,
|
|
542
|
+
shell: layout.shell
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const shell = slotShells.get(slot);
|
|
546
|
+
return shell ? h3(Shell, { children: child, shell }) : child;
|
|
547
|
+
};
|
|
548
|
+
slots[slot] = Object.fromEntries(
|
|
549
|
+
Object.entries(views).map(([path, view]) => [
|
|
550
|
+
path,
|
|
551
|
+
{ ...view, component: SlotRoute }
|
|
552
|
+
])
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
const Root = rootShell ? ({ children }) => h3(Shell, { children, shell: rootShell }) : void 0;
|
|
556
|
+
return {
|
|
557
|
+
$meta: { preactVersion: "11.0.1" },
|
|
558
|
+
$runtime: {
|
|
559
|
+
h: h3,
|
|
560
|
+
render: (vnode, parent) => {
|
|
561
|
+
render(vnode && Root ? h3(Root, { children: vnode }) : vnode, parent);
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
slots
|
|
565
|
+
};
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
// src/client/tools.ts
|
|
569
|
+
import * as v from "valibot";
|
|
570
|
+
var executeServerTool = async (session, path, input, request = globalThis.fetch, signal) => {
|
|
571
|
+
const response = await request(session.url, {
|
|
572
|
+
body: JSON.stringify({ input, path, requestId: crypto.randomUUID() }),
|
|
573
|
+
credentials: "omit",
|
|
574
|
+
headers: {
|
|
575
|
+
authorization: `Bearer ${session.token}`,
|
|
576
|
+
"content-type": "application/json"
|
|
577
|
+
},
|
|
578
|
+
method: "POST",
|
|
579
|
+
redirect: "error",
|
|
580
|
+
signal
|
|
581
|
+
});
|
|
582
|
+
if (!response.ok) {
|
|
583
|
+
throw new AppError(
|
|
584
|
+
(() => {
|
|
585
|
+
if (response.status === 401) {
|
|
586
|
+
return "UNAUTHORIZED";
|
|
587
|
+
}
|
|
588
|
+
if (response.status === 400) {
|
|
589
|
+
return "BAD_REQUEST";
|
|
590
|
+
}
|
|
591
|
+
return "UNAVAILABLE";
|
|
592
|
+
})(),
|
|
593
|
+
"Tool call failed"
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
return v.parse(
|
|
597
|
+
v.object({ output: v.optional(v.unknown()) }),
|
|
598
|
+
await response.json()
|
|
599
|
+
).output;
|
|
600
|
+
};
|
|
601
|
+
var callTool = async (kind, path, input) => {
|
|
602
|
+
const bridge = globalThis.__tailorkitTools;
|
|
603
|
+
if (!bridge) {
|
|
604
|
+
throw new AppError("UNAVAILABLE", "Tool bridge unavailable");
|
|
605
|
+
}
|
|
606
|
+
if (kind === "client") {
|
|
607
|
+
return await bridge.client(path, input);
|
|
608
|
+
}
|
|
609
|
+
return await bridge.session(path).then(async (session) => await executeServerTool(session, path, input));
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
export {
|
|
613
|
+
defineView,
|
|
614
|
+
createRemoteComponent,
|
|
615
|
+
ClientProvider,
|
|
616
|
+
useQuery,
|
|
617
|
+
useMutation,
|
|
618
|
+
useAction,
|
|
619
|
+
defineRoute,
|
|
620
|
+
Route,
|
|
621
|
+
createFileClient,
|
|
622
|
+
callTool
|
|
623
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AppError } from "#errors";
|
|
2
|
+
import type { Reference } from "./reference";
|
|
3
|
+
import type { Session } from "./session";
|
|
4
|
+
export type { Session } from "./session";
|
|
5
|
+
export { reference } from "./reference";
|
|
6
|
+
export type { Reference, References } from "./reference";
|
|
7
|
+
export { AppError } from "#errors";
|
|
8
|
+
type InputArguments<I> = undefined extends I ? [args?: I] : [args: I];
|
|
9
|
+
type MutationArguments<I> = undefined extends I ? [args?: I, options?: {
|
|
10
|
+
requestId?: string;
|
|
11
|
+
}] : [args: I, options?: {
|
|
12
|
+
requestId?: string;
|
|
13
|
+
}];
|
|
14
|
+
export interface Client {
|
|
15
|
+
query: <I, O>(reference: Reference<"query", I, O>, ...args: InputArguments<NoInfer<I>>) => Promise<O>;
|
|
16
|
+
mutate: <I, O>(reference: Reference<"mutation", I, O>, ...args: MutationArguments<NoInfer<I>>) => Promise<O>;
|
|
17
|
+
action: <I, O>(reference: Reference<"action", I, O>, ...args: InputArguments<NoInfer<I>>) => Promise<O>;
|
|
18
|
+
subscribe: <I, O>(reference: Reference<"query", I, O>, args: NoInfer<I>, next: (value: O) => void, options?: {
|
|
19
|
+
onError?: (error: AppError) => void;
|
|
20
|
+
}) => () => void;
|
|
21
|
+
close: () => void;
|
|
22
|
+
}
|
|
23
|
+
export declare const createClient: (options?: {
|
|
24
|
+
getSession?: (options: {
|
|
25
|
+
refresh: boolean;
|
|
26
|
+
}) => Promise<Session>;
|
|
27
|
+
connect?: (url: string, protocols: string[]) => WebSocket | Promise<WebSocket>;
|
|
28
|
+
fetch?: typeof fetch;
|
|
29
|
+
retryDelayMs?: number;
|
|
30
|
+
}) => Client;
|
|
31
|
+
export { createSessionProvider, parseSession } from "./session";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AppError } from "#errors";
|
|
2
|
+
import type { Client } from "./connection";
|
|
3
|
+
import type { Reference } from "./reference";
|
|
4
|
+
export interface QueryState<T> {
|
|
5
|
+
data: T | undefined;
|
|
6
|
+
error: AppError | null;
|
|
7
|
+
status: "pending" | "success" | "error";
|
|
8
|
+
}
|
|
9
|
+
export declare const pendingQuery: QueryState<never>;
|
|
10
|
+
export declare const serializeInput: (input: unknown) => string;
|
|
11
|
+
/** One subscription per active query/input pair, scoped to this provider's client. */
|
|
12
|
+
export declare class QueryStore {
|
|
13
|
+
readonly state: import("nanostores").PreinitializedWritableAtom<Map<string, QueryState<unknown>>> & object;
|
|
14
|
+
private readonly entries;
|
|
15
|
+
private disposed;
|
|
16
|
+
readonly client: Client;
|
|
17
|
+
private readonly ownsClient;
|
|
18
|
+
private readonly errorHandler;
|
|
19
|
+
constructor(client: Client, ownsClient: boolean, errorHandler?: (error: AppError) => void);
|
|
20
|
+
watch<I, O>(key: string, ref: Reference<"query", I, O>, input: I): () => void;
|
|
21
|
+
dispose(): void;
|
|
22
|
+
onError(error: AppError): void;
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import type { FunctionResult, Functions } from "#server/functions";
|
|
3
|
+
export interface Reference<K extends "query" | "mutation" | "action", I, O> {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly $types?: {
|
|
6
|
+
kind: K;
|
|
7
|
+
input: I;
|
|
8
|
+
output: O;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare const reference: <K extends "query" | "mutation" | "action", I, O>(name: string, _kind: K) => Reference<K, I, O>;
|
|
12
|
+
export type References<F> = {
|
|
13
|
+
readonly [N in keyof F]: F[N] extends {
|
|
14
|
+
kind: infer K extends "query" | "mutation" | "action";
|
|
15
|
+
args: infer A extends StandardSchemaV1;
|
|
16
|
+
handler: (...args: never[]) => infer _Output;
|
|
17
|
+
} ? Reference<K, StandardSchemaV1.InferInput<A>, FunctionResult<F[N]>> : F[N] extends Functions ? References<F[N]> : never;
|
|
18
|
+
};
|
|
19
|
+
/** Browser references inferred directly from the server's exported function type. */
|
|
20
|
+
export declare const createApi: <F extends Functions>() => References<F>;
|