@cowliss/cli 0.12.0 → 0.13.0

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.
@@ -1,4 +1,4 @@
1
- import { n as journeyStepOutputSchema, r as manifestOutputSchema, t as guestInputSchema } from "./journeys-v2-Djgs8U91.js";
1
+ import { n as journeyStepOutputSchema, r as manifestOutputSchema, t as guestInputSchema } from "./journeys-v2-Cndh4CAr.js";
2
2
  import { CapabilityError } from "./journeys.js";
3
3
  import { z } from "zod";
4
4
 
@@ -1,6 +1,6 @@
1
- import { i as templateRenderOutputSchema, t as guestInputSchema } from "./journeys-v2-Djgs8U91.js";
1
+ import { i as templateRenderOutputSchema, t as guestInputSchema } from "./journeys-v2-Cndh4CAr.js";
2
2
  import { CapabilityError } from "./journeys.js";
3
- import { n as readManifest, t as asTemplate } from "./driver-B_pjmy5g.js";
3
+ import { n as readManifest, t as asTemplate } from "./driver-BtQYaFXP.js";
4
4
  import { createElement } from "react";
5
5
  import { renderToStaticMarkup } from "react-dom/server.browser";
6
6
 
@@ -1,5 +1,5 @@
1
1
  import { c as TemplateRenderOutput, o as JourneyStepOutput, s as ManifestOutput } from "./index-DADBwPaG.js";
2
- import { n as Template } from "./emails-5GindgMQ.js";
2
+ import { n as Template } from "./emails-DUwxKLGO.js";
3
3
  //#region src/guest/driver.d.ts
4
4
  /**
5
5
  * The in-process driver: JSON in, JSON out (spec: Guest protocol). One
@@ -1,3 +1,3 @@
1
- import { n as readManifest, r as runGuest, t as asTemplate } from "./driver-B_pjmy5g.js";
1
+ import { n as readManifest, r as runGuest, t as asTemplate } from "./driver-BtQYaFXP.js";
2
2
 
3
3
  export { asTemplate, readManifest, runGuest };
@@ -23,7 +23,7 @@ type SendClass = (typeof SEND_CLASSES)[number];
23
23
  type Subject<Schema extends z.ZodType> = (props: z.infer<Schema>) => string;
24
24
  /**
25
25
  * The shape `emails/<key>.tsx` exports. `cow build` reads `props` (turned
26
- * into JSON Schema in the manifest and into `.cow/types.d.ts`), `subject`,
26
+ * into JSON Schema in the manifest and into `types.d.ts`), `subject`,
27
27
  * and the optional metadata below it; the sandbox renders `default`.
28
28
  */
29
29
  type Template<Schema extends z.ZodType = z.ZodType> = {
@@ -1,2 +1,2 @@
1
- import { n as Template, r as SendClass, t as Subject } from "./emails-5GindgMQ.js";
1
+ import { n as Template, r as SendClass, t as Subject } from "./emails-DUwxKLGO.js";
2
2
  export { type SendClass, Subject, Template };
@@ -513,7 +513,7 @@ function uniqueKeys(items, ctx, path) {
513
513
  }
514
514
  }
515
515
  /**
516
- * What `cow build` writes to `.cow/build/manifest.json`, and the shape a
516
+ * What `cow build` writes to `.cowliss/build/manifest.json`, and the shape a
517
517
  * push row stores for good, entry by entry, on the versions it creates.
518
518
  *
519
519
  * `protocol` is any positive integer rather than the current constant on
@@ -33,7 +33,7 @@ declare class CapabilityError extends Error {
33
33
  }
34
34
  /**
35
35
  * The repository's templates, keyed by template key. `cow build` writes the
36
- * real interface into `.cow/types.d.ts` and TypeScript merges it into this
36
+ * real interface into `types.d.ts` and TypeScript merges it into this
37
37
  * one, which is what types `api.send.email`. Empty here on purpose: a
38
38
  * repository that has not built yet still compiles, with string keys.
39
39
  */
@@ -1,4 +1,4 @@
1
- import { a as manifestJourneySchema } from "./journeys-v2-Djgs8U91.js";
1
+ import { a as manifestJourneySchema } from "./journeys-v2-Cndh4CAr.js";
2
2
 
3
3
  //#region src/guest/journeys.ts
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { o as manifestSegmentSchema } from "./journeys-v2-Djgs8U91.js";
1
+ import { o as manifestSegmentSchema } from "./journeys-v2-Cndh4CAr.js";
2
2
 
3
3
  //#region src/guest/segments.ts
4
4
  const segmentConfigSchema = manifestSegmentSchema.pick({
package/dist/index.js CHANGED
@@ -8,13 +8,13 @@ import { homedir } from "node:os";
8
8
  import { basename, dirname, join, relative, resolve, sep } from "node:path";
9
9
  import { Command, InvalidArgumentError } from "commander";
10
10
  import { execFile, spawn } from "node:child_process";
11
+ import { existsSync, readFileSync } from "node:fs";
11
12
  import { fileURLToPath, pathToFileURL } from "node:url";
12
13
  import { promisify } from "node:util";
13
14
  import { MessagePort } from "node:worker_threads";
14
15
  import { build, formatMessagesSync } from "esbuild";
15
16
  import { create, extract } from "tar";
16
17
  import { parse } from "@babel/parser";
17
- import { existsSync, readFileSync } from "node:fs";
18
18
  import { createServer } from "node:http";
19
19
  import { createInterface } from "node:readline/promises";
20
20
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -5173,7 +5173,7 @@ function uniqueKeys(items, ctx, path) {
5173
5173
  }
5174
5174
  }
5175
5175
  /**
5176
- * What `cow build` writes to `.cow/build/manifest.json`, and the shape a
5176
+ * What `cow build` writes to `.cowliss/build/manifest.json`, and the shape a
5177
5177
  * push row stores for good, entry by entry, on the versions it creates.
5178
5178
  *
5179
5179
  * `protocol` is any positive integer rather than the current constant on
@@ -8518,8 +8518,8 @@ function isNode(value) {
8518
8518
  */
8519
8519
  const execFileAsync$1 = promisify(execFile);
8520
8520
  /** Where build output lives, relative to the project root. */
8521
- const BUILD_DIR = join(".cow", "build");
8522
- const TYPES_FILE = join(".cow", "types.d.ts");
8521
+ const BUILD_DIR = "build";
8522
+ const TYPES_FILE = "types.d.ts";
8523
8523
  /**
8524
8524
  * The config files the source tarball carries besides `journeys/` and
8525
8525
  * `emails/`. Always the committed `cow.json`, never a `--config` override: a
@@ -8640,7 +8640,7 @@ function assertSegmentResolves(relPath, trigger, segmentKeys) {
8640
8640
  throw new Error(`${relPath}: this journey enrolls from the segment "${trigger.segment}", and there is no segments/${trigger.segment}.ts. ${available} Add that file, or write the predicate list in the trigger.`);
8641
8641
  }
8642
8642
  /**
8643
- * `.cow/types.d.ts`: one `typeof import(...)` per template, merged into the
8643
+ * `types.d.ts`: one `typeof import(...)` per template, merged into the
8644
8644
  * SDK's `CowTemplates`, which is what types `api.send.email`. Written before
8645
8645
  * the typecheck, because the typecheck is what it exists for.
8646
8646
  */
@@ -8650,7 +8650,7 @@ import type { z } from "zod";
8650
8650
 
8651
8651
  declare module "@cowliss/cli/journeys" {
8652
8652
  interface CowTemplates {
8653
- ${templates.map((template) => ` "${template.key}": z.infer<typeof import("../${template.relPath.replace(/\.tsx$/, "")}").props>;`).join("\n")}
8653
+ ${templates.map((template) => ` "${template.key}": z.infer<typeof import("./${template.relPath.replace(/\.tsx$/, "")}").props>;`).join("\n")}
8654
8654
  }
8655
8655
  }
8656
8656
 
@@ -8894,6 +8894,21 @@ function setConfigFile(name) {
8894
8894
  function projectConfigFile() {
8895
8895
  return configFile;
8896
8896
  }
8897
+ /**
8898
+ * The directory a cow project lives in: `.cowliss/` under the repo root, so
8899
+ * a host repository gets one directory instead of four plus a config file.
8900
+ * Standing inside the project directory itself (a `cow.json` right here) is
8901
+ * the other answer, which is how the project directory is also a directory
8902
+ * you can cd into and run `cow` from.
8903
+ *
8904
+ * A missing `.cowliss/` still resolves to it rather than to the cwd: the
8905
+ * error then names the path a project would be at, and `cow init` and
8906
+ * `cow pull` create it.
8907
+ */
8908
+ const PROJECT_DIR = ".cowliss";
8909
+ function resolveProjectDir(cwd = process.cwd()) {
8910
+ return existsSync(join(cwd, projectConfigFile())) ? cwd : join(cwd, PROJECT_DIR);
8911
+ }
8897
8912
  /** The project's config; throws unless `projectDir` holds a valid one. */
8898
8913
  async function assertCowConfig(projectDir) {
8899
8914
  const name = projectConfigFile();
@@ -8931,9 +8946,9 @@ async function configuredApp(projectDir) {
8931
8946
  }
8932
8947
  }
8933
8948
  /**
8934
- * Builds the project at `projectDir`. Writes `.cow/types.d.ts` first (the
8949
+ * Builds the project at `projectDir`. Writes `types.d.ts` first (the
8935
8950
  * typecheck needs it), then the bundles and the source tarball, and
8936
- * `.cow/build/manifest.json` last, so a failed build leaves no manifest and
8951
+ * `build/manifest.json` last, so a failed build leaves no manifest and
8937
8952
  * the next `cow test` rebuilds.
8938
8953
  */
8939
8954
  async function buildProject(projectDir) {
@@ -9242,7 +9257,7 @@ async function copyBlueprint(name, dir, force) {
9242
9257
  }
9243
9258
  function registerAdd(program, io) {
9244
9259
  program.command("add").description("add gallery blueprint code to a cow repo").command("blueprint <name>").description("copy a blueprint's journeys, emails, segments, and scenarios into this repo (local: no API call)").option("--force", "overwrite files that already exist").action(async (name, opts) => {
9245
- const dir = process.cwd();
9260
+ const dir = resolveProjectDir();
9246
9261
  await assertCowConfig(dir);
9247
9262
  const files = await copyBlueprint(name, dir, opts.force === true);
9248
9263
  emit({ data: {
@@ -11908,7 +11923,7 @@ function registerAuth(program, env, io) {
11908
11923
  if (opts.key) {
11909
11924
  if (!opts.source) throw new Error("`--key` needs `--source <src_...>`: the id you are about to send to is what this checks.");
11910
11925
  const client = createClient({
11911
- baseUrl: resolveApiUrl(env, await readCredentials(env), opts.api, (await readCowConfig(process.cwd()))?.apiUrl),
11926
+ baseUrl: resolveApiUrl(env, await readCredentials(env), opts.api, (await readCowConfig(resolveProjectDir()))?.apiUrl),
11912
11927
  auth: () => opts.key ?? null,
11913
11928
  headers: { [CLIENT_HEADER]: `@cowliss/cli/${(await readCliPackage()).version}` }
11914
11929
  });
@@ -11928,7 +11943,7 @@ function registerAuth(program, env, io) {
11928
11943
  }
11929
11944
  const claims = decodeSessionToken(credentials?.token ?? "");
11930
11945
  if (!claims) throw new Error("Cached token is not decodable. Run `cow login` again.");
11931
- const projectApiUrl = (await readCowConfig(process.cwd()))?.apiUrl;
11946
+ const projectApiUrl = (await readCowConfig(resolveProjectDir()))?.apiUrl;
11932
11947
  const apiUrl = resolveApiUrl(env, credentials, opts.api, projectApiUrl);
11933
11948
  const mintedFor = credentials?.apiUrl;
11934
11949
  emit({ data: {
@@ -11944,12 +11959,12 @@ function registerAuth(program, env, io) {
11944
11959
  //#endregion
11945
11960
  //#region src/commands/build.ts
11946
11961
  /**
11947
- * `cow build`: typecheck, bundle, and write `.cow/build` for the project in
11962
+ * `cow build`: typecheck, bundle, and write `build/` for the project in
11948
11963
  * the current directory. Local only, no API call and no network.
11949
11964
  */
11950
11965
  function registerBuild(program, io) {
11951
- program.command("build").description("typecheck and bundle the repo in the current directory, writing .cow/build (local: no API call)").action(async (opts) => {
11952
- emit({ data: await buildProject(process.cwd()) }, io, opts.json);
11966
+ program.command("build").description("typecheck and bundle the repo in the current directory, writing .cowliss/build (local: no API call)").action(async (opts) => {
11967
+ emit({ data: await buildProject(resolveProjectDir()) }, io, opts.json);
11953
11968
  });
11954
11969
  }
11955
11970
 
@@ -12424,7 +12439,7 @@ function flipSummary(journeys, status) {
12424
12439
  * segment's own key joins the set the events are filtered by.
12425
12440
  */
12426
12441
  async function unseenForKeys(client, keys) {
12427
- const dir = process.cwd();
12442
+ const dir = resolveProjectDir();
12428
12443
  const project = await assertCowConfig(dir).then(async (config) => ({
12429
12444
  config,
12430
12445
  built: await buildProject(dir)
@@ -12450,7 +12465,7 @@ function register(program, clientFor, io, verb, status) {
12450
12465
  };
12451
12466
  if (merged.all === true && keys.length > 0) throw new Error(`Name the journeys or pass --all, not both: cow ${verb} welcome.`);
12452
12467
  if (merged.all !== true && keys.length === 0) throw new Error(`Name at least one journey, or pass --all: cow ${verb} welcome.`);
12453
- const selector = merged.all === true ? { appId: (await assertCowConfig(process.cwd())).appId } : { keys };
12468
+ const selector = merged.all === true ? { appId: (await assertCowConfig(resolveProjectDir())).appId } : { keys };
12454
12469
  const client = await clientFor(merged);
12455
12470
  const updated = await setStatus(client, selector, status);
12456
12471
  if (merged.json === true) {
@@ -12550,7 +12565,7 @@ async function chooseApp(client, webUrl, flag, org, io, skipPrompts) {
12550
12565
  return { appId: await ask("App id", only ?? "", io, false) };
12551
12566
  }
12552
12567
  function registerInit(program, clientFor, env, io) {
12553
- program.command("init").argument("[dir]", "directory to create the repo in (default: here)").description("create a cow repo: cow.json, journeys/, emails/, segments/, and the files to build them").option("--org <id>", "organization id (default: the logged-in org)").option("--app <id>", "app id this repository pushes to (default: ask, or the org's only one)").option("--yes", "accept the defaults instead of prompting").option("--blueprint <name>", "start from a gallery blueprint").action(async (dirArg, opts) => {
12568
+ program.command("init").argument("[dir]", "repository to create the project in (default: here)").description("create a cow project in .cowliss/: cow.json, journeys/, emails/, segments/, and the files to build them").option("--org <id>", "organization id (default: the logged-in org)").option("--app <id>", "app id this repository pushes to (default: ask, or the org's only one)").option("--yes", "accept the defaults instead of prompting").option("--blueprint <name>", "start from a gallery blueprint").action(async (dirArg, opts) => {
12554
12569
  const merged = {
12555
12570
  ...program.opts(),
12556
12571
  ...opts
@@ -12559,7 +12574,8 @@ function registerInit(program, clientFor, env, io) {
12559
12574
  const cwd = process.cwd();
12560
12575
  const defaultName = basename(dirArg ? resolve(cwd, dirArg) : cwd);
12561
12576
  const name = await ask("Repo name", defaultName, io, skipPrompts);
12562
- const dir = dirArg ? resolve(cwd, dirArg) : name === defaultName ? cwd : resolve(cwd, name);
12577
+ const repo = dirArg ? resolve(cwd, dirArg) : name === defaultName ? cwd : resolve(cwd, name);
12578
+ const dir = join(repo, PROJECT_DIR);
12563
12579
  if (existsSync(join(dir, "cow.json"))) throw new Error(`"${dir}" already holds a cow.json. Use \`cow add blueprint\` to add code to it.`);
12564
12580
  const credentials = await readCredentials(env);
12565
12581
  const claimedOrg = credentials ? decodeSessionToken(credentials.token)?.orgId ?? null : null;
@@ -12589,10 +12605,10 @@ function registerInit(program, clientFor, env, io) {
12589
12605
  "journeys",
12590
12606
  "emails",
12591
12607
  "segments",
12592
- ".cow/types.d.ts"
12608
+ "types.d.ts"
12593
12609
  ]
12594
12610
  }),
12595
- ".gitignore": ".cow/\nnode_modules/\n",
12611
+ ".gitignore": "build/\ntypes.d.ts\nnode_modules/\n",
12596
12612
  "journeys/.gitkeep": "",
12597
12613
  "emails/.gitkeep": "",
12598
12614
  "segments/.gitkeep": ""
@@ -12614,7 +12630,7 @@ function registerInit(program, clientFor, env, io) {
12614
12630
  const journey = scenario.slice(10).split(".")[0] ?? "abandoned-checkout";
12615
12631
  const appLine = app.appId === "" ? `No app named yet (${app.reason}). Put its id in cow.json's "appId"; you will find it on the app's page at ${webUrl}/apps.` : `Pushes to ${app.appId}`;
12616
12632
  io.stderr([
12617
- `Created a cow repo in ${here}`,
12633
+ `Created a cow project in ${here}`,
12618
12634
  appLine,
12619
12635
  "",
12620
12636
  "Next steps:",
@@ -12766,8 +12782,8 @@ function registerStatus(program, clientFor, io) {
12766
12782
  ...program.opts(),
12767
12783
  ...opts
12768
12784
  };
12769
- const config = await assertCowConfig(process.cwd());
12770
- const built = await buildProject(process.cwd()).catch(() => null);
12785
+ const config = await assertCowConfig(resolveProjectDir());
12786
+ const built = await buildProject(resolveProjectDir()).catch(() => null);
12771
12787
  const client = await clientFor(merged);
12772
12788
  const status = await readStatus(client, config.appId);
12773
12789
  if (merged.json === true) {
@@ -13005,7 +13021,7 @@ function registerPush(program, clientFor, env, io) {
13005
13021
  ...opts
13006
13022
  };
13007
13023
  const client = await clientFor(merged);
13008
- const projectDir = process.cwd();
13024
+ const projectDir = resolveProjectDir();
13009
13025
  const json = merged.json === true;
13010
13026
  const { plan, built } = await planProject({
13011
13027
  client,
@@ -13073,7 +13089,7 @@ function registerPush(program, clientFor, env, io) {
13073
13089
  async function projectPlan(client) {
13074
13090
  const { plan } = await planProject({
13075
13091
  client,
13076
- projectDir: process.cwd()
13092
+ projectDir: resolveProjectDir()
13077
13093
  });
13078
13094
  return { data: plan };
13079
13095
  }
@@ -13086,7 +13102,7 @@ function registerPlan(program, clientFor, io) {
13086
13102
  const client = await clientFor(merged);
13087
13103
  const { plan, built } = await planProject({
13088
13104
  client,
13089
- projectDir: process.cwd()
13105
+ projectDir: resolveProjectDir()
13090
13106
  });
13091
13107
  if (merged.json === true) {
13092
13108
  emit({ data: plan }, io, true);
@@ -13234,7 +13250,7 @@ const execFileAsync = promisify(execFile);
13234
13250
  *
13235
13251
  * Untracked files count: a journey the developer wrote and has not committed
13236
13252
  * is exactly what the tarball's copy of the same path would bury, and it is
13237
- * the only copy. `.cow/` and `node_modules/` are gitignored by `cow init`, so
13253
+ * the only copy. `build/` and `node_modules/` are gitignored by `cow init`, so
13238
13254
  * they never show up here.
13239
13255
  */
13240
13256
  async function dirtyPaths(dir) {
@@ -13283,6 +13299,7 @@ async function pullPush({ client, projectDir, pushId, force }) {
13283
13299
  }
13284
13300
  const tarball = await client.request(contract.pushes["pushes.source"], { params: { id: push.id } });
13285
13301
  const files = [];
13302
+ await mkdir(projectDir, { recursive: true });
13286
13303
  const unpack = extract({
13287
13304
  cwd: projectDir,
13288
13305
  onReadEntry: (entry) => files.push(entry.path)
@@ -13305,7 +13322,7 @@ function registerPull(program, clientFor, io) {
13305
13322
  };
13306
13323
  const outcome = await pullPush({
13307
13324
  client: await clientFor(merged),
13308
- projectDir: process.cwd(),
13325
+ projectDir: resolveProjectDir(),
13309
13326
  pushId: opts.push,
13310
13327
  force: opts.force === true
13311
13328
  });
@@ -13560,7 +13577,7 @@ function registerTest(program, io) {
13560
13577
  }
13561
13578
  const parsed = journeyScenarioSchema.safeParse(json);
13562
13579
  if (!parsed.success) throw new Error(`Scenario file "${opts.scenario}" is not a valid scenario: ${firstIssueMessage(parsed.error)}`);
13563
- const projectDir = process.cwd();
13580
+ const projectDir = resolveProjectDir();
13564
13581
  await buildIfStale(projectDir);
13565
13582
  const result = await runScenario(projectDir, journey, parsed.data);
13566
13583
  emit({ data: result }, io, opts.json);
@@ -13585,7 +13602,7 @@ function buildProgram(env, io) {
13585
13602
  const clientFor = async (opts) => {
13586
13603
  const credentials = await readCredentials(env);
13587
13604
  return createClient({
13588
- baseUrl: resolveApiUrl(env, credentials, typeof opts.api === "string" ? opts.api : void 0, (await readCowConfig(process.cwd()))?.apiUrl),
13605
+ baseUrl: resolveApiUrl(env, credentials, typeof opts.api === "string" ? opts.api : void 0, (await readCowConfig(resolveProjectDir()))?.apiUrl),
13589
13606
  auth: async () => resolveCredential(env, await readCredentials(env)).token,
13590
13607
  headers: async () => ({ [CLIENT_HEADER]: `@cowliss/cli/${(await readCliPackage()).version}` })
13591
13608
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cowliss/cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "cow": "./dist/index.js"