zitejs 0.10.0-start.8 → 0.10.0-start.9
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/auth/index.d.ts +2 -2
- package/dist/cjs/check/index.d.ts +33 -1
- package/dist/cjs/check/index.js +136 -86
- package/dist/cjs/check/start/describeRules.d.ts +35 -0
- package/dist/cjs/check/start/describeRules.js +89 -0
- package/dist/cjs/check/start/problem.d.ts +11 -0
- package/dist/cjs/check/start/problem.js +5 -0
- package/dist/cjs/check/start/sourceRules.d.ts +3 -0
- package/dist/cjs/check/start/sourceRules.js +441 -0
- package/dist/cjs/cli.js +1 -1
- package/dist/esm/auth/index.d.ts +2 -2
- package/dist/esm/check/index.d.ts +33 -1
- package/dist/esm/check/index.js +135 -88
- package/dist/esm/check/start/describeRules.d.ts +35 -0
- package/dist/esm/check/start/describeRules.js +86 -0
- package/dist/esm/check/start/problem.d.ts +11 -0
- package/dist/esm/check/start/problem.js +1 -0
- package/dist/esm/check/start/sourceRules.d.ts +3 -0
- package/dist/esm/check/start/sourceRules.js +438 -0
- package/dist/esm/cli.js +1 -1
- package/dist/esm/start/describe.d.ts +5 -0
- package/dist/esm/start/describe.js +8 -1
- package/dist/esm/start/errors.d.ts +5 -0
- package/dist/esm/start/errors.js +5 -0
- package/dist/esm/start/factories.test-d.d.ts +1 -0
- package/dist/esm/start/factories.test-d.js +20 -0
- package/dist/esm/start/index.d.ts +7 -4
- package/dist/esm/start/index.js +2 -5
- package/dist/esm/start/router.d.ts +1 -0
- package/dist/esm/start/router.js +7 -3
- package/dist/esm/start/version.d.ts +1 -1
- package/dist/esm/start/version.js +1 -1
- package/dist/esm/start/workflow.d.ts +3 -0
- package/dist/esm/start/workflow.test-d.js +5 -0
- package/package.json +1 -1
package/dist/cjs/auth/index.d.ts
CHANGED
|
@@ -67,7 +67,7 @@ export declare const signIn: {
|
|
|
67
67
|
}, FetchOptions["throw"] extends true ? true : false>>;
|
|
68
68
|
} & {
|
|
69
69
|
social: <FetchOptions extends import("@better-auth/core").ClientFetchOption<Partial<{
|
|
70
|
-
provider: "salesforce" | "slack" | "linear" | "notion" | "linkedin" | "github" | "dropbox" | (string & {}) | "huggingface" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "spotify" | "twitch" | "twitter" | "kick" | "gitlab" | "tiktok" | "reddit" | "roblox" | "vk" | "zoom" | "kakao" | "naver" | "
|
|
70
|
+
provider: "salesforce" | "slack" | "linear" | "notion" | "linkedin" | "github" | "dropbox" | "line" | (string & {}) | "huggingface" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "spotify" | "twitch" | "twitter" | "kick" | "gitlab" | "tiktok" | "reddit" | "roblox" | "vk" | "zoom" | "kakao" | "naver" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
|
|
71
71
|
callbackURL?: string | undefined;
|
|
72
72
|
newUserCallbackURL?: string | undefined;
|
|
73
73
|
errorCallbackURL?: string | undefined;
|
|
@@ -91,7 +91,7 @@ export declare const signIn: {
|
|
|
91
91
|
loginHint?: string | undefined;
|
|
92
92
|
additionalData?: Record<string, any> | undefined;
|
|
93
93
|
}> & Record<string, any>, Partial<Record<string, any>> & Record<string, any>, Record<string, any> | undefined>>(data_0: import("better-auth/react").Prettify<{
|
|
94
|
-
provider: "salesforce" | "slack" | "linear" | "notion" | "linkedin" | "github" | "dropbox" | (string & {}) | "huggingface" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "spotify" | "twitch" | "twitter" | "kick" | "gitlab" | "tiktok" | "reddit" | "roblox" | "vk" | "zoom" | "kakao" | "naver" | "
|
|
94
|
+
provider: "salesforce" | "slack" | "linear" | "notion" | "linkedin" | "github" | "dropbox" | "line" | (string & {}) | "huggingface" | "apple" | "atlassian" | "cognito" | "discord" | "facebook" | "figma" | "microsoft" | "google" | "spotify" | "twitch" | "twitter" | "kick" | "gitlab" | "tiktok" | "reddit" | "roblox" | "vk" | "zoom" | "kakao" | "naver" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
|
|
95
95
|
callbackURL?: string | undefined;
|
|
96
96
|
newUserCallbackURL?: string | undefined;
|
|
97
97
|
errorCallbackURL?: string | undefined;
|
|
@@ -1 +1,33 @@
|
|
|
1
|
-
|
|
1
|
+
import { type Problem } from './start/problem.js';
|
|
2
|
+
type Step = {
|
|
3
|
+
name: string;
|
|
4
|
+
ok: boolean;
|
|
5
|
+
output?: string;
|
|
6
|
+
};
|
|
7
|
+
/** One app's check, as `--json` reports it. */
|
|
8
|
+
export type AppCheck = {
|
|
9
|
+
path: string;
|
|
10
|
+
runtime: 'classic' | 'start';
|
|
11
|
+
ok: boolean;
|
|
12
|
+
steps: Step[];
|
|
13
|
+
problems: Problem[];
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* While `--json` checks, stdout carries only the report: app code that logs as
|
|
17
|
+
* it's described in-process goes to stderr instead.
|
|
18
|
+
*/
|
|
19
|
+
export declare const withStdoutOnStderr: <T>(run: () => Promise<T>) => Promise<T>;
|
|
20
|
+
/** Prints as it goes, or with `--json` stays quiet and reports everything at the end. */
|
|
21
|
+
export declare class Reporter {
|
|
22
|
+
private readonly json;
|
|
23
|
+
readonly apps: AppCheck[];
|
|
24
|
+
constructor(json: boolean);
|
|
25
|
+
app(path: string, runtime: AppCheck['runtime'], heading: boolean): AppCheck;
|
|
26
|
+
step(app: AppCheck, name: string, result: {
|
|
27
|
+
ok: boolean;
|
|
28
|
+
output?: string;
|
|
29
|
+
}): boolean;
|
|
30
|
+
problems(app: AppCheck, problems: Problem[]): void;
|
|
31
|
+
}
|
|
32
|
+
export declare function runCheck(args?: string[]): Promise<void>;
|
|
33
|
+
export {};
|
package/dist/cjs/check/index.js
CHANGED
|
@@ -33,11 +33,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.Reporter = exports.withStdoutOnStderr = void 0;
|
|
36
37
|
exports.runCheck = runCheck;
|
|
37
38
|
const child_process_1 = require("child_process");
|
|
38
39
|
const fs_1 = require("fs");
|
|
39
40
|
const path_1 = require("path");
|
|
40
41
|
const sourceRoots_js_1 = require("../sourceRoots.js");
|
|
42
|
+
const problem_js_1 = require("./start/problem.js");
|
|
41
43
|
/**
|
|
42
44
|
* An argument array, not a command string, so no shell is involved.
|
|
43
45
|
*
|
|
@@ -93,41 +95,134 @@ function bundleFailures(output) {
|
|
|
93
95
|
}
|
|
94
96
|
const indent = (output) => output.trim().split('\n').map(l => ` ${l}`).join('\n');
|
|
95
97
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* refuses, read the same way.
|
|
98
|
+
* While `--json` checks, stdout carries only the report: app code that logs as
|
|
99
|
+
* it's described in-process goes to stderr instead.
|
|
99
100
|
*/
|
|
100
|
-
async
|
|
101
|
-
process.stdout.write
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
const withStdoutOnStderr = async (run) => {
|
|
102
|
+
const write = process.stdout.write;
|
|
103
|
+
process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
104
|
+
try {
|
|
105
|
+
return await run();
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
process.stdout.write = write;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
exports.withStdoutOnStderr = withStdoutOnStderr;
|
|
112
|
+
/** Prints as it goes, or with `--json` stays quiet and reports everything at the end. */
|
|
113
|
+
class Reporter {
|
|
114
|
+
json;
|
|
115
|
+
apps = [];
|
|
116
|
+
constructor(json) {
|
|
117
|
+
this.json = json;
|
|
118
|
+
}
|
|
119
|
+
app(path, runtime, heading) {
|
|
120
|
+
const app = { path, runtime, ok: true, steps: [], problems: [] };
|
|
121
|
+
this.apps.push(app);
|
|
122
|
+
if (heading && !this.json)
|
|
123
|
+
console.log(`\n── ${path} ──`);
|
|
124
|
+
return app;
|
|
125
|
+
}
|
|
126
|
+
step(app, name, result) {
|
|
127
|
+
const output = result.output?.trim();
|
|
128
|
+
app.steps.push({ name, ok: result.ok, ...(output && !result.ok && { output }) });
|
|
129
|
+
if (!result.ok)
|
|
130
|
+
app.ok = false;
|
|
131
|
+
if (!this.json) {
|
|
132
|
+
console.log(` ${name} ... ${result.ok ? '✓' : '✗'}`);
|
|
133
|
+
if (!result.ok && output)
|
|
134
|
+
console.log(indent(output));
|
|
135
|
+
}
|
|
136
|
+
return result.ok;
|
|
137
|
+
}
|
|
138
|
+
problems(app, problems) {
|
|
139
|
+
app.problems.push(...problems);
|
|
140
|
+
if (problems.some(p => p.severity === 'error'))
|
|
141
|
+
app.ok = false;
|
|
142
|
+
if (!this.json && problems.length)
|
|
143
|
+
console.log(problems.map(p => ` ${(0, problem_js_1.formatProblem)(p)}`).join('\n'));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.Reporter = Reporter;
|
|
147
|
+
const SKIPPED_DIRS = new Set(['node_modules', 'dist', '.output', '.tanstack', '.wrangler']);
|
|
148
|
+
/** The app's own source, by app-relative path. */
|
|
149
|
+
function readAppSources(appPath) {
|
|
150
|
+
const sources = {};
|
|
151
|
+
const visit = (dir) => {
|
|
152
|
+
for (const entry of (0, fs_1.readdirSync)(dir, { withFileTypes: true })) {
|
|
153
|
+
if (SKIPPED_DIRS.has(entry.name))
|
|
154
|
+
continue;
|
|
155
|
+
const full = (0, path_1.join)(dir, entry.name);
|
|
156
|
+
if (entry.isDirectory())
|
|
157
|
+
visit(full);
|
|
158
|
+
else if (/\.[cm]?[jt]sx?$/.test(entry.name))
|
|
159
|
+
sources[(0, path_1.relative)(appPath, full)] = (0, fs_1.readFileSync)(full, 'utf-8');
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const src = (0, path_1.join)(appPath, 'src');
|
|
163
|
+
if ((0, fs_1.existsSync)(src))
|
|
164
|
+
visit(src);
|
|
165
|
+
return sources;
|
|
166
|
+
}
|
|
167
|
+
const isInternalApp = (appPath) => {
|
|
168
|
+
try {
|
|
169
|
+
return JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(appPath, 'zite.config.json'), 'utf-8'))?.accessMode === 'internal';
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
106
172
|
return false;
|
|
107
173
|
}
|
|
108
|
-
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* A Start app: `vite build` first, since it regenerates the route tree tsc
|
|
177
|
+
* reads, then tsc, then describe, which reports what the platform's build
|
|
178
|
+
* refuses, read the same way, then the source hints.
|
|
179
|
+
*/
|
|
180
|
+
async function checkStartApp(reporter, app, appPath) {
|
|
181
|
+
if (!reporter.step(app, 'vite build', run('npx', ['vite', 'build'], appPath)))
|
|
182
|
+
return;
|
|
109
183
|
// --pretty for `file:line:col - error` lines, the shape the platform re-roots.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.log(indent(tsc.output));
|
|
114
|
-
process.stdout.write(' describe ... ');
|
|
184
|
+
reporter.step(app, 'tsc --noEmit', run('npx', ['tsc', '--noEmit', '--pretty', '-p', 'tsconfig.app.json'], appPath));
|
|
185
|
+
const { describedProblems } = await Promise.resolve().then(() => __importStar(require('./start/describeRules.js')));
|
|
186
|
+
const { sourceProblems } = await Promise.resolve().then(() => __importStar(require('./start/sourceRules.js')));
|
|
115
187
|
try {
|
|
116
188
|
const { describeBuiltApp } = await Promise.resolve().then(() => __importStar(require('../start/describeBuilt.js')));
|
|
117
|
-
const
|
|
118
|
-
const problems =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.log(problems.map(p => ` ${p}`).join('\n'));
|
|
122
|
-
return tsc.ok && problems.length === 0;
|
|
189
|
+
const description = (await describeBuiltApp(appPath));
|
|
190
|
+
const problems = describedProblems(description, { internal: isInternalApp(appPath) });
|
|
191
|
+
reporter.step(app, 'describe', { ok: !problems.some(p => p.severity === 'error') });
|
|
192
|
+
reporter.problems(app, problems);
|
|
123
193
|
}
|
|
124
194
|
catch (err) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
195
|
+
reporter.step(app, 'describe', { ok: false, output: err instanceof Error ? err.message : String(err) });
|
|
196
|
+
}
|
|
197
|
+
const hints = sourceProblems(readAppSources(appPath));
|
|
198
|
+
reporter.step(app, 'source', { ok: !hints.some(p => p.severity === 'error') });
|
|
199
|
+
reporter.problems(app, hints);
|
|
200
|
+
}
|
|
201
|
+
function checkClassicApp(reporter, app, root, dir, appPath) {
|
|
202
|
+
// Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
|
|
203
|
+
// solution-style config — `"files": []` plus a reference — so
|
|
204
|
+
// `tsc --noEmit -p tsconfig.json` type-checks zero files and exits 0. It
|
|
205
|
+
// used to be the fallback, which meant an app missing tsconfig.app.json
|
|
206
|
+
// reported a passing typecheck it never ran.
|
|
207
|
+
const tsconfigAppPath = (0, path_1.join)(appPath, 'tsconfig.app.json');
|
|
208
|
+
reporter.step(app, 'tsc --noEmit', (0, fs_1.existsSync)(tsconfigAppPath)
|
|
209
|
+
? run('npx', ['tsc', '--noEmit', '-p', tsconfigAppPath], '.')
|
|
210
|
+
: { ok: false, output: `no ${tsconfigAppPath}` });
|
|
211
|
+
// Endpoints never reach vite — they are bundled separately for the lambda,
|
|
212
|
+
// against a different resolver. tsc and vite both pass on an endpoint whose
|
|
213
|
+
// `zitejs/*` alias has no generated file behind it; only the bundler knows.
|
|
214
|
+
if ((0, sourceRoots_js_1.listEndpointNames)(root, appPath).length > 0) {
|
|
215
|
+
const bundle = run('npx', ['zitejs', 'bundle', '--app', dir], '.');
|
|
216
|
+
const failures = bundle.ok ? bundleFailures(bundle.output) : [bundle.output];
|
|
217
|
+
reporter.step(app, 'bundle endpoints', { ok: failures.length === 0, output: failures.join('\n') });
|
|
218
|
+
}
|
|
219
|
+
if ((0, fs_1.existsSync)((0, path_1.join)(appPath, 'vite.config.ts'))) {
|
|
220
|
+
reporter.step(app, 'vite build', run('npx', ['vite', 'build'], appPath));
|
|
128
221
|
}
|
|
129
222
|
}
|
|
130
|
-
async function runCheck() {
|
|
223
|
+
async function runCheck(args = process.argv.slice(3)) {
|
|
224
|
+
const json = args.includes('--json');
|
|
225
|
+
const reporter = new Reporter(json);
|
|
131
226
|
const { isStartAppDir } = await Promise.resolve().then(() => __importStar(require('../start/entries.js')));
|
|
132
227
|
// Run inside a Start app, its own zitejs checks just that app: the workspace
|
|
133
228
|
// root's is a classic release that doesn't know Start.
|
|
@@ -137,72 +232,27 @@ async function runCheck() {
|
|
|
137
232
|
console.error('No apps or automations found under apps/ or automations/.');
|
|
138
233
|
process.exit(1);
|
|
139
234
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
// Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
|
|
149
|
-
// solution-style config — `"files": []` plus a reference — so
|
|
150
|
-
// `tsc --noEmit -p tsconfig.json` type-checks zero files and exits 0. It
|
|
151
|
-
// used to be the fallback, which meant an app missing tsconfig.app.json
|
|
152
|
-
// reported a passing typecheck it never ran.
|
|
153
|
-
const tsconfigAppPath = (0, path_1.join)(appPath, 'tsconfig.app.json');
|
|
154
|
-
if (!(0, fs_1.existsSync)(tsconfigAppPath)) {
|
|
155
|
-
console.log(` tsc --noEmit ... ✗ (no ${tsconfigAppPath})`);
|
|
156
|
-
allPassed = false;
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
process.stdout.write(' tsc --noEmit ... ');
|
|
160
|
-
const tsc = run('npx', ['tsc', '--noEmit', '-p', tsconfigAppPath], '.');
|
|
161
|
-
if (tsc.ok) {
|
|
162
|
-
console.log('✓');
|
|
235
|
+
const checkAll = async () => {
|
|
236
|
+
if (inStartApp)
|
|
237
|
+
await checkStartApp(reporter, reporter.app('.', 'start', false), '.');
|
|
238
|
+
for (const { root, dir, path: appPath } of appDirs) {
|
|
239
|
+
if (isStartAppDir(appPath)) {
|
|
240
|
+
await checkStartApp(reporter, reporter.app(appPath, 'start', true), appPath);
|
|
163
241
|
}
|
|
164
242
|
else {
|
|
165
|
-
|
|
166
|
-
console.log(tsc.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
|
|
167
|
-
allPassed = false;
|
|
243
|
+
checkClassicApp(reporter, reporter.app(appPath, 'classic', true), root, dir, appPath);
|
|
168
244
|
}
|
|
169
245
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const bundle = run('npx', ['zitejs', 'bundle', '--app', app], '.');
|
|
176
|
-
const failures = bundle.ok ? bundleFailures(bundle.output) : [bundle.output];
|
|
177
|
-
if (failures.length === 0) {
|
|
178
|
-
console.log('✓');
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
console.log('✗');
|
|
182
|
-
console.log(failures.map(l => ` ${l}`).join('\n'));
|
|
183
|
-
allPassed = false;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const viteConfig = (0, path_1.join)(appPath, 'vite.config.ts');
|
|
187
|
-
if ((0, fs_1.existsSync)(viteConfig)) {
|
|
188
|
-
process.stdout.write(' vite build ... ');
|
|
189
|
-
const vite = run('npx', ['vite', 'build'], appPath);
|
|
190
|
-
if (vite.ok) {
|
|
191
|
-
console.log('✓');
|
|
192
|
-
}
|
|
193
|
-
else {
|
|
194
|
-
console.log('✗');
|
|
195
|
-
console.log(vite.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
|
|
196
|
-
allPassed = false;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
console.log('');
|
|
201
|
-
if (allPassed) {
|
|
202
|
-
console.log('All apps pass.');
|
|
246
|
+
};
|
|
247
|
+
await (json ? (0, exports.withStdoutOnStderr)(checkAll) : checkAll());
|
|
248
|
+
const allPassed = reporter.apps.every(app => app.ok);
|
|
249
|
+
if (json) {
|
|
250
|
+
process.stdout.write(`${JSON.stringify({ ok: allPassed, apps: reporter.apps })}\n`);
|
|
203
251
|
}
|
|
204
252
|
else {
|
|
205
|
-
console.log('
|
|
206
|
-
|
|
253
|
+
console.log('');
|
|
254
|
+
console.log(allPassed ? 'All apps pass.' : 'Some apps failed.');
|
|
207
255
|
}
|
|
256
|
+
if (!allPassed)
|
|
257
|
+
process.exit(1);
|
|
208
258
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Problem } from "./problem.js";
|
|
2
|
+
type Access = {
|
|
3
|
+
kind: string;
|
|
4
|
+
roles?: string[];
|
|
5
|
+
};
|
|
6
|
+
export type CheckedDescription = {
|
|
7
|
+
functions: Array<{
|
|
8
|
+
id: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
file?: string;
|
|
11
|
+
access: Access;
|
|
12
|
+
input?: unknown;
|
|
13
|
+
problems: string[];
|
|
14
|
+
}>;
|
|
15
|
+
routes: Array<{
|
|
16
|
+
path: string;
|
|
17
|
+
file?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
methods: Record<string, Access>;
|
|
20
|
+
problems: string[];
|
|
21
|
+
}>;
|
|
22
|
+
pages?: Array<{
|
|
23
|
+
path: string;
|
|
24
|
+
file?: string;
|
|
25
|
+
}>;
|
|
26
|
+
workflows?: Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
file: string;
|
|
29
|
+
problems: string[];
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
export declare function describedProblems(description: CheckedDescription, { internal }: {
|
|
33
|
+
internal: boolean;
|
|
34
|
+
}): Problem[];
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describedProblems = describedProblems;
|
|
4
|
+
// The host, TanStack and Cloudflare answer these before the app does.
|
|
5
|
+
const RESERVED_PATHS = [
|
|
6
|
+
/^\/_zite/,
|
|
7
|
+
/^\/__zite/,
|
|
8
|
+
/^\/_serverFn(\/|$)/,
|
|
9
|
+
/^\/cdn-cgi(\/|$)/,
|
|
10
|
+
/^\/auth(\/|$)/,
|
|
11
|
+
/^\/api\/auth(\/|$)/,
|
|
12
|
+
/^\/manifest\.webmanifest$/,
|
|
13
|
+
];
|
|
14
|
+
const OWNER_INPUTS = ["userId", "ownerId"];
|
|
15
|
+
/** `access-missing: no signedIn…`, or a route's `GET: access-missing: …`, as a named problem. */
|
|
16
|
+
const describeProblem = (problem, subject, file) => {
|
|
17
|
+
const match = /^(?:(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|ANY): )?([a-z][a-z-]*): (.*)$/s.exec(problem);
|
|
18
|
+
const [, method, rule, message] = match ?? [undefined, undefined, "describe", problem];
|
|
19
|
+
return {
|
|
20
|
+
rule: rule ?? "describe",
|
|
21
|
+
severity: "error",
|
|
22
|
+
...(file && { file }),
|
|
23
|
+
message: `${subject}${method ? ` ${method}` : ""}: ${message ?? problem}`,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
const inputProperties = (input) => {
|
|
27
|
+
const properties = input?.properties;
|
|
28
|
+
return properties && typeof properties === "object" ? Object.keys(properties) : [];
|
|
29
|
+
};
|
|
30
|
+
function describedProblems(description, { internal }) {
|
|
31
|
+
const problems = [];
|
|
32
|
+
for (const fn of description.functions) {
|
|
33
|
+
const subject = `function ${fn.name ?? fn.id}`;
|
|
34
|
+
problems.push(...fn.problems.map((p) => describeProblem(p, subject, fn.file)));
|
|
35
|
+
if (internal && fn.access.kind === "anyone") {
|
|
36
|
+
problems.push({
|
|
37
|
+
rule: "public-in-internal",
|
|
38
|
+
severity: "error",
|
|
39
|
+
...(fn.file && { file: fn.file }),
|
|
40
|
+
message: `${subject} is a publicFn, and this internal app signs everyone in; make it a signedInFn`,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const owner = fn.access.kind === "signedIn" && inputProperties(fn.input).find((p) => OWNER_INPUTS.includes(p));
|
|
44
|
+
if (owner) {
|
|
45
|
+
problems.push({
|
|
46
|
+
rule: "owner-from-input",
|
|
47
|
+
severity: "warning",
|
|
48
|
+
...(fn.file && { file: fn.file }),
|
|
49
|
+
message: `${subject} takes ${owner} as input, which any caller can set; use context.user.id`,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const route of description.routes) {
|
|
54
|
+
const subject = `route ${route.path}`;
|
|
55
|
+
problems.push(...route.problems.map((p) => describeProblem(p, subject, route.file)));
|
|
56
|
+
const publicMethods = Object.entries(route.methods).filter(([, access]) => access.kind === "anyone");
|
|
57
|
+
if (internal && publicMethods.length > 0) {
|
|
58
|
+
problems.push({
|
|
59
|
+
rule: "public-in-internal",
|
|
60
|
+
severity: "error",
|
|
61
|
+
...(route.file && { file: route.file }),
|
|
62
|
+
message: `${subject} lets anyone call ${publicMethods.map(([m]) => m).join(", ")}, and this internal app signs everyone in; use signedIn`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (!route.description) {
|
|
66
|
+
problems.push({
|
|
67
|
+
rule: "description-missing",
|
|
68
|
+
severity: "warning",
|
|
69
|
+
...(route.file && { file: route.file }),
|
|
70
|
+
message: `${subject} has no description; add staticData: { description } for the editor`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const workflow of description.workflows ?? []) {
|
|
75
|
+
problems.push(...workflow.problems.map((p) => describeProblem(p, `workflow ${workflow.id}`, workflow.file)));
|
|
76
|
+
}
|
|
77
|
+
const paths = new Map([...description.routes, ...(description.pages ?? [])].map((r) => [r.path, r.file]));
|
|
78
|
+
for (const [path, file] of paths) {
|
|
79
|
+
if (RESERVED_PATHS.some((reserved) => reserved.test(path))) {
|
|
80
|
+
problems.push({
|
|
81
|
+
rule: "reserved-path",
|
|
82
|
+
severity: "error",
|
|
83
|
+
...(file && { file }),
|
|
84
|
+
message: `${path} is the platform's; give the route another path`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return problems;
|
|
89
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type Severity = "error" | "warning";
|
|
2
|
+
/** One finding of `zitejs check`, named by its rule. Errors fail the check; warnings only report. */
|
|
3
|
+
export type Problem = {
|
|
4
|
+
rule: string;
|
|
5
|
+
severity: Severity;
|
|
6
|
+
/** App-relative. */
|
|
7
|
+
file?: string;
|
|
8
|
+
line?: number;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const formatProblem: ({ rule, severity, file, line, message }: Problem) => string;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatProblem = void 0;
|
|
4
|
+
const formatProblem = ({ rule, severity, file, line, message }) => `${severity} ${rule}${file ? ` ${file}${line ? `:${line}` : ""}` : ""}: ${message}`;
|
|
5
|
+
exports.formatProblem = formatProblem;
|