@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 +14 -8
- package/dist/guest/{driver-DaNdNhuF.js → driver-14FnzM-h.js} +1 -2
- package/dist/guest/driver.d.ts +1 -1
- package/dist/guest/driver.js +1 -1
- package/dist/guest/emails.d.ts +18 -1
- package/dist/guest/{index-DAGLEHDn.d.ts → index-BDj02EsQ.d.ts} +1 -5
- package/dist/guest/{journeys-B_xg_GnL.js → journeys-C8j8Sqvl.js} +40 -73
- package/dist/guest/journeys.d.ts +9 -7
- package/dist/guest/journeys.js +1 -1
- package/dist/guest/wasi.js +1 -1
- package/dist/index.js +1137 -1172
- package/package.json +1 -1
- package/dist/guest/constants-B0wk-87t.d.ts +0 -30
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
|
-
`
|
|
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
|
-
#
|
|
74
|
-
cow
|
|
73
|
+
# upload the project; the server compiles it
|
|
74
|
+
cow push
|
|
75
75
|
|
|
76
|
-
#
|
|
77
|
-
cow
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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-
|
|
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
|
}
|
package/dist/guest/driver.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as ManifestOutput, i as JourneyStepOutput, o as TemplateRenderOutput } from "./index-
|
|
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
|
package/dist/guest/driver.js
CHANGED
package/dist/guest/emails.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
55
|
-
const
|
|
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
|
|
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
|
|
91
|
-
*
|
|
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
|
|
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
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
299
|
-
* started on, so the day the protocol is bumped every stored
|
|
300
|
-
* still parse, or the runner
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
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(
|
|
311
|
-
templates: z.array(manifestTemplateSchema).max(
|
|
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
|
|
315
|
-
*
|
|
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
|
-
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
package/dist/guest/journeys.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { n as Duration, r as GuestEvent, t as Trigger } from "./index-
|
|
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
|
-
/**
|
|
145
|
-
|
|
146
|
-
|
|
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;
|
package/dist/guest/journeys.js
CHANGED
package/dist/guest/wasi.js
CHANGED