@cowliss/cli 0.7.0 → 0.9.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/dist/guest/driver-emails.d.ts +12 -0
- package/dist/guest/{driver-C1bsCjZT.js → driver-emails.js} +11 -312
- package/dist/guest/driver-poSdZIj8.js +320 -0
- package/dist/guest/driver.d.ts +19 -6
- package/dist/guest/driver.js +2 -2
- package/dist/guest/emails-5GindgMQ.d.ts +47 -0
- package/dist/guest/emails.d.ts +1 -46
- package/dist/guest/{index-3PMqJKmc.d.ts → index-EqBCZnpq.d.ts} +11 -12
- package/dist/guest/{journeys-Dpsp225V.js → journeys-F8Yk8s1P.js} +93 -45
- package/dist/guest/journeys.d.ts +13 -8
- package/dist/guest/journeys.js +1 -1
- package/dist/guest/wasi.d.ts +4 -2
- package/dist/guest/wasi.js +1 -3
- package/dist/index.js +546 -354
- package/package.json +17 -1
- package/examples/cross-app-pitch/emails/cross-app-pitch.tsx +0 -77
- package/examples/cross-app-pitch/journeys/cross-app-pitch.ts +0 -39
- package/examples/cross-app-pitch/scenarios/cross-app-pitch.json +0 -17
package/dist/guest/driver.d.ts
CHANGED
|
@@ -1,21 +1,34 @@
|
|
|
1
|
-
import { a as JourneyStepOutput, o as ManifestOutput, s as TemplateRenderOutput } from "./index-
|
|
1
|
+
import { a as JourneyStepOutput, o as ManifestOutput, s as TemplateRenderOutput } from "./index-EqBCZnpq.js";
|
|
2
|
+
import { n as Template } from "./emails-5GindgMQ.js";
|
|
2
3
|
//#region src/guest/driver.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* The in-process driver: JSON in, JSON out (spec: Guest protocol). One
|
|
5
|
-
* bundle is one module is one journey or one template, and this turns
|
|
6
|
-
*
|
|
6
|
+
* bundle is one module is one journey or one template, and this turns that
|
|
7
|
+
* bundle's inputs into its outputs. `wasi.ts` wraps it for the sandbox;
|
|
7
8
|
* the simulator and the tests call `runGuest` directly.
|
|
8
9
|
*
|
|
10
|
+
* There are two drivers, one per kind, because a bundle only pays for the
|
|
11
|
+
* one it is. This is the journey half: `manifest` and `journey`. The
|
|
12
|
+
* template half lives in `driver-emails.ts` and is the only one that
|
|
13
|
+
* imports `render.ts`, so react and react-dom (197KB minified) stay out of
|
|
14
|
+
* every journey bundle. `build/index.ts` picks the driver by source kind
|
|
15
|
+
* when it composes an entry; everything both kinds need is here and
|
|
16
|
+
* imported by that file.
|
|
17
|
+
*
|
|
9
18
|
* Every output is validated with the shared schema before it leaves, so a
|
|
10
19
|
* guest bug surfaces here rather than as a host-side parse failure.
|
|
11
20
|
*/
|
|
12
21
|
/** A compiled bundle's module namespace, as `import * as module` sees it. */
|
|
13
22
|
type GuestModule = Record<string, unknown>;
|
|
14
23
|
type GuestOutput = JourneyStepOutput | TemplateRenderOutput | ManifestOutput;
|
|
24
|
+
declare function asTemplate(module: GuestModule): Template;
|
|
25
|
+
declare function readManifest(module: GuestModule): ManifestOutput;
|
|
15
26
|
/**
|
|
16
|
-
* Runs one guest invocation. `module` is the tenant
|
|
17
|
-
* bundle's entry imported; `input` is the raw JSON the
|
|
27
|
+
* Runs one guest invocation against a journey bundle. `module` is the tenant
|
|
28
|
+
* module namespace the bundle's entry imported; `input` is the raw JSON the
|
|
29
|
+
* host handed over. A `template` input is a host bug, not a tenant one: this
|
|
30
|
+
* bundle was built from `journeys/` and carries no renderer.
|
|
18
31
|
*/
|
|
19
32
|
declare function runGuest(module: GuestModule, input: unknown): Promise<GuestOutput>;
|
|
20
33
|
//#endregion
|
|
21
|
-
export { GuestModule, GuestOutput, runGuest };
|
|
34
|
+
export { GuestModule, GuestOutput, asTemplate, readManifest, runGuest };
|
package/dist/guest/driver.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as readManifest, r as runGuest, t as asTemplate } from "./driver-poSdZIj8.js";
|
|
2
2
|
|
|
3
|
-
export { runGuest };
|
|
3
|
+
export { asTemplate, readManifest, runGuest };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
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
|
|
21
|
+
//#region src/guest/emails.d.ts
|
|
22
|
+
/** A template's subject line, computed from the props it declares. */
|
|
23
|
+
type Subject<Schema extends z.ZodType> = (props: z.infer<Schema>) => string;
|
|
24
|
+
/**
|
|
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`,
|
|
27
|
+
* and the optional metadata below it; the sandbox renders `default`.
|
|
28
|
+
*/
|
|
29
|
+
type Template<Schema extends z.ZodType = z.ZodType> = {
|
|
30
|
+
default: (props: z.infer<Schema>) => ReactElement;
|
|
31
|
+
props: Schema;
|
|
32
|
+
subject: Subject<Schema> | string;
|
|
33
|
+
/** Defaults to "marketing". */
|
|
34
|
+
sendClass?: SendClass;
|
|
35
|
+
/** The author's labels; the dashboard's only grouping. Defaults to none. */
|
|
36
|
+
tags?: string[];
|
|
37
|
+
/** True asks the host to mint a signed `verifyUrl` prop at send time. */
|
|
38
|
+
verifyLink?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* True asks the host to mint a signed `unsubscribeUrl` prop at send time,
|
|
41
|
+
* for the author's own footer link. Leave it off and a marketing template
|
|
42
|
+
* still gets a platform footer with the link appended at send time.
|
|
43
|
+
*/
|
|
44
|
+
unsubscribeLink?: boolean;
|
|
45
|
+
};
|
|
46
|
+
//#endregion
|
|
47
|
+
export { Template as n, SendClass as r, Subject as t };
|
package/dist/guest/emails.d.ts
CHANGED
|
@@ -1,47 +1,2 @@
|
|
|
1
|
-
import {
|
|
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
|
|
21
|
-
//#region src/guest/emails.d.ts
|
|
22
|
-
/** A template's subject line, computed from the props it declares. */
|
|
23
|
-
type Subject<Schema extends z.ZodType> = (props: z.infer<Schema>) => string;
|
|
24
|
-
/**
|
|
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`,
|
|
27
|
-
* and the optional metadata below it; the sandbox renders `default`.
|
|
28
|
-
*/
|
|
29
|
-
type Template<Schema extends z.ZodType = z.ZodType> = {
|
|
30
|
-
default: (props: z.infer<Schema>) => ReactElement;
|
|
31
|
-
props: Schema;
|
|
32
|
-
subject: Subject<Schema> | string;
|
|
33
|
-
/** Defaults to "marketing". */
|
|
34
|
-
sendClass?: SendClass;
|
|
35
|
-
/** The author's labels; the dashboard's only grouping. Defaults to none. */
|
|
36
|
-
tags?: string[];
|
|
37
|
-
/** True asks the host to mint a signed `verifyUrl` prop at send time. */
|
|
38
|
-
verifyLink?: boolean;
|
|
39
|
-
/**
|
|
40
|
-
* True asks the host to mint a signed `unsubscribeUrl` prop at send time,
|
|
41
|
-
* for the author's own footer link. Leave it off and a marketing template
|
|
42
|
-
* still gets a platform footer with the link appended at send time.
|
|
43
|
-
*/
|
|
44
|
-
unsubscribeLink?: boolean;
|
|
45
|
-
};
|
|
46
|
-
//#endregion
|
|
1
|
+
import { n as Template, r as SendClass, t as Subject } from "./emails-5GindgMQ.js";
|
|
47
2
|
export { type SendClass, Subject, Template };
|
|
@@ -252,14 +252,12 @@ declare const manifestOutputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
252
252
|
tags: z.ZodPipe<z.ZodDefault<z.ZodArray<z.ZodString>>, z.ZodTransform<string[], string[]>>;
|
|
253
253
|
trigger: z.ZodUnion<readonly [z.ZodObject<{
|
|
254
254
|
event: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
|
|
255
|
-
appId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
256
255
|
}, z.core.$strict>, z.ZodObject<{
|
|
257
256
|
segment: z.ZodObject<{
|
|
258
257
|
match: z.ZodDefault<z.ZodEnum<{
|
|
259
258
|
all: "all";
|
|
260
259
|
any: "any";
|
|
261
260
|
}>>;
|
|
262
|
-
appId: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
|
|
263
261
|
predicates: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
264
262
|
kind: z.ZodLiteral<"trait">;
|
|
265
263
|
name: z.ZodString;
|
|
@@ -323,9 +321,12 @@ type ManifestOutput = z.infer<typeof manifestOutputSchema>;
|
|
|
323
321
|
declare const durationSchema: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
324
322
|
type Duration = z.infer<typeof durationSchema>;
|
|
325
323
|
/**
|
|
326
|
-
* What starts a journey: an event
|
|
327
|
-
*
|
|
328
|
-
*
|
|
324
|
+
* What starts a journey: an event, or entry into the segment the trigger
|
|
325
|
+
* itself describes. The registry DTO in `../journeys` reuses it.
|
|
326
|
+
*
|
|
327
|
+
* No trigger names an app (ADR 0022): a repository is one app, the journey
|
|
328
|
+
* row carries its `app_id`, and the runtime compares that with the event's.
|
|
329
|
+
* An `appId` here would be a second way to say it, and the way to leak.
|
|
329
330
|
*
|
|
330
331
|
* A segment trigger carries the predicate list, not a name: the push
|
|
331
332
|
* materializes one segment row per journey that inlines a definition, owned
|
|
@@ -334,22 +335,20 @@ type Duration = z.infer<typeof durationSchema>;
|
|
|
334
335
|
* definition is data — validated here, carried on the version, never
|
|
335
336
|
* compiled and never executed.
|
|
336
337
|
*
|
|
337
|
-
* Both members are strict, so a journey holding the retired `source`
|
|
338
|
-
* the retired `{ segment: "name" }` form, fails to build
|
|
339
|
-
* triggering on every app or on nothing. There is
|
|
340
|
-
*
|
|
341
|
-
*
|
|
338
|
+
* Both members are strict, so a journey holding the retired `source` or
|
|
339
|
+
* `appId` key, or the retired `{ segment: "name" }` form, fails to build
|
|
340
|
+
* instead of silently triggering on every app or on nothing. There is
|
|
341
|
+
* deliberately no pipe filter and no app filter: the journey's own app is
|
|
342
|
+
* the only narrowing there is.
|
|
342
343
|
*/
|
|
343
344
|
declare const triggerSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
344
345
|
event: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
|
|
345
|
-
appId: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
346
346
|
}, z.core.$strict>, z.ZodObject<{
|
|
347
347
|
segment: z.ZodObject<{
|
|
348
348
|
match: z.ZodDefault<z.ZodEnum<{
|
|
349
349
|
all: "all";
|
|
350
350
|
any: "any";
|
|
351
351
|
}>>;
|
|
352
|
-
appId: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>;
|
|
353
352
|
predicates: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
354
353
|
kind: z.ZodLiteral<"trait">;
|
|
355
354
|
name: z.ZodString;
|
|
@@ -4,6 +4,34 @@ import { z } from "zod";
|
|
|
4
4
|
/** The public docs site, for the mail and the pages that point people at it. */
|
|
5
5
|
const DOCS_URL = "https://docs.cowliss.com";
|
|
6
6
|
/**
|
|
7
|
+
* Stripe-style ID prefixes per resource type, so ids in logs, URLs, and
|
|
8
|
+
* payloads are self-describing.
|
|
9
|
+
*/
|
|
10
|
+
const ID_PREFIXES = {
|
|
11
|
+
user: "usr_",
|
|
12
|
+
event: "evt_",
|
|
13
|
+
identifier: "idn_",
|
|
14
|
+
segment: "seg_",
|
|
15
|
+
/** The attribution unit: a readable `app_<slug>`, not a TypeID. */
|
|
16
|
+
app: "app_",
|
|
17
|
+
/** One inbound pipe into an app; a TypeID like everything else. */
|
|
18
|
+
source: "src_",
|
|
19
|
+
webhook: "whk_",
|
|
20
|
+
push: "psh_",
|
|
21
|
+
version: "ver_",
|
|
22
|
+
execution: "exe_",
|
|
23
|
+
delivery: "dlv_",
|
|
24
|
+
violation: "vio_",
|
|
25
|
+
quarantineEntry: "qtn_",
|
|
26
|
+
idempotencyKey: "idk_",
|
|
27
|
+
abuseEvent: "abu_",
|
|
28
|
+
suppression: "sup_",
|
|
29
|
+
senderDomain: "dom_",
|
|
30
|
+
journeyRun: "run_",
|
|
31
|
+
topup: "top_",
|
|
32
|
+
emailAddress: "eml_"
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
7
35
|
* Clerk ID prefix marking an organization-scoped subject. Ingestion API keys
|
|
8
36
|
* must resolve to an org subject; user-scoped keys are rejected.
|
|
9
37
|
*/
|
|
@@ -71,21 +99,18 @@ const COW_CONFIG_SCHEMA_URL = `${DOCS_URL}${COW_CONFIG_SCHEMA_PATH}`;
|
|
|
71
99
|
* history, combined with `all` or `any`. Deliberately flat — nested predicate groups are YAGNI for the
|
|
72
100
|
* prototype, and a flat list keeps the pure evaluator a fold.
|
|
73
101
|
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* app
|
|
77
|
-
*
|
|
78
|
-
* A definition and the membership it produces are both the
|
|
79
|
-
* organization's.
|
|
102
|
+
* A definition names no app (ADR 0022): the segment row's own `app_id` is
|
|
103
|
+
* the one app it evaluates, so the events and the profiles it reads are
|
|
104
|
+
* that app's and there is nothing to filter by here.
|
|
80
105
|
*
|
|
81
106
|
* Apart from `./segments` because a journey's trigger carries a definition
|
|
82
107
|
* and the trigger schema is bundled into the wasm guest, where an edge to
|
|
83
108
|
* `@cowliss/db` (which `./segments` has, for the row schema) is a hard
|
|
84
109
|
* bundler failure. Nothing here imports anything but zod.
|
|
85
110
|
*
|
|
86
|
-
* The object is strict: a definition holding the retired `sourceId`
|
|
87
|
-
* (which meant the app) fails loudly instead of parsing
|
|
88
|
-
* definition that evaluates over every app. There is deliberately no pipe
|
|
111
|
+
* The object is strict: a definition holding the retired `sourceId` or
|
|
112
|
+
* `appId` key (both of which meant the app) fails loudly instead of parsing
|
|
113
|
+
* as an unfiltered definition that evaluates over every app. There is deliberately no pipe
|
|
89
114
|
* filter here — filtering by source is a later feature, and accepting one
|
|
90
115
|
* now would make a stale `sourceId` parse as a filter on a pipe that does
|
|
91
116
|
* not exist and silently match nothing.
|
|
@@ -138,15 +163,8 @@ const segmentPredicateSchema = z.discriminatedUnion("kind", [z.object({
|
|
|
138
163
|
/** Rolling window, relative to evaluation time; absent means all history. */
|
|
139
164
|
withinDays: z.number().int().min(1).optional()
|
|
140
165
|
})]).refine((predicate) => predicate.kind !== "trait" || VALUELESS_TRAIT_OPS.includes(predicate.op) || predicate.value !== void 0, { message: "value is required unless op is exists or notExists" });
|
|
141
|
-
const appIdSchema = z.string().trim().min(1);
|
|
142
166
|
const segmentDefinitionSchema = z.strictObject({
|
|
143
167
|
match: z.enum(["all", "any"]).default("all"),
|
|
144
|
-
/**
|
|
145
|
-
* Optional app filter over the event side; null/absent spans apps.
|
|
146
|
-
* A list matches any of the named apps, which is how one definition
|
|
147
|
-
* names the development and the production id of the same app.
|
|
148
|
-
*/
|
|
149
|
-
appId: z.union([appIdSchema, z.array(appIdSchema).min(1)]).nullish(),
|
|
150
168
|
predicates: z.array(segmentPredicateSchema).min(1, "at least one predicate is required")
|
|
151
169
|
});
|
|
152
170
|
|
|
@@ -259,9 +277,12 @@ const purposesSchema = z.array(declaredPurposeSchema).max(20, "a project declare
|
|
|
259
277
|
const onePatternSchema = z.string().min(1).max(200, "a pattern must be at most 200 characters");
|
|
260
278
|
const patternSchema = z.union([onePatternSchema, z.array(onePatternSchema).min(1).max(20, "a matcher takes at most 20 patterns")]);
|
|
261
279
|
/**
|
|
262
|
-
* What starts a journey: an event
|
|
263
|
-
*
|
|
264
|
-
*
|
|
280
|
+
* What starts a journey: an event, or entry into the segment the trigger
|
|
281
|
+
* itself describes. The registry DTO in `../journeys` reuses it.
|
|
282
|
+
*
|
|
283
|
+
* No trigger names an app (ADR 0022): a repository is one app, the journey
|
|
284
|
+
* row carries its `app_id`, and the runtime compares that with the event's.
|
|
285
|
+
* An `appId` here would be a second way to say it, and the way to leak.
|
|
265
286
|
*
|
|
266
287
|
* A segment trigger carries the predicate list, not a name: the push
|
|
267
288
|
* materializes one segment row per journey that inlines a definition, owned
|
|
@@ -270,16 +291,13 @@ const patternSchema = z.union([onePatternSchema, z.array(onePatternSchema).min(1
|
|
|
270
291
|
* definition is data — validated here, carried on the version, never
|
|
271
292
|
* compiled and never executed.
|
|
272
293
|
*
|
|
273
|
-
* Both members are strict, so a journey holding the retired `source`
|
|
274
|
-
* the retired `{ segment: "name" }` form, fails to build
|
|
275
|
-
* triggering on every app or on nothing. There is
|
|
276
|
-
*
|
|
277
|
-
*
|
|
294
|
+
* Both members are strict, so a journey holding the retired `source` or
|
|
295
|
+
* `appId` key, or the retired `{ segment: "name" }` form, fails to build
|
|
296
|
+
* instead of silently triggering on every app or on nothing. There is
|
|
297
|
+
* deliberately no pipe filter and no app filter: the journey's own app is
|
|
298
|
+
* the only narrowing there is.
|
|
278
299
|
*/
|
|
279
|
-
const triggerSchema = z.union([z.strictObject({
|
|
280
|
-
event: patternSchema,
|
|
281
|
-
appId: patternSchema.optional()
|
|
282
|
-
}), z.strictObject({ segment: segmentDefinitionSchema })]);
|
|
300
|
+
const triggerSchema = z.union([z.strictObject({ event: patternSchema }), z.strictObject({ segment: segmentDefinitionSchema })]);
|
|
283
301
|
/**
|
|
284
302
|
* The address half of a from-header: a local part, an `@`, and a dotted
|
|
285
303
|
* domain. Deliberately narrower than RFC 5322 (no quoted local parts, no
|
|
@@ -501,30 +519,56 @@ const versionManifestSchema = z.union([manifestJourneySchema, manifestTemplateSc
|
|
|
501
519
|
//#endregion
|
|
502
520
|
//#region ../../packages/shared/src/journeys-v2/config.ts
|
|
503
521
|
/**
|
|
504
|
-
*
|
|
505
|
-
*
|
|
506
|
-
*
|
|
522
|
+
* An app id as `cow.json` carries it: the readable `app_<slug>`
|
|
523
|
+
* `appIdFromName` derives, which is the one id a developer types by hand.
|
|
524
|
+
*
|
|
525
|
+
* It lives here rather than beside the app DTOs in ../apps because that
|
|
526
|
+
* module imports the Drizzle schema, which neither the wasmtime guest nor
|
|
527
|
+
* the Temporal workflow isolate can carry, and because `cow.json` is the
|
|
528
|
+
* file a developer types it into. Everything that names an app on the wire
|
|
529
|
+
* reuses it.
|
|
530
|
+
*/
|
|
531
|
+
const appIdSchema = z.string().trim().max(200).regex(new RegExp(`^${ID_PREFIXES.app}[a-z0-9]+(-[a-z0-9]+)*$`), "an app id looks like \"app_website\"");
|
|
532
|
+
/**
|
|
533
|
+
* A source id: the one inbound pipe an ingestion or send call names. It
|
|
534
|
+
* lives beside `appIdSchema` for the same two reasons — the sources module
|
|
535
|
+
* imports the Drizzle schema, and both are ids that travel on the wire — and
|
|
536
|
+
* it has the same shape, so a bare `src_` is refused here rather than at the
|
|
537
|
+
* database.
|
|
538
|
+
*
|
|
539
|
+
* The prefixes above and here are interpolated raw; `journeys-v2.test.ts`
|
|
540
|
+
* asserts they are plain `<word>_` literals, so none of them can quietly
|
|
541
|
+
* change what these patterns match.
|
|
507
542
|
*/
|
|
508
|
-
const
|
|
543
|
+
const sourceIdSchema = z.string().trim().max(200).regex(new RegExp(`^${ID_PREFIXES.source}[a-z0-9_]+$`), "a source id looks like \"src_01j8x…\"");
|
|
509
544
|
/**
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
*
|
|
545
|
+
* A Clerk organization id, as `cow.json` spells it. Exported beside
|
|
546
|
+
* `appIdSchema` because the push body carries the org the file names so the
|
|
547
|
+
* server can refuse a push aimed at another one (ADR 0022).
|
|
548
|
+
*/
|
|
549
|
+
const orgIdSchema = z.string().trim().max(200).startsWith(CLERK_ORG_SUBJECT_PREFIX, "orgId must be a Clerk org id");
|
|
550
|
+
/**
|
|
551
|
+
* `cow.json` (spec: Project layout): the org, and which of its apps this
|
|
552
|
+
* repository pushes to. Auth never lives in the file. Strict, so a typo'd
|
|
553
|
+
* key is a build error rather than a silently ignored setting — which is
|
|
554
|
+
* also what turns the retired `project` key into a refusal (`cow build`
|
|
555
|
+
* says what to rename it to).
|
|
513
556
|
*/
|
|
514
557
|
const cowConfigSchema = z.strictObject({
|
|
515
558
|
$schema: z.url().optional(),
|
|
516
|
-
orgId:
|
|
559
|
+
orgId: orgIdSchema,
|
|
517
560
|
/**
|
|
518
|
-
* Which
|
|
519
|
-
*
|
|
520
|
-
*
|
|
561
|
+
* Which app of the org this repository pushes to (ADR 0022). The app is
|
|
562
|
+
* the push unit: it owns the journey and template keys, and `cow
|
|
563
|
+
* status`, `cow enable --all` and `cow pull` all resolve against it. One
|
|
564
|
+
* repository is one app, so no journey file names one.
|
|
521
565
|
*/
|
|
522
|
-
|
|
566
|
+
appId: appIdSchema,
|
|
523
567
|
/**
|
|
524
|
-
* The consent purposes this
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
568
|
+
* The consent purposes this repository declares. They are org-wide, so
|
|
569
|
+
* two apps declaring one key must agree on its label and default or the
|
|
570
|
+
* push is refused; a push adds and updates them and never deletes one,
|
|
571
|
+
* because profiles hold answers against them.
|
|
528
572
|
*/
|
|
529
573
|
purposes: purposesSchema.optional(),
|
|
530
574
|
/** Overrides the API the CLI talks to; the hosted product needs none. */
|
|
@@ -537,7 +581,7 @@ const cowConfigSchema = z.strictObject({
|
|
|
537
581
|
webUrl: z.url().optional()
|
|
538
582
|
}).meta({
|
|
539
583
|
title: "cow.json",
|
|
540
|
-
description: "A cow project: the organization and the
|
|
584
|
+
description: "A cow project: the organization and the app it pushes to."
|
|
541
585
|
});
|
|
542
586
|
|
|
543
587
|
//#endregion
|
|
@@ -866,6 +910,10 @@ const journeyConfigSchema = manifestJourneySchema.pick({
|
|
|
866
910
|
*
|
|
867
911
|
* There is no environment gate here: an organization has one data space, and
|
|
868
912
|
* a journey runs wherever it is on and nowhere else (ADR 0011, ADR 0013).
|
|
913
|
+
*
|
|
914
|
+
* There is no app gate either. A journey belongs to the one app `cow.json`
|
|
915
|
+
* names, so it only ever sees that app's events, profiles and segments
|
|
916
|
+
* (ADR 0022); a trigger naming an app is refused rather than honoured.
|
|
869
917
|
*/
|
|
870
918
|
function defineJourney(input) {
|
|
871
919
|
const config = journeyConfigSchema.parse({
|
package/dist/guest/journeys.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as GuestEvent, n as Trigger, r as TriggerInput, t as Duration } from "./index-
|
|
1
|
+
import { i as GuestEvent, n as Trigger, r as TriggerInput, t as Duration } from "./index-EqBCZnpq.js";
|
|
2
2
|
//#region src/guest/journeys.d.ts
|
|
3
3
|
/** The event a journey runs for, or the one a `waitForEvent` resolved with. */
|
|
4
4
|
type Event = GuestEvent;
|
|
@@ -32,10 +32,10 @@ declare class CapabilityError extends Error {
|
|
|
32
32
|
constructor(code: string, message: string);
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* The
|
|
35
|
+
* The repository's templates, keyed by template key. `cow build` writes the
|
|
36
36
|
* real interface into `.cow/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
|
*/
|
|
40
40
|
interface CowTemplates {}
|
|
41
41
|
type TemplateKey = keyof CowTemplates extends never ? string : keyof CowTemplates;
|
|
@@ -122,8 +122,8 @@ type JourneyConfig = {
|
|
|
122
122
|
trigger: Trigger;
|
|
123
123
|
/**
|
|
124
124
|
* Why this journey contacts someone: `marketing`, `transactional`, or a
|
|
125
|
-
* purpose this
|
|
126
|
-
* here; that the org declares it is checked when the
|
|
125
|
+
* purpose this repository declares in `cow.json`. The key's shape is
|
|
126
|
+
* checked here; that the org declares it is checked when the push lands.
|
|
127
127
|
*/
|
|
128
128
|
purpose: string;
|
|
129
129
|
/**
|
|
@@ -167,12 +167,17 @@ type Journey = JourneyConfig & {
|
|
|
167
167
|
*
|
|
168
168
|
* There is no environment gate here: an organization has one data space, and
|
|
169
169
|
* a journey runs wherever it is on and nowhere else (ADR 0011, ADR 0013).
|
|
170
|
+
*
|
|
171
|
+
* There is no app gate either. A journey belongs to the one app `cow.json`
|
|
172
|
+
* names, so it only ever sees that app's events, profiles and segments
|
|
173
|
+
* (ADR 0022); a trigger naming an app is refused rather than honoured.
|
|
170
174
|
*/
|
|
171
175
|
declare function defineJourney(input: Omit<JourneyConfig, "tags" | "trigger"> & {
|
|
172
176
|
/**
|
|
173
|
-
* What starts it: `{ event }`,
|
|
174
|
-
*
|
|
175
|
-
*
|
|
177
|
+
* What starts it: `{ event }`, one event-name pattern or a list of
|
|
178
|
+
* them, or `{ segment }` carrying the predicate list of the segment
|
|
179
|
+
* this journey enrolls from. Written form, so a predicate can leave
|
|
180
|
+
* `match` and `atLeast` out. No app: the repository's app is the scope.
|
|
176
181
|
*/
|
|
177
182
|
trigger: TriggerInput;
|
|
178
183
|
tags?: string[];
|
package/dist/guest/journeys.js
CHANGED
package/dist/guest/wasi.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { GuestModule } from "./driver.js";
|
|
1
|
+
import { GuestModule, GuestOutput } from "./driver.js";
|
|
2
2
|
//#region src/guest/wasi.d.ts
|
|
3
|
+
/** The kind-specific driver the entry hands over. */
|
|
4
|
+
type RunGuest = (module: GuestModule, input: unknown) => Promise<GuestOutput>;
|
|
3
5
|
/** Drives one invocation of the module this bundle was built for. */
|
|
4
|
-
declare function main(module: GuestModule): void;
|
|
6
|
+
declare function main(module: GuestModule, runGuest: RunGuest): void;
|
|
5
7
|
//#endregion
|
|
6
8
|
export { main };
|
package/dist/guest/wasi.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { t as runGuest } from "./driver-C1bsCjZT.js";
|
|
2
|
-
|
|
3
1
|
//#region src/guest/wasi.ts
|
|
4
2
|
const STDIN = 0;
|
|
5
3
|
const STDOUT = 1;
|
|
@@ -27,7 +25,7 @@ function write(fd, text) {
|
|
|
27
25
|
Javy.IO.writeSync(fd, new TextEncoder().encode(text));
|
|
28
26
|
}
|
|
29
27
|
/** Drives one invocation of the module this bundle was built for. */
|
|
30
|
-
function main(module) {
|
|
28
|
+
function main(module, runGuest) {
|
|
31
29
|
const fail = (error) => {
|
|
32
30
|
write(STDERR, error instanceof Error ? error.message : String(error));
|
|
33
31
|
throw error;
|