@tailorkit/app 0.1.0-beta.17 → 0.1.0-beta.19
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/builder/client-views.d.ts +16 -0
- package/dist/builder/instances.d.ts +19 -0
- package/dist/builder/server.d.ts +4 -1
- package/dist/builder/upload-manifest.d.ts +7 -1
- package/dist/builder.js +598 -91
- package/dist/{chunk-3XA36UOQ.js → chunk-43L6XVW6.js} +24 -4
- package/dist/{chunk-46CTW3XJ.js → chunk-7NOEYJOB.js} +77 -0
- package/dist/client.js +1 -1
- package/dist/config/config.d.ts +1 -0
- package/dist/index.js +6 -4
- package/dist/protocol.js +1 -1
- package/dist/server/functions.d.ts +1 -2
- package/dist/server/instances.d.ts +16 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +5 -3
- package/dist/views.d.ts +34 -4
- package/package.json +4 -4
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { InstanceRegistration } from "./instances";
|
|
2
|
+
export declare function readClientViews(filename: string): Promise<{
|
|
3
|
+
slot: string;
|
|
4
|
+
path: string;
|
|
5
|
+
instances?: true | undefined;
|
|
6
|
+
disabled?: true | undefined;
|
|
7
|
+
}[]>;
|
|
8
|
+
export declare function readClientManifest(filename: string): Promise<{
|
|
9
|
+
views: {
|
|
10
|
+
slot: string;
|
|
11
|
+
path: string;
|
|
12
|
+
instances?: true | undefined;
|
|
13
|
+
disabled?: true | undefined;
|
|
14
|
+
}[];
|
|
15
|
+
instanceResolvers: InstanceRegistration[];
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
export interface InstanceModule {
|
|
3
|
+
filename: string;
|
|
4
|
+
names: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface InstanceRegistration {
|
|
7
|
+
slot: string;
|
|
8
|
+
path: string;
|
|
9
|
+
resolver: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function extractInstances(filename: string, source: string, root: string, includedNames?: string[]): Promise<{
|
|
12
|
+
names: string[];
|
|
13
|
+
server: string;
|
|
14
|
+
browser: string;
|
|
15
|
+
} | undefined>;
|
|
16
|
+
export declare function instanceExtractionPlugin(root: string, serverConfigured: boolean, modules: Map<string, InstanceModule>): Plugin;
|
|
17
|
+
export declare function instanceServerPlugin(root: string, modules: InstanceModule[]): Plugin;
|
|
18
|
+
export declare function instanceServerImports(modules: InstanceModule[]): string;
|
|
19
|
+
export declare function instanceServerBindings(modules: InstanceModule[], registrations: InstanceRegistration[]): string;
|
package/dist/builder/server.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { LoadedTailorKitConfig } from "../config/loader";
|
|
2
|
+
import type { InstanceModule, InstanceRegistration } from "./instances";
|
|
2
3
|
/** Bundle the private app backend and migrations. */
|
|
3
|
-
export declare function buildServer(loaded: LoadedTailorKitConfig, watch?: boolean, buildOutDir?: string): Promise<import("@voidzero-dev/vite-plus-core/rolldown").RolldownOutput[] | import("@voidzero-dev/vite-plus-core/rolldown").RolldownOutput | import("@voidzero-dev/vite-plus-core/rolldown").RolldownWatcher | undefined>;
|
|
4
|
+
export declare function buildServer(loaded: LoadedTailorKitConfig, watch?: boolean, buildOutDir?: string, instances?: InstanceModule[], registrations?: InstanceRegistration[]): Promise<import("@voidzero-dev/vite-plus-core/rolldown").RolldownOutput[] | import("@voidzero-dev/vite-plus-core/rolldown").RolldownOutput | import("@voidzero-dev/vite-plus-core/rolldown").RolldownWatcher | undefined>;
|
|
5
|
+
/** Check before either build can empty an output directory. */
|
|
6
|
+
export declare function validateServerBuildOutput(loaded: LoadedTailorKitConfig, output: string): void;
|
|
@@ -8,7 +8,13 @@ export declare const tailorkitUploadManifestSchema: z.ZodObject<{
|
|
|
8
8
|
light: z.ZodOptional<z.ZodString>;
|
|
9
9
|
}, z.core.$strip>>;
|
|
10
10
|
}, z.core.$strip>;
|
|
11
|
+
views: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
12
|
+
slot: z.ZodString;
|
|
13
|
+
path: z.ZodString;
|
|
14
|
+
instances: z.ZodOptional<z.ZodLiteral<true>>;
|
|
15
|
+
disabled: z.ZodOptional<z.ZodLiteral<true>>;
|
|
16
|
+
}, z.core.$strip>>>;
|
|
11
17
|
version: z.ZodLiteral<1>;
|
|
12
18
|
}, z.core.$strip>;
|
|
13
19
|
export type TailorKitUploadManifest = z.output<typeof tailorkitUploadManifestSchema>;
|
|
14
|
-
export declare const createTailorKitUploadManifest: (logos?: TailorKitUploadManifest["assets"]["logos"], server?: boolean) => TailorKitUploadManifest;
|
|
20
|
+
export declare const createTailorKitUploadManifest: (logos?: TailorKitUploadManifest["assets"]["logos"], server?: boolean, views?: TailorKitUploadManifest["views"]) => TailorKitUploadManifest;
|
package/dist/builder.js
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
import "./chunk-GVB2PAK7.js";
|
|
5
5
|
|
|
6
6
|
// src/builder/index.ts
|
|
7
|
-
import
|
|
7
|
+
import path7 from "path";
|
|
8
8
|
import { createRequire as createRequire2 } from "module";
|
|
9
|
-
import { readFile as
|
|
9
|
+
import { readFile as readFile4, realpath, writeFile as writeFile2 } from "fs/promises";
|
|
10
10
|
|
|
11
11
|
// ../asset-delivery/dist/logo-validation.mjs
|
|
12
12
|
var maxLogoBytes = 262144;
|
|
@@ -96,9 +96,88 @@ var assertSupportedPreactVersion = (version) => {
|
|
|
96
96
|
return check;
|
|
97
97
|
};
|
|
98
98
|
|
|
99
|
+
// src/builder/client-views.ts
|
|
100
|
+
import { execFile } from "child_process";
|
|
101
|
+
import { promisify } from "util";
|
|
102
|
+
|
|
103
|
+
// src/builder/upload-manifest.ts
|
|
104
|
+
import { z } from "zod";
|
|
105
|
+
var tailorkitUploadManifestSchema = z.object({
|
|
106
|
+
assets: z.object({
|
|
107
|
+
client: z.literal("client/client.js"),
|
|
108
|
+
server: z.literal("server/server.js").optional(),
|
|
109
|
+
logos: z.object({
|
|
110
|
+
dark: z.string().regex(/^client\/logo-dark\.(?:png|svg|webp)$/u).optional(),
|
|
111
|
+
light: z.string().regex(/^client\/logo-light\.(?:png|svg|webp)$/u).optional()
|
|
112
|
+
}).optional()
|
|
113
|
+
}),
|
|
114
|
+
views: z.array(
|
|
115
|
+
z.object({
|
|
116
|
+
slot: z.string().min(1).max(255),
|
|
117
|
+
path: z.string().startsWith("/").max(1024),
|
|
118
|
+
instances: z.literal(true).optional(),
|
|
119
|
+
disabled: z.literal(true).optional()
|
|
120
|
+
})
|
|
121
|
+
).max(1e3).optional(),
|
|
122
|
+
version: z.literal(1)
|
|
123
|
+
});
|
|
124
|
+
var createTailorKitUploadManifest = (logos, server = false, views) => tailorkitUploadManifestSchema.parse({
|
|
125
|
+
assets: {
|
|
126
|
+
client: "client/client.js",
|
|
127
|
+
...server ? { server: "server/server.js" } : {},
|
|
128
|
+
...logos && Object.keys(logos).length > 0 ? { logos } : {}
|
|
129
|
+
},
|
|
130
|
+
views,
|
|
131
|
+
version: 1
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// src/builder/client-views.ts
|
|
135
|
+
var inspectClient = `
|
|
136
|
+
import { readFile } from "node:fs/promises";
|
|
137
|
+
const source = await readFile(process.argv[1], "utf8");
|
|
138
|
+
const { default: client } = await import("data:text/javascript;base64," + Buffer.from(source).toString("base64"));
|
|
139
|
+
if (!client?.slots || typeof client.slots !== "object") throw new Error("Client must default-export defineClient(...)");
|
|
140
|
+
const instanceResolvers = [];
|
|
141
|
+
const views = Object.entries(client.slots).flatMap(([slot, definitions]) =>
|
|
142
|
+
Object.entries(definitions).flatMap(([path, view]) => {
|
|
143
|
+
if (view === false) return [{ slot, path, disabled: true }];
|
|
144
|
+
const resolver = view?.instances?.resolver;
|
|
145
|
+
if (resolver !== undefined) instanceResolvers.push({ slot, path, resolver });
|
|
146
|
+
return [{ slot, path, ...(resolver !== undefined ? { instances: true } : {}) }];
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
process.stdout.write("\\nTAILORKIT_VIEWS=" + JSON.stringify({ views, instanceResolvers }));
|
|
150
|
+
process.exit(0);
|
|
151
|
+
`;
|
|
152
|
+
async function readClientManifest(filename) {
|
|
153
|
+
try {
|
|
154
|
+
const { stdout } = await promisify(execFile)(
|
|
155
|
+
process.execPath,
|
|
156
|
+
["--input-type=module", "--eval", inspectClient, filename],
|
|
157
|
+
{ timeout: 1e4, maxBuffer: 2 * 1024 * 1024 }
|
|
158
|
+
);
|
|
159
|
+
const result = stdout.slice(
|
|
160
|
+
stdout.lastIndexOf("\nTAILORKIT_VIEWS=") + "\nTAILORKIT_VIEWS=".length
|
|
161
|
+
);
|
|
162
|
+
const manifest = JSON.parse(result);
|
|
163
|
+
const views = tailorkitUploadManifestSchema.shape.views.unwrap().parse(manifest.views);
|
|
164
|
+
if (!Array.isArray(manifest.instanceResolvers) || manifest.instanceResolvers.some(
|
|
165
|
+
(ref) => !ref || typeof ref !== "object" || typeof ref.slot !== "string" || typeof ref.path !== "string" || typeof ref.resolver !== "string" || !/^i[0-9a-f]{24}$/u.test(ref.resolver)
|
|
166
|
+
)) {
|
|
167
|
+
throw new Error("Invalid instance resolver references");
|
|
168
|
+
}
|
|
169
|
+
return { views, instanceResolvers: manifest.instanceResolvers };
|
|
170
|
+
} catch (error) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"Unable to read app views during the build. Keep browser-only side effects inside components or effects, and default-export defineClient(...).",
|
|
173
|
+
{ cause: error }
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
99
178
|
// src/builder/server.ts
|
|
100
179
|
import { mkdir, mkdtemp as mkdtemp2, writeFile, cp, rm as rm2 } from "fs/promises";
|
|
101
|
-
import
|
|
180
|
+
import path5 from "path";
|
|
102
181
|
import { build } from "vite";
|
|
103
182
|
|
|
104
183
|
// src/builder/migrations.ts
|
|
@@ -144,11 +223,11 @@ var runtimeManifest = {
|
|
|
144
223
|
};
|
|
145
224
|
|
|
146
225
|
// src/builder/schema-check.ts
|
|
147
|
-
import { execFile } from "child_process";
|
|
226
|
+
import { execFile as execFile2 } from "child_process";
|
|
148
227
|
import { access as access2, mkdtemp, readFile as readFile2, readdir as readdir2, rm } from "fs/promises";
|
|
149
228
|
import { tmpdir } from "os";
|
|
150
229
|
import path3 from "path";
|
|
151
|
-
import { promisify } from "util";
|
|
230
|
+
import { promisify as promisify2 } from "util";
|
|
152
231
|
|
|
153
232
|
// src/builder/drizzle.ts
|
|
154
233
|
import { access } from "fs/promises";
|
|
@@ -179,7 +258,7 @@ async function drizzleGenerateArguments(root, output, name) {
|
|
|
179
258
|
}
|
|
180
259
|
|
|
181
260
|
// src/builder/schema-check.ts
|
|
182
|
-
var run =
|
|
261
|
+
var run = promisify2(execFile2);
|
|
183
262
|
async function snapshot(directory) {
|
|
184
263
|
const entries = await readdir2(directory, { withFileTypes: true }).catch((error) => {
|
|
185
264
|
if (error.code === "ENOENT") return [];
|
|
@@ -247,37 +326,428 @@ async function checkAppSchema(root, migrationsDirectory) {
|
|
|
247
326
|
}
|
|
248
327
|
}
|
|
249
328
|
|
|
329
|
+
// src/builder/instances.ts
|
|
330
|
+
import { createHash as createHash2 } from "crypto";
|
|
331
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
332
|
+
import path4 from "path";
|
|
333
|
+
import { parseSync, transformWithOxc } from "vite";
|
|
334
|
+
var suffix = "?tailorkit-instances";
|
|
335
|
+
var clientImports = /* @__PURE__ */ new Set([
|
|
336
|
+
"tailorkit/client",
|
|
337
|
+
"tailorkit/app/client",
|
|
338
|
+
"@tailorkit/app/client"
|
|
339
|
+
]);
|
|
340
|
+
var browserGlobals = /* @__PURE__ */ new Set([
|
|
341
|
+
"window",
|
|
342
|
+
"document",
|
|
343
|
+
"navigator",
|
|
344
|
+
"localStorage",
|
|
345
|
+
"sessionStorage"
|
|
346
|
+
]);
|
|
347
|
+
function isNode(value) {
|
|
348
|
+
return !!value && typeof value === "object" && "type" in value;
|
|
349
|
+
}
|
|
350
|
+
function children(node) {
|
|
351
|
+
return Object.values(node).flatMap(
|
|
352
|
+
(value) => Array.isArray(value) ? value.filter(isNode) : isNode(value) ? [value] : []
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
function walk(node, visit, ancestors = []) {
|
|
356
|
+
visit(node, ancestors);
|
|
357
|
+
for (const child of children(node)) walk(child, visit, [...ancestors, node]);
|
|
358
|
+
}
|
|
359
|
+
function boundNames(node) {
|
|
360
|
+
if (!node) return [];
|
|
361
|
+
switch (node.type) {
|
|
362
|
+
case "Identifier":
|
|
363
|
+
return [node.name];
|
|
364
|
+
case "RestElement":
|
|
365
|
+
return boundNames(node.argument);
|
|
366
|
+
case "AssignmentPattern":
|
|
367
|
+
return boundNames(node.left);
|
|
368
|
+
case "ArrayPattern":
|
|
369
|
+
return node.elements.flatMap(boundNames);
|
|
370
|
+
case "ObjectPattern":
|
|
371
|
+
return node.properties.flatMap(
|
|
372
|
+
(p) => boundNames(p.type === "Property" ? p.value : p.argument)
|
|
373
|
+
);
|
|
374
|
+
default:
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
function declarations(node) {
|
|
379
|
+
if (node.type === "VariableDeclaration")
|
|
380
|
+
return node.declarations.flatMap((d) => boundNames(d.id));
|
|
381
|
+
if (node.type === "FunctionDeclaration" || node.type === "ClassDeclaration")
|
|
382
|
+
return boundNames(node.id);
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
385
|
+
function isFunction(node) {
|
|
386
|
+
return ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(
|
|
387
|
+
node.type
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
function freeNames(root) {
|
|
391
|
+
const references = /* @__PURE__ */ new Set();
|
|
392
|
+
function pattern(node, scope) {
|
|
393
|
+
if (node.type === "AssignmentPattern") {
|
|
394
|
+
visit(node.right, scope);
|
|
395
|
+
pattern(node.left, scope);
|
|
396
|
+
} else if (node.type === "ObjectPattern") {
|
|
397
|
+
for (const p of node.properties) {
|
|
398
|
+
if (p.type === "Property") {
|
|
399
|
+
if (p.computed) visit(p.key, scope);
|
|
400
|
+
pattern(p.value, scope);
|
|
401
|
+
} else pattern(p.argument, scope);
|
|
402
|
+
}
|
|
403
|
+
} else if (node.type === "ArrayPattern") {
|
|
404
|
+
for (const element of node.elements) if (element) pattern(element, scope);
|
|
405
|
+
} else if (node.type === "RestElement") pattern(node.argument, scope);
|
|
406
|
+
}
|
|
407
|
+
function visit(node, parent) {
|
|
408
|
+
if (node.type === "Identifier") {
|
|
409
|
+
if (!parent.has(node.name)) references.add(node.name);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (node.type === "ImportDeclaration") return;
|
|
413
|
+
if (node.type === "ExportSpecifier") {
|
|
414
|
+
visit(node.local, parent);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
|
|
418
|
+
let hoisted2 = function(body) {
|
|
419
|
+
if (isFunction(body)) return;
|
|
420
|
+
if (body.type === "VariableDeclaration" && body.kind === "var") {
|
|
421
|
+
for (const name of declarations(body)) scope.add(name);
|
|
422
|
+
}
|
|
423
|
+
for (const child of children(body)) hoisted2(child);
|
|
424
|
+
};
|
|
425
|
+
var hoisted = hoisted2;
|
|
426
|
+
const scope = /* @__PURE__ */ new Set([
|
|
427
|
+
...parent,
|
|
428
|
+
...boundNames(node.id),
|
|
429
|
+
...node.params.flatMap(boundNames)
|
|
430
|
+
]);
|
|
431
|
+
if (node.body) hoisted2(node.body);
|
|
432
|
+
for (const p of node.params) pattern(p, scope);
|
|
433
|
+
if (node.body) visit(node.body, scope);
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
if (node.type === "BlockStatement" || node.type === "Program") {
|
|
437
|
+
const scope = /* @__PURE__ */ new Set([...parent, ...node.body.flatMap(declarations)]);
|
|
438
|
+
for (const statement of node.body) visit(statement, scope);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (node.type === "VariableDeclarator") {
|
|
442
|
+
pattern(node.id, parent);
|
|
443
|
+
if (node.init) visit(node.init, parent);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (node.type === "Property" || node.type === "MethodDefinition" || node.type === "PropertyDefinition") {
|
|
447
|
+
if (node.computed) visit(node.key, parent);
|
|
448
|
+
if (node.value) visit(node.value, parent);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (node.type === "MemberExpression") {
|
|
452
|
+
visit(node.object, parent);
|
|
453
|
+
if (node.computed) visit(node.property, parent);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (node.type === "CatchClause") {
|
|
457
|
+
const scope = /* @__PURE__ */ new Set([...parent, ...boundNames(node.param)]);
|
|
458
|
+
if (node.param) pattern(node.param, scope);
|
|
459
|
+
visit(node.body, scope);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement") {
|
|
463
|
+
const declaration = node.type === "ForStatement" ? node.init : node.left;
|
|
464
|
+
const scope = /* @__PURE__ */ new Set([...parent, ...declaration ? declarations(declaration) : []]);
|
|
465
|
+
for (const child of children(node)) visit(child, scope);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
|
|
469
|
+
const scope = /* @__PURE__ */ new Set([...parent, ...boundNames(node.id)]);
|
|
470
|
+
if (node.superClass) visit(node.superClass, scope);
|
|
471
|
+
visit(node.body, scope);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (node.type === "LabeledStatement") {
|
|
475
|
+
visit(node.body, parent);
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
if (node.type === "BreakStatement" || node.type === "ContinueStatement") return;
|
|
479
|
+
for (const child of children(node)) visit(child, parent);
|
|
480
|
+
}
|
|
481
|
+
visit(root, /* @__PURE__ */ new Set());
|
|
482
|
+
return references;
|
|
483
|
+
}
|
|
484
|
+
function property(node, name) {
|
|
485
|
+
return node.properties.find(
|
|
486
|
+
(p) => p.type === "Property" && !p.computed && (p.key.type === "Identifier" ? p.key.name === name : p.key.type === "Literal" && p.key.value === name)
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
function parse(filename, source) {
|
|
490
|
+
const result = parseSync(filename, source);
|
|
491
|
+
if (result.errors.length)
|
|
492
|
+
throw new Error(`Unable to parse ${filename}: ${result.errors[0]?.message}`);
|
|
493
|
+
return result.program;
|
|
494
|
+
}
|
|
495
|
+
function units(program, source) {
|
|
496
|
+
const imports = [];
|
|
497
|
+
const statements = [];
|
|
498
|
+
for (const statement of program.body) {
|
|
499
|
+
if (statement.type === "ImportDeclaration") {
|
|
500
|
+
imports.push(statement);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
const exported = statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration";
|
|
504
|
+
const node = statement.type === "ExportNamedDeclaration" && statement.declaration ? statement.declaration : statement;
|
|
505
|
+
if (node.type === "VariableDeclaration") {
|
|
506
|
+
for (const d of node.declarations)
|
|
507
|
+
statements.push({
|
|
508
|
+
node: d,
|
|
509
|
+
names: boundNames(d.id),
|
|
510
|
+
code: `${exported ? "export " : ""}${node.kind} ${source.slice(d.start, d.end)};`,
|
|
511
|
+
exported
|
|
512
|
+
});
|
|
513
|
+
} else
|
|
514
|
+
statements.push({
|
|
515
|
+
node,
|
|
516
|
+
names: declarations(node),
|
|
517
|
+
code: source.slice(statement.start, statement.end),
|
|
518
|
+
exported
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
return { imports, statements };
|
|
522
|
+
}
|
|
523
|
+
function closure(roots, statements) {
|
|
524
|
+
const bindings = new Map(statements.flatMap((u) => u.names.map((name) => [name, u])));
|
|
525
|
+
const names = /* @__PURE__ */ new Set();
|
|
526
|
+
const selected = /* @__PURE__ */ new Set();
|
|
527
|
+
function add(name) {
|
|
528
|
+
if (names.has(name)) return;
|
|
529
|
+
names.add(name);
|
|
530
|
+
const unit = bindings.get(name);
|
|
531
|
+
if (!unit || selected.has(unit)) return;
|
|
532
|
+
selected.add(unit);
|
|
533
|
+
for (const ref of freeNames(unit.node)) add(ref);
|
|
534
|
+
}
|
|
535
|
+
for (const root of roots) add(root);
|
|
536
|
+
return { names, selected };
|
|
537
|
+
}
|
|
538
|
+
function renderImports(imports, needed, source, sideEffects) {
|
|
539
|
+
return imports.flatMap((i) => {
|
|
540
|
+
if (!i.specifiers.length) return sideEffects ? [source.slice(i.start, i.end)] : [];
|
|
541
|
+
const parts = i.specifiers.filter((s) => needed(s.local.name));
|
|
542
|
+
if (!parts.length) return [];
|
|
543
|
+
const defaultImport = parts.find((s) => s.type === "ImportDefaultSpecifier");
|
|
544
|
+
const namespace = parts.find((s) => s.type === "ImportNamespaceSpecifier");
|
|
545
|
+
const named = parts.filter((s) => s.type === "ImportSpecifier");
|
|
546
|
+
const clauses = [
|
|
547
|
+
defaultImport?.local.name,
|
|
548
|
+
namespace ? `* as ${namespace.local.name}` : void 0,
|
|
549
|
+
named.length ? `{ ${named.map((s) => `${source.slice(s.imported.start, s.imported.end)} as ${s.local.name}`).join(", ")} }` : void 0
|
|
550
|
+
].filter(Boolean);
|
|
551
|
+
return [`import ${clauses.join(", ")} from ${JSON.stringify(i.source.value)};`];
|
|
552
|
+
}).join("\n");
|
|
553
|
+
}
|
|
554
|
+
async function extractInstances(filename, source, root, includedNames) {
|
|
555
|
+
const transformed = await transformWithOxc(source, filename, {
|
|
556
|
+
jsx: { importSource: "preact" },
|
|
557
|
+
sourcemap: false
|
|
558
|
+
});
|
|
559
|
+
source = transformed.code;
|
|
560
|
+
const program = parse(filename.replace(/\.[^.]+$/u, ".js"), source);
|
|
561
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
562
|
+
for (const statement of program.body) {
|
|
563
|
+
if (statement.type !== "ImportDeclaration" || !clientImports.has(String(statement.source.value)))
|
|
564
|
+
continue;
|
|
565
|
+
for (const s of statement.specifiers) {
|
|
566
|
+
if (s.type === "ImportSpecifier" && s.imported.type === "Identifier" && s.imported.name === "createView")
|
|
567
|
+
aliases.add(s.local.name);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
let resolvers = [];
|
|
571
|
+
walk(program, (node, ancestors) => {
|
|
572
|
+
if (node.type !== "CallExpression" || node.callee.type !== "Identifier" || !aliases.has(node.callee.name))
|
|
573
|
+
return;
|
|
574
|
+
const options = node.arguments[1];
|
|
575
|
+
if (options?.type !== "ObjectExpression") return;
|
|
576
|
+
const instances = property(options, "instances");
|
|
577
|
+
if (!instances) return;
|
|
578
|
+
const fail = (message) => {
|
|
579
|
+
throw new Error(`${filename}: ${message}`);
|
|
580
|
+
};
|
|
581
|
+
if (ancestors.some(isFunction))
|
|
582
|
+
fail("createView with instances must be declared at module scope.");
|
|
583
|
+
const owner = ancestors.findLast((n) => n.type === "VariableDeclarator");
|
|
584
|
+
const viewName = owner?.type === "VariableDeclarator" && owner.id.type === "Identifier" ? owner.id.name : "default";
|
|
585
|
+
const view = node.arguments[0];
|
|
586
|
+
if (view?.type !== "Literal" || typeof view.value !== "string")
|
|
587
|
+
fail("Instance view paths must be string literals.");
|
|
588
|
+
if (instances.value.type !== "ObjectExpression")
|
|
589
|
+
fail("instances must be an inline object with dataSchema and resolve.");
|
|
590
|
+
const config = instances.value;
|
|
591
|
+
if (config.properties.some((p) => p.type !== "Property" || p.computed || p.kind !== "init"))
|
|
592
|
+
fail("Instance options cannot contain spreads, getters or computed properties.");
|
|
593
|
+
const resolve = property(config, "resolve");
|
|
594
|
+
const schema = property(config, "dataSchema");
|
|
595
|
+
if (!resolve || !schema || resolve.method || !["ArrowFunctionExpression", "FunctionExpression"].includes(resolve.value.type))
|
|
596
|
+
fail("instances requires dataSchema and an inline resolve function.");
|
|
597
|
+
const name = `i${createHash2("sha256").update(
|
|
598
|
+
`${path4.relative(root, filename).split(path4.sep).join("/")}\0${viewName}\0${view.value}`
|
|
599
|
+
).digest("hex").slice(0, 24)}`;
|
|
600
|
+
if (resolvers.some((r) => r.name === name))
|
|
601
|
+
fail("Instance resolvers must have distinct module-level variable names.");
|
|
602
|
+
resolvers.push({
|
|
603
|
+
name,
|
|
604
|
+
view: view.value,
|
|
605
|
+
call: node,
|
|
606
|
+
instances,
|
|
607
|
+
resolve,
|
|
608
|
+
schema
|
|
609
|
+
});
|
|
610
|
+
});
|
|
611
|
+
if (includedNames) {
|
|
612
|
+
resolvers = resolvers.filter((r) => includedNames.includes(r.name));
|
|
613
|
+
if (resolvers.length !== includedNames.length)
|
|
614
|
+
throw new Error(`${filename}: Instance definitions changed; rebuild the app client.`);
|
|
615
|
+
}
|
|
616
|
+
if (!resolvers.length) return;
|
|
617
|
+
const original = units(program, source);
|
|
618
|
+
const server = closure(
|
|
619
|
+
resolvers.flatMap((r) => [...freeNames(r.resolve.value), ...freeNames(r.schema.value)]),
|
|
620
|
+
original.statements
|
|
621
|
+
);
|
|
622
|
+
for (const name of server.names) {
|
|
623
|
+
if (browserGlobals.has(name))
|
|
624
|
+
throw new Error(`${filename}: Instance resolvers cannot access browser global ${name}.`);
|
|
625
|
+
}
|
|
626
|
+
for (const unit of server.selected) {
|
|
627
|
+
if (resolvers.some((r) => unit.node.start <= r.call.start && unit.node.end >= r.call.end))
|
|
628
|
+
throw new Error(
|
|
629
|
+
`${filename}: Instance resolvers cannot capture a view definition. Use the supplied context and queries.`
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
const serverSource = [
|
|
633
|
+
renderImports(original.imports, (name) => server.names.has(name), source, false),
|
|
634
|
+
...original.statements.filter((u) => server.selected.has(u)).map((u) => u.code.replace(/^export /u, "")),
|
|
635
|
+
`export const resolvers = { ${resolvers.map((r) => `${r.name}: { dataSchema: ${source.slice(r.schema.value.start, r.schema.value.end)}, resolve: ${source.slice(r.resolve.value.start, r.resolve.value.end)} }`).join(", ")} };`
|
|
636
|
+
].join("\n");
|
|
637
|
+
let browserSource = source;
|
|
638
|
+
for (const r of [...resolvers].sort(
|
|
639
|
+
(a, b) => b.instances.value.start - a.instances.value.start
|
|
640
|
+
)) {
|
|
641
|
+
browserSource = browserSource.slice(0, r.instances.value.start) + `{ resolver: ${JSON.stringify(r.name)} }` + browserSource.slice(r.instances.value.end);
|
|
642
|
+
}
|
|
643
|
+
const browser = units(parse("browser.js", browserSource), browserSource);
|
|
644
|
+
const serverBindings = new Set([...server.selected].flatMap((u) => u.names));
|
|
645
|
+
const browserRoots = browser.statements.filter(
|
|
646
|
+
(u) => u.exported || !u.names.length || !u.names.some((name) => serverBindings.has(name))
|
|
647
|
+
);
|
|
648
|
+
const browserNeeded = closure(
|
|
649
|
+
browserRoots.flatMap((u) => [...freeNames(u.node), ...u.names]),
|
|
650
|
+
browser.statements
|
|
651
|
+
);
|
|
652
|
+
return {
|
|
653
|
+
names: resolvers.map((r) => r.name),
|
|
654
|
+
server: serverSource,
|
|
655
|
+
browser: [
|
|
656
|
+
renderImports(
|
|
657
|
+
browser.imports,
|
|
658
|
+
(name) => !server.names.has(name) || browserNeeded.names.has(name),
|
|
659
|
+
browserSource,
|
|
660
|
+
true
|
|
661
|
+
),
|
|
662
|
+
...browser.statements.filter((u) => browserRoots.includes(u) || browserNeeded.selected.has(u)).map((u) => u.code)
|
|
663
|
+
].join("\n")
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
function instanceExtractionPlugin(root, serverConfigured, modules) {
|
|
667
|
+
return {
|
|
668
|
+
name: "tailorkit-instance-extraction",
|
|
669
|
+
enforce: "pre",
|
|
670
|
+
async transform(source, filename) {
|
|
671
|
+
if (filename.includes("node_modules") || !/\.[cm]?[jt]sx?$/u.test(filename) || !source.includes("instances") && !modules.has(filename))
|
|
672
|
+
return;
|
|
673
|
+
const result = await extractInstances(filename, source, root);
|
|
674
|
+
if (!result) {
|
|
675
|
+
modules.delete(filename);
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if (!serverConfigured)
|
|
679
|
+
throw new Error("View instances require server configuration in tailorkit.config.ts.");
|
|
680
|
+
modules.set(filename, { filename, names: result.names });
|
|
681
|
+
return { code: result.browser, map: null };
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function instanceServerPlugin(root, modules) {
|
|
686
|
+
return {
|
|
687
|
+
name: "tailorkit-instance-server-modules",
|
|
688
|
+
enforce: "pre",
|
|
689
|
+
resolveId(id) {
|
|
690
|
+
if (id.endsWith(suffix)) return id;
|
|
691
|
+
},
|
|
692
|
+
async load(id) {
|
|
693
|
+
if (!id.endsWith(suffix)) return;
|
|
694
|
+
const filename = id.slice(0, -suffix.length);
|
|
695
|
+
this.addWatchFile(filename);
|
|
696
|
+
const names = modules.find((module) => module.filename === filename)?.names;
|
|
697
|
+
const result = await extractInstances(
|
|
698
|
+
filename,
|
|
699
|
+
await readFile3(filename, "utf8"),
|
|
700
|
+
root,
|
|
701
|
+
names
|
|
702
|
+
);
|
|
703
|
+
if (!result)
|
|
704
|
+
throw new Error(`Instance resolver missing from ${filename}; rebuild the app client.`);
|
|
705
|
+
return result.server;
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function instanceServerImports(modules) {
|
|
710
|
+
return modules.map(
|
|
711
|
+
(m, i) => `import { resolvers as instances${i} } from ${JSON.stringify(m.filename + suffix)};`
|
|
712
|
+
).join("\n");
|
|
713
|
+
}
|
|
714
|
+
function instanceServerBindings(modules, registrations) {
|
|
715
|
+
return `[${registrations.map(({ slot, path: viewPath, resolver }) => {
|
|
716
|
+
const index = modules.findIndex((module) => module.names.includes(resolver));
|
|
717
|
+
if (index < 0) throw new Error("Instance resolver was not extracted by the app build.");
|
|
718
|
+
return `{ slot: ${JSON.stringify(slot)}, path: ${JSON.stringify(viewPath)}, ...instances${index}[${JSON.stringify(resolver)}] }`;
|
|
719
|
+
}).join(", ")}]`;
|
|
720
|
+
}
|
|
721
|
+
|
|
250
722
|
// src/builder/server.ts
|
|
251
|
-
async function buildServer(loaded, watch = false, buildOutDir) {
|
|
723
|
+
async function buildServer(loaded, watch = false, buildOutDir, instances = [], registrations = []) {
|
|
252
724
|
const config = loaded.config.server;
|
|
253
725
|
if (!config) {
|
|
254
726
|
return;
|
|
255
727
|
}
|
|
256
|
-
const output = buildOutDir ??
|
|
257
|
-
const directory =
|
|
258
|
-
const migrationsDirectory =
|
|
259
|
-
const migrationsOutput =
|
|
260
|
-
const temporaryRoot =
|
|
261
|
-
|
|
262
|
-
directory,
|
|
263
|
-
path4.join(output, "client"),
|
|
264
|
-
migrationsOutput,
|
|
265
|
-
temporaryRoot
|
|
266
|
-
]) {
|
|
267
|
-
if (overlaps(migrationsDirectory, destination)) {
|
|
268
|
-
throw new Error("App build output must not overlap migration source");
|
|
269
|
-
}
|
|
270
|
-
}
|
|
728
|
+
const output = buildOutDir ?? path5.resolve(loaded.root, loaded.config.build?.outDir ?? ".tailorkit");
|
|
729
|
+
const directory = path5.join(output, "server");
|
|
730
|
+
const migrationsDirectory = path5.resolve(loaded.root, config.migrations ?? "./migrations");
|
|
731
|
+
const migrationsOutput = path5.join(output, "migrations");
|
|
732
|
+
const temporaryRoot = path5.join(output, "tmp");
|
|
733
|
+
validateServerBuildOutput(loaded, output);
|
|
271
734
|
await rm2(directory, { recursive: true, force: true });
|
|
272
735
|
await mkdir(directory, { recursive: true });
|
|
273
736
|
await mkdir(temporaryRoot, { recursive: true });
|
|
274
|
-
const temporary = await mkdtemp2(
|
|
275
|
-
const entry =
|
|
737
|
+
const temporary = await mkdtemp2(path5.join(temporaryRoot, "server-"));
|
|
738
|
+
const entry = path5.join(temporary, "entry.mjs");
|
|
276
739
|
let watching = false;
|
|
740
|
+
let ready;
|
|
741
|
+
const initialBuild = watch ? new Promise((resolve) => {
|
|
742
|
+
ready = resolve;
|
|
743
|
+
}) : void 0;
|
|
277
744
|
try {
|
|
278
745
|
await writeFile(
|
|
279
746
|
entry,
|
|
280
|
-
|
|
747
|
+
`${instances.length ? `import app from ${JSON.stringify(path5.resolve(loaded.root, config.entry ?? "src/server.ts"))};
|
|
748
|
+
import { withInstanceResolvers } from "tailorkit/server";
|
|
749
|
+
${instanceServerImports(instances)}
|
|
750
|
+
export default withInstanceResolvers(app, ${instanceServerBindings(instances, registrations)});` : `export { default } from ${JSON.stringify(path5.resolve(loaded.root, config.entry ?? "src/server.ts"))};`}
|
|
281
751
|
export { default as migrations } from "tailorkit:migrations";
|
|
282
752
|
export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
|
|
283
753
|
`
|
|
@@ -286,6 +756,7 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
|
|
|
286
756
|
root: loaded.root,
|
|
287
757
|
configFile: false,
|
|
288
758
|
plugins: [
|
|
759
|
+
instanceServerPlugin(loaded.root, instances),
|
|
289
760
|
{
|
|
290
761
|
name: "tailorkit-app-migrations",
|
|
291
762
|
closeWatcher: () => rm2(temporary, { recursive: true, force: true }),
|
|
@@ -303,7 +774,7 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
|
|
|
303
774
|
config.migrations === void 0
|
|
304
775
|
);
|
|
305
776
|
await checkAppSchema(loaded.root, migrationsDirectory);
|
|
306
|
-
this.addWatchFile(
|
|
777
|
+
this.addWatchFile(path5.join(loaded.root, "src/schema.ts"));
|
|
307
778
|
for (const file of files) {
|
|
308
779
|
this.addWatchFile(file);
|
|
309
780
|
}
|
|
@@ -327,55 +798,65 @@ export const runtimeManifest = ${JSON.stringify(runtimeManifest)};
|
|
|
327
798
|
emptyOutDir: false,
|
|
328
799
|
target: "es2022",
|
|
329
800
|
watch: watch ? {} : null,
|
|
801
|
+
minify: "oxc",
|
|
330
802
|
rollupOptions: {
|
|
331
803
|
external: [],
|
|
332
|
-
output: {
|
|
804
|
+
output: {
|
|
805
|
+
entryFileNames: "server.js",
|
|
806
|
+
inlineDynamicImports: true,
|
|
807
|
+
minify: true,
|
|
808
|
+
minifyInternalExports: true
|
|
809
|
+
}
|
|
333
810
|
}
|
|
334
811
|
},
|
|
335
812
|
ssr: { noExternal: true }
|
|
336
813
|
});
|
|
337
814
|
watching = watch;
|
|
815
|
+
if (watch && "on" in result && initialBuild) {
|
|
816
|
+
try {
|
|
817
|
+
await Promise.race([
|
|
818
|
+
initialBuild,
|
|
819
|
+
new Promise(
|
|
820
|
+
(_, reject) => result.on("event", (event) => {
|
|
821
|
+
if (event.code === "ERROR") reject(event.error);
|
|
822
|
+
if (event.code === "END") ready?.();
|
|
823
|
+
})
|
|
824
|
+
)
|
|
825
|
+
]);
|
|
826
|
+
} catch (error) {
|
|
827
|
+
await result.close();
|
|
828
|
+
throw error;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
338
831
|
return result;
|
|
339
832
|
} finally {
|
|
340
|
-
if (!watching) {
|
|
341
|
-
|
|
833
|
+
if (!watching) await rm2(temporary, { recursive: true, force: true });
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
function validateServerBuildOutput(loaded, output) {
|
|
837
|
+
if (!loaded.config.server) return;
|
|
838
|
+
const migrationsDirectory = path5.resolve(
|
|
839
|
+
loaded.root,
|
|
840
|
+
loaded.config.server.migrations ?? "./migrations"
|
|
841
|
+
);
|
|
842
|
+
for (const name of ["server", "client", "migrations", "tmp"]) {
|
|
843
|
+
if (overlaps(migrationsDirectory, path5.join(output, name))) {
|
|
844
|
+
throw new Error("App build output must not overlap migration source");
|
|
342
845
|
}
|
|
343
846
|
}
|
|
344
847
|
}
|
|
345
848
|
function overlaps(source, destination) {
|
|
346
849
|
const contains = (parent, child) => {
|
|
347
|
-
const relative =
|
|
348
|
-
return relative === "" || !
|
|
850
|
+
const relative = path5.relative(parent, child);
|
|
851
|
+
return relative === "" || !path5.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path5.sep}`);
|
|
349
852
|
};
|
|
350
853
|
return contains(source, destination) || contains(destination, source);
|
|
351
854
|
}
|
|
352
855
|
|
|
353
|
-
// src/builder/upload-manifest.ts
|
|
354
|
-
import { z } from "zod";
|
|
355
|
-
var tailorkitUploadManifestSchema = z.object({
|
|
356
|
-
assets: z.object({
|
|
357
|
-
client: z.literal("client/client.js"),
|
|
358
|
-
server: z.literal("server/server.js").optional(),
|
|
359
|
-
logos: z.object({
|
|
360
|
-
dark: z.string().regex(/^client\/logo-dark\.(?:png|svg|webp)$/u).optional(),
|
|
361
|
-
light: z.string().regex(/^client\/logo-light\.(?:png|svg|webp)$/u).optional()
|
|
362
|
-
}).optional()
|
|
363
|
-
}),
|
|
364
|
-
version: z.literal(1)
|
|
365
|
-
});
|
|
366
|
-
var createTailorKitUploadManifest = (logos, server = false) => tailorkitUploadManifestSchema.parse({
|
|
367
|
-
assets: {
|
|
368
|
-
client: "client/client.js",
|
|
369
|
-
...server ? { server: "server/server.js" } : {},
|
|
370
|
-
...logos && Object.keys(logos).length > 0 ? { logos } : {}
|
|
371
|
-
},
|
|
372
|
-
version: 1
|
|
373
|
-
});
|
|
374
|
-
|
|
375
856
|
// src/builder/generate-migrations.ts
|
|
376
857
|
import { spawn } from "child_process";
|
|
377
858
|
import { access as access3 } from "fs/promises";
|
|
378
|
-
import
|
|
859
|
+
import path6 from "path";
|
|
379
860
|
async function generateAppMigrations(options = {}) {
|
|
380
861
|
const loaded = await loadTailorKitConfig(options.configPath, options.cwd);
|
|
381
862
|
if (!loaded.config.server) {
|
|
@@ -383,13 +864,13 @@ async function generateAppMigrations(options = {}) {
|
|
|
383
864
|
"Enable server: {} in tailorkit.config.ts before generating database migrations."
|
|
384
865
|
);
|
|
385
866
|
}
|
|
386
|
-
await access3(
|
|
867
|
+
await access3(path6.join(loaded.root, "src/schema.ts")).catch((error) => {
|
|
387
868
|
throw new Error(
|
|
388
869
|
"Define the app database schema in src/schema.ts before generating migrations.",
|
|
389
870
|
{ cause: error }
|
|
390
871
|
);
|
|
391
872
|
});
|
|
392
|
-
const directory =
|
|
873
|
+
const directory = path6.resolve(loaded.root, loaded.config.server.migrations ?? "./migrations");
|
|
393
874
|
const args = await drizzleGenerateArguments(loaded.root, directory, options.name);
|
|
394
875
|
await new Promise((resolve, reject) => {
|
|
395
876
|
const child = spawn(process.execPath, args, { cwd: loaded.root, stdio: "inherit" });
|
|
@@ -408,35 +889,61 @@ var preactPackageJson = "preact/package.json";
|
|
|
408
889
|
var preactPackageJsonModuleId = "\0tailorkit-preact-package-json";
|
|
409
890
|
var buildApp = async (options = {}) => {
|
|
410
891
|
const loaded = await loadTailorKitConfig(options.configPath, options.cwd);
|
|
411
|
-
|
|
412
|
-
const
|
|
892
|
+
loaded.root = await realpath(loaded.root);
|
|
893
|
+
const entry = path7.resolve(loaded.root, loaded.config.client?.entry ?? "src/client.ts");
|
|
894
|
+
const serverEntry = path7.resolve(loaded.root, loaded.config.server?.entry ?? "src/server.ts");
|
|
413
895
|
const outDir = options.outDir ?? loaded.config.build?.outDir ?? ".tailorkit";
|
|
414
896
|
const preactVersion = getInstalledPreactVersion(loaded.root);
|
|
415
897
|
assertSupportedPreactVersion(preactVersion);
|
|
416
|
-
const resolvedOutDir =
|
|
417
|
-
|
|
418
|
-
const
|
|
898
|
+
const resolvedOutDir = path7.resolve(loaded.root, outDir);
|
|
899
|
+
validateServerBuildOutput(loaded, resolvedOutDir);
|
|
900
|
+
const clientOutDir = path7.join(resolvedOutDir, "client");
|
|
901
|
+
const instanceModules = /* @__PURE__ */ new Map();
|
|
902
|
+
let serverWatcher;
|
|
419
903
|
const writeBuildExtras = async () => {
|
|
904
|
+
const { views, instanceResolvers } = await readClientManifest(
|
|
905
|
+
path7.join(clientOutDir, "client.js")
|
|
906
|
+
);
|
|
907
|
+
const resolverNames = new Set(instanceResolvers.map((registration) => registration.resolver));
|
|
908
|
+
const activeModules = [...instanceModules.values()].map((module) => ({
|
|
909
|
+
...module,
|
|
910
|
+
names: module.names.filter((name) => resolverNames.has(name))
|
|
911
|
+
})).filter((module) => module.names.length).sort((a, b) => a.filename.localeCompare(b.filename));
|
|
912
|
+
if (activeModules.reduce((count, module) => count + module.names.length, 0) !== resolverNames.size) {
|
|
913
|
+
throw new Error(
|
|
914
|
+
"Client references an instance resolver that was not extracted by the app build."
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
if (serverWatcher && "close" in serverWatcher) await serverWatcher.close();
|
|
918
|
+
serverWatcher = await buildServer(
|
|
919
|
+
loaded,
|
|
920
|
+
options.watch,
|
|
921
|
+
resolvedOutDir,
|
|
922
|
+
activeModules,
|
|
923
|
+
instanceResolvers
|
|
924
|
+
);
|
|
420
925
|
const logoManifest = {};
|
|
421
926
|
for (const variant of ["light", "dark"]) {
|
|
422
927
|
const configuredPath = loaded.config.logos?.[variant];
|
|
423
928
|
if (!configuredPath) {
|
|
424
929
|
continue;
|
|
425
930
|
}
|
|
426
|
-
const extension =
|
|
931
|
+
const extension = path7.extname(configuredPath).toLowerCase().slice(1);
|
|
427
932
|
const contentType = { png: "image/png", svg: "image/svg+xml", webp: "image/webp" }[extension];
|
|
428
933
|
if (!contentType) {
|
|
429
934
|
throw new Error(`The ${variant} logo must be an SVG, PNG, or WebP file.`);
|
|
430
935
|
}
|
|
431
|
-
const content = await
|
|
936
|
+
const content = await readFile4(path7.resolve(loaded.root, configuredPath));
|
|
432
937
|
validateLogoAsset(content, contentType);
|
|
433
938
|
const filename = `logo-${variant}.${extension}`;
|
|
434
|
-
await writeFile2(
|
|
939
|
+
await writeFile2(path7.join(clientOutDir, filename), content);
|
|
435
940
|
logoManifest[variant] = `client/${filename}`;
|
|
436
941
|
}
|
|
942
|
+
await writeFile2(path7.join(clientOutDir, "views.json"), `${JSON.stringify(views)}
|
|
943
|
+
`);
|
|
437
944
|
await writeFile2(
|
|
438
|
-
|
|
439
|
-
`${JSON.stringify(createTailorKitUploadManifest(logoManifest, Boolean(loaded.config.server)), null, 2)}
|
|
945
|
+
path7.join(resolvedOutDir, "tailorkit-upload.json"),
|
|
946
|
+
`${JSON.stringify(createTailorKitUploadManifest(logoManifest, Boolean(loaded.config.server), views), null, 2)}
|
|
440
947
|
`,
|
|
441
948
|
"utf-8"
|
|
442
949
|
);
|
|
@@ -451,6 +958,15 @@ var buildApp = async (options = {}) => {
|
|
|
451
958
|
void firstBuild.catch(() => {
|
|
452
959
|
});
|
|
453
960
|
}
|
|
961
|
+
const completeBuild = async () => {
|
|
962
|
+
try {
|
|
963
|
+
await writeBuildExtras();
|
|
964
|
+
firstBuildDone?.();
|
|
965
|
+
} catch (error) {
|
|
966
|
+
firstBuildFailed?.(error instanceof Error ? error : new Error(String(error)));
|
|
967
|
+
throw error;
|
|
968
|
+
}
|
|
969
|
+
};
|
|
454
970
|
const result = await viteBuild({
|
|
455
971
|
build: {
|
|
456
972
|
emptyOutDir: true,
|
|
@@ -478,13 +994,14 @@ var buildApp = async (options = {}) => {
|
|
|
478
994
|
mode: options.mode,
|
|
479
995
|
oxc: { jsx: { importSource: "preact" } },
|
|
480
996
|
plugins: [
|
|
997
|
+
instanceExtractionPlugin(loaded.root, Boolean(loaded.config.server), instanceModules),
|
|
481
998
|
{
|
|
482
999
|
name: "tailorkit-browser-server-boundary",
|
|
483
1000
|
enforce: "pre",
|
|
484
1001
|
async resolveId(id, importer) {
|
|
485
1002
|
if (/^(?:@tailorkit\/app\/server$|tailorkit\/(?:server|app\/server)$|effect(?:\/|$))/u.test(
|
|
486
1003
|
id
|
|
487
|
-
) || importer &&
|
|
1004
|
+
) || importer && path7.resolve(path7.dirname(importer), id) === serverEntry) {
|
|
488
1005
|
throw new Error(
|
|
489
1006
|
"App server implementations cannot be imported into a browser bundle. Import api from #tailorkit and server types with import type."
|
|
490
1007
|
);
|
|
@@ -503,13 +1020,7 @@ var buildApp = async (options = {}) => {
|
|
|
503
1020
|
{
|
|
504
1021
|
name: "tailorkit-preview-build-ready",
|
|
505
1022
|
async writeBundle() {
|
|
506
|
-
|
|
507
|
-
await writeBuildExtras();
|
|
508
|
-
firstBuildDone?.();
|
|
509
|
-
} catch (error) {
|
|
510
|
-
firstBuildFailed?.(error instanceof Error ? error : new Error(String(error)));
|
|
511
|
-
throw error;
|
|
512
|
-
}
|
|
1023
|
+
await completeBuild();
|
|
513
1024
|
}
|
|
514
1025
|
},
|
|
515
1026
|
{
|
|
@@ -532,9 +1043,7 @@ var buildApp = async (options = {}) => {
|
|
|
532
1043
|
],
|
|
533
1044
|
root: loaded.root
|
|
534
1045
|
}).catch(async (error) => {
|
|
535
|
-
if (serverWatcher &&
|
|
536
|
-
await serverWatcher.close();
|
|
537
|
-
}
|
|
1046
|
+
if (serverWatcher && "close" in serverWatcher) await serverWatcher.close();
|
|
538
1047
|
throw error;
|
|
539
1048
|
});
|
|
540
1049
|
if (options.watch) {
|
|
@@ -551,26 +1060,24 @@ var buildApp = async (options = {}) => {
|
|
|
551
1060
|
if (result && typeof result === "object" && "close" in result) {
|
|
552
1061
|
await result.close();
|
|
553
1062
|
}
|
|
554
|
-
if (serverWatcher &&
|
|
555
|
-
await serverWatcher.close();
|
|
556
|
-
}
|
|
1063
|
+
if (serverWatcher && "close" in serverWatcher) await serverWatcher.close();
|
|
557
1064
|
throw error;
|
|
558
1065
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
}
|
|
568
|
-
}
|
|
1066
|
+
if (result && "close" in result && serverWatcher && "close" in serverWatcher) {
|
|
1067
|
+
const clientWatcher = result;
|
|
1068
|
+
return {
|
|
1069
|
+
on: clientWatcher.on.bind(clientWatcher),
|
|
1070
|
+
async close() {
|
|
1071
|
+
await clientWatcher.close();
|
|
1072
|
+
if (serverWatcher && "close" in serverWatcher) await serverWatcher.close();
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
569
1076
|
}
|
|
570
1077
|
return result;
|
|
571
1078
|
};
|
|
572
1079
|
function getInstalledPreactVersion(root) {
|
|
573
|
-
const require2 = createRequire2(
|
|
1080
|
+
const require2 = createRequire2(path7.join(root, "package.json"));
|
|
574
1081
|
try {
|
|
575
1082
|
const packageJsonPath = require2.resolve("preact/package.json");
|
|
576
1083
|
const packageJson = require2(packageJsonPath);
|
|
@@ -8,26 +8,46 @@ import { createContext, h, render } from "preact";
|
|
|
8
8
|
import { useContext } from "preact/hooks";
|
|
9
9
|
var preactVersion = "10.29.8";
|
|
10
10
|
var createView = (path, options) => {
|
|
11
|
-
const Context2 = createContext(
|
|
11
|
+
const Context2 = createContext(
|
|
12
|
+
null
|
|
13
|
+
);
|
|
14
|
+
const instances = options.instances;
|
|
15
|
+
if (instances && !instances.resolver) {
|
|
16
|
+
throw new Error("View instance resolvers must be compiled with the TailorKit app build.");
|
|
17
|
+
}
|
|
12
18
|
const View = (props) => {
|
|
13
19
|
if (props.status !== "ready") {
|
|
14
20
|
return null;
|
|
15
21
|
}
|
|
22
|
+
if (instances && !props.instance) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`View "${path}" requires a selected instance. Pass instanceKey to Slot or instance to Slot.Controlled.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
16
27
|
return h(
|
|
17
28
|
Context2.Provider,
|
|
18
|
-
{ value: props.context },
|
|
19
|
-
h(options.component, {})
|
|
29
|
+
{ value: { context: props.context, instance: props.instance } },
|
|
30
|
+
h(options.component, { key: props.instance?.key })
|
|
20
31
|
);
|
|
21
32
|
};
|
|
22
33
|
return {
|
|
23
34
|
component: View,
|
|
24
35
|
path,
|
|
36
|
+
...instances?.resolver ? { instances: { resolver: instances.resolver } } : {},
|
|
25
37
|
useContext: () => {
|
|
26
38
|
const context = useContext(Context2);
|
|
27
39
|
if (context === null) {
|
|
28
40
|
throw new Error(`View context is only available while rendering "${path}".`);
|
|
29
41
|
}
|
|
30
|
-
return context;
|
|
42
|
+
return context.context;
|
|
43
|
+
},
|
|
44
|
+
useInstance: () => {
|
|
45
|
+
const value = useContext(Context2);
|
|
46
|
+
if (!value?.instance)
|
|
47
|
+
throw new Error(
|
|
48
|
+
`View instance is only available while rendering an instance of "${path}".`
|
|
49
|
+
);
|
|
50
|
+
return value.instance;
|
|
31
51
|
}
|
|
32
52
|
};
|
|
33
53
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppError
|
|
3
|
+
} from "./chunk-IJBQ77AH.js";
|
|
4
|
+
|
|
1
5
|
// src/server/functions.ts
|
|
2
6
|
function query(definition) {
|
|
3
7
|
return Object.freeze({ ...definition, kind: "query" });
|
|
@@ -59,6 +63,78 @@ var tk = Object.freeze({
|
|
|
59
63
|
action: new FunctionBuilder("action", z.undefined(), void 0, {})
|
|
60
64
|
});
|
|
61
65
|
|
|
66
|
+
// src/server/instances.ts
|
|
67
|
+
import { z as z2 } from "zod";
|
|
68
|
+
var addressSchema = z2.object({
|
|
69
|
+
slot: z2.string().min(1).max(255),
|
|
70
|
+
path: z2.string().startsWith("/").max(1024)
|
|
71
|
+
});
|
|
72
|
+
function registeredQueries(functions) {
|
|
73
|
+
const queries = {};
|
|
74
|
+
for (const [name, fn] of Object.entries(functions)) {
|
|
75
|
+
if (isFunction(fn)) {
|
|
76
|
+
if (fn.kind === "query") queries[name] = fn;
|
|
77
|
+
} else {
|
|
78
|
+
const nested = registeredQueries(fn);
|
|
79
|
+
if (Object.keys(nested).length) queries[name] = nested;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return queries;
|
|
83
|
+
}
|
|
84
|
+
function withInstanceResolvers(app, resolvers) {
|
|
85
|
+
if (Object.hasOwn(app.functions, "_tailorkit")) {
|
|
86
|
+
throw new Error("The _tailorkit function namespace is reserved by TailorKit.");
|
|
87
|
+
}
|
|
88
|
+
const queries = registeredQueries(app.functions);
|
|
89
|
+
const registry = /* @__PURE__ */ new Map();
|
|
90
|
+
for (const resolver of resolvers) {
|
|
91
|
+
addressSchema.parse(resolver);
|
|
92
|
+
let paths = registry.get(resolver.slot);
|
|
93
|
+
if (!paths) {
|
|
94
|
+
paths = /* @__PURE__ */ new Map();
|
|
95
|
+
registry.set(resolver.slot, paths);
|
|
96
|
+
}
|
|
97
|
+
if (paths.has(resolver.path)) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Duplicate instance resolver for slot "${resolver.slot}" and path "${resolver.path}".`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const result = z2.array(
|
|
103
|
+
z2.object({
|
|
104
|
+
key: z2.string().min(1),
|
|
105
|
+
metadata: z2.record(z2.string(), z2.unknown()),
|
|
106
|
+
data: resolver.dataSchema
|
|
107
|
+
})
|
|
108
|
+
).refine((items) => new Set(items.map((item) => item.key)).size === items.length, {
|
|
109
|
+
message: "Instance keys must be unique"
|
|
110
|
+
});
|
|
111
|
+
paths.set(resolver.path, { resolver, result });
|
|
112
|
+
}
|
|
113
|
+
const instances = {
|
|
114
|
+
resolve: action({
|
|
115
|
+
functions: queries,
|
|
116
|
+
args: addressSchema.extend({ context: z2.record(z2.string(), z2.unknown()) }),
|
|
117
|
+
async handler({ args, identity, signal, queries: queries2 }) {
|
|
118
|
+
const entry = registry.get(args.slot)?.get(args.path);
|
|
119
|
+
if (!entry) throw new AppError("NOT_FOUND", "Instance resolver not found");
|
|
120
|
+
const value = await entry.resolver.resolve({
|
|
121
|
+
context: args.context,
|
|
122
|
+
identity,
|
|
123
|
+
signal,
|
|
124
|
+
queries: queries2
|
|
125
|
+
});
|
|
126
|
+
const result = entry.result.safeParse(value);
|
|
127
|
+
if (!result.success) throw new AppError("INTERNAL_SERVER_ERROR", "Invalid function result");
|
|
128
|
+
return result.data;
|
|
129
|
+
}
|
|
130
|
+
})
|
|
131
|
+
};
|
|
132
|
+
const internal = defineServer({ instances });
|
|
133
|
+
return Object.freeze({
|
|
134
|
+
functions: { ...app.functions, _tailorkit: internal.functions }
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
62
138
|
// src/database/schema.ts
|
|
63
139
|
import {
|
|
64
140
|
sqliteTable,
|
|
@@ -98,6 +174,7 @@ export {
|
|
|
98
174
|
action,
|
|
99
175
|
defineServer,
|
|
100
176
|
tk,
|
|
177
|
+
withInstanceResolvers,
|
|
101
178
|
table,
|
|
102
179
|
text,
|
|
103
180
|
integer,
|
package/dist/client.js
CHANGED
package/dist/config/config.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface TailorKitConfig extends z.input<typeof tailorkitConfigSchema> {
|
|
|
25
25
|
/**
|
|
26
26
|
* ID of the app on the TailorKit host, used for deployment and remote preview.
|
|
27
27
|
* When omitted, the first deployment creates an app and saves its ID here.
|
|
28
|
+
* The agent command prompts to select or create an app and saves its ID here.
|
|
28
29
|
* Remote preview requires an existing app ID.
|
|
29
30
|
* @default undefined
|
|
30
31
|
*/
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
useAction,
|
|
11
11
|
useMutation,
|
|
12
12
|
useQuery
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-43L6XVW6.js";
|
|
14
14
|
import {
|
|
15
15
|
action,
|
|
16
16
|
and,
|
|
@@ -35,8 +35,9 @@ import {
|
|
|
35
35
|
real,
|
|
36
36
|
table,
|
|
37
37
|
text,
|
|
38
|
-
tk
|
|
39
|
-
|
|
38
|
+
tk,
|
|
39
|
+
withInstanceResolvers
|
|
40
|
+
} from "./chunk-7NOEYJOB.js";
|
|
40
41
|
import {
|
|
41
42
|
AppError,
|
|
42
43
|
appError
|
|
@@ -78,5 +79,6 @@ export {
|
|
|
78
79
|
tk,
|
|
79
80
|
useAction,
|
|
80
81
|
useMutation,
|
|
81
|
-
useQuery
|
|
82
|
+
useQuery,
|
|
83
|
+
withInstanceResolvers
|
|
82
84
|
};
|
package/dist/protocol.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { oc, type } from "@orpc/contract";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
var invocationSchema = z.strictObject({
|
|
5
|
-
name: z.string().max(512).regex(/^[a-zA-Z][a-zA-Z0-9_]{0,63}(?:\.[a-zA-Z][a-zA-Z0-9_]{0,63})*$/u),
|
|
5
|
+
name: z.string().max(512).regex(/^(?:[a-zA-Z][a-zA-Z0-9_]{0,63}|_tailorkit)(?:\.[a-zA-Z][a-zA-Z0-9_]{0,63})*$/u),
|
|
6
6
|
args: z.unknown().optional()
|
|
7
7
|
});
|
|
8
8
|
var procedure = oc.errors({
|
|
@@ -14,7 +14,7 @@ export type FunctionResult<D> = D extends {
|
|
|
14
14
|
} ? z.output<R> : D extends {
|
|
15
15
|
handler: (context: never) => infer O;
|
|
16
16
|
} ? Awaited<O> : never;
|
|
17
|
-
type FunctionCalls<F, K extends "query" | "mutation"> = {
|
|
17
|
+
export type FunctionCalls<F, K extends "query" | "mutation"> = {
|
|
18
18
|
readonly [N in keyof F as F[N] extends {
|
|
19
19
|
kind: FunctionKind;
|
|
20
20
|
} ? (F[N] extends {
|
|
@@ -87,4 +87,3 @@ export declare function defineServer<const F extends Functions>(functions: F): R
|
|
|
87
87
|
functions: F;
|
|
88
88
|
}>;
|
|
89
89
|
export type AppDefinition = ReturnType<typeof defineServer>;
|
|
90
|
-
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { AppDefinition, Identity } from "./functions";
|
|
3
|
+
interface InstanceResolver {
|
|
4
|
+
slot: string;
|
|
5
|
+
path: string;
|
|
6
|
+
dataSchema: z.ZodType;
|
|
7
|
+
resolve: (context: {
|
|
8
|
+
context: Record<string, unknown>;
|
|
9
|
+
identity: Identity;
|
|
10
|
+
signal: AbortSignal;
|
|
11
|
+
queries: unknown;
|
|
12
|
+
}) => unknown;
|
|
13
|
+
}
|
|
14
|
+
/** Build-generated actions run through the existing authenticated action runtime. */
|
|
15
|
+
export declare function withInstanceResolvers(app: AppDefinition, resolvers: readonly InstanceResolver[]): AppDefinition;
|
|
16
|
+
export {};
|
package/dist/server.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { defineServer, query, mutation, action } from "./server/functions";
|
|
2
2
|
export { tk } from "./server/builder";
|
|
3
|
+
export { withInstanceResolvers } from "./server/instances";
|
|
3
4
|
export type { QueryDatabase, MutationDatabase } from "./database/types";
|
|
4
5
|
export type { Identity, FunctionDefinition, AppDefinition, ActionContext, FunctionKind, } from "./server/functions";
|
|
5
6
|
export { table, text, integer, real, boolean, eq, ne, gt, gte, lt, lte, and, or, asc, desc, count, isNull, isNotNull, inArray, } from "./database/schema";
|
package/dist/server.js
CHANGED
|
@@ -22,8 +22,9 @@ import {
|
|
|
22
22
|
real,
|
|
23
23
|
table,
|
|
24
24
|
text,
|
|
25
|
-
tk
|
|
26
|
-
|
|
25
|
+
tk,
|
|
26
|
+
withInstanceResolvers
|
|
27
|
+
} from "./chunk-7NOEYJOB.js";
|
|
27
28
|
import {
|
|
28
29
|
AppError
|
|
29
30
|
} from "./chunk-IJBQ77AH.js";
|
|
@@ -52,5 +53,6 @@ export {
|
|
|
52
53
|
real,
|
|
53
54
|
table,
|
|
54
55
|
text,
|
|
55
|
-
tk
|
|
56
|
+
tk,
|
|
57
|
+
withInstanceResolvers
|
|
56
58
|
};
|
package/dist/views.d.ts
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import { h } from "preact";
|
|
2
2
|
import type { ComponentChild, ComponentChildren, ComponentType, VNode } from "preact";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
import type { FunctionCalls, Identity } from "./server/functions";
|
|
3
5
|
export interface TailorKitViews {
|
|
4
6
|
}
|
|
5
7
|
export interface TailorKitSlots {
|
|
6
8
|
}
|
|
9
|
+
export interface TailorKitServerFunctions {
|
|
10
|
+
}
|
|
11
|
+
export interface ViewInstance<TData = unknown> {
|
|
12
|
+
key: string;
|
|
13
|
+
metadata: Record<string, unknown>;
|
|
14
|
+
data: TData;
|
|
15
|
+
}
|
|
16
|
+
export interface InstanceResolverContext<TPath extends AppViewPath> {
|
|
17
|
+
context: ViewContext<TPath>;
|
|
18
|
+
identity: Identity;
|
|
19
|
+
signal: AbortSignal;
|
|
20
|
+
queries: FunctionCalls<TailorKitServerFunctions, "query">;
|
|
21
|
+
}
|
|
22
|
+
export interface ViewInstances<TPath extends AppViewPath, TSchema extends z.ZodType> {
|
|
23
|
+
dataSchema: TSchema;
|
|
24
|
+
/** Runs on the app server. Only registered queries are available. */
|
|
25
|
+
resolve: (context: InstanceResolverContext<TPath>) => ViewInstance<z.input<TSchema>>[] | Promise<ViewInstance<z.input<TSchema>>[]>;
|
|
26
|
+
}
|
|
7
27
|
export type SlotName = keyof TailorKitSlots & string;
|
|
8
28
|
export type ViewPath = Extract<keyof TailorKitViews & string, `/${string}`>;
|
|
9
29
|
export type AppViewPath = ViewPath;
|
|
@@ -13,23 +33,32 @@ export type ViewContext<TPath extends AppViewPath> = ViewPropsForPath<TPath> ext
|
|
|
13
33
|
context: infer TContext;
|
|
14
34
|
} ? TContext : Record<string, never>;
|
|
15
35
|
export type View<TProps extends object = Record<string, never>> = (props: TProps) => ComponentChild;
|
|
16
|
-
export type ViewRuntimeProps<TPath extends AppViewPath> = {
|
|
36
|
+
export type ViewRuntimeProps<TPath extends AppViewPath, TData = unknown> = {
|
|
17
37
|
context: ViewContext<TPath>;
|
|
18
38
|
view: TPath;
|
|
19
39
|
status: "ready";
|
|
40
|
+
instance?: ViewInstance<TData>;
|
|
20
41
|
} | {
|
|
21
42
|
context?: never;
|
|
22
43
|
view: TPath;
|
|
23
44
|
status: "loading";
|
|
45
|
+
instance?: never;
|
|
24
46
|
} | {
|
|
25
47
|
context?: never;
|
|
26
48
|
view: TPath;
|
|
27
49
|
status: "error";
|
|
50
|
+
instance?: never;
|
|
28
51
|
};
|
|
29
|
-
export interface ViewDefinition<TPath extends AppViewPath = AppViewPath> {
|
|
52
|
+
export interface ViewDefinition<TPath extends AppViewPath = AppViewPath, TData = unknown> {
|
|
30
53
|
component: View<ViewRuntimeProps<TPath>>;
|
|
31
54
|
path: TPath;
|
|
32
55
|
useContext: () => ViewContext<TPath>;
|
|
56
|
+
/** The selected instance, with data inferred from instances.dataSchema. */
|
|
57
|
+
useInstance: () => ViewInstance<TData>;
|
|
58
|
+
/** Build-only lookup key connecting this view to its extracted server implementation. */
|
|
59
|
+
instances?: {
|
|
60
|
+
resolver: string;
|
|
61
|
+
};
|
|
33
62
|
}
|
|
34
63
|
type RegisteredViews = {
|
|
35
64
|
[TPath in AppViewPath]: ViewDefinition<TPath>;
|
|
@@ -72,9 +101,10 @@ export type TailorKitClientWithMeta<TViews extends SlotDefinitions = SlotDefinit
|
|
|
72
101
|
$meta: TailorKitClientMeta;
|
|
73
102
|
$runtime: TailorKitClientRuntime;
|
|
74
103
|
};
|
|
75
|
-
export declare const createView: <const TPath extends AppViewPath>(path: TPath, options: {
|
|
104
|
+
export declare const createView: <const TPath extends AppViewPath, TSchema extends z.ZodType>(path: TPath, options: {
|
|
76
105
|
component: View<Record<string, never>>;
|
|
77
|
-
|
|
106
|
+
instances?: ViewInstances<TPath, TSchema>;
|
|
107
|
+
}) => ViewDefinition<TPath, z.output<TSchema>>;
|
|
78
108
|
export declare const defineClient: <const TSlots extends SlotDefinitions>(client: TailorKitClient<TSlots> & {
|
|
79
109
|
slots: { [V in keyof TSlots]: TSlots[V] & RequireViewPaths<TSlots[V]> & RequireMatchingViewKeys<TSlots[V]> & RequireSlotViews<V, TSlots[V]>; };
|
|
80
110
|
} & (Exclude<keyof TSlots, SlotName> extends never ? unknown : {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailorkit/app",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.19",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@orpc/client": "2.0.0-beta.40",
|
|
49
49
|
"@orpc/contract": "2.0.0-beta.40",
|
|
50
|
-
"@tanstack/preact-store": "0.13.
|
|
51
|
-
"@tanstack/store": "0.11.
|
|
50
|
+
"@tanstack/preact-store": "0.13.4",
|
|
51
|
+
"@tanstack/store": "0.11.2",
|
|
52
52
|
"cosmiconfig": "^10.0.1",
|
|
53
53
|
"drizzle-orm": "1.0.0-rc.4-5d5b77c",
|
|
54
54
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.0.0",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@tailorkit/asset-delivery": "0.0.0",
|
|
59
59
|
"@tailorkit/config": "0.0.0",
|
|
60
|
-
"@types/node": "26.6.
|
|
60
|
+
"@types/node": "26.6.4",
|
|
61
61
|
"happy-dom": "^20.14.5",
|
|
62
62
|
"preact": "^10.29.8",
|
|
63
63
|
"tsup": "^8.5.1",
|