zitejs 0.10.0-start.1 → 0.10.0-start.12

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.
Files changed (120) hide show
  1. package/dist/cjs/auth/index.d.ts +2 -2
  2. package/dist/cjs/auth/index.test.js +30 -0
  3. package/dist/cjs/backend/index.d.ts +115 -3
  4. package/dist/cjs/backend/index.test.js +58 -0
  5. package/dist/cjs/check/index.d.ts +33 -1
  6. package/dist/cjs/check/index.js +187 -59
  7. package/dist/cjs/check/start/describeRules.d.ts +35 -0
  8. package/dist/cjs/check/start/describeRules.js +89 -0
  9. package/dist/cjs/check/start/problem.d.ts +11 -0
  10. package/dist/cjs/check/start/problem.js +5 -0
  11. package/dist/cjs/check/start/sourceRules.d.ts +3 -0
  12. package/dist/cjs/check/start/sourceRules.js +441 -0
  13. package/dist/cjs/cli.js +1 -1
  14. package/dist/cjs/integrations/rules.d.ts +13 -0
  15. package/dist/cjs/integrations/rules.js +29 -0
  16. package/dist/cjs/integrations/rules.test.js +1 -0
  17. package/dist/cjs/start/entries.d.ts +11 -2
  18. package/dist/cjs/start/entries.js +48 -6
  19. package/dist/cjs/start/problems.d.ts +19 -0
  20. package/dist/cjs/start/problems.js +11 -0
  21. package/dist/cjs/sync/lib.js +10 -13
  22. package/dist/cjs/vite/index.js +8 -3
  23. package/dist/cjs/vite/index.test.js +10 -2
  24. package/dist/esm/auth/index.d.ts +2 -2
  25. package/dist/esm/auth/index.test.js +30 -0
  26. package/dist/esm/backend/index.d.ts +115 -3
  27. package/dist/esm/backend/index.test.js +58 -0
  28. package/dist/esm/check/index.d.ts +33 -1
  29. package/dist/esm/check/index.js +153 -61
  30. package/dist/esm/check/start/describeRules.d.ts +35 -0
  31. package/dist/esm/check/start/describeRules.js +86 -0
  32. package/dist/esm/check/start/problem.d.ts +11 -0
  33. package/dist/esm/check/start/problem.js +1 -0
  34. package/dist/esm/check/start/sourceRules.d.ts +3 -0
  35. package/dist/esm/check/start/sourceRules.js +438 -0
  36. package/dist/esm/cli.js +1 -1
  37. package/dist/esm/integrations/rules.d.ts +13 -0
  38. package/dist/esm/integrations/rules.js +29 -0
  39. package/dist/esm/integrations/rules.test.js +1 -0
  40. package/dist/esm/start/airtable/arrayFields.d.ts +10 -0
  41. package/dist/esm/start/airtable/arrayFields.js +30 -0
  42. package/dist/esm/start/airtable/dispatch.d.ts +54 -0
  43. package/dist/esm/start/airtable/dispatch.js +465 -0
  44. package/dist/esm/start/airtable/formulas.d.ts +69 -0
  45. package/dist/esm/start/airtable/formulas.js +235 -0
  46. package/dist/esm/start/airtable/index.d.ts +11 -0
  47. package/dist/esm/start/airtable/index.js +70 -0
  48. package/dist/esm/start/authHooks.d.ts +7 -0
  49. package/dist/esm/start/authHooks.js +10 -0
  50. package/dist/esm/start/authHooksModule.d.ts +1 -0
  51. package/dist/esm/start/authHooksModule.js +3 -0
  52. package/dist/esm/start/clientErrorMessage.test.d.ts +1 -0
  53. package/dist/esm/start/clientErrorMessage.test.js +105 -0
  54. package/dist/esm/start/data/baseRunner.d.ts +92 -0
  55. package/dist/esm/start/data/baseRunner.js +83 -0
  56. package/dist/esm/start/data/dispatch.d.ts +16 -0
  57. package/dist/esm/start/data/dispatch.js +198 -0
  58. package/dist/esm/start/data/index.d.ts +18 -0
  59. package/dist/esm/start/data/index.js +76 -0
  60. package/dist/esm/start/data/sqlNameMappings.d.ts +58 -0
  61. package/dist/esm/start/data/sqlNameMappings.js +92 -0
  62. package/dist/esm/start/data/transformations.d.ts +128 -0
  63. package/dist/esm/start/data/transformations.js +318 -0
  64. package/dist/esm/start/data/types.d.ts +91 -0
  65. package/dist/esm/start/data/types.js +14 -0
  66. package/dist/esm/start/describe.d.ts +46 -1
  67. package/dist/esm/start/describe.js +91 -9
  68. package/dist/esm/start/descriptions.d.ts +4 -0
  69. package/dist/esm/start/descriptions.js +47 -0
  70. package/dist/esm/start/entries.d.ts +11 -2
  71. package/dist/esm/start/entries.js +46 -7
  72. package/dist/esm/start/errors.d.ts +51 -0
  73. package/dist/esm/start/errors.js +104 -0
  74. package/dist/esm/start/errors.test.d.ts +1 -0
  75. package/dist/esm/start/errors.test.js +23 -0
  76. package/dist/esm/start/factories.test-d.d.ts +1 -0
  77. package/dist/esm/start/factories.test-d.js +20 -0
  78. package/dist/esm/start/index.d.ts +24 -22
  79. package/dist/esm/start/index.js +61 -38
  80. package/dist/esm/start/instance.d.ts +3 -3
  81. package/dist/esm/start/instance.js +22 -22
  82. package/dist/esm/start/previewClaim.d.ts +10 -0
  83. package/dist/esm/start/previewClaim.js +32 -0
  84. package/dist/esm/start/problems.d.ts +19 -0
  85. package/dist/esm/start/problems.js +8 -0
  86. package/dist/esm/start/quiet.d.ts +2 -0
  87. package/dist/esm/start/quiet.js +27 -0
  88. package/dist/esm/start/redactSecrets.d.ts +7 -0
  89. package/dist/esm/start/redactSecrets.js +131 -0
  90. package/dist/esm/start/redactSecrets.test.d.ts +1 -0
  91. package/dist/esm/start/redactSecrets.test.js +151 -0
  92. package/dist/esm/start/registration.d.ts +13 -0
  93. package/dist/esm/start/registration.js +21 -0
  94. package/dist/esm/start/router.d.ts +12 -3
  95. package/dist/esm/start/router.js +86 -7
  96. package/dist/esm/start/runLog.d.ts +8 -0
  97. package/dist/esm/start/runLog.js +76 -0
  98. package/dist/esm/start/scope.d.ts +2 -2
  99. package/dist/esm/start/server.d.ts +47 -35
  100. package/dist/esm/start/server.js +320 -42
  101. package/dist/esm/start/steps.d.ts +28 -0
  102. package/dist/esm/start/steps.js +97 -0
  103. package/dist/esm/start/types.d.ts +145 -5
  104. package/dist/esm/start/version.d.ts +1 -0
  105. package/dist/esm/start/version.js +2 -0
  106. package/dist/esm/start/vite.js +67 -8
  107. package/dist/esm/start/worker.d.ts +11 -0
  108. package/dist/esm/start/worker.js +20 -0
  109. package/dist/esm/start/workflow.d.ts +59 -0
  110. package/dist/esm/start/workflow.js +13 -0
  111. package/dist/esm/start/workflow.test-d.d.ts +1 -0
  112. package/dist/esm/start/workflow.test-d.js +50 -0
  113. package/dist/esm/start/workflowModules.d.ts +1 -0
  114. package/dist/esm/start/workflowModules.js +3 -0
  115. package/dist/esm/start/workflowRun.d.ts +93 -0
  116. package/dist/esm/start/workflowRun.js +128 -0
  117. package/dist/esm/sync/lib.js +10 -13
  118. package/dist/esm/vite/index.js +8 -3
  119. package/dist/esm/vite/index.test.js +10 -2
  120. package/package.json +21 -8
@@ -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" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
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" | "line" | "paybin" | "paypal" | "polar" | "railway" | "vercel" | "wechat";
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;
@@ -187,3 +187,33 @@ function redirectParamOf(href) {
187
187
  (0, vitest_1.expect)(params.get('usageToken')).toBeNull();
188
188
  });
189
189
  });
190
+ // AUTH-03: what the platform's auth routes receive from the client, on the app's own origin.
191
+ (0, vitest_1.describe)("the client's calls", () => {
192
+ const calls = () => {
193
+ const sent = [];
194
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn(async (input, init) => {
195
+ const request = new Request(new URL(String(input instanceof Request ? input.url : input), 'https://app.zite.so'), init);
196
+ sent.push({
197
+ path: new URL(request.url).pathname,
198
+ method: request.method,
199
+ body: request.method === 'POST' ? await request.json().catch(() => null) : null,
200
+ });
201
+ return Response.json({});
202
+ }));
203
+ return sent;
204
+ };
205
+ (0, vitest_1.it)('sign in by link, sign out and edit the profile on /api/auth', async () => {
206
+ const sent = calls();
207
+ // The client takes its fetch when the module loads.
208
+ vitest_1.vi.resetModules();
209
+ const { signIn, signOut, updateProfile } = await Promise.resolve().then(() => __importStar(require('./index.js')));
210
+ await signIn.magicLink({ email: 'ada@x.co' });
211
+ await signOut();
212
+ await updateProfile({ firstName: 'Ada' });
213
+ (0, vitest_1.expect)(sent).toEqual([
214
+ { path: '/api/auth/sign-in/magic-link', method: 'POST', body: vitest_1.expect.objectContaining({ email: 'ada@x.co' }) },
215
+ { path: '/api/auth/sign-out', method: 'POST', body: vitest_1.expect.anything() },
216
+ { path: '/api/auth/update-user', method: 'POST', body: { firstName: 'Ada' } },
217
+ ]);
218
+ });
219
+ });
@@ -23,6 +23,8 @@ export type ZiteTrigger = {
23
23
  paused?: boolean;
24
24
  every?: never;
25
25
  events?: never;
26
+ table?: never;
27
+ form?: never;
26
28
  } | {
27
29
  /**
28
30
  * The connection's name in `zite.config.json`, for a provider whose
@@ -37,6 +39,8 @@ export type ZiteTrigger = {
37
39
  label?: string;
38
40
  paused?: boolean;
39
41
  every?: never;
42
+ table?: never;
43
+ form?: never;
40
44
  } | {
41
45
  /** `<n>m` (1–59) or `<n>h` (1–23). What makes the trigger a poll. */
42
46
  every: string;
@@ -46,6 +50,78 @@ export type ZiteTrigger = {
46
50
  label?: string;
47
51
  paused?: boolean;
48
52
  events?: never;
53
+ table?: never;
54
+ form?: never;
55
+ } | {
56
+ /**
57
+ * A table in the project's own database, as `zite.<table>` names it.
58
+ * Every write to it reaches the trigger, from apps, forms, imports, the
59
+ * database view and other automations; nothing is registered.
60
+ */
61
+ table: string;
62
+ on: ZiteDatabaseTriggerEvent[];
63
+ /** Narrows `"updated"` to updates that changed one of these fields. */
64
+ fields?: string[];
65
+ /** The sentence the editor shows, e.g. "When an order ships". */
66
+ label?: string;
67
+ paused?: boolean;
68
+ integration?: never;
69
+ every?: never;
70
+ events?: never;
71
+ form?: never;
72
+ } | {
73
+ /**
74
+ * One of the project's own Fillout forms, by its name as the project
75
+ * lists it, or its id. Each finished live submission fires the
76
+ * endpoint; nothing is registered.
77
+ */
78
+ form: string;
79
+ on: "submitted"[];
80
+ /** The sentence the editor shows, e.g. "When someone fills in the intake form". */
81
+ label?: string;
82
+ paused?: boolean;
83
+ integration?: never;
84
+ every?: never;
85
+ events?: never;
86
+ table?: never;
87
+ };
88
+ /**
89
+ * What a form trigger's run gets as `input`: one submission. `answers` is
90
+ * keyed by question name, `answersById` by question id; `submission` is the
91
+ * whole export, as the Fillout API returns it (`questions`, `calculations`,
92
+ * `urlParameters`, …). Two questions with the same label are named
93
+ * `Email` and `Email (1)` in an order the form doesn't show, so read those by
94
+ * id.
95
+ */
96
+ export type ZiteFormTriggerInput = {
97
+ event: "submitted";
98
+ form: string;
99
+ formId: string;
100
+ submissionId: string;
101
+ submittedAt: string | null;
102
+ answers: Record<string, unknown>;
103
+ answersById: Record<string, unknown>;
104
+ submission: Record<string, unknown> & {
105
+ submissionId: string;
106
+ };
107
+ };
108
+ export type ZiteDatabaseTriggerEvent = "created" | "updated" | "deleted";
109
+ /**
110
+ * What a database trigger's run gets as `input`: one changed record. Field
111
+ * keys are the SDK's, as `zite.<table>.findOne` returns them; cast `record`
112
+ * and `previous` to the table's generated `<Table>RecordType` from
113
+ * `zitejs/db` to type their fields.
114
+ */
115
+ export type ZiteDatabaseTriggerInput<TRecord extends Record<string, unknown> = Record<string, unknown>> = {
116
+ event: ZiteDatabaseTriggerEvent;
117
+ table: string;
118
+ recordId: string;
119
+ /** Null if it was deleted before the run read it; on `"deleted"`, the record as it was. */
120
+ record: TRecord | null;
121
+ /** The record before an `"updated"`; null otherwise, and on a very large bulk write. */
122
+ previous: TRecord | null;
123
+ /** What an `"updated"` changed. */
124
+ changedFields: string[];
49
125
  };
50
126
  /**
51
127
  * What a provider trigger's `subscribe` export receives. Write it as
@@ -115,10 +191,20 @@ export type ZiteTriggerDeliveryRun = ZiteTriggerRun & ZiteTriggerDeliveryInfo;
115
191
  export type ZiteTriggerContext<TTrigger> = TTrigger extends {
116
192
  every: string;
117
193
  } ? ZiteTriggerRun : TTrigger extends {
194
+ table: string;
195
+ } | {
196
+ form: string;
197
+ } ? ZiteTriggerDeliveryInfo : TTrigger extends {
118
198
  events: string[];
119
199
  } ? ZiteTriggerDeliveryInfo : TTrigger extends {
120
200
  integration: string;
121
201
  } ? ZiteTriggerDeliveryRun : never;
202
+ /** A database or form trigger's runs get the change or submission as `input`. */
203
+ export type ZiteEndpointInput<TInput, TTrigger> = TTrigger extends {
204
+ table: string;
205
+ } ? ZiteDatabaseTriggerInput : TTrigger extends {
206
+ form: string;
207
+ } ? ZiteFormTriggerInput : TInput;
122
208
  export interface ZiteRequestContext {
123
209
  user: {
124
210
  id: string;
@@ -215,13 +301,15 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
215
301
  */
216
302
  validatesInput?: boolean;
217
303
  execute: (params: {
218
- input: TInput;
304
+ input: ZiteEndpointInput<TInput, TTrigger>;
219
305
  /**
220
306
  * Durable steps. `step.run(name, fn)` runs `fn` once per logical run:
221
307
  * its result is saved before anything after it executes, and when a
222
308
  * failed run is resumed, completed steps return their saved results
223
309
  * instead of re-running their side effects. Names must be unique within
224
- * a run, and results must be plain JSON data under 256KB.
310
+ * a run, and results must be plain JSON data under 256KB. In a run
311
+ * fired by a schedule, webhook or trigger, `step.sleep` pauses the run
312
+ * and a failed step is tried again.
225
313
  */
226
314
  step: ZiteStep;
227
315
  context: TSchedule extends ZiteSchedule ? ZiteRequestContext | ZiteScheduledContext : TWebhook extends ZiteWebhook ? ZiteRequestContext | ZiteScheduledContext : TTrigger extends ZiteTrigger ? ZiteRequestContext | ZiteScheduledContext : ZiteRequestContext;
@@ -240,8 +328,32 @@ export interface EndpointConfig<TInput = unknown, TOutput = unknown, TStream ext
240
328
  * it would matter, and give a step inside a loop the item's id in its name.
241
329
  */
242
330
  export interface ZiteStep {
243
- run<T>(name: string, fn: () => Promise<T> | T): Promise<T>;
331
+ /**
332
+ * In a run fired by a schedule, webhook or trigger, a step that throws is
333
+ * tried again `retries` more times (default 3, at most 10), 30 seconds,
334
+ * 2 minutes, then 10 minutes apart. Elsewhere its error fails the request.
335
+ */
336
+ run<T>(name: string, fn: () => Promise<T> | T, options?: ZiteStepOptions): Promise<T>;
337
+ /**
338
+ * Pause the run for `duration`: `"30s"`, `"15m"`, `"2h"`, `"3d"`, `"1w"`,
339
+ * or milliseconds, at most 30 days. Only a run fired by a schedule, webhook
340
+ * or trigger can wait; an editor test run goes straight past, and a request
341
+ * from the app throws.
342
+ *
343
+ * The run continues in a later call, which runs `execute` again from the
344
+ * top: finished steps return their saved results, and code outside a step
345
+ * runs again, so keep side effects inside steps.
346
+ */
347
+ sleep(name: string, duration: ZiteStepDuration): Promise<void>;
348
+ /** Pause the run until `when`, at most 30 days ahead. See {@link ZiteStep.sleep}. */
349
+ sleepUntil(name: string, when: Date | string): Promise<void>;
350
+ }
351
+ export interface ZiteStepOptions {
352
+ /** How many more times to try the step after it throws: 0 to 10, default 3. */
353
+ retries?: number;
244
354
  }
355
+ /** Milliseconds, or a number with a unit: `ms`, `s`, `m`, `h`, `d` or `w`. */
356
+ export type ZiteStepDuration = number | `${number}${"ms" | "s" | "m" | "h" | "d" | "w"}`;
245
357
  /**
246
358
  * Create an endpoint definition, parsing `input` against `inputSchema` before
247
359
  * `execute` runs. Nothing downstream parses it — the worker calls `execute` on
@@ -8,6 +8,8 @@ const requestContext = { user: { id: "u1", email: "a@b.c", roles: [] } };
8
8
  // Direct execute() calls need the step surface the runtime always provides.
9
9
  const stepStub = {
10
10
  run: async (_name, fn) => await fn(),
11
+ sleep: async () => { },
12
+ sleepUntil: async () => { },
11
13
  };
12
14
  (0, vitest_1.afterEach)(() => {
13
15
  vitest_1.vi.unstubAllGlobals();
@@ -207,6 +209,62 @@ const stepStub = {
207
209
  return params.trigger;
208
210
  },
209
211
  });
212
+ (0, index_js_1.createEndpoint)({
213
+ trigger: { table: "orders", on: ["updated"], fields: ["status"] },
214
+ execute: async ({ input, trigger }) => {
215
+ // @ts-expect-error a database change has no cursor API
216
+ trigger?.advance("x");
217
+ const status = input.record?.status;
218
+ return { status, changed: input.changedFields, at: input.event };
219
+ },
220
+ });
221
+ (0, index_js_1.createEndpoint)({
222
+ // @ts-expect-error a database trigger names its table, not a connection
223
+ trigger: { table: "orders", on: ["created"], integration: "x" },
224
+ execute: async () => 1,
225
+ });
226
+ (0, index_js_1.createEndpoint)({
227
+ trigger: { form: "Intake form", on: ["submitted"] },
228
+ execute: async ({ input, trigger }) => {
229
+ // @ts-expect-error a form submission has no cursor API
230
+ trigger?.advance("x");
231
+ return {
232
+ email: input.answers.Email,
233
+ workEmail: input.answersById["wWorkEmail"],
234
+ id: input.submissionId,
235
+ };
236
+ },
237
+ });
238
+ (0, index_js_1.createEndpoint)({
239
+ // @ts-expect-error a form trigger watches submissions only
240
+ trigger: { form: "Intake form", on: ["updated"] },
241
+ execute: async () => 1,
242
+ });
243
+ (0, index_js_1.createEndpoint)({
244
+ // @ts-expect-error only created, updated and deleted
245
+ trigger: { table: "orders", on: ["archived"] },
246
+ execute: async () => 1,
247
+ });
248
+ });
249
+ (0, vitest_1.it)("hands a database trigger its change without parsing it against the input schema", async () => {
250
+ const endpoint = (0, index_js_1.createEndpoint)({
251
+ inputSchema: zod_1.z.object({ orderId: zod_1.z.string() }),
252
+ trigger: { table: "orders", on: ["created"] },
253
+ execute: async ({ input }) => input.recordId,
254
+ });
255
+ await (0, vitest_1.expect)(endpoint.execute({
256
+ step: stepStub,
257
+ input: {
258
+ event: "created",
259
+ table: "orders",
260
+ recordId: "rec_1",
261
+ record: { id: "rec_1" },
262
+ previous: null,
263
+ changedFields: [],
264
+ },
265
+ context: { user: null },
266
+ trigger: { headers: {}, deliveryId: "db:e1:rec_1" },
267
+ })).resolves.toBe("rec_1");
210
268
  });
211
269
  (0, vitest_1.it)("types a trigger's registration exports and checks what subscribe returns", async () => {
212
270
  const subscribe = (async (input) => ({
@@ -1 +1,33 @@
1
- export declare function runCheck(): Promise<void>;
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 {};
@@ -1,10 +1,45 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Reporter = exports.withStdoutOnStderr = void 0;
3
37
  exports.runCheck = runCheck;
4
38
  const child_process_1 = require("child_process");
5
39
  const fs_1 = require("fs");
6
40
  const path_1 = require("path");
7
41
  const sourceRoots_js_1 = require("../sourceRoots.js");
42
+ const problem_js_1 = require("./start/problem.js");
8
43
  /**
9
44
  * An argument array, not a command string, so no shell is involved.
10
45
  *
@@ -58,73 +93,166 @@ function bundleFailures(output) {
58
93
  }
59
94
  return failures;
60
95
  }
61
- async function runCheck() {
62
- const appDirs = (0, sourceRoots_js_1.listSourceDirs)();
63
- if (appDirs.length === 0) {
64
- console.error('No apps or automations found under apps/ or automations/.');
65
- process.exit(1);
96
+ const indent = (output) => output.trim().split('\n').map(l => ` ${l}`).join('\n');
97
+ /**
98
+ * While `--json` checks, stdout carries only the report: app code that logs as
99
+ * it's described in-process goes to stderr instead.
100
+ */
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();
66
106
  }
67
- let allPassed = true;
68
- for (const { root, dir: app, path: appPath } of appDirs) {
69
- console.log(`\n── ${appPath} ──`);
70
- // Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
71
- // solution-style config — `"files": []` plus a reference — so
72
- // `tsc --noEmit -p tsconfig.json` type-checks zero files and exits 0. It
73
- // used to be the fallback, which meant an app missing tsconfig.app.json
74
- // reported a passing typecheck it never ran.
75
- const tsconfigAppPath = (0, path_1.join)(appPath, 'tsconfig.app.json');
76
- if (!(0, fs_1.existsSync)(tsconfigAppPath)) {
77
- console.log(` tsc --noEmit ... ✗ (no ${tsconfigAppPath})`);
78
- allPassed = false;
79
- }
80
- else {
81
- process.stdout.write(' tsc --noEmit ... ');
82
- const tsc = run('npx', ['tsc', '--noEmit', '-p', tsconfigAppPath], '.');
83
- if (tsc.ok) {
84
- console.log('✓');
85
- }
86
- else {
87
- console.log('✗');
88
- console.log(tsc.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
89
- allPassed = false;
90
- }
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));
91
135
  }
92
- // Endpoints never reach vite — they are bundled separately for the lambda,
93
- // against a different resolver. tsc and vite both pass on an endpoint whose
94
- // `zitejs/*` alias has no generated file behind it; only the bundler knows.
95
- if ((0, sourceRoots_js_1.listEndpointNames)(root, appPath).length > 0) {
96
- process.stdout.write(' bundle endpoints ... ');
97
- const bundle = run('npx', ['zitejs', 'bundle', '--app', app], '.');
98
- const failures = bundle.ok ? bundleFailures(bundle.output) : [bundle.output];
99
- if (failures.length === 0) {
100
- console.log('✓');
101
- }
102
- else {
103
- console.log('✗');
104
- console.log(failures.map(l => ` ${l}`).join('\n'));
105
- allPassed = false;
106
- }
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');
107
160
  }
108
- const viteConfig = (0, path_1.join)(appPath, 'vite.config.ts');
109
- if ((0, fs_1.existsSync)(viteConfig)) {
110
- process.stdout.write(' vite build ... ');
111
- const vite = run('npx', ['vite', 'build'], appPath);
112
- if (vite.ok) {
113
- console.log('✓');
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 {
172
+ return false;
173
+ }
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;
183
+ // --pretty for `file:line:col - error` lines, the shape the platform re-roots.
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')));
187
+ try {
188
+ const { describeBuiltApp } = await Promise.resolve().then(() => __importStar(require('../start/describeBuilt.js')));
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);
193
+ }
194
+ catch (err) {
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));
221
+ }
222
+ }
223
+ async function runCheck(args = process.argv.slice(3)) {
224
+ const json = args.includes('--json');
225
+ const reporter = new Reporter(json);
226
+ const { isStartAppDir } = await Promise.resolve().then(() => __importStar(require('../start/entries.js')));
227
+ // Run inside a Start app, its own zitejs checks just that app: the workspace
228
+ // root's is a classic release that doesn't know Start.
229
+ const inStartApp = isStartAppDir('.');
230
+ const appDirs = inStartApp ? [] : (0, sourceRoots_js_1.listSourceDirs)();
231
+ if (!inStartApp && appDirs.length === 0) {
232
+ console.error('No apps or automations found under apps/ or automations/.');
233
+ process.exit(1);
234
+ }
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);
114
241
  }
115
242
  else {
116
- console.log('✗');
117
- console.log(vite.output.trim().split('\n').map(l => ` ${l}`).join('\n'));
118
- allPassed = false;
243
+ checkClassicApp(reporter, reporter.app(appPath, 'classic', true), root, dir, appPath);
119
244
  }
120
245
  }
121
- }
122
- console.log('');
123
- if (allPassed) {
124
- 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`);
125
251
  }
126
252
  else {
127
- console.log('Some apps failed.');
128
- process.exit(1);
253
+ console.log('');
254
+ console.log(allPassed ? 'All apps pass.' : 'Some apps failed.');
129
255
  }
256
+ if (!allPassed)
257
+ process.exit(1);
130
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 {};