zitejs 0.9.141 → 0.9.143
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/cjs/integrations/rules.d.ts +9 -0
- package/dist/cjs/integrations/rules.js +22 -0
- package/dist/cjs/meta/index.d.ts +14 -0
- package/dist/cjs/meta/index.js +12 -0
- package/dist/cjs/notifications/index.d.ts +25 -0
- package/dist/cjs/notifications/index.js +12 -0
- package/dist/esm/integrations/rules.d.ts +9 -0
- package/dist/esm/integrations/rules.js +22 -0
- package/dist/esm/meta/index.d.ts +14 -0
- package/dist/esm/meta/index.js +8 -0
- package/dist/esm/notifications/index.d.ts +25 -0
- package/dist/esm/notifications/index.js +8 -0
- package/package.json +1 -1
- package/dist/cjs/start/describeBuilt.d.ts +0 -3
- package/dist/cjs/start/describeBuilt.js +0 -106
- package/dist/cjs/start/entries.d.ts +0 -20
- package/dist/cjs/start/entries.js +0 -104
- package/dist/cjs/start/problems.d.ts +0 -19
- package/dist/cjs/start/problems.js +0 -11
- package/dist/esm/start/describe.d.ts +0 -64
- package/dist/esm/start/describe.js +0 -176
- package/dist/esm/start/describeBuilt.d.ts +0 -3
- package/dist/esm/start/describeBuilt.js +0 -66
- package/dist/esm/start/descriptions.d.ts +0 -4
- package/dist/esm/start/descriptions.js +0 -47
- package/dist/esm/start/entries.d.ts +0 -20
- package/dist/esm/start/entries.js +0 -93
- package/dist/esm/start/host.d.ts +0 -3
- package/dist/esm/start/host.js +0 -4
- package/dist/esm/start/index.d.ts +0 -79
- package/dist/esm/start/index.js +0 -157
- package/dist/esm/start/instance.d.ts +0 -12
- package/dist/esm/start/instance.js +0 -73
- package/dist/esm/start/problems.d.ts +0 -19
- package/dist/esm/start/problems.js +0 -8
- package/dist/esm/start/registration.d.ts +0 -13
- package/dist/esm/start/registration.js +0 -21
- package/dist/esm/start/router.d.ts +0 -17
- package/dist/esm/start/router.js +0 -37
- package/dist/esm/start/runLog.d.ts +0 -5
- package/dist/esm/start/runLog.js +0 -18
- package/dist/esm/start/scope.d.ts +0 -15
- package/dist/esm/start/scope.js +0 -58
- package/dist/esm/start/server.d.ts +0 -51
- package/dist/esm/start/server.js +0 -215
- package/dist/esm/start/types.d.ts +0 -94
- package/dist/esm/start/types.js +0 -1
- package/dist/esm/start/vite.d.ts +0 -9
- package/dist/esm/start/vite.js +0 -144
- package/dist/esm/start/worker.d.ts +0 -11
- package/dist/esm/start/worker.js +0 -20
- package/dist/esm/start/workflow.d.ts +0 -50
- package/dist/esm/start/workflow.js +0 -13
- package/dist/esm/start/workflow.test-d.d.ts +0 -1
- package/dist/esm/start/workflow.test-d.js +0 -39
- package/dist/esm/start/workflowModules.d.ts +0 -1
- package/dist/esm/start/workflowModules.js +0 -3
- package/dist/esm/start/workflowRun.d.ts +0 -80
- package/dist/esm/start/workflowRun.js +0 -107
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
// Describes a built app by loading it, not by reading its source: each server
|
|
2
|
-
// function's real middleware chain, each server route's, and input schemas.
|
|
3
|
-
import { createMiddleware, flattenMiddlewares } from "@tanstack/react-start";
|
|
4
|
-
import { createSsrRpc } from "@tanstack/react-start/ssr-rpc";
|
|
5
|
-
import { runWithStartContext } from "@tanstack/start-storage-context";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
import { ziteMiddlewareKind } from "./index.js";
|
|
8
|
-
const accessOf = (chain, problems) => {
|
|
9
|
-
const kinds = chain.map(ziteMiddlewareKind).filter((k) => !!k);
|
|
10
|
-
const roles = kinds.flatMap((k) => (k.kind === "role" ? [k.role] : []));
|
|
11
|
-
const base = [...new Set(kinds.flatMap((k) => (k.kind === "signedIn" || k.kind === "anyone" || k.kind === "webhook" ? [k.kind] : [])))];
|
|
12
|
-
if (base.length === 0) {
|
|
13
|
-
problems.push("access-missing: no signedIn, anyone or webhook middleware in its chain");
|
|
14
|
-
return { kind: "none", roles };
|
|
15
|
-
}
|
|
16
|
-
if (base.length > 1)
|
|
17
|
-
problems.push(`access-conflict: ${base.join(" + ")}`);
|
|
18
|
-
const kind = base.includes("webhook") ? "webhook" : base.includes("signedIn") ? "signedIn" : "anyone";
|
|
19
|
-
const paused = kinds.some((k) => k.kind === "webhook" && k.paused === true);
|
|
20
|
-
return paused ? { kind, roles, paused } : { kind, roles };
|
|
21
|
-
};
|
|
22
|
-
const jsonSchemaOf = (validator) => {
|
|
23
|
-
if (validator && typeof validator === "object" && "_zod" in validator) {
|
|
24
|
-
try {
|
|
25
|
-
return z.toJSONSchema(validator);
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return { unrepresentable: true };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return validator ? { opaque: true } : undefined;
|
|
32
|
-
};
|
|
33
|
-
// Describe walks functions one at a time, so one probe slot is enough.
|
|
34
|
-
let probe = null;
|
|
35
|
-
// The only global function middleware while describing: it records what it was
|
|
36
|
-
// handed and ends the call, so no app middleware or handler runs.
|
|
37
|
-
const describeProbe = createMiddleware({ type: "function" }).server(({ serverFnMeta, method }) => {
|
|
38
|
-
if (probe)
|
|
39
|
-
Object.assign(probe, { meta: serverFnMeta, method, reached: true });
|
|
40
|
-
return "zite:describe";
|
|
41
|
-
});
|
|
42
|
-
const SERVER_ONLY = "server-only: nothing in the browser calls it, and a server function that only other server functions call can't run. " +
|
|
43
|
-
"Move the shared logic into a *.server.ts helper and call that.";
|
|
44
|
-
export async function describeFunction(id, request) {
|
|
45
|
-
const problems = [];
|
|
46
|
-
const seen = [];
|
|
47
|
-
probe = { reached: false };
|
|
48
|
-
// Before running a chain, TanStack asks this set about every middleware in it.
|
|
49
|
-
// Recording the questions gives the exact chain it would have run.
|
|
50
|
-
const executedRequestMiddlewares = {
|
|
51
|
-
has: (middleware) => {
|
|
52
|
-
seen.push(middleware);
|
|
53
|
-
return false;
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
const startContext = {
|
|
57
|
-
request,
|
|
58
|
-
startOptions: { functionMiddleware: [describeProbe] },
|
|
59
|
-
executedRequestMiddlewares,
|
|
60
|
-
contextAfterGlobalMiddlewares: {},
|
|
61
|
-
handlerType: "serverFn",
|
|
62
|
-
getRouter: () => {
|
|
63
|
-
throw new Error("describe has no router");
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
try {
|
|
67
|
-
await runWithStartContext(startContext, () => createSsrRpc(id)({ data: undefined, context: {} }));
|
|
68
|
-
}
|
|
69
|
-
catch (error) {
|
|
70
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
-
// TanStack registers only functions something can call through it. One that
|
|
72
|
-
// only other server functions call isn't registered, and calling it fails.
|
|
73
|
-
if (message === `Server function info not found for ${id}`) {
|
|
74
|
-
probe = null;
|
|
75
|
-
return { id, access: { kind: "none", roles: [] }, problems: [SERVER_ONLY] };
|
|
76
|
-
}
|
|
77
|
-
problems.push(`probe-failed: ${message}`);
|
|
78
|
-
}
|
|
79
|
-
const state = probe;
|
|
80
|
-
probe = null;
|
|
81
|
-
// Fails closed: if TanStack stops asking, or stops running globals first, every
|
|
82
|
-
// function reports a problem and the build stops.
|
|
83
|
-
if (!state.reached || seen[0] !== describeProbe)
|
|
84
|
-
problems.push("probe-not-first: chain could not be read");
|
|
85
|
-
const chain = seen.filter((m) => m !== describeProbe);
|
|
86
|
-
if (!chain.some((m) => ziteMiddlewareKind(m)?.kind === "factory")) {
|
|
87
|
-
problems.push("raw-server-fn: define it with signedInFn or publicFn from zitejs/start");
|
|
88
|
-
}
|
|
89
|
-
// TanStack keeps the validator under `inputValidator` up to 1.168 and `validator` from 1.169 (Start 1.0).
|
|
90
|
-
const handlerStep = chain.at(-1);
|
|
91
|
-
return {
|
|
92
|
-
id,
|
|
93
|
-
name: state.meta?.name,
|
|
94
|
-
file: state.meta?.filename,
|
|
95
|
-
method: state.method,
|
|
96
|
-
access: accessOf(chain, problems),
|
|
97
|
-
input: jsonSchemaOf(handlerStep?.options?.validator ?? handlerStep?.options?.inputValidator),
|
|
98
|
-
problems,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
export function describeRoutes(routesById) {
|
|
102
|
-
const out = [];
|
|
103
|
-
for (const route of Object.values(routesById)) {
|
|
104
|
-
const server = route.options?.server;
|
|
105
|
-
if (!server?.handlers)
|
|
106
|
-
continue;
|
|
107
|
-
const problems = [];
|
|
108
|
-
const ancestry = [];
|
|
109
|
-
for (let r = route; r; r = r.parentRoute)
|
|
110
|
-
ancestry.unshift(r);
|
|
111
|
-
const routeMiddleware = ancestry.flatMap((r) => r.options?.server?.middleware ?? []);
|
|
112
|
-
const handlers = (typeof server.handlers === "function"
|
|
113
|
-
? server.handlers({ createHandlers: (d) => d })
|
|
114
|
-
: server.handlers);
|
|
115
|
-
const methods = {};
|
|
116
|
-
for (const [method, handler] of Object.entries(handlers)) {
|
|
117
|
-
const own = typeof handler === "function" ? [] : (handler.middleware ?? []);
|
|
118
|
-
const chain = flattenMiddlewares([...routeMiddleware, ...own]);
|
|
119
|
-
const methodProblems = [];
|
|
120
|
-
methods[method] = accessOf(chain, methodProblems);
|
|
121
|
-
problems.push(...methodProblems.map((p) => `${method}: ${p}`));
|
|
122
|
-
}
|
|
123
|
-
out.push({ path: route.fullPath, methods, problems });
|
|
124
|
-
}
|
|
125
|
-
return out;
|
|
126
|
-
}
|
|
127
|
-
// cyrb53: enough to tell when the code changed, which is all publish asks.
|
|
128
|
-
const sourceHash = (text) => {
|
|
129
|
-
let h1 = 0xdeadbeef;
|
|
130
|
-
let h2 = 0x41c6ce57;
|
|
131
|
-
for (let i = 0; i < text.length; i++) {
|
|
132
|
-
const ch = text.charCodeAt(i);
|
|
133
|
-
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
134
|
-
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
135
|
-
}
|
|
136
|
-
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
137
|
-
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
138
|
-
return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16).padStart(14, "0");
|
|
139
|
-
};
|
|
140
|
-
// A workflow's id is its export name: what the host invokes it by and the editor shows.
|
|
141
|
-
export function describeWorkflows(workflows) {
|
|
142
|
-
return workflows.map(({ id, file, workflow }) => {
|
|
143
|
-
const problems = [];
|
|
144
|
-
if (id === "default")
|
|
145
|
-
problems.push("workflow-default-export: export it by name, which becomes its id");
|
|
146
|
-
const elsewhere = workflows.find((other) => other.id === id && other.file !== file);
|
|
147
|
-
if (elsewhere)
|
|
148
|
-
problems.push(`duplicate-workflow: ${id} is also exported from ${elsewhere.file}`);
|
|
149
|
-
const { description, schedule, trigger } = workflow.config;
|
|
150
|
-
if (schedule)
|
|
151
|
-
return { id, file, description, schedule, problems };
|
|
152
|
-
if (!trigger || trigger.every !== undefined || trigger.events !== undefined) {
|
|
153
|
-
return { id, file, description, trigger, problems };
|
|
154
|
-
}
|
|
155
|
-
const { subscribe, unsubscribe, ...declared } = trigger;
|
|
156
|
-
if (typeof subscribe !== "function") {
|
|
157
|
-
problems.push("workflow-trigger-unregistered: a provider trigger needs `subscribe` to register it with the provider");
|
|
158
|
-
return { id, file, description, trigger: declared, problems };
|
|
159
|
-
}
|
|
160
|
-
const registration = {
|
|
161
|
-
unsubscribe: typeof unsubscribe === "function",
|
|
162
|
-
hash: sourceHash(`${String(subscribe)}\n${typeof unsubscribe === "function" ? String(unsubscribe) : ""}`),
|
|
163
|
-
};
|
|
164
|
-
return { id, file, description, trigger: declared, registration, problems };
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
export async function describeApp(opts) {
|
|
168
|
-
const functions = [];
|
|
169
|
-
for (const id of opts.fnIds) {
|
|
170
|
-
const described = await describeFunction(id, opts.request);
|
|
171
|
-
const description = opts.descriptions?.[id];
|
|
172
|
-
functions.push(description ? { ...described, description } : described);
|
|
173
|
-
}
|
|
174
|
-
const router = (await opts.getRouter());
|
|
175
|
-
return { functions, routes: describeRoutes(router.routesById), workflows: describeWorkflows(opts.workflows ?? []) };
|
|
176
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { createServer } from "node:net";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
// Only the host can set x-zite-* in production: the front strips them from public traffic.
|
|
7
|
-
const DESCRIBE_HEADERS = { "x-zite-invoke": "describe" };
|
|
8
|
-
// Not "localhost": it can resolve to ::1 for vite's listen and to 127.0.0.1 for fetch.
|
|
9
|
-
const HOST = "127.0.0.1";
|
|
10
|
-
const freePort = () => new Promise((resolve, reject) => {
|
|
11
|
-
const server = createServer();
|
|
12
|
-
server.once("error", reject);
|
|
13
|
-
server.listen(0, HOST, () => {
|
|
14
|
-
const { port } = server.address();
|
|
15
|
-
server.close(() => resolve(port));
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
// A Workers build runs where it will run: workerd, through the app's own
|
|
19
|
-
// `vite preview`, so it uses the same Cloudflare plugin and config as the build.
|
|
20
|
-
async function describeInWorkerd(appDir) {
|
|
21
|
-
const port = await freePort();
|
|
22
|
-
// Its own process group, so stopping it also stops vite and workerd under npx.
|
|
23
|
-
const preview = spawn("npx", ["--no-install", "vite", "preview", "--host", HOST, "--port", String(port), "--strictPort"], {
|
|
24
|
-
cwd: appDir,
|
|
25
|
-
stdio: ["ignore", "ignore", "pipe"],
|
|
26
|
-
detached: true,
|
|
27
|
-
});
|
|
28
|
-
let stderr = "";
|
|
29
|
-
preview.stderr.on("data", (chunk) => (stderr += chunk));
|
|
30
|
-
try {
|
|
31
|
-
const deadline = Date.now() + 60_000;
|
|
32
|
-
while (true) {
|
|
33
|
-
try {
|
|
34
|
-
const response = await fetch(`http://${HOST}:${port}/`, { headers: DESCRIBE_HEADERS });
|
|
35
|
-
// Right after start, preview can answer before the Worker does; only the
|
|
36
|
-
// app's own describe answer is JSON.
|
|
37
|
-
if (response.ok && response.headers.get("content-type")?.includes("application/json")) {
|
|
38
|
-
return await response.json();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
catch { }
|
|
42
|
-
if (preview.exitCode !== null || Date.now() > deadline) {
|
|
43
|
-
throw new Error(`vite preview didn't serve the app: ${stderr.trim().slice(-500)}`);
|
|
44
|
-
}
|
|
45
|
-
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
finally {
|
|
49
|
-
try {
|
|
50
|
-
process.kill(-preview.pid, "SIGTERM");
|
|
51
|
-
}
|
|
52
|
-
catch { }
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
/** Describes a built Start app from its `dist/server`: a Workers build in workerd, a Node build in-process. */
|
|
56
|
-
export async function describeBuiltApp(appDir) {
|
|
57
|
-
const serverDir = path.join(appDir, "dist/server");
|
|
58
|
-
if (existsSync(path.join(serverDir, "wrangler.json")))
|
|
59
|
-
return describeInWorkerd(appDir);
|
|
60
|
-
const { default: server } = await import(pathToFileURL(path.join(serverDir, "server.js")).href);
|
|
61
|
-
return (await server.fetch(new Request("http://describe.local/", { headers: DESCRIBE_HEADERS }))).json();
|
|
62
|
-
}
|
|
63
|
-
export async function runDescribe() {
|
|
64
|
-
const appDir = path.resolve(process.argv[3] ?? ".");
|
|
65
|
-
process.stdout.write(`${JSON.stringify(await describeBuiltApp(appDir), null, 2)}\n`);
|
|
66
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
const stringValue = (node) => {
|
|
2
|
-
if (node?.type === "Literal" && typeof node.value === "string")
|
|
3
|
-
return node.value;
|
|
4
|
-
const quasis = node?.quasis;
|
|
5
|
-
if (node?.type === "TemplateLiteral" && node.expressions.length === 0)
|
|
6
|
-
return quasis?.[0]?.value.cooked;
|
|
7
|
-
return undefined;
|
|
8
|
-
};
|
|
9
|
-
const descriptionProperty = (node) => {
|
|
10
|
-
for (const property of node.properties) {
|
|
11
|
-
const key = property.key;
|
|
12
|
-
const named = key?.type === "Identifier" ? key.name : key?.type === "Literal" ? key.value : undefined;
|
|
13
|
-
if (property.type === "Property" && named === "description")
|
|
14
|
-
return stringValue(property.value);
|
|
15
|
-
}
|
|
16
|
-
return undefined;
|
|
17
|
-
};
|
|
18
|
-
// Walks a builder chain, `signedInFn({ description }).validator(…).handler(…)`, to an object option.
|
|
19
|
-
const descriptionIn = (node) => {
|
|
20
|
-
if (node?.type === "CallExpression") {
|
|
21
|
-
for (const arg of node.arguments) {
|
|
22
|
-
const found = arg.type === "ObjectExpression" ? descriptionProperty(arg) : undefined;
|
|
23
|
-
if (found !== undefined)
|
|
24
|
-
return found;
|
|
25
|
-
}
|
|
26
|
-
return descriptionIn(node.callee);
|
|
27
|
-
}
|
|
28
|
-
if (node?.type === "MemberExpression")
|
|
29
|
-
return descriptionIn(node.object);
|
|
30
|
-
return undefined;
|
|
31
|
-
};
|
|
32
|
-
/** `name -> description` for each top-level const built with a description option. */
|
|
33
|
-
export function factoryDescriptions(program) {
|
|
34
|
-
const found = new Map();
|
|
35
|
-
for (const statement of program.body) {
|
|
36
|
-
const declaration = (statement.type === "ExportNamedDeclaration" ? statement.declaration : statement);
|
|
37
|
-
if (declaration?.type !== "VariableDeclaration")
|
|
38
|
-
continue;
|
|
39
|
-
for (const declarator of declaration.declarations) {
|
|
40
|
-
const id = declarator.id;
|
|
41
|
-
const description = descriptionIn(declarator.init);
|
|
42
|
-
if (id.type === "Identifier" && description)
|
|
43
|
-
found.set(id.name, description);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return found;
|
|
47
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { SourceDir } from "../sourceRoots.js";
|
|
2
|
-
export declare const START_SERVER_ENTRY = ".zite/server.ts";
|
|
3
|
-
export declare const START_INSTANCE_ENTRY = ".zite/start.ts";
|
|
4
|
-
export type StartTarget = "cloudflare" | "node";
|
|
5
|
-
/**
|
|
6
|
-
* The server entry every Start app builds with. Platform-owned: regenerated,
|
|
7
|
-
* never edited. On Workers it's an entrypoint the host can also call over RPC.
|
|
8
|
-
*/
|
|
9
|
-
export declare const renderStartServerEntry: (target?: StartTarget) => string;
|
|
10
|
-
/** The start instance: the platform's middleware, then the app's own `src/start.ts` when it has one. */
|
|
11
|
-
export declare const renderStartInstance: (hasAppStart: boolean) => string;
|
|
12
|
-
export declare const hasAppStart: (appDir: string) => boolean;
|
|
13
|
-
/** Writes `file` under `appDir` when its content differs. Returns whether it wrote. */
|
|
14
|
-
export declare const writeIfChanged: (appDir: string, file: string, content: string) => boolean;
|
|
15
|
-
export declare const isStartAppDir: (appPath: string) => boolean;
|
|
16
|
-
export declare function regenerateStartEntries(app: SourceDir): void;
|
|
17
|
-
/** App modules that mention createWorkflow: what the workflow registry imports. Describe decides what each one really exports. */
|
|
18
|
-
export declare function workflowFiles(appDir: string): string[];
|
|
19
|
-
/** The registry zite() builds in place of zitejs's empty one. */
|
|
20
|
-
export declare const renderWorkflowModules: (appDir: string, files: string[]) => string;
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname, join, relative } from "node:path";
|
|
3
|
-
export const START_SERVER_ENTRY = ".zite/server.ts";
|
|
4
|
-
export const START_INSTANCE_ENTRY = ".zite/start.ts";
|
|
5
|
-
const APP_START_FILES = ["src/start.ts", "src/start.tsx"];
|
|
6
|
-
/**
|
|
7
|
-
* The server entry every Start app builds with. Platform-owned: regenerated,
|
|
8
|
-
* never edited. On Workers it's an entrypoint the host can also call over RPC.
|
|
9
|
-
*/
|
|
10
|
-
export const renderStartServerEntry = (target = "cloudflare") => target === "cloudflare"
|
|
11
|
-
? `// Generated by zitejs for TanStack Start apps. The platform owns this file.
|
|
12
|
-
import handler from '@tanstack/react-start/server-entry';
|
|
13
|
-
import { createZiteWorker } from 'zitejs/start/worker';
|
|
14
|
-
import { getRouter } from '../src/router';
|
|
15
|
-
|
|
16
|
-
export default createZiteWorker({ handler, getRouter });
|
|
17
|
-
`
|
|
18
|
-
: `// Generated by zitejs for TanStack Start apps. The platform owns this file.
|
|
19
|
-
import handler, { createServerEntry } from '@tanstack/react-start/server-entry';
|
|
20
|
-
import { createZiteFetch } from 'zitejs/start/server';
|
|
21
|
-
import { getRouter } from '../src/router';
|
|
22
|
-
|
|
23
|
-
export default createServerEntry({ fetch: createZiteFetch({ handler, getRouter }) });
|
|
24
|
-
`;
|
|
25
|
-
/** The start instance: the platform's middleware, then the app's own `src/start.ts` when it has one. */
|
|
26
|
-
export const renderStartInstance = (hasAppStart) => hasAppStart
|
|
27
|
-
? `// Generated by zitejs for TanStack Start apps. The platform owns this file.
|
|
28
|
-
import { createZiteStart } from 'zitejs/start/instance';
|
|
29
|
-
import { startInstance as app } from '../src/start';
|
|
30
|
-
|
|
31
|
-
export const startInstance = createZiteStart(app);
|
|
32
|
-
`
|
|
33
|
-
: `// Generated by zitejs for TanStack Start apps. The platform owns this file.
|
|
34
|
-
import { createZiteStart } from 'zitejs/start/instance';
|
|
35
|
-
|
|
36
|
-
export const startInstance = createZiteStart();
|
|
37
|
-
`;
|
|
38
|
-
export const hasAppStart = (appDir) => APP_START_FILES.some((file) => existsSync(join(appDir, file)));
|
|
39
|
-
/** Writes `file` under `appDir` when its content differs. Returns whether it wrote. */
|
|
40
|
-
export const writeIfChanged = (appDir, file, content) => {
|
|
41
|
-
const target = join(appDir, file);
|
|
42
|
-
if (existsSync(target) && readFileSync(target, "utf-8") === content)
|
|
43
|
-
return false;
|
|
44
|
-
mkdirSync(dirname(target), { recursive: true });
|
|
45
|
-
writeFileSync(target, content);
|
|
46
|
-
return true;
|
|
47
|
-
};
|
|
48
|
-
export const isStartAppDir = (appPath) => {
|
|
49
|
-
try {
|
|
50
|
-
return JSON.parse(readFileSync(join(appPath, "zite.config.json"), "utf-8"))?.runtime === "start";
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
const isStartApp = (app) => isStartAppDir(app.path);
|
|
57
|
-
export function regenerateStartEntries(app) {
|
|
58
|
-
if (!isStartApp(app))
|
|
59
|
-
return;
|
|
60
|
-
const entries = [
|
|
61
|
-
[START_SERVER_ENTRY, renderStartServerEntry()],
|
|
62
|
-
[START_INSTANCE_ENTRY, renderStartInstance(hasAppStart(app.path))],
|
|
63
|
-
];
|
|
64
|
-
for (const [file, content] of entries) {
|
|
65
|
-
if (writeIfChanged(app.path, file, content))
|
|
66
|
-
console.log(`Wrote ${join(app.path, file)}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
const SOURCE_FILE = /\.[mc]?[jt]sx?$/;
|
|
70
|
-
const NOT_APP_CODE = /(\.(test|spec)\.[^.]+|\.d\.ts|routeTree\.gen\.ts)$/;
|
|
71
|
-
/** App modules that mention createWorkflow: what the workflow registry imports. Describe decides what each one really exports. */
|
|
72
|
-
export function workflowFiles(appDir) {
|
|
73
|
-
const found = [];
|
|
74
|
-
const walk = (dir) => {
|
|
75
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
76
|
-
const full = join(dir, entry.name);
|
|
77
|
-
if (entry.isDirectory()) {
|
|
78
|
-
if (entry.name !== "node_modules")
|
|
79
|
-
walk(full);
|
|
80
|
-
}
|
|
81
|
-
else if (SOURCE_FILE.test(entry.name) && !NOT_APP_CODE.test(entry.name) && readFileSync(full, "utf-8").includes("createWorkflow")) {
|
|
82
|
-
found.push(relative(appDir, full).split("\\").join("/"));
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
if (existsSync(join(appDir, "src")))
|
|
87
|
-
walk(join(appDir, "src"));
|
|
88
|
-
return found.sort();
|
|
89
|
-
}
|
|
90
|
-
/** The registry zite() builds in place of zitejs's empty one. */
|
|
91
|
-
export const renderWorkflowModules = (appDir, files) => `export const workflowModules = {\n${files
|
|
92
|
-
.map((file) => ` ${JSON.stringify(file)}: () => import(${JSON.stringify(join(appDir, file))}),`)
|
|
93
|
-
.join("\n")}\n};\n`;
|
package/dist/esm/start/host.d.ts
DELETED
package/dist/esm/start/host.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
// Isomorphic, so it can't import the server-only store: it reads it through the
|
|
2
|
-
// global key ./scope.ts registers it under. In the browser there is none.
|
|
3
|
-
export const HOST_STORE_KEY = Symbol.for("zitejs:host-context");
|
|
4
|
-
export const currentHost = () => globalThis[HOST_STORE_KEY]?.getStore();
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { User } from "./types.js";
|
|
2
|
-
export type { User, ZiteRequestContext } from "./types.js";
|
|
3
|
-
export { createZiteRouter, type ZiteRouterContext } from "./router.js";
|
|
4
|
-
export { createWorkflow, type StartProviderTrigger, type StartTrigger, type Workflow, type WorkflowArgs, type WorkflowConfig, } from "./workflow.js";
|
|
5
|
-
export type { ZiteSchedule } from "../schedules/index.js";
|
|
6
|
-
export type { ZiteTrigger, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput, } from "../backend/index.js";
|
|
7
|
-
declare module "@tanstack/start-client-core" {
|
|
8
|
-
interface ServerFnOptions {
|
|
9
|
-
/** What this function does, in a sentence. Shown in the editor. */
|
|
10
|
-
description?: string;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
declare const STATUS: {
|
|
14
|
-
readonly BAD_REQUEST: 400;
|
|
15
|
-
readonly INVALID_INPUT: 400;
|
|
16
|
-
readonly UNAUTHORIZED: 401;
|
|
17
|
-
readonly FORBIDDEN: 403;
|
|
18
|
-
readonly NOT_FOUND: 404;
|
|
19
|
-
readonly CONFLICT: 409;
|
|
20
|
-
readonly RATE_LIMITED: 429;
|
|
21
|
-
readonly INTERNAL_ERROR: 500;
|
|
22
|
-
};
|
|
23
|
-
export type ZiteErrorCode = keyof typeof STATUS;
|
|
24
|
-
/** @internal The HTTP status each code answers with. */
|
|
25
|
-
export declare const ZITE_ERROR_STATUS: Readonly<Record<ZiteErrorCode, number>>;
|
|
26
|
-
/** Fail with a status: `throw new ZiteError('FORBIDDEN', 'Only the owner can edit this')`. */
|
|
27
|
-
export declare class ZiteError extends Error {
|
|
28
|
-
readonly code: ZiteErrorCode;
|
|
29
|
-
constructor(code: ZiteErrorCode, message?: string);
|
|
30
|
-
}
|
|
31
|
-
export declare const hasRole: (user: User, role: string) => boolean;
|
|
32
|
-
/** The message a server function fails with when it needs a session; the platform's client middleware sends the user to sign in. */
|
|
33
|
-
export declare const SIGN_IN_REQUIRED = "ZITE_SIGN_IN_REQUIRED";
|
|
34
|
-
export declare const isSignInRequired: (error: unknown) => boolean;
|
|
35
|
-
/**
|
|
36
|
-
* The platform's sign-in page, returning to `redirectTo` (default: the current
|
|
37
|
-
* page). For guards: `throw redirect({ href: loginHref(), reloadDocument: true })`.
|
|
38
|
-
*/
|
|
39
|
-
export declare function loginHref(redirectTo?: string): string;
|
|
40
|
-
/** What a zitejs middleware means, for describe. Keyed by identity, so composing or re-exporting keeps it. */
|
|
41
|
-
export type ZiteMiddlewareKind = {
|
|
42
|
-
kind: "signedIn" | "anyone" | "factory";
|
|
43
|
-
} | {
|
|
44
|
-
kind: "webhook";
|
|
45
|
-
paused?: true;
|
|
46
|
-
} | {
|
|
47
|
-
kind: "role";
|
|
48
|
-
role: string;
|
|
49
|
-
};
|
|
50
|
-
/** @internal */
|
|
51
|
-
export declare const ziteMiddlewareKind: (middleware: unknown) => ZiteMiddlewareKind | undefined;
|
|
52
|
-
/** Only signed-in users. Handlers get `context.user: User`. */
|
|
53
|
-
export declare const signedIn: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, {
|
|
54
|
-
user: User;
|
|
55
|
-
}>;
|
|
56
|
-
/** Anyone, signed in or not. Handlers get `context.user: User | null`. */
|
|
57
|
-
export declare const anyone: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, {
|
|
58
|
-
user: User | null;
|
|
59
|
-
}>;
|
|
60
|
-
/**
|
|
61
|
-
* Webhooks delivered by the platform, which already checked the URL token.
|
|
62
|
-
* `webhook.paused` keeps the URL but runs nothing: the platform answers the
|
|
63
|
-
* provider 200 and drops the delivery until it's switched back.
|
|
64
|
-
*/
|
|
65
|
-
export declare const webhook: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, undefined> & {
|
|
66
|
-
paused: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, undefined>;
|
|
67
|
-
};
|
|
68
|
-
/** Signed-in users with this role. */
|
|
69
|
-
export declare const role: (name: string) => import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, readonly [import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, {
|
|
70
|
-
user: User;
|
|
71
|
-
}>], undefined>;
|
|
72
|
-
declare const compiled: import("@tanstack/react-start").FunctionMiddlewareAfterClient<{}, unknown, undefined, undefined, undefined>;
|
|
73
|
-
declare const builder: import("@tanstack/react-start").ServerFnBuilder<import("@tanstack/react-start").Register, import("@tanstack/react-start").Method, true>;
|
|
74
|
-
type SignedInFn = ReturnType<typeof builder.middleware<readonly [typeof compiled, typeof signedIn]>>;
|
|
75
|
-
type PublicFn = ReturnType<typeof builder.middleware<readonly [typeof compiled, typeof anyone]>>;
|
|
76
|
-
/** A server function only signed-in users can call. */
|
|
77
|
-
export declare const signedInFn: SignedInFn;
|
|
78
|
-
/** A server function anyone can call (external apps only). */
|
|
79
|
-
export declare const publicFn: PublicFn;
|
package/dist/esm/start/index.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { createMiddleware, createServerFn } from "@tanstack/react-start";
|
|
2
|
-
import { setResponseStatus } from "@tanstack/react-start/server";
|
|
3
|
-
import { USAGE_TOKEN_QUERY_PARAM } from "../auth/constants.js";
|
|
4
|
-
import { currentHost } from "./host.js";
|
|
5
|
-
import { getHost, inScope, runInScope } from "./scope.js";
|
|
6
|
-
export { createZiteRouter } from "./router.js";
|
|
7
|
-
export { createWorkflow, } from "./workflow.js";
|
|
8
|
-
const STATUS = {
|
|
9
|
-
BAD_REQUEST: 400,
|
|
10
|
-
INVALID_INPUT: 400,
|
|
11
|
-
UNAUTHORIZED: 401,
|
|
12
|
-
FORBIDDEN: 403,
|
|
13
|
-
NOT_FOUND: 404,
|
|
14
|
-
CONFLICT: 409,
|
|
15
|
-
RATE_LIMITED: 429,
|
|
16
|
-
INTERNAL_ERROR: 500,
|
|
17
|
-
};
|
|
18
|
-
/** @internal The HTTP status each code answers with. */
|
|
19
|
-
export const ZITE_ERROR_STATUS = STATUS;
|
|
20
|
-
/** Fail with a status: `throw new ZiteError('FORBIDDEN', 'Only the owner can edit this')`. */
|
|
21
|
-
export class ZiteError extends Error {
|
|
22
|
-
code;
|
|
23
|
-
constructor(code, message) {
|
|
24
|
-
super(message ?? code);
|
|
25
|
-
this.name = "ZiteError";
|
|
26
|
-
this.code = code;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export const hasRole = (user, role) => user.roles.includes(role);
|
|
30
|
-
/** The message a server function fails with when it needs a session; the platform's client middleware sends the user to sign in. */
|
|
31
|
-
export const SIGN_IN_REQUIRED = "ZITE_SIGN_IN_REQUIRED";
|
|
32
|
-
export const isSignInRequired = (error) => error instanceof Error && error.message === SIGN_IN_REQUIRED;
|
|
33
|
-
/**
|
|
34
|
-
* The platform's sign-in page, returning to `redirectTo` (default: the current
|
|
35
|
-
* page). For guards: `throw redirect({ href: loginHref(), reloadDocument: true })`.
|
|
36
|
-
*/
|
|
37
|
-
export function loginHref(redirectTo) {
|
|
38
|
-
const current = typeof window !== "undefined" ? window.location.href : currentHost()?.url;
|
|
39
|
-
// Absolute on purpose: better-auth drops a relative callback with an encoded
|
|
40
|
-
// space, fragment or non-ASCII path, and never checks an absolute one.
|
|
41
|
-
const target = current ? new URL(redirectTo ?? current, current) : undefined;
|
|
42
|
-
const params = new URLSearchParams();
|
|
43
|
-
if (target && !(target.pathname === "/" && !target.search && !target.hash))
|
|
44
|
-
params.set("redirectUrl", target.toString());
|
|
45
|
-
else if (!target && redirectTo)
|
|
46
|
-
params.set("redirectUrl", redirectTo);
|
|
47
|
-
// The editor preview's sign-in page needs the preview's token.
|
|
48
|
-
const usageToken = current ? new URL(current).searchParams.get(USAGE_TOKEN_QUERY_PARAM) : null;
|
|
49
|
-
if (usageToken)
|
|
50
|
-
params.set(USAGE_TOKEN_QUERY_PARAM, usageToken);
|
|
51
|
-
const query = params.toString();
|
|
52
|
-
return query ? `/auth/login?${query}` : "/auth/login";
|
|
53
|
-
}
|
|
54
|
-
const kinds = new WeakMap();
|
|
55
|
-
const tag = (middleware, kind) => {
|
|
56
|
-
kinds.set(middleware, kind);
|
|
57
|
-
return middleware;
|
|
58
|
-
};
|
|
59
|
-
/** @internal */
|
|
60
|
-
export const ziteMiddlewareKind = (middleware) => typeof middleware === "object" && middleware !== null ? kinds.get(middleware) : undefined;
|
|
61
|
-
// Identity comes from server-only storage, never TanStack's context, which
|
|
62
|
-
// merges whatever context the browser sends.
|
|
63
|
-
const host = () => {
|
|
64
|
-
const current = getHost();
|
|
65
|
-
if (!current)
|
|
66
|
-
throw new ZiteError("UNAUTHORIZED", "Missing platform context");
|
|
67
|
-
return current;
|
|
68
|
-
};
|
|
69
|
-
// TanStack fails a validator with a plain Error whose message is the issues as JSON.
|
|
70
|
-
const validationIssues = (error) => {
|
|
71
|
-
if (!(error instanceof Error) || error.constructor !== Error)
|
|
72
|
-
return undefined;
|
|
73
|
-
try {
|
|
74
|
-
const issues = JSON.parse(error.message);
|
|
75
|
-
return Array.isArray(issues) && issues.length > 0 && issues.every((issue) => typeof issue?.message === "string")
|
|
76
|
-
? issues
|
|
77
|
-
: undefined;
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
const invalidInput = (issues) => {
|
|
84
|
-
if (getHost()?.mode === "live")
|
|
85
|
-
return new ZiteError("INVALID_INPUT", "Invalid input");
|
|
86
|
-
const describe = ({ message, path = [] }) => {
|
|
87
|
-
const at = path.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
|
|
88
|
-
return at ? `${at}: ${message}` : message;
|
|
89
|
-
};
|
|
90
|
-
return new ZiteError("INVALID_INPUT", issues.map(describe).join("; "));
|
|
91
|
-
};
|
|
92
|
-
// Server functions report a status through TanStack's error envelope; server
|
|
93
|
-
// routes need a Response. A nested call's failure is the caller's to handle.
|
|
94
|
-
const withStatus = async (isServerFn, fn) => {
|
|
95
|
-
const nested = inScope();
|
|
96
|
-
try {
|
|
97
|
-
return await fn();
|
|
98
|
-
}
|
|
99
|
-
catch (caught) {
|
|
100
|
-
const issues = validationIssues(caught);
|
|
101
|
-
const error = issues ? invalidInput(issues) : caught;
|
|
102
|
-
if (!(error instanceof ZiteError))
|
|
103
|
-
throw error;
|
|
104
|
-
if (!isServerFn)
|
|
105
|
-
return new Response(error.message, { status: STATUS[error.code] });
|
|
106
|
-
if (!nested)
|
|
107
|
-
setResponseStatus(STATUS[error.code]);
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
/** Only signed-in users. Handlers get `context.user: User`. */
|
|
112
|
-
export const signedIn = tag(createMiddleware().server(({ next, serverFnMeta }) => withStatus(!!serverFnMeta, async () => {
|
|
113
|
-
const { user } = host();
|
|
114
|
-
if (!user)
|
|
115
|
-
throw new ZiteError("UNAUTHORIZED", SIGN_IN_REQUIRED);
|
|
116
|
-
return runInScope({ kind: "signedIn", user }, () => next({ context: { user } }));
|
|
117
|
-
})), { kind: "signedIn" });
|
|
118
|
-
/** Anyone, signed in or not. Handlers get `context.user: User | null`. */
|
|
119
|
-
export const anyone = tag(createMiddleware().server(({ next, serverFnMeta }) => withStatus(!!serverFnMeta, async () => {
|
|
120
|
-
const { user } = host();
|
|
121
|
-
return runInScope({ kind: "anyone", user }, () => next({ context: { user } }));
|
|
122
|
-
})), { kind: "anyone" });
|
|
123
|
-
const webhookMiddleware = (paused) => tag(createMiddleware().server(({ next }) => {
|
|
124
|
-
if (host().invoke?.kind !== "webhook")
|
|
125
|
-
return new Response("Not found", { status: 404 });
|
|
126
|
-
return runInScope({ kind: "webhook", user: null }, () => next());
|
|
127
|
-
}), paused ? { kind: "webhook", paused: true } : { kind: "webhook" });
|
|
128
|
-
/**
|
|
129
|
-
* Webhooks delivered by the platform, which already checked the URL token.
|
|
130
|
-
* `webhook.paused` keeps the URL but runs nothing: the platform answers the
|
|
131
|
-
* provider 200 and drops the delivery until it's switched back.
|
|
132
|
-
*/
|
|
133
|
-
export const webhook = Object.assign(webhookMiddleware(false), { paused: webhookMiddleware(true) });
|
|
134
|
-
/** Signed-in users with this role. */
|
|
135
|
-
export const role = (name) => tag(createMiddleware()
|
|
136
|
-
.middleware([signedIn])
|
|
137
|
-
.server(({ next, context, serverFnMeta }) => withStatus(!!serverFnMeta, async () => {
|
|
138
|
-
if (!hasRole(context.user, name))
|
|
139
|
-
throw new ZiteError("FORBIDDEN", `Requires the ${name} role`);
|
|
140
|
-
return next();
|
|
141
|
-
})), { kind: "role", role: name });
|
|
142
|
-
// TanStack's compiler tags every server function it compiles with an id. A
|
|
143
|
-
// builder it didn't compile (an object property, one returned from a helper)
|
|
144
|
-
// has none and would run its handler wherever it was called, so it fails here.
|
|
145
|
-
const compiled = tag(createMiddleware({ type: "function" }).client(({ next, serverFnMeta }) => {
|
|
146
|
-
if (!serverFnMeta?.id) {
|
|
147
|
-
throw new Error("This server function was not compiled by TanStack Start. Assign signedInFn(...)/publicFn(...)" +
|
|
148
|
-
".handler(...) directly to its own top-level const, not an object property or a helper's return value.");
|
|
149
|
-
}
|
|
150
|
-
return next();
|
|
151
|
-
}), { kind: "factory" });
|
|
152
|
-
// TanStack doesn't export its builder-after-middleware type, so name it through the public builder.
|
|
153
|
-
const builder = createServerFn();
|
|
154
|
-
/** A server function only signed-in users can call. */
|
|
155
|
-
export const signedInFn = createServerFn().middleware([compiled, signedIn]);
|
|
156
|
-
/** A server function anyone can call (external apps only). */
|
|
157
|
-
export const publicFn = createServerFn().middleware([compiled, anyone]);
|