@cowliss/cli 0.4.0 → 0.5.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.
package/README.md CHANGED
@@ -26,7 +26,7 @@ cow whoami
26
26
  ```
27
27
 
28
28
  `cow login` opens the dashboard and caches a session token. In CI, set
29
- `COW_DEPLOY_KEY` to a deploy key instead.
29
+ `COW_PIPELINE_KEY` to a pipeline key instead.
30
30
 
31
31
  ## Journeys and emails as code
32
32
 
@@ -70,16 +70,22 @@ cow test abandoned-checkout --scenario scenarios/cart.json
70
70
  # typecheck and bundle, no API call
71
71
  cow build
72
72
 
73
- # publish it and make it the environment's live code
74
- cow deploy --env development
73
+ # upload the project; the server compiles it
74
+ cow push
75
75
 
76
- # rebuild and redeploy on every save
77
- cow dev
76
+ # what the server holds, and what is on where
77
+ cow status
78
+
79
+ # turn it on, per environment
80
+ cow enable abandoned-checkout --env development
78
81
  ```
79
82
 
80
- Every push creates a numbered release. `cow deploy` makes one release an
81
- environment's current code, `cow rollback` puts the previous one back, and a
82
- run that has already started stays on the release it started on.
83
+ A push creates a new version of every journey and template whose code
84
+ changed, and changes no flag: `cow enable` and `cow disable` are the only
85
+ things that put code in front of anyone. A journey runs its latest version
86
+ that finished compiling, so a push that fails to compile leaves the previous
87
+ one running, and a run that has already started stays on the version it
88
+ started on. To undo, `git revert` and push again.
83
89
 
84
90
  ## Admin, and everything else
85
91
 
@@ -1,4 +1,4 @@
1
- import { a as manifestOutputSchema, i as journeyStepOutputSchema, o as templateRenderOutputSchema, r as guestInputSchema, t as CapabilityError } from "./journeys-B_xg_GnL.js";
1
+ import { a as manifestOutputSchema, i as journeyStepOutputSchema, o as templateRenderOutputSchema, r as guestInputSchema, t as CapabilityError } from "./journeys-C8j8Sqvl.js";
2
2
  import { z } from "zod";
3
3
  import { createElement } from "react";
4
4
  import { renderToStaticMarkup } from "react-dom/server.browser";
@@ -5573,7 +5573,6 @@ function readManifest(module) {
5573
5573
  trigger: journey.trigger,
5574
5574
  purpose: journey.purpose,
5575
5575
  senderIdentity: journey.senderIdentity,
5576
- environments: journey.environments,
5577
5576
  tags: journey.tags
5578
5577
  });
5579
5578
  }
@@ -1,4 +1,4 @@
1
- import { a as ManifestOutput, i as JourneyStepOutput, o as TemplateRenderOutput } from "./index-DAGLEHDn.js";
1
+ import { a as ManifestOutput, i as JourneyStepOutput, o as TemplateRenderOutput } from "./index-BDj02EsQ.js";
2
2
  //#region src/guest/driver.d.ts
3
3
  /**
4
4
  * The in-process driver: JSON in, JSON out (spec: Guest protocol). One
@@ -1,3 +1,3 @@
1
- import { t as runGuest } from "./driver-DaNdNhuF.js";
1
+ import { t as runGuest } from "./driver-14FnzM-h.js";
2
2
 
3
3
  export { runGuest };
@@ -1,6 +1,23 @@
1
- import { n as SendClass } from "./constants-B0wk-87t.js";
2
1
  import { z } from "zod";
3
2
  import { ReactElement } from "react";
3
+ //#region ../../packages/shared/src/constants.d.ts
4
+ /**
5
+ * The two classes of send, declared on the template rather than passed per
6
+ * call so the class cannot drift between two sends of the same message.
7
+ *
8
+ * Marketing is everything a journey sends on the org's behalf: it carries
9
+ * the RFC 8058 unsubscribe headers and runs the full gate list.
10
+ * Transactional is the developer's own operational mail (a receipt, an
11
+ * export-is-ready notice): it carries neither header and skips consent and
12
+ * the per-user frequency cap, because an unsubscribe link on an invoice is
13
+ * wrong and a receipt must not silently vanish for anyone who once left a
14
+ * newsletter. Suppression, the quota, the sending pause, and the from-domain
15
+ * check still apply to both: those bound cost and protect the shared SES
16
+ * account, and none of them are about what the recipient asked for.
17
+ */
18
+ declare const SEND_CLASSES: readonly ["marketing", "transactional"];
19
+ type SendClass = (typeof SEND_CLASSES)[number];
20
+ //#endregion
4
21
  //#region src/guest/emails.d.ts
5
22
  /** A template's subject line, computed from the props it declares. */
6
23
  type Subject<Schema extends z.ZodType> = (props: z.infer<Schema>) => string;
@@ -258,10 +258,6 @@ declare const manifestOutputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
258
258
  }, z.core.$strict>]>;
259
259
  purpose: z.ZodString;
260
260
  senderIdentity: z.ZodOptional<z.ZodString>;
261
- environments: z.ZodArray<z.ZodEnum<{
262
- development: "development";
263
- production: "production";
264
- }>>;
265
261
  kind: z.ZodLiteral<"journey">;
266
262
  }, z.core.$strip>, z.ZodObject<{
267
263
  tags: z.ZodPipe<z.ZodDefault<z.ZodArray<z.ZodString>>, z.ZodTransform<string[], string[]>>;
@@ -282,7 +278,7 @@ type ManifestOutput = z.infer<typeof manifestOutputSchema>;
282
278
  * reuses it.
283
279
  *
284
280
  * Both members are strict, so a journey holding the retired `source` key
285
- * fails to compile a release instead of silently triggering on every app.
281
+ * fails to compile instead of silently triggering on every app.
286
282
  * There is deliberately no pipe filter: a trigger narrows by the app the
287
283
  * write is attributed to, the same token a segment definition names.
288
284
  */
@@ -9,16 +9,6 @@ const DOCS_URL = "https://docs.cowliss.com";
9
9
  */
10
10
  const CLERK_ORG_SUBJECT_PREFIX = "org_";
11
11
  /**
12
- * The two fixed environments every org has (spec: Environments). An app
13
- * belongs to one, so the environment of every write is the app's;
14
- * profiles, identifiers, events, memberships, journey instances,
15
- * deliveries, violations, quarantine, the address ledger, and idempotency
16
- * keys are per environment, while the catalog, sending domains, the
17
- * suppression mirror, billing, and journey code are shared. A third
18
- * environment is a one-line change here plus the mirrored db enum.
19
- */
20
- const ENVIRONMENTS = ["development", "production"];
21
- /**
22
12
  * Fixed consent purposes for the prototype. Consent is a per-purpose map on
23
13
  * the profile, checked at send-step execution time.
24
14
  *
@@ -51,8 +41,8 @@ const EXECUTION_LIMITS = {
51
41
  logLines: 100,
52
42
  logLineBytes: 1024
53
43
  };
54
- /** Per release: manifest counts, bundle size, and the source tarball. */
55
- const RELEASE_LIMITS = {
44
+ /** Per push: manifest counts, bundle size, and the source tarball. */
45
+ const PUSH_LIMITS = {
56
46
  journeys: 100,
57
47
  templates: 200,
58
48
  bundleBytes: 2097152,
@@ -66,29 +56,13 @@ const RELEASE_LIMITS = {
66
56
  const COW_CONFIG_SCHEMA_PATH = "/schemas/cow.json";
67
57
  const COW_CONFIG_SCHEMA_URL = `${DOCS_URL}${COW_CONFIG_SCHEMA_PATH}`;
68
58
 
69
- //#endregion
70
- //#region ../../packages/shared/src/environments.ts
71
- /**
72
- * The environment on the wire: the source's `environment` field, the
73
- * `X-Cow-Environment` header admin calls select with, and the
74
- * `environment` column every per-environment DTO carries.
75
- */
76
- const environmentSchema = z.enum(ENVIRONMENTS);
77
- /**
78
- * The environments a shared definition (a segment, a journey) declares it
79
- * works on: a non-empty list of distinct values. A definition declaring none
80
- * would be dead code with a row behind it, and a repeated value is a typo
81
- * rather than an intent, which is the rule `defineJourney` applies too.
82
- */
83
- const environmentsSchema = z.array(environmentSchema).min(1, "at least one environment is required").refine((values) => new Set(values).size === values.length, { message: "environments must not repeat" });
84
-
85
59
  //#endregion
86
60
  //#region ../../packages/shared/src/journeys-v2/manifest.ts
87
61
  /**
88
- * The release manifest (spec: Build; Push and compile): what `cow build`
62
+ * The pushed manifest (spec: Build; Push and compile): what `cow build`
89
63
  * extracts from a project and `cow push` uploads with the bundles. The
90
- * server validates it with these schemas, compiles every bundle, and stores
91
- * the compiled form on the release row.
64
+ * server validates it with these schemas, compiles every bundle whose key
65
+ * changed, and stores that entry on the version it creates.
92
66
  */
93
67
  /**
94
68
  * A journey or template key: the file basename under `journeys/` or
@@ -168,7 +142,7 @@ const patternSchema = z.union([onePatternSchema, z.array(onePatternSchema).min(1
168
142
  * reuses it.
169
143
  *
170
144
  * Both members are strict, so a journey holding the retired `source` key
171
- * fails to compile a release instead of silently triggering on every app.
145
+ * fails to compile instead of silently triggering on every app.
172
146
  * There is deliberately no pipe filter: a trigger narrows by the app the
173
147
  * write is attributed to, the same token a segment definition names.
174
148
  */
@@ -237,6 +211,13 @@ const spineEntrySchema = z.object({
237
211
  return z.array(spineEntrySchema).optional();
238
212
  }
239
213
  }).meta({ id: "JourneySpineEntry" });
214
+ /**
215
+ * One journey in a stored manifest. A plain (non-strict) object on purpose:
216
+ * a manifest pushed before the author's rollout gate went away still carries
217
+ * `environments`, and every stored manifest has to keep parsing for good
218
+ * (ADR 0011). Zod strips the key, and the journey runs wherever the
219
+ * per-environment enabled flag says it runs, which is the one gate there is.
220
+ */
240
221
  const manifestJourneySchema = z.object({
241
222
  key: journeyKeySchema,
242
223
  /** The author's labels; the dashboard's only grouping. */
@@ -244,7 +225,7 @@ const manifestJourneySchema = z.object({
244
225
  trigger: triggerSchema,
245
226
  /**
246
227
  * A fixed purpose or one the project declares. Checked against the org's
247
- * declared set at deploy time, not here: a manifest is built and pushed
228
+ * declared set at push time, not here: a manifest is built and pushed
248
229
  * without ever reaching the org whose rows say what exists.
249
230
  */
250
231
  purpose: consentPurposeKeySchema,
@@ -255,12 +236,10 @@ const manifestJourneySchema = z.object({
255
236
  * development, which is the one thing a journey must not do.
256
237
  *
257
238
  * Optional here and required at `defineJourney`, exactly like `purposes`: a
258
- * release pushed before the field existed carries none and its stored
239
+ * version pushed before the field existed carries none and its stored
259
240
  * manifest still parses. The author's build is where the error is useful.
260
241
  */
261
242
  senderIdentity: destinationNameSchema.optional(),
262
- /** The author's rollout gate: the journey is active only in these. */
263
- environments: environmentsSchema,
264
243
  spine: z.array(spineEntrySchema),
265
244
  bundle: digestSchema
266
245
  });
@@ -292,27 +271,27 @@ function uniqueKeys(items, ctx, path) {
292
271
  }
293
272
  /**
294
273
  * What `cow build` writes to `.cow/build/manifest.json`, and the shape a
295
- * release row stores for good.
274
+ * push row stores for good, entry by entry, on the versions it creates.
296
275
  *
297
276
  * `protocol` is any positive integer rather than the current constant on
298
- * purpose: a release is immutable and an execution stays pinned to the one it
299
- * started on, so the day the protocol is bumped every stored release must
300
- * still parse, or the runner, the deploy, and every API response the client
301
- * validates all break at once for any org with history. The literal lives at
302
- * push time only (`createReleaseBodySchema`), which is where a stale CLI is
303
- * the developer's own fixable problem, and deploy plus the runner refuse a
304
- * release built for another protocol with a message naming both numbers.
277
+ * purpose: a version is immutable and an execution stays pinned to the one
278
+ * it started on, so the day the protocol is bumped every stored push must
279
+ * still parse, or the runner and every API response the client validates
280
+ * break at once for any org with history. The literal lives at push time
281
+ * only (`createPushBodySchema`), which is where a stale CLI is the
282
+ * developer's own fixable problem, and the runner refuses a version built
283
+ * for another protocol with a message naming both numbers.
305
284
  */
306
285
  const manifestSchema = z.object({
307
286
  protocol: z.number().int().positive(),
308
287
  /** The `@cowliss/cli` version the project was built with. */
309
288
  sdk: z.string().min(1),
310
- journeys: z.array(manifestJourneySchema).max(RELEASE_LIMITS.journeys),
311
- templates: z.array(manifestTemplateSchema).max(RELEASE_LIMITS.templates),
289
+ journeys: z.array(manifestJourneySchema).max(PUSH_LIMITS.journeys),
290
+ templates: z.array(manifestTemplateSchema).max(PUSH_LIMITS.templates),
312
291
  /**
313
292
  * The consent purposes this project declares, copied from `cow.json`.
314
- * Optional rather than defaulted: a release pushed before purposes
315
- * existed carries none, and its stored manifest still parses.
293
+ * Optional rather than defaulted: a push made before purposes existed
294
+ * carries none, and its stored manifest still parses.
316
295
  */
317
296
  purposes: purposesSchema.optional(),
318
297
  /** Digest of the gzipped source tarball. */
@@ -323,27 +302,12 @@ const manifestSchema = z.object({
323
302
  uniqueKeys(manifest.purposes ?? [], ctx, "purposes");
324
303
  });
325
304
  /**
326
- * The manifest as the release row stores it once compilation succeeded:
327
- * the pushed manifest plus the compiled module digest per key, and the
328
- * digest of the Javy engine plugin the toolchain that compiled them was
329
- * built from.
330
- *
331
- * Journeys and templates are keyed separately because they share a key
332
- * space: `welcome.ts` and `welcome.tsx` are one journey and the email it
333
- * sends in every example, and a flat map would let one overwrite the other.
334
- *
335
- * `plugin` is a toolchain record, not a linked artifact: modules are
336
- * statically linked, so the plugin bytes are inside each module. It says
337
- * which engine compiled the release, which is what a later bug report or a
338
- * reproducible rebuild needs.
305
+ * One entry of a stored manifest: what a version is a snapshot of. Journeys
306
+ * and templates are kept apart because they share a key space (`welcome.ts`
307
+ * and `welcome.tsx` are one journey and the email it sends in every
308
+ * example), and the version's own `kind` says which of the two this is.
339
309
  */
340
- const compiledManifestSchema = manifestSchema.safeExtend({
341
- modules: z.object({
342
- journeys: z.record(journeyKeySchema, digestSchema),
343
- templates: z.record(journeyKeySchema, digestSchema)
344
- }),
345
- plugin: digestSchema
346
- });
310
+ const versionManifestSchema = z.union([manifestJourneySchema, manifestTemplateSchema]);
347
311
 
348
312
  //#endregion
349
313
  //#region ../../packages/shared/src/journeys-v2/config.ts
@@ -385,7 +349,7 @@ const cowConfigSchema = z.strictObject({
385
349
  webUrl: z.url().optional()
386
350
  }).meta({
387
351
  title: "cow.json",
388
- description: "A cow project: the organization and the project it deploys to."
352
+ description: "A cow project: the organization and the project it pushes to."
389
353
  });
390
354
 
391
355
  //#endregion
@@ -581,7 +545,6 @@ const manifestOutputSchema = z.discriminatedUnion("kind", [manifestJourneySchema
581
545
  trigger: true,
582
546
  purpose: true,
583
547
  senderIdentity: true,
584
- environments: true,
585
548
  tags: true
586
549
  }).extend({ kind: z.literal("journey") }), manifestTemplateSchema.pick({
587
550
  sendClass: true,
@@ -701,17 +664,21 @@ const journeyConfigSchema = manifestJourneySchema.pick({
701
664
  trigger: true,
702
665
  purpose: true,
703
666
  senderIdentity: true,
704
- environments: true,
705
667
  tags: true
706
668
  }).extend({ senderIdentity: destinationNameSchema });
707
- /** Author a journey. Throws at definition time on an invalid config. */
669
+ /**
670
+ * Author a journey. Throws at definition time on an invalid config.
671
+ *
672
+ * There is no environment gate here: a journey runs wherever it is enabled,
673
+ * and nowhere else (ADR 0011). A source that still passes `environments` is
674
+ * a typecheck error in `cow build`, which is where the author can fix it.
675
+ */
708
676
  function defineJourney(input) {
709
677
  return {
710
678
  ...journeyConfigSchema.parse({
711
679
  trigger: input.trigger,
712
680
  purpose: input.purpose,
713
681
  senderIdentity: input.senderIdentity,
714
- environments: input.environments ?? [...ENVIRONMENTS],
715
682
  tags: input.tags
716
683
  }),
717
684
  run: input.run
@@ -1,5 +1,4 @@
1
- import { n as Duration, r as GuestEvent, t as Trigger } from "./index-DAGLEHDn.js";
2
- import { t as Environment } from "./constants-B0wk-87t.js";
1
+ import { n as Duration, r as GuestEvent, t as Trigger } from "./index-BDj02EsQ.js";
3
2
  //#region src/guest/journeys.d.ts
4
3
  /** The event a journey runs for, or the one a `waitForEvent` resolved with. */
5
4
  type Event = GuestEvent;
@@ -133,17 +132,20 @@ type JourneyConfig = {
133
132
  * resolved per environment, which is why it is a name and not an id.
134
133
  */
135
134
  senderIdentity: string;
136
- /** The author's rollout gate; defaults to every environment. */
137
- environments: Environment[];
138
135
  /** The author's labels; the dashboard's only grouping. Defaults to none. */
139
136
  tags: string[];
140
137
  };
141
138
  type Journey = JourneyConfig & {
142
139
  run: (event: Event, api: Api) => Promise<void>;
143
140
  };
144
- /** Author a journey. Throws at definition time on an invalid config. */
145
- declare function defineJourney(input: Omit<JourneyConfig, "environments" | "tags"> & {
146
- environments?: Environment[];
141
+ /**
142
+ * Author a journey. Throws at definition time on an invalid config.
143
+ *
144
+ * There is no environment gate here: a journey runs wherever it is enabled,
145
+ * and nowhere else (ADR 0011). A source that still passes `environments` is
146
+ * a typecheck error in `cow build`, which is where the author can fix it.
147
+ */
148
+ declare function defineJourney(input: Omit<JourneyConfig, "tags"> & {
147
149
  tags?: string[];
148
150
  run: (event: Event, api: Api) => Promise<void>;
149
151
  }): Journey;
@@ -1,3 +1,3 @@
1
- import { n as defineJourney, t as CapabilityError } from "./journeys-B_xg_GnL.js";
1
+ import { n as defineJourney, t as CapabilityError } from "./journeys-C8j8Sqvl.js";
2
2
 
3
3
  export { CapabilityError, defineJourney };
@@ -1,4 +1,4 @@
1
- import { t as runGuest } from "./driver-DaNdNhuF.js";
1
+ import { t as runGuest } from "./driver-14FnzM-h.js";
2
2
 
3
3
  //#region src/guest/wasi.ts
4
4
  const STDIN = 0;