zitejs 0.9.122 → 0.9.123

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.
@@ -2,7 +2,7 @@
2
2
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
3
3
  *
4
4
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
5
- * Reads endpoints from src/api/*.ts, resolves SDK imports via .zite/backend.ts
5
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
6
6
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
7
7
  * code suitable for the cloudflare-lambda worker runtime.
8
8
  *
@@ -39,7 +39,7 @@ exports.runBundle = runBundle;
39
39
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
40
40
  *
41
41
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
42
- * Reads endpoints from src/api/*.ts, resolves SDK imports via .zite/backend.ts
42
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
43
43
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
44
44
  * code suitable for the cloudflare-lambda worker runtime.
45
45
  *
@@ -334,7 +334,7 @@ function createAliasPlugin(opts) {
334
334
  },
335
335
  };
336
336
  }
337
- function generateEndpointWrapper(endpointName, usedImports, sdkExportKinds, sdkSource) {
337
+ function generateEndpointWrapper(entryImport, usedImports, sdkExportKinds, sdkSource) {
338
338
  // Always import the prebundled runtime to ensure globalThis.__wrapSdkCall
339
339
  // is available for createTableClient/createSqlClient from zitejs/runtime.
340
340
  const runtimeInit = `import { __wrapSdkCall, initRuntime } from '@zite/endpoints-runtime-sdk';
@@ -345,14 +345,14 @@ initRuntime();`;
345
345
  ${runtimeInit}
346
346
  import * as sdk from '${sdkSource}';
347
347
  Object.assign(globalThis, sdk);
348
- import endpoint from './api/${endpointName}';
348
+ import endpoint from '${entryImport}';
349
349
  globalThis.__endpoint = endpoint;
350
350
  `;
351
351
  }
352
352
  if (usedImports.length === 0) {
353
353
  return `
354
354
  ${runtimeInit}
355
- import endpoint from './api/${endpointName}';
355
+ import endpoint from '${entryImport}';
356
356
  globalThis.__endpoint = endpoint;
357
357
  `;
358
358
  }
@@ -380,11 +380,11 @@ globalThis.__endpoint = endpoint;
380
380
  ${runtimeInit}
381
381
  ${importStatements.join('\n')}
382
382
  ${globalAssign}
383
- import endpoint from './api/${endpointName}';
383
+ import endpoint from '${entryImport}';
384
384
  globalThis.__endpoint = endpoint;
385
385
  `;
386
386
  }
387
- async function bundleEndpointsImpl(baseDir, endpointNames) {
387
+ async function bundleEndpointsImpl(baseDir, root, endpointNames) {
388
388
  const bundledEndpoints = {};
389
389
  const endpointErrors = {};
390
390
  const aliasPlugin = createAliasPlugin({ baseDir });
@@ -399,14 +399,20 @@ async function bundleEndpointsImpl(baseDir, endpointNames) {
399
399
  }
400
400
  catch { }
401
401
  for (const name of endpointNames) {
402
+ if (root === 'automations' && name !== sourceRoots_js_1.AUTOMATION_ENDPOINT_NAME) {
403
+ endpointErrors[name] =
404
+ `An automation is one workflow, src/workflow.ts, bundled as "${sourceRoots_js_1.AUTOMATION_ENDPOINT_NAME}"; there is no endpoint "${name}"`;
405
+ continue;
406
+ }
407
+ const entryFile = (0, sourceRoots_js_1.endpointEntryFile)(root, name);
402
408
  let usedImports = null;
403
409
  try {
404
- const endpointPath = path.join(baseDir, 'src', 'api', `${name}.ts`);
405
- const endpointCode = fs.readFileSync(endpointPath, 'utf-8');
410
+ const endpointCode = fs.readFileSync(path.join(baseDir, entryFile), 'utf-8');
406
411
  usedImports = getUsedSdkImports(endpointCode);
407
412
  }
408
413
  catch { }
409
- const wrapperCode = generateEndpointWrapper(name, usedImports, sdkExportKinds, sdkSource);
414
+ // The wrapper resolves from `src/`, so the entry is imported relative to it.
415
+ const wrapperCode = generateEndpointWrapper('./' + path.relative('src', entryFile).replace(/\.ts$/, ''), usedImports, sdkExportKinds, sdkSource);
410
416
  try {
411
417
  const result = await esbuild.build({
412
418
  ...exports.BASE_BUILD_OPTIONS,
@@ -629,18 +635,13 @@ async function runBundle() {
629
635
  // as missing.
630
636
  const explicitNames = args.filter((a, i) => !a.startsWith('--') && !(appFlag !== -1 && i === appFlag + 1));
631
637
  let endpointNames;
638
+ const root = (0, sourceRoots_js_1.sourceRootOf)(baseDir) ?? 'apps';
632
639
  if (explicitNames.length > 0) {
633
640
  endpointNames = explicitNames;
634
641
  }
635
642
  else {
636
- const apiDir = path.join(baseDir, 'src', 'api');
637
- endpointNames = fs.existsSync(apiDir)
638
- ? fs
639
- .readdirSync(apiDir)
640
- .filter(f => f.endsWith('.ts'))
641
- .map(f => f.replace('.ts', ''))
642
- : [];
643
+ endpointNames = (0, sourceRoots_js_1.listEndpointNames)(root, baseDir);
643
644
  }
644
645
  // Even with no endpoints, zite.auth.ts may still need bundling.
645
- return bundleEndpointsImpl(baseDir, endpointNames);
646
+ return bundleEndpointsImpl(baseDir, root, endpointNames);
646
647
  }
@@ -65,7 +65,7 @@ async function runCheck() {
65
65
  process.exit(1);
66
66
  }
67
67
  let allPassed = true;
68
- for (const { dir: app, path: appPath } of appDirs) {
68
+ for (const { root, dir: app, path: appPath } of appDirs) {
69
69
  console.log(`\n── ${appPath} ──`);
70
70
  // Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
71
71
  // solution-style config — `"files": []` plus a reference — so
@@ -92,7 +92,7 @@ async function runCheck() {
92
92
  // Endpoints never reach vite — they are bundled separately for the lambda,
93
93
  // against a different resolver. tsc and vite both pass on an endpoint whose
94
94
  // `zitejs/*` alias has no generated file behind it; only the bundler knows.
95
- if ((0, fs_1.existsSync)((0, path_1.join)(appPath, 'src', 'api'))) {
95
+ if ((0, sourceRoots_js_1.listEndpointNames)(root, appPath).length > 0) {
96
96
  process.stdout.write(' bundle endpoints ... ');
97
97
  const bundle = run('npx', ['zitejs', 'bundle', '--app', app], '.');
98
98
  const failures = bundle.ok ? bundleFailures(bundle.output) : [bundle.output];
package/dist/cjs/cli.js CHANGED
@@ -75,7 +75,7 @@ async function main() {
75
75
  console.error(' dev Run sync then watch for changes (like npx convex dev)');
76
76
  console.error(' generate Regenerate every app and automation .zite/ (monorepo)');
77
77
  console.error(' check Run tsc --noEmit (and vite build where there is one) for every app and automation');
78
- console.error(' bundle Bundle src/api/*.ts endpoints for cloudflare-lambda');
78
+ console.error(' bundle Bundle endpoints (src/api/*.ts, or an automation\'s src/workflow.ts) for cloudflare-lambda');
79
79
  process.exit(1);
80
80
  }
81
81
  }
@@ -194,7 +194,7 @@ async function runDev() {
194
194
  });
195
195
  }
196
196
  if (!watchingAny) {
197
- console.log("No apps or automations with src/api/ found.");
197
+ console.log("No apps with src/api/ found.");
198
198
  }
199
199
  // 4. Watch zite.schema.json for schema drift
200
200
  if ((0, fs_2.existsSync)("zite.schema.json")) {
@@ -17,3 +17,16 @@ export interface SourceDir {
17
17
  export declare function listSourceDirs(cwd?: string): SourceDir[];
18
18
  /** The source dir with this bare name, under whichever root holds it. */
19
19
  export declare function findSourceDir(name: string, cwd?: string): SourceDir | undefined;
20
+ /**
21
+ * An automation is one workflow: its single endpoint is `src/workflow.ts` and
22
+ * its name is fixed, so nothing about an automation is keyed on a file name
23
+ * the way an app's `src/api/<name>.ts` endpoints are.
24
+ */
25
+ export declare const AUTOMATION_ENDPOINT_NAME = "workflow";
26
+ export declare const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
27
+ /** The root a source dir sits under, read from its parent directory. */
28
+ export declare function sourceRootOf(sourceDirPath: string): SourceRoot | undefined;
29
+ /** An endpoint's entry file, relative to the source dir. */
30
+ export declare function endpointEntryFile(root: SourceRoot, name: string): string;
31
+ /** Every endpoint name a source dir has on disk. */
32
+ export declare function listEndpointNames(root: SourceRoot, sourceDirPath: string): string[];
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SOURCE_ROOTS = void 0;
3
+ exports.AUTOMATION_WORKFLOW_FILE = exports.AUTOMATION_ENDPOINT_NAME = exports.SOURCE_ROOTS = void 0;
4
4
  exports.listSourceDirs = listSourceDirs;
5
5
  exports.findSourceDir = findSourceDir;
6
+ exports.sourceRootOf = sourceRootOf;
7
+ exports.endpointEntryFile = endpointEntryFile;
8
+ exports.listEndpointNames = listEndpointNames;
6
9
  const fs_1 = require("fs");
7
10
  const path_1 = require("path");
8
11
  /**
@@ -31,3 +34,37 @@ function listSourceDirs(cwd = ".") {
31
34
  function findSourceDir(name, cwd = ".") {
32
35
  return listSourceDirs(cwd).find((d) => d.dir === name);
33
36
  }
37
+ /**
38
+ * An automation is one workflow: its single endpoint is `src/workflow.ts` and
39
+ * its name is fixed, so nothing about an automation is keyed on a file name
40
+ * the way an app's `src/api/<name>.ts` endpoints are.
41
+ */
42
+ exports.AUTOMATION_ENDPOINT_NAME = "workflow";
43
+ exports.AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
44
+ /** The root a source dir sits under, read from its parent directory. */
45
+ function sourceRootOf(sourceDirPath) {
46
+ const parent = (0, path_1.basename)((0, path_1.dirname)((0, path_1.resolve)(sourceDirPath)));
47
+ return exports.SOURCE_ROOTS.includes(parent)
48
+ ? parent
49
+ : undefined;
50
+ }
51
+ /** An endpoint's entry file, relative to the source dir. */
52
+ function endpointEntryFile(root, name) {
53
+ return root === "automations"
54
+ ? exports.AUTOMATION_WORKFLOW_FILE
55
+ : (0, path_1.join)("src", "api", `${name}.ts`);
56
+ }
57
+ /** Every endpoint name a source dir has on disk. */
58
+ function listEndpointNames(root, sourceDirPath) {
59
+ if (root === "automations") {
60
+ return (0, fs_1.existsSync)((0, path_1.join)(sourceDirPath, exports.AUTOMATION_WORKFLOW_FILE))
61
+ ? [exports.AUTOMATION_ENDPOINT_NAME]
62
+ : [];
63
+ }
64
+ const apiDir = (0, path_1.join)(sourceDirPath, "src", "api");
65
+ return (0, fs_1.existsSync)(apiDir)
66
+ ? (0, fs_1.readdirSync)(apiDir)
67
+ .filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts"))
68
+ .map((f) => f.replace(/\.ts$/, ""))
69
+ : [];
70
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const fs_1 = require("fs");
4
+ const os_1 = require("os");
5
+ const path_1 = require("path");
6
+ const vitest_1 = require("vitest");
7
+ const sourceRoots_js_1 = require("./sourceRoots.js");
8
+ (0, vitest_1.describe)("source roots", () => {
9
+ (0, vitest_1.it)("reads the root from the source dir's parent", () => {
10
+ (0, vitest_1.expect)((0, sourceRoots_js_1.sourceRootOf)("/repo/apps/crm")).toBe("apps");
11
+ (0, vitest_1.expect)((0, sourceRoots_js_1.sourceRootOf)("/repo/automations/digest")).toBe("automations");
12
+ (0, vitest_1.expect)((0, sourceRoots_js_1.sourceRootOf)("/repo/packages/ui")).toBeUndefined();
13
+ });
14
+ (0, vitest_1.it)("an app's endpoints are src/api/<name>.ts; an automation's one endpoint is src/workflow.ts", () => {
15
+ (0, vitest_1.expect)((0, sourceRoots_js_1.endpointEntryFile)("apps", "listItems")).toBe("src/api/listItems.ts");
16
+ (0, vitest_1.expect)((0, sourceRoots_js_1.endpointEntryFile)("automations", "workflow")).toBe("src/workflow.ts");
17
+ });
18
+ (0, vitest_1.it)("lists what is on disk under each layout", () => {
19
+ const repo = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), "zitejs-roots-"));
20
+ (0, fs_1.mkdirSync)((0, path_1.join)(repo, "apps/crm/src/api"), { recursive: true });
21
+ (0, fs_1.writeFileSync)((0, path_1.join)(repo, "apps/crm/src/api/listItems.ts"), "");
22
+ (0, fs_1.writeFileSync)((0, path_1.join)(repo, "apps/crm/src/api/apiTypes.d.ts"), "");
23
+ (0, fs_1.mkdirSync)((0, path_1.join)(repo, "automations/digest/src"), { recursive: true });
24
+ (0, fs_1.mkdirSync)((0, path_1.join)(repo, "automations/empty/src"), { recursive: true });
25
+ (0, fs_1.writeFileSync)((0, path_1.join)(repo, "automations/digest/src/workflow.ts"), "");
26
+ (0, vitest_1.expect)((0, sourceRoots_js_1.listEndpointNames)("apps", (0, path_1.join)(repo, "apps/crm"))).toEqual(["listItems"]);
27
+ (0, vitest_1.expect)((0, sourceRoots_js_1.listEndpointNames)("automations", (0, path_1.join)(repo, "automations/digest"))).toEqual(["workflow"]);
28
+ (0, vitest_1.expect)((0, sourceRoots_js_1.listEndpointNames)("automations", (0, path_1.join)(repo, "automations/empty"))).toEqual([]);
29
+ });
30
+ });
@@ -2,7 +2,7 @@
2
2
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
3
3
  *
4
4
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
5
- * Reads endpoints from src/api/*.ts, resolves SDK imports via .zite/backend.ts
5
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
6
6
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
7
7
  * code suitable for the cloudflare-lambda worker runtime.
8
8
  *
@@ -2,7 +2,7 @@
2
2
  * `npx zitejs bundle` — Bundle endpoint files for cloudflare-lambda deployment.
3
3
  *
4
4
  * Replaces the legacy `scripts/bundle-endpoints.js` from zitejs-starter.
5
- * Reads endpoints from src/api/*.ts, resolves SDK imports via .zite/backend.ts
5
+ * Reads an app's src/api/*.ts endpoints (an automation's single src/workflow.ts), resolves SDK imports via .zite/backend.ts
6
6
  * (monorepo) or src/__zite__/integrations.ts (legacy), and outputs bundled ESM
7
7
  * code suitable for the cloudflare-lambda worker runtime.
8
8
  *
@@ -24,7 +24,7 @@ import * as path from 'path';
24
24
  import * as fs from 'fs';
25
25
  import { parse } from '@babel/parser';
26
26
  import { builtinModules } from 'node:module';
27
- import { findSourceDir } from '../sourceRoots.js';
27
+ import { AUTOMATION_ENDPOINT_NAME, endpointEntryFile, findSourceDir, listEndpointNames, sourceRootOf, } from '../sourceRoots.js';
28
28
  // workerd's `nodejs_compat` does not provide these. Left external they bundle
29
29
  // fine and then kill the worker at load time with `No such module`; excluded,
30
30
  // esbuild fails the one endpoint with a resolvable error instead. Pinned by
@@ -297,7 +297,7 @@ function createAliasPlugin(opts) {
297
297
  },
298
298
  };
299
299
  }
300
- function generateEndpointWrapper(endpointName, usedImports, sdkExportKinds, sdkSource) {
300
+ function generateEndpointWrapper(entryImport, usedImports, sdkExportKinds, sdkSource) {
301
301
  // Always import the prebundled runtime to ensure globalThis.__wrapSdkCall
302
302
  // is available for createTableClient/createSqlClient from zitejs/runtime.
303
303
  const runtimeInit = `import { __wrapSdkCall, initRuntime } from '@zite/endpoints-runtime-sdk';
@@ -308,14 +308,14 @@ initRuntime();`;
308
308
  ${runtimeInit}
309
309
  import * as sdk from '${sdkSource}';
310
310
  Object.assign(globalThis, sdk);
311
- import endpoint from './api/${endpointName}';
311
+ import endpoint from '${entryImport}';
312
312
  globalThis.__endpoint = endpoint;
313
313
  `;
314
314
  }
315
315
  if (usedImports.length === 0) {
316
316
  return `
317
317
  ${runtimeInit}
318
- import endpoint from './api/${endpointName}';
318
+ import endpoint from '${entryImport}';
319
319
  globalThis.__endpoint = endpoint;
320
320
  `;
321
321
  }
@@ -343,11 +343,11 @@ globalThis.__endpoint = endpoint;
343
343
  ${runtimeInit}
344
344
  ${importStatements.join('\n')}
345
345
  ${globalAssign}
346
- import endpoint from './api/${endpointName}';
346
+ import endpoint from '${entryImport}';
347
347
  globalThis.__endpoint = endpoint;
348
348
  `;
349
349
  }
350
- async function bundleEndpointsImpl(baseDir, endpointNames) {
350
+ async function bundleEndpointsImpl(baseDir, root, endpointNames) {
351
351
  const bundledEndpoints = {};
352
352
  const endpointErrors = {};
353
353
  const aliasPlugin = createAliasPlugin({ baseDir });
@@ -362,14 +362,20 @@ async function bundleEndpointsImpl(baseDir, endpointNames) {
362
362
  }
363
363
  catch { }
364
364
  for (const name of endpointNames) {
365
+ if (root === 'automations' && name !== AUTOMATION_ENDPOINT_NAME) {
366
+ endpointErrors[name] =
367
+ `An automation is one workflow, src/workflow.ts, bundled as "${AUTOMATION_ENDPOINT_NAME}"; there is no endpoint "${name}"`;
368
+ continue;
369
+ }
370
+ const entryFile = endpointEntryFile(root, name);
365
371
  let usedImports = null;
366
372
  try {
367
- const endpointPath = path.join(baseDir, 'src', 'api', `${name}.ts`);
368
- const endpointCode = fs.readFileSync(endpointPath, 'utf-8');
373
+ const endpointCode = fs.readFileSync(path.join(baseDir, entryFile), 'utf-8');
369
374
  usedImports = getUsedSdkImports(endpointCode);
370
375
  }
371
376
  catch { }
372
- const wrapperCode = generateEndpointWrapper(name, usedImports, sdkExportKinds, sdkSource);
377
+ // The wrapper resolves from `src/`, so the entry is imported relative to it.
378
+ const wrapperCode = generateEndpointWrapper('./' + path.relative('src', entryFile).replace(/\.ts$/, ''), usedImports, sdkExportKinds, sdkSource);
373
379
  try {
374
380
  const result = await esbuild.build({
375
381
  ...BASE_BUILD_OPTIONS,
@@ -592,18 +598,13 @@ export async function runBundle() {
592
598
  // as missing.
593
599
  const explicitNames = args.filter((a, i) => !a.startsWith('--') && !(appFlag !== -1 && i === appFlag + 1));
594
600
  let endpointNames;
601
+ const root = sourceRootOf(baseDir) ?? 'apps';
595
602
  if (explicitNames.length > 0) {
596
603
  endpointNames = explicitNames;
597
604
  }
598
605
  else {
599
- const apiDir = path.join(baseDir, 'src', 'api');
600
- endpointNames = fs.existsSync(apiDir)
601
- ? fs
602
- .readdirSync(apiDir)
603
- .filter(f => f.endsWith('.ts'))
604
- .map(f => f.replace('.ts', ''))
605
- : [];
606
+ endpointNames = listEndpointNames(root, baseDir);
606
607
  }
607
608
  // Even with no endpoints, zite.auth.ts may still need bundling.
608
- return bundleEndpointsImpl(baseDir, endpointNames);
609
+ return bundleEndpointsImpl(baseDir, root, endpointNames);
609
610
  }
@@ -1,7 +1,7 @@
1
1
  import { execFileSync } from 'child_process';
2
2
  import { existsSync } from 'fs';
3
3
  import { join } from 'path';
4
- import { listSourceDirs } from '../sourceRoots.js';
4
+ import { listEndpointNames, listSourceDirs } from '../sourceRoots.js';
5
5
  /**
6
6
  * An argument array, not a command string, so no shell is involved.
7
7
  *
@@ -62,7 +62,7 @@ export async function runCheck() {
62
62
  process.exit(1);
63
63
  }
64
64
  let allPassed = true;
65
- for (const { dir: app, path: appPath } of appDirs) {
65
+ for (const { root, dir: app, path: appPath } of appDirs) {
66
66
  console.log(`\n── ${appPath} ──`);
67
67
  // Only tsconfig.app.json compiles anything. The app's tsconfig.json is a
68
68
  // solution-style config — `"files": []` plus a reference — so
@@ -89,7 +89,7 @@ export async function runCheck() {
89
89
  // Endpoints never reach vite — they are bundled separately for the lambda,
90
90
  // against a different resolver. tsc and vite both pass on an endpoint whose
91
91
  // `zitejs/*` alias has no generated file behind it; only the bundler knows.
92
- if (existsSync(join(appPath, 'src', 'api'))) {
92
+ if (listEndpointNames(root, appPath).length > 0) {
93
93
  process.stdout.write(' bundle endpoints ... ');
94
94
  const bundle = run('npx', ['zitejs', 'bundle', '--app', app], '.');
95
95
  const failures = bundle.ok ? bundleFailures(bundle.output) : [bundle.output];
package/dist/esm/cli.js CHANGED
@@ -40,7 +40,7 @@ async function main() {
40
40
  console.error(' dev Run sync then watch for changes (like npx convex dev)');
41
41
  console.error(' generate Regenerate every app and automation .zite/ (monorepo)');
42
42
  console.error(' check Run tsc --noEmit (and vite build where there is one) for every app and automation');
43
- console.error(' bundle Bundle src/api/*.ts endpoints for cloudflare-lambda');
43
+ console.error(' bundle Bundle endpoints (src/api/*.ts, or an automation\'s src/workflow.ts) for cloudflare-lambda');
44
44
  process.exit(1);
45
45
  }
46
46
  }
@@ -190,7 +190,7 @@ export async function runDev() {
190
190
  });
191
191
  }
192
192
  if (!watchingAny) {
193
- console.log("No apps or automations with src/api/ found.");
193
+ console.log("No apps with src/api/ found.");
194
194
  }
195
195
  // 4. Watch zite.schema.json for schema drift
196
196
  if (existsSync("zite.schema.json")) {
@@ -17,3 +17,16 @@ export interface SourceDir {
17
17
  export declare function listSourceDirs(cwd?: string): SourceDir[];
18
18
  /** The source dir with this bare name, under whichever root holds it. */
19
19
  export declare function findSourceDir(name: string, cwd?: string): SourceDir | undefined;
20
+ /**
21
+ * An automation is one workflow: its single endpoint is `src/workflow.ts` and
22
+ * its name is fixed, so nothing about an automation is keyed on a file name
23
+ * the way an app's `src/api/<name>.ts` endpoints are.
24
+ */
25
+ export declare const AUTOMATION_ENDPOINT_NAME = "workflow";
26
+ export declare const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
27
+ /** The root a source dir sits under, read from its parent directory. */
28
+ export declare function sourceRootOf(sourceDirPath: string): SourceRoot | undefined;
29
+ /** An endpoint's entry file, relative to the source dir. */
30
+ export declare function endpointEntryFile(root: SourceRoot, name: string): string;
31
+ /** Every endpoint name a source dir has on disk. */
32
+ export declare function listEndpointNames(root: SourceRoot, sourceDirPath: string): string[];
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readdirSync } from "fs";
2
- import { join } from "path";
2
+ import { basename, dirname, join, resolve } from "path";
3
3
  /**
4
4
  * The repo-root directories a project's own source lives under: `apps/` for
5
5
  * apps and `automations/` for automations (endpoints with no frontend). The
@@ -26,3 +26,37 @@ export function listSourceDirs(cwd = ".") {
26
26
  export function findSourceDir(name, cwd = ".") {
27
27
  return listSourceDirs(cwd).find((d) => d.dir === name);
28
28
  }
29
+ /**
30
+ * An automation is one workflow: its single endpoint is `src/workflow.ts` and
31
+ * its name is fixed, so nothing about an automation is keyed on a file name
32
+ * the way an app's `src/api/<name>.ts` endpoints are.
33
+ */
34
+ export const AUTOMATION_ENDPOINT_NAME = "workflow";
35
+ export const AUTOMATION_WORKFLOW_FILE = "src/workflow.ts";
36
+ /** The root a source dir sits under, read from its parent directory. */
37
+ export function sourceRootOf(sourceDirPath) {
38
+ const parent = basename(dirname(resolve(sourceDirPath)));
39
+ return SOURCE_ROOTS.includes(parent)
40
+ ? parent
41
+ : undefined;
42
+ }
43
+ /** An endpoint's entry file, relative to the source dir. */
44
+ export function endpointEntryFile(root, name) {
45
+ return root === "automations"
46
+ ? AUTOMATION_WORKFLOW_FILE
47
+ : join("src", "api", `${name}.ts`);
48
+ }
49
+ /** Every endpoint name a source dir has on disk. */
50
+ export function listEndpointNames(root, sourceDirPath) {
51
+ if (root === "automations") {
52
+ return existsSync(join(sourceDirPath, AUTOMATION_WORKFLOW_FILE))
53
+ ? [AUTOMATION_ENDPOINT_NAME]
54
+ : [];
55
+ }
56
+ const apiDir = join(sourceDirPath, "src", "api");
57
+ return existsSync(apiDir)
58
+ ? readdirSync(apiDir)
59
+ .filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts"))
60
+ .map((f) => f.replace(/\.ts$/, ""))
61
+ : [];
62
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import { mkdirSync, mkdtempSync, writeFileSync } from "fs";
2
+ import { tmpdir } from "os";
3
+ import { join } from "path";
4
+ import { describe, expect, it } from "vitest";
5
+ import { endpointEntryFile, listEndpointNames, sourceRootOf, } from "./sourceRoots.js";
6
+ describe("source roots", () => {
7
+ it("reads the root from the source dir's parent", () => {
8
+ expect(sourceRootOf("/repo/apps/crm")).toBe("apps");
9
+ expect(sourceRootOf("/repo/automations/digest")).toBe("automations");
10
+ expect(sourceRootOf("/repo/packages/ui")).toBeUndefined();
11
+ });
12
+ it("an app's endpoints are src/api/<name>.ts; an automation's one endpoint is src/workflow.ts", () => {
13
+ expect(endpointEntryFile("apps", "listItems")).toBe("src/api/listItems.ts");
14
+ expect(endpointEntryFile("automations", "workflow")).toBe("src/workflow.ts");
15
+ });
16
+ it("lists what is on disk under each layout", () => {
17
+ const repo = mkdtempSync(join(tmpdir(), "zitejs-roots-"));
18
+ mkdirSync(join(repo, "apps/crm/src/api"), { recursive: true });
19
+ writeFileSync(join(repo, "apps/crm/src/api/listItems.ts"), "");
20
+ writeFileSync(join(repo, "apps/crm/src/api/apiTypes.d.ts"), "");
21
+ mkdirSync(join(repo, "automations/digest/src"), { recursive: true });
22
+ mkdirSync(join(repo, "automations/empty/src"), { recursive: true });
23
+ writeFileSync(join(repo, "automations/digest/src/workflow.ts"), "");
24
+ expect(listEndpointNames("apps", join(repo, "apps/crm"))).toEqual(["listItems"]);
25
+ expect(listEndpointNames("automations", join(repo, "automations/digest"))).toEqual(["workflow"]);
26
+ expect(listEndpointNames("automations", join(repo, "automations/empty"))).toEqual([]);
27
+ });
28
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zitejs",
3
- "version": "0.9.122",
3
+ "version": "0.9.123",
4
4
  "description": "The Zite framework — build apps on Zite Database",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",