zitejs 0.10.0-start.6 → 0.10.0-start.7
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/backend/index.d.ts +115 -3
- package/dist/cjs/backend/index.test.js +58 -0
- package/dist/cjs/integrations/rules.d.ts +13 -0
- package/dist/cjs/integrations/rules.js +29 -0
- package/dist/cjs/integrations/rules.test.js +1 -0
- package/dist/cjs/sync/lib.js +6 -6
- package/dist/esm/backend/index.d.ts +115 -3
- package/dist/esm/backend/index.test.js +58 -0
- package/dist/esm/integrations/rules.d.ts +13 -0
- package/dist/esm/integrations/rules.js +29 -0
- package/dist/esm/integrations/rules.test.js +1 -0
- package/dist/esm/start/authHooks.d.ts +7 -0
- package/dist/esm/start/authHooks.js +10 -0
- package/dist/esm/start/authHooksModule.d.ts +1 -0
- package/dist/esm/start/authHooksModule.js +3 -0
- package/dist/esm/start/clientErrorMessage.test.d.ts +1 -0
- package/dist/esm/start/clientErrorMessage.test.js +105 -0
- package/dist/esm/start/describe.d.ts +17 -1
- package/dist/esm/start/describe.js +27 -5
- package/dist/esm/start/errors.d.ts +46 -0
- package/dist/esm/start/errors.js +99 -0
- package/dist/esm/start/errors.test.d.ts +1 -0
- package/dist/esm/start/errors.test.js +23 -0
- package/dist/esm/start/index.d.ts +3 -19
- package/dist/esm/start/index.js +28 -36
- package/dist/esm/start/instance.d.ts +1 -2
- package/dist/esm/start/instance.js +11 -4
- package/dist/esm/start/previewClaim.d.ts +10 -0
- package/dist/esm/start/previewClaim.js +32 -0
- package/dist/esm/start/redactSecrets.d.ts +7 -0
- package/dist/esm/start/redactSecrets.js +131 -0
- package/dist/esm/start/redactSecrets.test.d.ts +1 -0
- package/dist/esm/start/redactSecrets.test.js +151 -0
- package/dist/esm/start/router.d.ts +6 -3
- package/dist/esm/start/router.js +47 -7
- package/dist/esm/start/runLog.d.ts +7 -4
- package/dist/esm/start/runLog.js +64 -6
- package/dist/esm/start/server.d.ts +7 -3
- package/dist/esm/start/server.js +74 -21
- package/dist/esm/start/types.d.ts +28 -3
- package/dist/esm/start/version.d.ts +1 -0
- package/dist/esm/start/version.js +2 -0
- package/dist/esm/start/vite.js +31 -3
- package/dist/esm/start/workflowRun.js +2 -1
- package/dist/esm/sync/lib.js +6 -6
- package/package.json +16 -8
package/dist/esm/start/server.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { ZiteError, ZITE_ERROR_STATUS } from "./
|
|
1
|
+
import { asZiteError, isZiteErrorCode, ZiteError, ZITE_ERROR_STATUS } from "./errors.js";
|
|
2
2
|
import { describeApp } from "./describe.js";
|
|
3
3
|
import { runRegistration } from "./registration.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { isAuthHookName, loadAuthHooks } from "./authHooks.js";
|
|
5
|
+
import { errorOf, preview } from "./runLog.js";
|
|
6
|
+
import { getHost, requireScope, runInScope, runWithHost } from "./scope.js";
|
|
7
|
+
import { ZITEJS_VERSION } from "./version.js";
|
|
6
8
|
import { loadWorkflows, loggedInput, runWorkflow } from "./workflowRun.js";
|
|
7
9
|
export { runWithHost } from "./scope.js";
|
|
8
10
|
export { describeApp } from "./describe.js";
|
|
@@ -23,8 +25,8 @@ export function installPlatformTransport() {
|
|
|
23
25
|
const result = await (await host.session).call({ integrationId, className, methodName, params, access: scope.kind });
|
|
24
26
|
if ("value" in result)
|
|
25
27
|
return result.value;
|
|
26
|
-
const { code, message } = result.error;
|
|
27
|
-
throw new ZiteError(code
|
|
28
|
+
const { code, message, userFacingMessage } = result.error;
|
|
29
|
+
throw new ZiteError({ code: isZiteErrorCode(code) ? code : "INTERNAL_ERROR", message, userFacingMessage });
|
|
28
30
|
};
|
|
29
31
|
globalThis.__wrapSdkCall = sdkCall;
|
|
30
32
|
watchOutboundFetches();
|
|
@@ -53,7 +55,9 @@ const watchOutboundFetches = () => {
|
|
|
53
55
|
};
|
|
54
56
|
};
|
|
55
57
|
const decodeUser = (value) => JSON.parse(new TextDecoder().decode(Uint8Array.from(atob(value.replace(/-/g, "+").replace(/_/g, "/")), (c) => c.charCodeAt(0))));
|
|
56
|
-
const INVOKE_KINDS = new Set(["webhook", "describe", "workflow", "register", "warm"]);
|
|
58
|
+
const INVOKE_KINDS = new Set(["webhook", "describe", "workflow", "register", "warm", "authHook"]);
|
|
59
|
+
// What a request without props may ask for: neither runs app code on anyone's behalf.
|
|
60
|
+
const UNHOSTED_INVOKE_KINDS = new Set(["warm", "describe"]);
|
|
57
61
|
/** What the host dispatched this request with. Headers are ignored: props are the only thing it alone can set. */
|
|
58
62
|
export function hostContextFromProps(request, props) {
|
|
59
63
|
return {
|
|
@@ -62,32 +66,48 @@ export function hostContextFromProps(request, props) {
|
|
|
62
66
|
user: props.user,
|
|
63
67
|
mode: props.mode,
|
|
64
68
|
invoke: props.invoke,
|
|
69
|
+
appUrl: props.appUrl,
|
|
70
|
+
baseId: props.baseId,
|
|
71
|
+
previewClaim: props.previewClaim,
|
|
65
72
|
runs: [],
|
|
66
73
|
platform: props.platform,
|
|
67
74
|
};
|
|
68
75
|
}
|
|
76
|
+
// Classic sets these per run. Node and workerd both refuse getters on process.env, so each request writes what the
|
|
77
|
+
// host sent, which is the same for every request a build serves: the live URL even on a demo.
|
|
78
|
+
const setRequestEnv = ({ appUrl, baseId }) => {
|
|
79
|
+
const env = globalThis.process?.env;
|
|
80
|
+
if (!env)
|
|
81
|
+
return;
|
|
82
|
+
if (appUrl)
|
|
83
|
+
env.ZITE_APP_URL = appUrl;
|
|
84
|
+
if (baseId)
|
|
85
|
+
env.ZITE_BASE_ID = baseId;
|
|
86
|
+
};
|
|
69
87
|
/**
|
|
70
|
-
* A request the host didn't dispatch: `zitejs describe` under `vite preview`,
|
|
71
|
-
*
|
|
72
|
-
*
|
|
88
|
+
* A request the host didn't dispatch: the host's warm-up, `zitejs describe` under `vite preview`, and tests. Only the
|
|
89
|
+
* host can set props, so without them a build answers warm and describe and nobody is signed in. An entry that passes
|
|
90
|
+
* its own platform (tests, local dev) owns its requests and may say who is calling in `x-zite-*` headers.
|
|
73
91
|
*/
|
|
74
92
|
export function hostContextFromRequest(request, platform) {
|
|
75
93
|
const { headers } = request;
|
|
76
|
-
const
|
|
94
|
+
const trusted = platform !== undefined;
|
|
95
|
+
const user = trusted ? headers.get("x-zite-user") : null;
|
|
77
96
|
const [kind, name = ""] = headers.get("x-zite-invoke")?.split(":") ?? [];
|
|
97
|
+
const kinds = trusted ? INVOKE_KINDS : UNHOSTED_INVOKE_KINDS;
|
|
78
98
|
return {
|
|
79
|
-
requestId: headers.get("x-zite-request-id")
|
|
99
|
+
requestId: (trusted && headers.get("x-zite-request-id")) || crypto.randomUUID(),
|
|
80
100
|
url: request.url,
|
|
81
101
|
user: user ? decodeUser(user) : null,
|
|
82
102
|
mode: "live",
|
|
83
|
-
invoke:
|
|
103
|
+
invoke: kinds.has(kind) ? { kind: kind, name } : null,
|
|
84
104
|
runs: [],
|
|
85
105
|
platform,
|
|
86
106
|
};
|
|
87
107
|
}
|
|
88
108
|
/** Every server function TanStack compiled, written in by `zite()` at build. Empty in dev, where TanStack doesn't call its id hook. */
|
|
89
109
|
export const builtFunctionIds = () => "__ZITE_FN_IDS__".split(",").filter((id) => id && !id.startsWith("__ZITE"));
|
|
90
|
-
const
|
|
110
|
+
const parseBuiltJson = (json) => {
|
|
91
111
|
try {
|
|
92
112
|
return JSON.parse(json);
|
|
93
113
|
}
|
|
@@ -96,7 +116,9 @@ const parseDescriptions = (json) => {
|
|
|
96
116
|
}
|
|
97
117
|
};
|
|
98
118
|
/** Each server function's `description` option, written in by `zite()` at build. */
|
|
99
|
-
export const builtFunctionDescriptions = () =>
|
|
119
|
+
export const builtFunctionDescriptions = () => parseBuiltJson("__ZITE_FN_DESCRIPTIONS__");
|
|
120
|
+
/** Each route's file, by route id, written in by `zite()` at build. */
|
|
121
|
+
export const builtRouteFiles = () => parseBuiltJson("__ZITE_ROUTE_FILES__");
|
|
100
122
|
// Server functions also run while a streamed page renders, so the run log is
|
|
101
123
|
// complete only once the body has been read to the end.
|
|
102
124
|
const onBodyEnd = (response, done) => {
|
|
@@ -107,6 +129,8 @@ const onBodyEnd = (response, done) => {
|
|
|
107
129
|
return new Response(response.body.pipeThrough(new TransformStream({ flush: done })), response);
|
|
108
130
|
};
|
|
109
131
|
const INVOKE_PATH = "/_zite/invoke";
|
|
132
|
+
/** On describe's answer: the zitejs this build's server code was bundled from. */
|
|
133
|
+
export const RUNTIME_VERSION_HEADER = "x-zitejs-version";
|
|
110
134
|
// The host sends `{ input }`; a platform-fired run's input carries the runner's
|
|
111
135
|
// `__cron`, `__poll` or `__webhook` state, exactly as for classic endpoints.
|
|
112
136
|
async function invokeWorkflow(request, host, workflows) {
|
|
@@ -143,13 +167,35 @@ async function invokeRegistration(request, host, workflows) {
|
|
|
143
167
|
}
|
|
144
168
|
return Response.json(result, { status: result.ok ? 200 : 500 });
|
|
145
169
|
}
|
|
146
|
-
|
|
170
|
+
// The host sends `{ input }`, the same fields classic hooks get. A hook runs as
|
|
171
|
+
// a workflow does: no user, and the app's data and integrations through its platform.
|
|
172
|
+
async function invokeAuthHook(request, host) {
|
|
173
|
+
if (request.method !== "POST" || new URL(request.url).pathname !== INVOKE_PATH)
|
|
174
|
+
return new Response("Not found", { status: 404 });
|
|
175
|
+
const name = host.invoke?.name ?? "";
|
|
176
|
+
const hook = isAuthHookName(name) ? (await loadAuthHooks())[name] : undefined;
|
|
177
|
+
if (!hook) {
|
|
178
|
+
return Response.json({ ok: false, error: { code: "NOT_FOUND", message: `No auth hook named ${name}` } }, { status: 404 });
|
|
179
|
+
}
|
|
180
|
+
const input = (await request.json().catch(() => ({})))?.input ?? {};
|
|
181
|
+
const startedAt = Date.now();
|
|
182
|
+
try {
|
|
183
|
+
const output = await runInScope({ kind: "workflow", user: null }, () => hook(input));
|
|
184
|
+
host.runs.push({ fn: `authHook:${name}`, name, nested: false, startedAt, durationMs: Date.now() - startedAt, input: preview(input), output: preview(output) });
|
|
185
|
+
return Response.json({ ok: true, output: output ?? null });
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
const failed = errorOf(error);
|
|
189
|
+
host.runs.push({ fn: `authHook:${name}`, name, nested: false, startedAt, durationMs: Date.now() - startedAt, input: preview(input), error: failed });
|
|
190
|
+
return Response.json({ ok: false, error: failed }, { status: 500 });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
147
193
|
// TanStack renders any loader error as a 500. A page refused with a ZiteError
|
|
148
194
|
// (a missing role, a record that isn't there) answers with that error's status.
|
|
149
195
|
const pageStatus = (host) => {
|
|
150
196
|
const matches = host.router?.state?.matches;
|
|
151
|
-
const failed = matches?.find((match) => match.status === "error")?.error;
|
|
152
|
-
return
|
|
197
|
+
const failed = asZiteError(matches?.find((match) => match.status === "error")?.error);
|
|
198
|
+
return failed ? ZITE_ERROR_STATUS[failed.code] : undefined;
|
|
153
199
|
};
|
|
154
200
|
/**
|
|
155
201
|
* The handlers of a Start app's server entry. A request the host dispatched
|
|
@@ -168,6 +214,7 @@ export function createZiteHandlers(options) {
|
|
|
168
214
|
: hostContextFromRequest(request, options.platform);
|
|
169
215
|
if (watch)
|
|
170
216
|
host.watch = watch;
|
|
217
|
+
setRequestEnv(host);
|
|
171
218
|
// Starting this isolate was the point: the platform warms a build before its
|
|
172
219
|
// first visitors, and no app code runs.
|
|
173
220
|
if (host.invoke?.kind === "warm")
|
|
@@ -176,26 +223,32 @@ export function createZiteHandlers(options) {
|
|
|
176
223
|
return Response.json(await describeApp({
|
|
177
224
|
fnIds: builtFunctionIds(),
|
|
178
225
|
descriptions: builtFunctionDescriptions(),
|
|
226
|
+
routeFiles: builtRouteFiles(),
|
|
179
227
|
getRouter: options.getRouter,
|
|
180
228
|
request,
|
|
181
229
|
workflows: await loadedWorkflows(),
|
|
182
|
-
}));
|
|
230
|
+
}), { headers: { [RUNTIME_VERSION_HEADER]: ZITEJS_VERSION } });
|
|
183
231
|
}
|
|
184
232
|
let response = await runWithHost(host, async () => host.invoke?.kind === "workflow"
|
|
185
233
|
? invokeWorkflow(request, host, await loadedWorkflows())
|
|
186
234
|
: host.invoke?.kind === "register"
|
|
187
235
|
? invokeRegistration(request, host, await loadedWorkflows())
|
|
188
|
-
:
|
|
236
|
+
: host.invoke?.kind === "authHook"
|
|
237
|
+
? invokeAuthHook(request, host)
|
|
238
|
+
: options.handler.fetch(request));
|
|
189
239
|
const status = response.status === 500 ? pageStatus(host) : undefined;
|
|
190
240
|
if (status)
|
|
191
241
|
response = new Response(response.body, { status, headers: response.headers });
|
|
192
242
|
return onBodyEnd(response, () => {
|
|
193
243
|
const { platform } = host;
|
|
194
|
-
if (!host.runs.length || !platform)
|
|
244
|
+
if ((!host.runs.length && !host.pending?.length) || !platform)
|
|
195
245
|
return;
|
|
196
|
-
const batch = { runs: host.runs };
|
|
197
246
|
// On the request's session when it has one: one call, not another binding round trip.
|
|
198
247
|
const flushed = (async () => {
|
|
248
|
+
await Promise.all(host.pending ?? []);
|
|
249
|
+
if (!host.runs.length)
|
|
250
|
+
return;
|
|
251
|
+
const batch = { runs: host.runs };
|
|
199
252
|
const session = host.session ? await host.session : undefined;
|
|
200
253
|
await (session?.runs ? session.runs(batch) : platform.runs(batch));
|
|
201
254
|
})().catch((error) => {
|
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
export type Role = {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
/** As classic's `context.user`, plus `name`. */
|
|
1
6
|
export type User = {
|
|
2
7
|
id: string;
|
|
3
8
|
email: string;
|
|
9
|
+
/** First and last name, or the email when neither is set. */
|
|
4
10
|
name: string;
|
|
5
|
-
|
|
11
|
+
firstName?: string;
|
|
12
|
+
lastName?: string;
|
|
13
|
+
/** The built-in roles, then the ones assigned on the Members tab. */
|
|
14
|
+
roles: ReadonlyArray<Role>;
|
|
15
|
+
/** A member of the workspace's team, not an external user. */
|
|
16
|
+
isInternal: boolean;
|
|
6
17
|
};
|
|
7
18
|
/** One server function call, as the platform's run log shows it. Input and output are truncated JSON. */
|
|
8
19
|
export type RunRecord = {
|
|
@@ -16,6 +27,7 @@ export type RunRecord = {
|
|
|
16
27
|
error?: {
|
|
17
28
|
code: string;
|
|
18
29
|
message: string;
|
|
30
|
+
stack?: string;
|
|
19
31
|
};
|
|
20
32
|
};
|
|
21
33
|
export type AccessKind = "signedIn" | "anyone" | "webhook" | "workflow";
|
|
@@ -38,6 +50,7 @@ export type PlatformCallResult = {
|
|
|
38
50
|
error: {
|
|
39
51
|
code: string;
|
|
40
52
|
message: string;
|
|
53
|
+
userFacingMessage?: string;
|
|
41
54
|
};
|
|
42
55
|
};
|
|
43
56
|
export type PlatformSession = {
|
|
@@ -63,9 +76,10 @@ export type PlatformBinding = {
|
|
|
63
76
|
runs: RunRecord[];
|
|
64
77
|
}): Promise<void>;
|
|
65
78
|
};
|
|
66
|
-
|
|
79
|
+
/** A demo is the published build serving a read-only visitor: as live, but every write is refused. */
|
|
80
|
+
export type HostMode = "live" | "preview" | "demo" | "validate";
|
|
67
81
|
export type HostInvoke = {
|
|
68
|
-
kind: "webhook" | "describe" | "workflow" | "register" | "warm";
|
|
82
|
+
kind: "webhook" | "describe" | "workflow" | "register" | "warm" | "authHook";
|
|
69
83
|
name: string;
|
|
70
84
|
};
|
|
71
85
|
/** The per-request props the host dispatches with. Only the host can set them. */
|
|
@@ -75,6 +89,12 @@ export type HostProps = {
|
|
|
75
89
|
user: User | null;
|
|
76
90
|
mode: HostMode;
|
|
77
91
|
invoke: HostInvoke | null;
|
|
92
|
+
/** The app's live URL, demo included: `process.env.ZITE_APP_URL`. Hosts before 0.10.0-start.7 don't send it. */
|
|
93
|
+
appUrl?: string;
|
|
94
|
+
/** The app's database: `process.env.ZITE_BASE_ID`. */
|
|
95
|
+
baseId?: string;
|
|
96
|
+
/** An editor preview's access, which zitejs keeps on the app's links and calls. */
|
|
97
|
+
previewClaim?: string;
|
|
78
98
|
};
|
|
79
99
|
/** What the host resolved for this request. Set by the generated server entry, never by the browser. */
|
|
80
100
|
export type ZiteRequestContext = {
|
|
@@ -83,8 +103,13 @@ export type ZiteRequestContext = {
|
|
|
83
103
|
user: User | null;
|
|
84
104
|
mode: HostMode;
|
|
85
105
|
invoke: HostInvoke | null;
|
|
106
|
+
appUrl?: string;
|
|
107
|
+
baseId?: string;
|
|
108
|
+
previewClaim?: string;
|
|
86
109
|
/** Filled by run capture, flushed to the platform once the response ends. */
|
|
87
110
|
runs: RunRecord[];
|
|
111
|
+
/** Records still reading a copy of a body; the flush waits for them. */
|
|
112
|
+
pending?: Promise<void>[];
|
|
88
113
|
platform?: PlatformBinding;
|
|
89
114
|
session?: Promise<PlatformSession>;
|
|
90
115
|
/** Set when the host renders a page it may cache. */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ZITEJS_VERSION = "0.10.0-start.7";
|
package/dist/esm/start/vite.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
|
|
3
4
|
import viteReact from "@vitejs/plugin-react";
|
|
@@ -7,8 +8,14 @@ import { hasAppStart, renderStartInstance, renderStartServerEntry, renderWorkflo
|
|
|
7
8
|
// Must match the literals in ./server.ts, which the build rewrites.
|
|
8
9
|
const FN_IDS_TOKEN = "__ZITE_FN_IDS__";
|
|
9
10
|
const FN_DESCRIPTIONS_LITERAL = /(["'`])__ZITE_FN_DESCRIPTIONS__\1/;
|
|
11
|
+
const ROUTE_FILES_LITERAL = /(["'`])__ZITE_ROUTE_FILES__\1/;
|
|
12
|
+
// TanStack's generator writes each route file's id into its createFileRoute call and keeps it in step.
|
|
13
|
+
const FILE_ROUTE_ID = /\bcreateFileRoute\(\s*(["'`])([^"'`]+)\1\s*\)/;
|
|
10
14
|
// zitejs's empty registry, which the build replaces with the app's.
|
|
11
15
|
const WORKFLOW_REGISTRY = /[\\/]zitejs[\\/]dist[\\/]esm[\\/]start[\\/]workflowModules\.js$/;
|
|
16
|
+
// zitejs's null auth-hooks module, which the build points at the app's `zite.auth.ts`.
|
|
17
|
+
const AUTH_HOOKS_REGISTRY = /[\\/]zitejs[\\/]dist[\\/]esm[\\/]start[\\/]authHooksModule\.js$/;
|
|
18
|
+
const AUTH_HOOKS_FILE = "zite.auth.ts";
|
|
12
19
|
// Platform-pinned Worker settings. The deploy uses its own fixed template, so
|
|
13
20
|
// these only have to match it; apps don't carry a wrangler config.
|
|
14
21
|
const WORKER_COMPATIBILITY_DATE = "2026-09-25";
|
|
@@ -71,14 +78,21 @@ export function zite(options = {}) {
|
|
|
71
78
|
// time; Vite leaves SSR output unminified.
|
|
72
79
|
configEnvironment: (name, config) => name === "ssr" && options.cloudflare !== false && config.build?.minify === undefined ? { build: { minify: true } } : undefined,
|
|
73
80
|
};
|
|
74
|
-
// Each server function's `description` option, by name, for the editor.
|
|
81
|
+
// Each server function's `description` option, by name, and each route's file, by route id: for the editor.
|
|
75
82
|
const descriptions = new Map();
|
|
83
|
+
const routeFiles = new Map();
|
|
76
84
|
const srcDir = `${path.join(appDir, "src")}${path.sep}`;
|
|
77
85
|
const functionIds = {
|
|
78
86
|
name: "zite:function-ids",
|
|
79
87
|
apply: "build",
|
|
80
88
|
transform(code, id) {
|
|
81
|
-
if (this.environment?.config.consumer !== "server" || !id.startsWith(srcDir)
|
|
89
|
+
if (this.environment?.config.consumer !== "server" || !id.startsWith(srcDir))
|
|
90
|
+
return;
|
|
91
|
+
const file = id.split("?")[0];
|
|
92
|
+
const routeId = FILE_ROUTE_ID.exec(code)?.[2];
|
|
93
|
+
if (routeId && !id.includes("?"))
|
|
94
|
+
routeFiles.set(routeId, path.relative(appDir, file).split(path.sep).join("/"));
|
|
95
|
+
if (!code.includes("description"))
|
|
82
96
|
return;
|
|
83
97
|
try {
|
|
84
98
|
for (const [name, text] of factoryDescriptions(this.parse(code)))
|
|
@@ -95,7 +109,9 @@ export function zite(options = {}) {
|
|
|
95
109
|
continue;
|
|
96
110
|
if (chunk.code.includes(FN_IDS_TOKEN))
|
|
97
111
|
chunk.code = chunk.code.replace(FN_IDS_TOKEN, [...ids.keys()].join(","));
|
|
98
|
-
chunk.code = chunk.code
|
|
112
|
+
chunk.code = chunk.code
|
|
113
|
+
.replace(FN_DESCRIPTIONS_LITERAL, () => JSON.stringify(JSON.stringify(described)))
|
|
114
|
+
.replace(ROUTE_FILES_LITERAL, () => JSON.stringify(JSON.stringify(Object.fromEntries(routeFiles))));
|
|
99
115
|
}
|
|
100
116
|
},
|
|
101
117
|
};
|
|
@@ -103,6 +119,17 @@ export function zite(options = {}) {
|
|
|
103
119
|
name: "zite:workflow-registry",
|
|
104
120
|
load: (id) => (WORKFLOW_REGISTRY.test(id.split("?")[0]) ? renderWorkflowModules(appDir, workflowFiles(appDir)) : undefined),
|
|
105
121
|
};
|
|
122
|
+
const authHooksRegistry = {
|
|
123
|
+
name: "zite:auth-hooks",
|
|
124
|
+
load: (id) => {
|
|
125
|
+
if (!AUTH_HOOKS_REGISTRY.test(id.split("?")[0]))
|
|
126
|
+
return undefined;
|
|
127
|
+
const file = path.join(appDir, AUTH_HOOKS_FILE);
|
|
128
|
+
return existsSync(file)
|
|
129
|
+
? `export const authHooksModule = () => import(${JSON.stringify(file)});\n`
|
|
130
|
+
: "export const authHooksModule = null;\n";
|
|
131
|
+
},
|
|
132
|
+
};
|
|
106
133
|
return [
|
|
107
134
|
tanstackStart({
|
|
108
135
|
// TanStack resolves these from `src/`.
|
|
@@ -140,5 +167,6 @@ export function zite(options = {}) {
|
|
|
140
167
|
compileZitejs,
|
|
141
168
|
functionIds,
|
|
142
169
|
workflowRegistry,
|
|
170
|
+
authHooksRegistry,
|
|
143
171
|
];
|
|
144
172
|
}
|
|
@@ -23,7 +23,8 @@ const idOf = (id) => {
|
|
|
23
23
|
*/
|
|
24
24
|
export function triggerContext(workflow, rawInput) {
|
|
25
25
|
const raw = asRecord(rawInput);
|
|
26
|
-
|
|
26
|
+
// `__cron` stays in the input, as on classic: scheduled code reads its scheduleId and scheduledAt there.
|
|
27
|
+
const { __poll, __webhook, ...input } = raw ?? {};
|
|
27
28
|
const declared = workflow.config.trigger;
|
|
28
29
|
if (!declared)
|
|
29
30
|
return { input: raw ? input : rawInput };
|
package/dist/esm/sync/lib.js
CHANGED
|
@@ -509,7 +509,7 @@ export function generateDbTs(inputSchema) {
|
|
|
509
509
|
lines.push('// `not: null` means "is set". An empty `in: []` matches nothing; an', "// empty `notIn: []` applies no filter.");
|
|
510
510
|
lines.push("// sort is currently IGNORED here — it is stripped before the request.", "// (it does work on zite.auth.findAllUsers). Order in SQL instead.");
|
|
511
511
|
lines.push("// limit defaults to 500, max 2000; offset is a row count (a number)");
|
|
512
|
-
lines.push(
|
|
512
|
+
lines.push("// a filter value of `undefined` is REJECTED (it would query unfiltered", "// and match an arbitrary record). Check the value first.");
|
|
513
513
|
lines.push("// .findOne({ id?, filters?, fields? }) → T | undefined");
|
|
514
514
|
lines.push("// .create({ record }) → T");
|
|
515
515
|
lines.push("// .update({ id, record }) → { id: string, fields: Partial<T> }");
|
|
@@ -1217,13 +1217,13 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1217
1217
|
"// Auto-generated type-narrowing wrapper. Do not edit manually.",
|
|
1218
1218
|
"// Re-exports createEndpoint with context.user typed to the app User.",
|
|
1219
1219
|
"",
|
|
1220
|
-
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteTriggerContext, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1220
|
+
"import type { ZiteRequestContext as _ZiteRequestContext, ZiteScheduledContext, ZiteErrorCode, ZiteSchedule, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteTriggerContext, ZiteEndpointInput, ZiteWebhook } from 'zitejs/backend/base';",
|
|
1221
1221
|
"import type { User } from 'zitejs/auth';",
|
|
1222
1222
|
"",
|
|
1223
1223
|
"export type { ZiteErrorCode, ZiteSchedule, ZiteScheduledContext, ZiteStep, ZiteStreamInterface, ZiteTrigger, ZiteWebhook };",
|
|
1224
1224
|
// A trigger's registration exports are typed against these, and they
|
|
1225
1225
|
// only reach app code through this file.
|
|
1226
|
-
"export type { ZiteTriggerContext, ZiteTriggerDeliveryInfo, ZiteTriggerDeliveryRun, ZiteTriggerRun, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1226
|
+
"export type { ZiteDatabaseTriggerEvent, ZiteDatabaseTriggerInput, ZiteFormTriggerInput, ZiteTriggerContext, ZiteTriggerDeliveryInfo, ZiteTriggerDeliveryRun, ZiteTriggerRun, ZiteTriggerSubscribe, ZiteTriggerSubscribeInput, ZiteTriggerSubscribeResult, ZiteTriggerUnsubscribe, ZiteTriggerUnsubscribeInput } from 'zitejs/backend/base';",
|
|
1227
1227
|
// The pre-monorepo SDK put this in scope for every endpoint, so migrated
|
|
1228
1228
|
// code can name it. `createEndpoint` infers the same thing without it.
|
|
1229
1229
|
"export type InferSchemaType<T> = T extends { _output: infer U } ? U : T;",
|
|
@@ -1311,12 +1311,12 @@ export function generateBackendWrapperTs(envVarNames = []) {
|
|
|
1311
1311
|
" schedule?: TSchedule;",
|
|
1312
1312
|
" /** When set, an inbound webhook can also trigger this endpoint. Like `schedule`, it widens `context` — a webhook fire has no session. */",
|
|
1313
1313
|
" webhook?: TWebhook;",
|
|
1314
|
-
" /** When set, a connection's events (registered by this file's own `subscribe` export)
|
|
1314
|
+
" /** When set, a connection's events (registered by this file's own `subscribe` export), a poll (`every`), a change to one of the project's tables (`table`) or a submission to one of its forms (`form`) also fire this endpoint. Like `webhook`, it widens `context` — a trigger fire has no session. */",
|
|
1315
1315
|
" trigger?: TTrigger;",
|
|
1316
1316
|
" execute: (",
|
|
1317
1317
|
" params: {",
|
|
1318
|
-
" input: TInput
|
|
1319
|
-
" /** Durable steps: step.run(name, fn) runs fn once per logical run — its result is saved before anything after it executes, and a resumed run replays completed steps from their saved results. Provided by the platform; never define a local stand-in. */",
|
|
1318
|
+
" input: ZiteEndpointInput<TInput, TTrigger>;",
|
|
1319
|
+
" /** Durable steps: step.run(name, fn) runs fn once per logical run — its result is saved before anything after it executes, and a resumed run replays completed steps from their saved results. In a run fired by a schedule, webhook or trigger, step.sleep(name, \"2d\") pauses the run and a failed step is tried again. Provided by the platform; never define a local stand-in. */",
|
|
1320
1320
|
" step: ZiteStep;",
|
|
1321
1321
|
" context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;",
|
|
1322
1322
|
// Mirrors the base module: a trigger's run gets `trigger`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zitejs",
|
|
3
|
-
"version": "0.10.0-start.
|
|
3
|
+
"version": "0.10.0-start.7",
|
|
4
4
|
"description": "The Zite framework — build apps on Zite Database",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -123,11 +123,13 @@
|
|
|
123
123
|
}
|
|
124
124
|
},
|
|
125
125
|
"scripts": {
|
|
126
|
-
"build": "tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
|
|
126
|
+
"build": "node scripts/write-version.mjs && tsc && tsc -p tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
|
|
127
127
|
"test": "vitest run",
|
|
128
128
|
"test:start": "node tests/start/run-contract.mjs",
|
|
129
129
|
"test:watch": "vitest",
|
|
130
130
|
"typecheck": "tsc --noEmit",
|
|
131
|
+
"version": "node scripts/write-version.mjs && git add src/start/version.ts",
|
|
132
|
+
"release": "node ./scripts/release.mjs",
|
|
131
133
|
"prepublishOnly": "npm run build"
|
|
132
134
|
},
|
|
133
135
|
"files": [
|
|
@@ -177,14 +179,15 @@
|
|
|
177
179
|
}
|
|
178
180
|
},
|
|
179
181
|
"peerDependencies": {
|
|
180
|
-
"@
|
|
181
|
-
"react": ">=
|
|
182
|
-
"
|
|
182
|
+
"@cloudflare/vite-plugin": ">=1",
|
|
183
|
+
"@tanstack/react-query": ">=5.102.0",
|
|
184
|
+
"@tanstack/react-router": "1.170.41",
|
|
185
|
+
"@tanstack/react-router-ssr-query": "1.167.3",
|
|
183
186
|
"@tanstack/react-start": "1.168.60",
|
|
184
187
|
"@tanstack/start-storage-context": "1.167.36",
|
|
185
188
|
"@vitejs/plugin-react": ">=5",
|
|
186
|
-
"
|
|
187
|
-
"
|
|
189
|
+
"react": ">=18",
|
|
190
|
+
"vite": ">=5"
|
|
188
191
|
},
|
|
189
192
|
"peerDependenciesMeta": {
|
|
190
193
|
"react": {
|
|
@@ -210,11 +213,16 @@
|
|
|
210
213
|
},
|
|
211
214
|
"@tanstack/react-router": {
|
|
212
215
|
"optional": true
|
|
216
|
+
},
|
|
217
|
+
"@tanstack/react-router-ssr-query": {
|
|
218
|
+
"optional": true
|
|
213
219
|
}
|
|
214
220
|
},
|
|
215
221
|
"devDependencies": {
|
|
216
|
-
"@tanstack/
|
|
222
|
+
"@tanstack/query-core": "5.104.0",
|
|
223
|
+
"@tanstack/react-query": "5.104.0",
|
|
217
224
|
"@tanstack/react-router": "1.170.41",
|
|
225
|
+
"@tanstack/react-router-ssr-query": "1.167.3",
|
|
218
226
|
"@tanstack/react-start": "1.168.60",
|
|
219
227
|
"@tanstack/start-storage-context": "1.167.36",
|
|
220
228
|
"@types/node": "^25.9.1",
|