@samva/cli 0.17.0 → 0.19.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/AGENTS.md +14 -0
- package/dist/bin.mjs +431 -48
- package/package.json +9 -9
package/AGENTS.md
CHANGED
|
@@ -37,6 +37,12 @@ confirmation (`--yes` outside a terminal), and every mutation supports `--dry-ru
|
|
|
37
37
|
`SAMVA_API_KEY`, `samva org current` asks the API who the key is (`GET /v1/whoami`) and reports its
|
|
38
38
|
organization and key; with OAuth it reports the profile's selected organization.
|
|
39
39
|
|
|
40
|
+
`samva webhooks secret <id>` reads the current signing secret with `webhooks:update` permission.
|
|
41
|
+
Human output includes the secret, its creation time, and when the previous secret stops verifying
|
|
42
|
+
if one exists. `--json` returns `{secret, createdAt, previousExpiresAt}` inside the result envelope.
|
|
43
|
+
`create` and `rotate-secret` return readable secrets too; retrieve them again with `secret <id>`.
|
|
44
|
+
Their JSON results contain `secret` and `warning` (`create` also includes `endpoint`).
|
|
45
|
+
|
|
40
46
|
`samva templates export --out <dir>` renders every local template's fixtures to HTML
|
|
41
47
|
and text with the asset manifest, needing a Samva credential only to resolve a live `samva:brand`; `--asset-base <origin>` is for an
|
|
42
48
|
export that will be delivered, which cannot carry a relative reference.
|
|
@@ -73,6 +79,14 @@ uploads again. `--dry-run` lists each attachment and uploads nothing.
|
|
|
73
79
|
or the newest publication that still validates input identically. They are mutually exclusive and
|
|
74
80
|
each requires `--template-id`, because a reference is a statement about one template's history.
|
|
75
81
|
|
|
82
|
+
`samva templates list --shared true|false` filters by whether the template's project is shared with
|
|
83
|
+
every namespace, and every listed template reports `shared`. Sharing belongs to an organization-level
|
|
84
|
+
project: `templates create --shared` shares the new project and refuses a namespace (exit `2`), and
|
|
85
|
+
`templates update <id> --data '{}' --shared true|false` shares or unshares the project for every
|
|
86
|
+
entry in it. `--shared` that disagrees with `shared` in `--data` exits `2`. A namespace-scoped key
|
|
87
|
+
lists, reads, and sends shared templates but gets 403 changing them, and the API refuses moving a
|
|
88
|
+
shared project into a namespace.
|
|
89
|
+
|
|
76
90
|
`samva templates workspace <open|diff|patch|add-asset|check|fixture|save|history|publish>` is the
|
|
77
91
|
hosted project workspace over the CLI, for a caller with no clone: an agent in a sandbox, a CI job,
|
|
78
92
|
a script. It reaches the same operations the hosted MCP tools reach, so an agent can start a journey in one
|
package/dist/bin.mjs
CHANGED
|
@@ -14106,7 +14106,7 @@ var layer12 = /* @__PURE__ */ layer.pipe(/* @__PURE__ */ provideMerge(/* @__PURE
|
|
|
14106
14106
|
// package.json
|
|
14107
14107
|
var package_default = {
|
|
14108
14108
|
name: "@samva/cli",
|
|
14109
|
-
version: "0.
|
|
14109
|
+
version: "0.19.0",
|
|
14110
14110
|
description: "Samva CLI \u2014 email and platform operations from your terminal",
|
|
14111
14111
|
keywords: [
|
|
14112
14112
|
"cli",
|
|
@@ -14157,21 +14157,21 @@ var package_default = {
|
|
|
14157
14157
|
"@samva/contracts": "workspace:*",
|
|
14158
14158
|
"@samva/core": "workspace:*",
|
|
14159
14159
|
"@samva/mailbox": "workspace:*",
|
|
14160
|
-
"@samva/markup": "
|
|
14161
|
-
"@samva/vite": "
|
|
14160
|
+
"@samva/markup": "catalog:",
|
|
14161
|
+
"@samva/vite": "catalog:",
|
|
14162
14162
|
"@types/node": "catalog:",
|
|
14163
14163
|
"better-auth": "catalog:",
|
|
14164
14164
|
effect: "catalog:effect",
|
|
14165
|
-
samva: "workspace:0.
|
|
14165
|
+
samva: "workspace:0.22.0",
|
|
14166
14166
|
typescript: "catalog:",
|
|
14167
14167
|
vite: "~8.3.0",
|
|
14168
14168
|
vitest: "catalog:"
|
|
14169
14169
|
},
|
|
14170
14170
|
optionalDependencies: {
|
|
14171
|
-
"@samva/cli-darwin-arm64": "workspace:0.
|
|
14172
|
-
"@samva/cli-darwin-x64": "workspace:0.
|
|
14173
|
-
"@samva/cli-linux-arm64": "workspace:0.
|
|
14174
|
-
"@samva/cli-linux-x64": "workspace:0.
|
|
14171
|
+
"@samva/cli-darwin-arm64": "workspace:0.19.0",
|
|
14172
|
+
"@samva/cli-darwin-x64": "workspace:0.19.0",
|
|
14173
|
+
"@samva/cli-linux-arm64": "workspace:0.19.0",
|
|
14174
|
+
"@samva/cli-linux-x64": "workspace:0.19.0"
|
|
14175
14175
|
},
|
|
14176
14176
|
engines: {
|
|
14177
14177
|
node: ">=18"
|
|
@@ -19596,6 +19596,9 @@ var ENTITY_PREFIXES = {
|
|
|
19596
19596
|
waflow: "WhatsAppFlowId",
|
|
19597
19597
|
wafver: "WhatsAppFlowVersionId",
|
|
19598
19598
|
wafreg: "WhatsAppFlowRegistrationId",
|
|
19599
|
+
wafkey: "WhatsAppFlowEncryptionKeyId",
|
|
19600
|
+
wafx: "WhatsAppFlowExchangeId",
|
|
19601
|
+
wasl: "WhatsAppSetupLinkId",
|
|
19599
19602
|
mbx: "MailboxId",
|
|
19600
19603
|
mbxep: "MailboxEndpointId",
|
|
19601
19604
|
thread: "MailboxThreadId",
|
|
@@ -19691,6 +19694,9 @@ var WhatsAppBusinessAccountId = entityId("waba").pipe(brand(ENTITY_PREFIXES.waba
|
|
|
19691
19694
|
var WhatsAppFlowId = entityId("waflow").pipe(brand(ENTITY_PREFIXES.waflow));
|
|
19692
19695
|
var WhatsAppFlowVersionId = entityId("wafver").pipe(brand(ENTITY_PREFIXES.wafver));
|
|
19693
19696
|
var WhatsAppFlowRegistrationId = entityId("wafreg").pipe(brand(ENTITY_PREFIXES.wafreg));
|
|
19697
|
+
var WhatsAppFlowEncryptionKeyId = entityId("wafkey").pipe(brand(ENTITY_PREFIXES.wafkey));
|
|
19698
|
+
var WhatsAppFlowExchangeId = entityId("wafx").pipe(brand(ENTITY_PREFIXES.wafx));
|
|
19699
|
+
var WhatsAppSetupLinkId = entityId("wasl").pipe(brand(ENTITY_PREFIXES.wasl));
|
|
19694
19700
|
var WhatsAppServiceWindowId = entityId("wasw").pipe(brand(ENTITY_PREFIXES.wasw));
|
|
19695
19701
|
var WebhookEndpointId = entityId("wep").pipe(brand(ENTITY_PREFIXES.wep));
|
|
19696
19702
|
var WebhookCredentialId = entityId("whcred").pipe(brand(ENTITY_PREFIXES.whcred));
|
|
@@ -20398,6 +20404,7 @@ var DOMAIN_WEBHOOK_EVENT_TYPE_VALUES = [
|
|
|
20398
20404
|
var domainWebhookEventTypeSchema = Literals(DOMAIN_WEBHOOK_EVENT_TYPE_VALUES);
|
|
20399
20405
|
var TEMPLATE_WEBHOOK_EVENT_TYPE_VALUES = ["template.registration.updated"];
|
|
20400
20406
|
var FLOW_WEBHOOK_EVENT_TYPE_VALUES = ["flow.registration.updated"];
|
|
20407
|
+
var WHATSAPP_NUMBER_WEBHOOK_EVENT_TYPE_VALUES = ["whatsapp.number.connected"];
|
|
20401
20408
|
var MAILBOX_WEBHOOK_EVENT_TYPE_VALUES = [
|
|
20402
20409
|
"mailbox.message.received",
|
|
20403
20410
|
"mailbox.message.sent",
|
|
@@ -20422,6 +20429,7 @@ var WEBHOOK_EVENT_TYPE_VALUES = [
|
|
|
20422
20429
|
...DOMAIN_WEBHOOK_EVENT_TYPE_VALUES,
|
|
20423
20430
|
...TEMPLATE_WEBHOOK_EVENT_TYPE_VALUES,
|
|
20424
20431
|
...FLOW_WEBHOOK_EVENT_TYPE_VALUES,
|
|
20432
|
+
...WHATSAPP_NUMBER_WEBHOOK_EVENT_TYPE_VALUES,
|
|
20425
20433
|
...MAILBOX_WEBHOOK_EVENT_TYPE_VALUES,
|
|
20426
20434
|
...JOURNEY_WEBHOOK_EVENT_TYPE_VALUES
|
|
20427
20435
|
];
|
|
@@ -24149,6 +24157,7 @@ var CreateTemplatePayload = /* @__PURE__ */ Struct({
|
|
|
24149
24157
|
]), Null2]), Null2])),
|
|
24150
24158
|
isActive: optionalKey2(Union2([Boolean3, Null2])),
|
|
24151
24159
|
namespaceId: optionalKey2(Union2([NamespaceId2, Null2]).annotate({ description: "The namespace the new template project belongs to; the project and every entry added to it share it. Omit for organization level. A namespace-scoped key always creates in its own namespace." })),
|
|
24160
|
+
shared: optionalKey2(Union2([Boolean3, Null2]).annotate({ description: "Share the new organization-level project with every namespace: namespace-scoped keys read and send its entries but cannot change them. Defaults to false. Not with `namespaceId`, and not from a namespace-scoped key." })),
|
|
24152
24161
|
idempotencyKey: String4.check(isMinLength(1).annotate({ expected: "a value with a length of at least 1" })).check(isMaxCodePoints(255).annotate({ expected: "a string with at most 255 code points" })).check(isMaxLength(255).annotate({ expected: "a value with a length of at most 255" }))
|
|
24153
24162
|
}).annotate({ identifier: "CreateTemplatePayload" });
|
|
24154
24163
|
var CreateWebhookPayload = /* @__PURE__ */ Struct({
|
|
@@ -24180,6 +24189,9 @@ var CreateWebhookPayload = /* @__PURE__ */ Struct({
|
|
|
24180
24189
|
Literal2("webhook.test"),
|
|
24181
24190
|
Literal2("domain.updated"),
|
|
24182
24191
|
Literal2("domain.warming.updated"),
|
|
24192
|
+
Literal2("template.registration.updated"),
|
|
24193
|
+
Literal2("flow.registration.updated"),
|
|
24194
|
+
Literal2("whatsapp.number.connected"),
|
|
24183
24195
|
Literal2("mailbox.message.received"),
|
|
24184
24196
|
Literal2("mailbox.message.sent"),
|
|
24185
24197
|
Literal2("mailbox.action.pending_approval"),
|
|
@@ -24224,6 +24236,9 @@ var UpdateWebhookPayload = /* @__PURE__ */ Struct({
|
|
|
24224
24236
|
Literal2("webhook.test"),
|
|
24225
24237
|
Literal2("domain.updated"),
|
|
24226
24238
|
Literal2("domain.warming.updated"),
|
|
24239
|
+
Literal2("template.registration.updated"),
|
|
24240
|
+
Literal2("flow.registration.updated"),
|
|
24241
|
+
Literal2("whatsapp.number.connected"),
|
|
24227
24242
|
Literal2("mailbox.message.received"),
|
|
24228
24243
|
Literal2("mailbox.message.sent"),
|
|
24229
24244
|
Literal2("mailbox.action.pending_approval"),
|
|
@@ -24462,7 +24477,8 @@ var UpdateTemplatePayload = /* @__PURE__ */ Struct({ data: Struct({
|
|
|
24462
24477
|
]), Null2]), Null2])),
|
|
24463
24478
|
fromDefault: optionalKey2(Union2([Union2([TemplateFromDefault, Null2]), Null2])),
|
|
24464
24479
|
replyToDefault: optionalKey2(Union2([Union2([ArraySchema(String4), Null2]), Null2])),
|
|
24465
|
-
isActive: optionalKey2(Union2([Boolean3, Null2]))
|
|
24480
|
+
isActive: optionalKey2(Union2([Boolean3, Null2])),
|
|
24481
|
+
shared: optionalKey2(Union2([Boolean3, Null2]).annotate({ description: "Share the template's project with every namespace, or stop sharing it. Applies to every entry in the project. Only an organization-level project is shared." }))
|
|
24466
24482
|
}) }).annotate({ identifier: "UpdateTemplatePayload" });
|
|
24467
24483
|
var ApiKeyAccess = /* @__PURE__ */ Union2([Struct({ mode: Literal2("full") }), Struct({
|
|
24468
24484
|
mode: Literal2("restricted"),
|
|
@@ -24799,6 +24815,7 @@ var apiStatuses = {
|
|
|
24799
24815
|
MailboxSendConflictError: 409,
|
|
24800
24816
|
MailboxSendRefusedError: 403,
|
|
24801
24817
|
CursorExpiredError: 410,
|
|
24818
|
+
WhatsAppSetupLinkUnavailableError: 410,
|
|
24802
24819
|
MethodNotAllowedError: 405,
|
|
24803
24820
|
UpgradeRequiredError: 426,
|
|
24804
24821
|
RateLimitedError: 429,
|
|
@@ -25082,7 +25099,7 @@ var __exportAll = (all, no_symbols) => {
|
|
|
25082
25099
|
};
|
|
25083
25100
|
|
|
25084
25101
|
// ../sdk/dist/package.js
|
|
25085
|
-
var version = "0.
|
|
25102
|
+
var version = "0.22.0";
|
|
25086
25103
|
|
|
25087
25104
|
// ../sdk/dist/client-attribution.js
|
|
25088
25105
|
var SDK_VERSION = version;
|
|
@@ -25421,6 +25438,7 @@ var commandTree = {
|
|
|
25421
25438
|
list: {},
|
|
25422
25439
|
get: {},
|
|
25423
25440
|
create: {},
|
|
25441
|
+
"from-website": {},
|
|
25424
25442
|
update: {},
|
|
25425
25443
|
remove: {}
|
|
25426
25444
|
},
|
|
@@ -25473,6 +25491,7 @@ var commandTree = {
|
|
|
25473
25491
|
webhooks: {
|
|
25474
25492
|
list: {},
|
|
25475
25493
|
get: {},
|
|
25494
|
+
secret: {},
|
|
25476
25495
|
create: {},
|
|
25477
25496
|
update: {},
|
|
25478
25497
|
test: {},
|
|
@@ -25903,7 +25922,12 @@ var getAuthenticatedJsonForContext = fn2("CliClient.getAuthenticatedJson")(funct
|
|
|
25903
25922
|
};
|
|
25904
25923
|
const url = `${config.baseUrl.replace(/\/$/, "")}${path}`;
|
|
25905
25924
|
const response = yield* tryPromise2({
|
|
25906
|
-
try: (signal) => fetch(url, request === undefined ? { headers, signal } : {
|
|
25925
|
+
try: (signal) => fetch(url, request === undefined ? { headers, signal } : {
|
|
25926
|
+
method: request.method ?? "POST",
|
|
25927
|
+
headers,
|
|
25928
|
+
body: JSON.stringify(request.body),
|
|
25929
|
+
signal
|
|
25930
|
+
}),
|
|
25907
25931
|
catch: (cause) => new SamvaCliError({ message: `Request to ${url} failed: ${String(cause)}` })
|
|
25908
25932
|
});
|
|
25909
25933
|
const body = yield* tryPromise2({
|
|
@@ -27045,6 +27069,7 @@ var SDK_OPERATION_CALLS = {
|
|
|
27045
27069
|
},
|
|
27046
27070
|
templatesList: { kind: "params" },
|
|
27047
27071
|
templatesCreate: { kind: "payload" },
|
|
27072
|
+
templatesCreateFromWebsite: { kind: "payload" },
|
|
27048
27073
|
templatesAddEntry: {
|
|
27049
27074
|
kind: "pathPayload",
|
|
27050
27075
|
paths: ["id"]
|
|
@@ -27166,6 +27191,10 @@ var SDK_OPERATION_CALLS = {
|
|
|
27166
27191
|
whoamiGet: { kind: "none" },
|
|
27167
27192
|
webhooksList: { kind: "params" },
|
|
27168
27193
|
webhooksCreate: { kind: "payload" },
|
|
27194
|
+
webhooksGetSecret: {
|
|
27195
|
+
kind: "path",
|
|
27196
|
+
paths: ["id"]
|
|
27197
|
+
},
|
|
27169
27198
|
webhooksGet: {
|
|
27170
27199
|
kind: "path",
|
|
27171
27200
|
paths: ["id"]
|
|
@@ -28532,6 +28561,7 @@ var SamvaOperations = {
|
|
|
28532
28561
|
templatesAttachFamilyMember: operationMetadata85,
|
|
28533
28562
|
templatesDetachFamilyMember: operationMetadata87,
|
|
28534
28563
|
templatesCreateFamilyFromTemplate: operationMetadata85,
|
|
28564
|
+
templatesCreateFromWebsite: operationMetadata85,
|
|
28535
28565
|
templatesAddEntry: operationMetadata85,
|
|
28536
28566
|
unsubscribeList: operationMetadata90,
|
|
28537
28567
|
unsubscribeCreate: operationMetadata91,
|
|
@@ -28558,6 +28588,7 @@ var SamvaOperations = {
|
|
|
28558
28588
|
webhooksRetryDelivery: operationMetadata96,
|
|
28559
28589
|
webhooksGetLogs: operationMetadata95,
|
|
28560
28590
|
webhooksRegenerateSecret: operationMetadata96,
|
|
28591
|
+
webhooksGetSecret: operationMetadata97,
|
|
28561
28592
|
webhooksGetStats: operationMetadata97,
|
|
28562
28593
|
webhooksTest: operationMetadata96,
|
|
28563
28594
|
whoamiGet: {
|
|
@@ -28804,6 +28835,7 @@ var ApiKey = /* @__PURE__ */ Struct({
|
|
|
28804
28835
|
name: String4,
|
|
28805
28836
|
key: optionalKey2(Union2([String4, Null2])),
|
|
28806
28837
|
start: Union2([String4, Null2]),
|
|
28838
|
+
end: Union2([String4, Null2]),
|
|
28807
28839
|
description: optionalKey2(Union2([String4, Null2])),
|
|
28808
28840
|
createdAt: String4,
|
|
28809
28841
|
expiresAt: Union2([String4, Null2]),
|
|
@@ -28817,6 +28849,7 @@ var ApiKeyWithSecret = /* @__PURE__ */ Struct({
|
|
|
28817
28849
|
name: String4,
|
|
28818
28850
|
key: SensitiveString,
|
|
28819
28851
|
start: Union2([String4, Null2]),
|
|
28852
|
+
end: Union2([String4, Null2]),
|
|
28820
28853
|
description: optionalKey2(Union2([String4, Null2])),
|
|
28821
28854
|
createdAt: String4,
|
|
28822
28855
|
expiresAt: Union2([String4, Null2]),
|
|
@@ -29427,6 +29460,10 @@ var apiKeysCommand = make29("api-keys").pipe(withDescription3("Create, inspect,
|
|
|
29427
29460
|
|
|
29428
29461
|
// src/commands/brands.ts
|
|
29429
29462
|
import { join as join3, relative, resolve as resolve5 } from "path";
|
|
29463
|
+
// ../../node_modules/.bun/@samva+markup@0.15.0+a19bcd7db259aaf7/node_modules/@samva/markup/dist/dark-scheme-CbCwGDWH.js
|
|
29464
|
+
var DARK_SCHEME_QUERY = /^(?:only\s+)?(?:screen\s+and\s+)?\(\s*prefers-color-scheme\s*:\s*dark\s*\)$/i;
|
|
29465
|
+
var isDarkSchemeQuery = (prelude) => DARK_SCHEME_QUERY.test(prelude.trim());
|
|
29466
|
+
|
|
29430
29467
|
// ../../node_modules/.bun/css-tree@3.2.1/node_modules/css-tree/lib/tokenizer/types.js
|
|
29431
29468
|
var EOF = 0;
|
|
29432
29469
|
var Ident = 1;
|
|
@@ -53199,21 +53236,21 @@ var $ = (e) => {
|
|
|
53199
53236
|
};
|
|
53200
53237
|
var ae = { ok: l, isOk: u, err: d, isError: f, try: T, tryPromise: E, map: D, mapError: O, tryRecover: k, andThen: A, tryRecoverAsync: ee, andThenAsync: te, match: ne, tap: j, tapAsync: M, tapError: N6, tapErrorAsync: P, tapBoth: F4, tapBothAsync: I, unwrap: L, unwrapOr: z, gen: B, await: V, codec: re, all: Q, allAsync: ie, partition: $, partitionAsync: async (e) => $(await Z(e, `Result.partitionAsync input promise rejected`)), flatten: (e) => e.status === `ok` ? e.value : e };
|
|
53201
53238
|
|
|
53202
|
-
// ../../node_modules/.bun/@samva+markup@0.
|
|
53239
|
+
// ../../node_modules/.bun/@samva+markup@0.15.0+a19bcd7db259aaf7/node_modules/@samva/markup/dist/theme-css-s9QwD5Ko.js
|
|
53203
53240
|
var BRAND_NAMESPACES = [
|
|
53204
53241
|
"--color-",
|
|
53205
53242
|
"--font-",
|
|
53206
53243
|
"--text-",
|
|
53207
53244
|
"--leading-",
|
|
53208
53245
|
"--tracking-",
|
|
53209
|
-
"--radius
|
|
53246
|
+
"--radius",
|
|
53210
53247
|
"--spacing",
|
|
53211
53248
|
"--container-",
|
|
53212
53249
|
"--shadow-"
|
|
53213
53250
|
];
|
|
53251
|
+
var inNamespace = (property, namespace) => namespace.endsWith("-") ? property.startsWith(namespace) : property === namespace || property.startsWith(`${namespace}-`);
|
|
53214
53252
|
var BRAND_PROPERTY = /^--[a-z][a-z0-9-]*$/;
|
|
53215
53253
|
var BRAND_UNSAFE_VALUE = /[{};<>\\@]|url\s*\(|expression\s*\(|\/\*/i;
|
|
53216
|
-
var BRAND_DARK_MEDIA = /^\(\s*prefers-color-scheme\s*:\s*dark\s*\)$/i;
|
|
53217
53254
|
var BRAND_FONT_DESCRIPTORS = /* @__PURE__ */ new Set([
|
|
53218
53255
|
"font-family",
|
|
53219
53256
|
"src",
|
|
@@ -53222,7 +53259,6 @@ var BRAND_FONT_DESCRIPTORS = /* @__PURE__ */ new Set([
|
|
|
53222
53259
|
"font-display",
|
|
53223
53260
|
"unicode-range"
|
|
53224
53261
|
]);
|
|
53225
|
-
var darkVariableName = (property) => `${property}-dark`;
|
|
53226
53262
|
var spanOfNode = (node) => ({
|
|
53227
53263
|
start: node.loc?.start.offset ?? 0,
|
|
53228
53264
|
end: node.loc?.end.offset ?? 0
|
|
@@ -53232,6 +53268,7 @@ var unquoteFamily = (value) => {
|
|
|
53232
53268
|
const quoted = /^(['"])(.*)\1$/s.exec(trimmed);
|
|
53233
53269
|
return quoted === null ? trimmed : quoted[2].replace(/\\(.)/g, "$1");
|
|
53234
53270
|
};
|
|
53271
|
+
var quoteFamily = (family) => `"${family.replace(/["\\]/g, "\\$&")}"`;
|
|
53235
53272
|
var parseBrandCss = (css) => {
|
|
53236
53273
|
const diagnostics = [];
|
|
53237
53274
|
const report = (code, message, span) => {
|
|
@@ -53273,7 +53310,7 @@ var parseBrandCss = (css) => {
|
|
|
53273
53310
|
report("brand-css-property", `${property} is not a theme variable; name theme variables --namespace-name.`, span);
|
|
53274
53311
|
continue;
|
|
53275
53312
|
}
|
|
53276
|
-
if (!BRAND_NAMESPACES.some((namespace) => property
|
|
53313
|
+
if (!BRAND_NAMESPACES.some((namespace) => inNamespace(property, namespace))) {
|
|
53277
53314
|
report("brand-css-namespace", `${property} is outside the brand namespaces (${BRAND_NAMESPACES.map((namespace) => namespace.endsWith("-") ? `${namespace}*` : namespace).join(", ")}).`, span);
|
|
53278
53315
|
continue;
|
|
53279
53316
|
}
|
|
@@ -53369,7 +53406,7 @@ var parseBrandCss = (css) => {
|
|
|
53369
53406
|
readFontFace(node);
|
|
53370
53407
|
continue;
|
|
53371
53408
|
}
|
|
53372
|
-
if (name === "media" && node.prelude !== null &&
|
|
53409
|
+
if (name === "media" && node.prelude !== null && isDarkSchemeQuery(generate55(node.prelude))) {
|
|
53373
53410
|
for (const child of node.block.children)
|
|
53374
53411
|
if (child.type === "Atrule" && child.name.toLowerCase() === "theme")
|
|
53375
53412
|
readTheme(child, dark, "dark");
|
|
@@ -53380,19 +53417,103 @@ var parseBrandCss = (css) => {
|
|
|
53380
53417
|
}
|
|
53381
53418
|
report("brand-css-unsupported", "A brand theme contains only @theme, a dark-mode @theme (@media (prefers-color-scheme: dark)), and @font-face.", spanOfNode(node));
|
|
53382
53419
|
}
|
|
53383
|
-
for (const property of Object.keys(dark))
|
|
53384
|
-
if (darkVariableName(property) in variables)
|
|
53385
|
-
report("brand-css-dark-conflict", `${darkVariableName(property)} and the dark-mode ${property} name the same theme variable; keep one.`, {
|
|
53386
|
-
start: 0,
|
|
53387
|
-
end: 0
|
|
53388
|
-
});
|
|
53389
53420
|
return diagnostics.length > 0 ? ae.err(diagnostics) : ae.ok({
|
|
53390
53421
|
variables,
|
|
53391
53422
|
dark,
|
|
53392
53423
|
fontFaces
|
|
53393
53424
|
});
|
|
53394
53425
|
};
|
|
53395
|
-
|
|
53426
|
+
var printDeclarations = (entries, indent) => Object.entries(entries).map(([property, value]) => `${indent}${property}: ${value};`).join(`
|
|
53427
|
+
`);
|
|
53428
|
+
var printFontFace = (face) => [
|
|
53429
|
+
"@font-face {",
|
|
53430
|
+
` font-family: ${quoteFamily(face.family)};`,
|
|
53431
|
+
` src: ${face.src};`,
|
|
53432
|
+
...face.weight === undefined ? [] : [` font-weight: ${face.weight};`],
|
|
53433
|
+
...face.style === undefined ? [] : [` font-style: ${face.style};`],
|
|
53434
|
+
...face.display === undefined ? [] : [` font-display: ${face.display};`],
|
|
53435
|
+
...face.unicodeRange === undefined ? [] : [` unicode-range: ${face.unicodeRange};`],
|
|
53436
|
+
"}"
|
|
53437
|
+
].join(`
|
|
53438
|
+
`);
|
|
53439
|
+
var printBrandCss = (brand) => {
|
|
53440
|
+
const blocks = [
|
|
53441
|
+
...brand.fontFaces.map(printFontFace),
|
|
53442
|
+
...Object.keys(brand.variables).length === 0 ? [] : [`@theme {
|
|
53443
|
+
${printDeclarations(brand.variables, " ")}
|
|
53444
|
+
}`],
|
|
53445
|
+
...Object.keys(brand.dark).length === 0 ? [] : [`@media (prefers-color-scheme: dark) {
|
|
53446
|
+
@theme {
|
|
53447
|
+
${printDeclarations(brand.dark, " ")}
|
|
53448
|
+
}
|
|
53449
|
+
}`]
|
|
53450
|
+
];
|
|
53451
|
+
return blocks.length === 0 ? "" : `${blocks.join(`
|
|
53452
|
+
|
|
53453
|
+
`)}
|
|
53454
|
+
`;
|
|
53455
|
+
};
|
|
53456
|
+
var THEME_COLOR_ROLES = {
|
|
53457
|
+
background: "The page, behind everything.",
|
|
53458
|
+
foreground: "Text on the page.",
|
|
53459
|
+
card: "A surface that groups the message's content.",
|
|
53460
|
+
"card-foreground": "Text on a card.",
|
|
53461
|
+
primary: "The main action's fill (a button), and links.",
|
|
53462
|
+
"primary-foreground": "Text on a primary fill.",
|
|
53463
|
+
secondary: "A quieter action's fill.",
|
|
53464
|
+
"secondary-foreground": "Text on a secondary fill.",
|
|
53465
|
+
muted: "A subdued surface: a code block, a table header.",
|
|
53466
|
+
"muted-foreground": "Secondary text on the page, a card or a muted surface: labels, captions, the footer.",
|
|
53467
|
+
accent: "A highlighted surface: a callout, a selected row.",
|
|
53468
|
+
"accent-foreground": "Text on an accent surface.",
|
|
53469
|
+
border: "Rules, dividers and outlines."
|
|
53470
|
+
};
|
|
53471
|
+
var THEME_COLORS = Object.keys(THEME_COLOR_ROLES);
|
|
53472
|
+
var colorVariables = (colors) => Object.fromEntries(THEME_COLORS.map((name) => [`--color-${name}`, colors[name]]));
|
|
53473
|
+
var DEFAULT_THEME = {
|
|
53474
|
+
variables: {
|
|
53475
|
+
...colorVariables({
|
|
53476
|
+
background: "#ffffff",
|
|
53477
|
+
foreground: "#0a0a0a",
|
|
53478
|
+
card: "#ffffff",
|
|
53479
|
+
"card-foreground": "#0a0a0a",
|
|
53480
|
+
primary: "#171717",
|
|
53481
|
+
"primary-foreground": "#fafafa",
|
|
53482
|
+
secondary: "#f5f5f5",
|
|
53483
|
+
"secondary-foreground": "#171717",
|
|
53484
|
+
muted: "#f5f5f5",
|
|
53485
|
+
"muted-foreground": "#737373",
|
|
53486
|
+
accent: "#f5f5f5",
|
|
53487
|
+
"accent-foreground": "#171717",
|
|
53488
|
+
border: "#e5e5e5"
|
|
53489
|
+
}),
|
|
53490
|
+
"--radius": "8px",
|
|
53491
|
+
"--radius-sm": "calc(var(--radius) - 4px)",
|
|
53492
|
+
"--radius-md": "calc(var(--radius) - 2px)",
|
|
53493
|
+
"--radius-lg": "var(--radius)",
|
|
53494
|
+
"--radius-xl": "calc(var(--radius) + 4px)",
|
|
53495
|
+
"--font-sans": '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
|
|
53496
|
+
"--font-heading": "var(--font-sans)"
|
|
53497
|
+
},
|
|
53498
|
+
dark: colorVariables({
|
|
53499
|
+
background: "#0a0a0a",
|
|
53500
|
+
foreground: "#fafafa",
|
|
53501
|
+
card: "#171717",
|
|
53502
|
+
"card-foreground": "#fafafa",
|
|
53503
|
+
primary: "#fafafa",
|
|
53504
|
+
"primary-foreground": "#171717",
|
|
53505
|
+
secondary: "#262626",
|
|
53506
|
+
"secondary-foreground": "#fafafa",
|
|
53507
|
+
muted: "#262626",
|
|
53508
|
+
"muted-foreground": "#a3a3a3",
|
|
53509
|
+
accent: "#262626",
|
|
53510
|
+
"accent-foreground": "#fafafa",
|
|
53511
|
+
border: "#2e2e2e"
|
|
53512
|
+
}),
|
|
53513
|
+
fontFaces: []
|
|
53514
|
+
};
|
|
53515
|
+
var DEFAULT_THEME_CSS = printBrandCss(DEFAULT_THEME);
|
|
53516
|
+
// ../../node_modules/.bun/@samva+markup@0.15.0+a19bcd7db259aaf7/node_modules/@samva/markup/dist/diagnostic-codes-UXwOO3A-.js
|
|
53396
53517
|
var buildLineMap = (source) => {
|
|
53397
53518
|
const lineStarts = [0];
|
|
53398
53519
|
for (let index = 0;index < source.length; index++) {
|
|
@@ -53617,10 +53738,22 @@ var DIAGNOSTIC_CODES = codes2({
|
|
|
53617
53738
|
summary: "<Columns per> is not a positive integer literal.",
|
|
53618
53739
|
fix: "Write per={2}."
|
|
53619
53740
|
},
|
|
53741
|
+
"invalid-columns-gap": {
|
|
53742
|
+
summary: "<Columns gap> is set on a Columns whose cells are repeated with each or rendered conditionally.",
|
|
53743
|
+
fix: "Write each Column out unconditionally, or pad the cells instead of using gap."
|
|
53744
|
+
},
|
|
53745
|
+
"invalid-columns-stack": {
|
|
53746
|
+
summary: "<Columns stack> is not a true or false literal.",
|
|
53747
|
+
fix: "Write stack={false}, or leave it out to stack on narrow screens."
|
|
53748
|
+
},
|
|
53620
53749
|
"invalid-button-number": {
|
|
53621
53750
|
summary: "A Button size prop is not a finite number.",
|
|
53622
53751
|
fix: "Pass a finite number."
|
|
53623
53752
|
},
|
|
53753
|
+
"invalid-number-prop": {
|
|
53754
|
+
summary: "A primitive's numeric prop, such as <Email gutter>, is not a finite number in range.",
|
|
53755
|
+
fix: "Pass a finite number the message allows, for example gutter={16}."
|
|
53756
|
+
},
|
|
53624
53757
|
"unsupported-element": {
|
|
53625
53758
|
summary: "An element outside the supported email vocabulary.",
|
|
53626
53759
|
fix: "Use an element from the contract's element table."
|
|
@@ -53721,6 +53854,18 @@ var DIAGNOSTIC_CODES = codes2({
|
|
|
53721
53854
|
summary: "A custom property is redefined inside a conditional at-rule.",
|
|
53722
53855
|
fix: "Keep the override on a class rule."
|
|
53723
53856
|
},
|
|
53857
|
+
"email-contrast": {
|
|
53858
|
+
summary: "Text, or a filled button against its surroundings, falls below the contrast ratio in light or dark mode (below 1.5:1 a button is indistinguishable from its surface; WCAG 1.4.11 asks 3:1).",
|
|
53859
|
+
fix: "Pair background and text tokens (bg-primary with text-primary-foreground), give the theme's dark-mode @theme a value for the token, or choose colors that reach the required ratio.",
|
|
53860
|
+
upgrade: [
|
|
53861
|
+
"Move the project's theme onto the theme vocabulary with one dark-mode block. In starter.css and theme.css, rename each color token to the vocabulary name for its role (reference.md, Theme): a page color to --color-background, the main text to --color-foreground, a surface to --color-card with --color-card-foreground, the brand or action color to --color-primary with --color-primary-foreground, subdued text to --color-muted-foreground, a highlight to --color-accent with --color-accent-foreground, rules to --color-border, body and heading fonts to --font-sans and --font-heading, and a corner radius to --radius. Keep every value.",
|
|
53862
|
+
"Move each --color-<name>-dark value into @media (prefers-color-scheme: dark) { @theme { --color-<name>: <value>; } } under the renamed token, and delete the -dark tokens. In every template and partial, rename the utilities to match (bg-surface to bg-card, text-muted to text-muted-foreground, and so on) and delete each dark:\u2026-dark class whose token now has a dark value; keep a dark: class only where it picks a different token in dark mode.",
|
|
53863
|
+
"Then fix what email-contrast still reports at its location: pair each fill with its -foreground token, or give the dark block a value that reaches the ratio. Do not weaken the design to pass: keep the brand's colors and adjust the paired token.",
|
|
53864
|
+
"Compile every entry and render every fixture in light and dark mode; confirm the light render is unchanged apart from fixes. Resolve errors before requesting one saved version for the whole project, approved by the user; do not publish."
|
|
53865
|
+
].join(`
|
|
53866
|
+
|
|
53867
|
+
`)
|
|
53868
|
+
},
|
|
53724
53869
|
"email-font-fallback": {
|
|
53725
53870
|
summary: "A font stack names a project font without a generic fallback.",
|
|
53726
53871
|
fix: "End the stack with sans-serif, serif or monospace."
|
|
@@ -54206,6 +54351,7 @@ var CampaignLatestRun = /* @__PURE__ */ Struct({
|
|
|
54206
54351
|
dispatchedCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54207
54352
|
failedCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54208
54353
|
skippedCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54354
|
+
pendingCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54209
54355
|
estimatedCompletionAt: Union2([String4, Null2])
|
|
54210
54356
|
}).annotate({ identifier: "CampaignLatestRun" });
|
|
54211
54357
|
var CampaignRecipientPage = /* @__PURE__ */ Struct({
|
|
@@ -54304,11 +54450,14 @@ var CampaignRun = /* @__PURE__ */ Struct({
|
|
|
54304
54450
|
dispatchedCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54305
54451
|
failedCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54306
54452
|
skippedCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54453
|
+
pendingCount: Finite.check(isFinite4().annotate({ expected: "a finite number" })),
|
|
54307
54454
|
skippedReasons: ArraySchema(CampaignRunSkippedReason),
|
|
54308
54455
|
estimatedCompletionAt: Union2([String4, Null2]),
|
|
54309
54456
|
failureReason: Union2([String4, Null2]),
|
|
54310
54457
|
holdReason: Union2([String4, Null2]),
|
|
54311
54458
|
holdDomainId: Union2([EmailDomainId2, Null2]),
|
|
54459
|
+
holdUntil: Union2([String4, Null2]),
|
|
54460
|
+
capacityWarning: Union2([String4, Null2]),
|
|
54312
54461
|
lastContactId: Union2([String4, Null2]),
|
|
54313
54462
|
createdBy: Union2([String4, Null2]),
|
|
54314
54463
|
actorEmail: String4,
|
|
@@ -54659,6 +54808,23 @@ var resumeRun = /* @__PURE__ */ make42("campaignsResumeRun");
|
|
|
54659
54808
|
var cancelRun = /* @__PURE__ */ make42("campaignsCancelRun");
|
|
54660
54809
|
var listRecipients = /* @__PURE__ */ make42("campaignsListRecipients");
|
|
54661
54810
|
|
|
54811
|
+
// src/lib/run-hold.ts
|
|
54812
|
+
var WHATSAPP_MESSAGING_LIMIT_HOLD = "whatsapp_messaging_limit";
|
|
54813
|
+
var holdLabel = (reason) => reason === null || reason === undefined ? null : reason === WHATSAPP_MESSAGING_LIMIT_HOLD ? `${reason} (the messaging limit its business portfolio shares across numbers; the run resumes on its own, sooner if Meta raises the limit)` : reason;
|
|
54814
|
+
var holdFields = (run) => [
|
|
54815
|
+
["Held", holdLabel(run.holdReason)],
|
|
54816
|
+
["Resumes by", run.holdUntil]
|
|
54817
|
+
];
|
|
54818
|
+
var campaignPacingFields = (run, options) => [
|
|
54819
|
+
...holdFields(run),
|
|
54820
|
+
[
|
|
54821
|
+
"Pending",
|
|
54822
|
+
run.pendingCount !== undefined && (run.pendingCount > 0 || run.holdReason != null) ? String(run.pendingCount) : null
|
|
54823
|
+
],
|
|
54824
|
+
["Est. completion", run.estimatedCompletionAt],
|
|
54825
|
+
...options.warning ? [["Capacity warning", run.capacityWarning]] : []
|
|
54826
|
+
];
|
|
54827
|
+
|
|
54662
54828
|
// src/lib/template-reference.ts
|
|
54663
54829
|
var publicationIdFlag = String9("publication-id").pipe(withDescription2("Pin one exact publication, so nothing published later changes what this renders"), optional4);
|
|
54664
54830
|
var inputContractIdFlag = String9("input-contract-id").pipe(withDescription2("Follow the newest publication that still validates input identically to this contract"), optional4);
|
|
@@ -55131,7 +55297,7 @@ var archive2 = make29("archive", {
|
|
|
55131
55297
|
["Definition", data.definition.state]
|
|
55132
55298
|
]);
|
|
55133
55299
|
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
55134
|
-
var printRun = (run) => printFields([
|
|
55300
|
+
var printRun = (run, options = { warning: true }) => printFields([
|
|
55135
55301
|
["Run ID", run.id],
|
|
55136
55302
|
["Campaign", run.campaignId],
|
|
55137
55303
|
["Status", run.status],
|
|
@@ -55146,8 +55312,10 @@ var printRun = (run) => printFields([
|
|
|
55146
55312
|
"Skipped by reason",
|
|
55147
55313
|
(run.skippedReasons ?? []).map(({ reason, count }) => `${reason} ${count}`).join(", ")
|
|
55148
55314
|
],
|
|
55149
|
-
["Failure", run.failureReason]
|
|
55315
|
+
["Failure", run.failureReason],
|
|
55316
|
+
...campaignPacingFields(run, options)
|
|
55150
55317
|
]);
|
|
55318
|
+
var warnCapacity = (run) => run.capacityWarning ? error(`Warning: ${run.capacityWarning}`) : void_3;
|
|
55151
55319
|
var runCreate = make29("create", {
|
|
55152
55320
|
campaignId: String10("campaignId"),
|
|
55153
55321
|
at: atFlag,
|
|
@@ -55186,7 +55354,8 @@ var runCreate = make29("create", {
|
|
|
55186
55354
|
return yield* printJson(data);
|
|
55187
55355
|
yield* logInfo(`Run scheduled.
|
|
55188
55356
|
`);
|
|
55189
|
-
yield* printRun(data);
|
|
55357
|
+
yield* printRun(data, { warning: false });
|
|
55358
|
+
yield* warnCapacity(data);
|
|
55190
55359
|
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
55191
55360
|
var runList = make29("list", {
|
|
55192
55361
|
campaignId: String10("campaignId"),
|
|
@@ -67136,6 +67305,18 @@ class WhatsAppTemplateIneligibleError extends TaggedError3()("WhatsAppTemplateIn
|
|
|
67136
67305
|
message: String4
|
|
67137
67306
|
}, { httpApiStatus: 422 }) {
|
|
67138
67307
|
}
|
|
67308
|
+
|
|
67309
|
+
class WhatsAppSetupLinkUnavailableError extends TaggedError3()("WhatsAppSetupLinkUnavailableError", {
|
|
67310
|
+
reason: Literals(["completed", "revoked", "expired"]),
|
|
67311
|
+
message: String4
|
|
67312
|
+
}, { httpApiStatus: 410 }) {
|
|
67313
|
+
static new(reason) {
|
|
67314
|
+
return new WhatsAppSetupLinkUnavailableError({
|
|
67315
|
+
reason,
|
|
67316
|
+
message: reason === "completed" ? "This setup link already connected a WhatsApp number. Ask for a new link to connect another." : reason === "revoked" ? "This setup link was revoked. Ask for a new link." : "This setup link expired. Ask for a new link."
|
|
67317
|
+
});
|
|
67318
|
+
}
|
|
67319
|
+
}
|
|
67139
67320
|
var EmailChannelErrorReason = Literals([
|
|
67140
67321
|
"transport",
|
|
67141
67322
|
"rejected",
|
|
@@ -67678,6 +67859,8 @@ var JourneyRun = /* @__PURE__ */ Struct({
|
|
|
67678
67859
|
exitReason: Union2([JourneyRunExitReason, Null2]),
|
|
67679
67860
|
failureReason: Union2([String4, Null2]),
|
|
67680
67861
|
currentNodeId: Union2([JourneyNodeId, Null2]),
|
|
67862
|
+
holdReason: Union2([String4, Null2]),
|
|
67863
|
+
holdUntil: Union2([String4, Null2]),
|
|
67681
67864
|
trigger: JourneyRunTrigger,
|
|
67682
67865
|
enteredAt: String4,
|
|
67683
67866
|
endedAt: Union2([String4, Null2])
|
|
@@ -67701,6 +67884,8 @@ var JourneyRunDetail = /* @__PURE__ */ Struct({
|
|
|
67701
67884
|
exitReason: Union2([JourneyRunExitReason, Null2]),
|
|
67702
67885
|
failureReason: Union2([String4, Null2]),
|
|
67703
67886
|
currentNodeId: Union2([JourneyNodeId, Null2]),
|
|
67887
|
+
holdReason: Union2([String4, Null2]),
|
|
67888
|
+
holdUntil: Union2([String4, Null2]),
|
|
67704
67889
|
trigger: JourneyRunTrigger,
|
|
67705
67890
|
enteredAt: String4,
|
|
67706
67891
|
endedAt: Union2([String4, Null2]),
|
|
@@ -68329,6 +68514,7 @@ var printRun2 = (run) => printFields([
|
|
|
68329
68514
|
["Exit reason", run.exitReason],
|
|
68330
68515
|
["Failure", run.failureReason],
|
|
68331
68516
|
["Current node", run.currentNodeId],
|
|
68517
|
+
...holdFields(run),
|
|
68332
68518
|
["Entered", run.enteredAt],
|
|
68333
68519
|
["Ended", run.endedAt]
|
|
68334
68520
|
]);
|
|
@@ -73851,7 +74037,8 @@ var TemplateFamilyId2 = /* @__PURE__ */ String4.check(isPattern2(/* @__PURE__ */
|
|
|
73851
74037
|
var VersionCursor2 = /* @__PURE__ */ String4.annotate({ identifier: "VersionCursor" });
|
|
73852
74038
|
var BrandFooterBlock = /* @__PURE__ */ Struct({
|
|
73853
74039
|
html: String4,
|
|
73854
|
-
text: String4
|
|
74040
|
+
text: String4,
|
|
74041
|
+
headCss: optionalKey2(Union2([String4, Null2]))
|
|
73855
74042
|
}).annotate({ identifier: "BrandFooterBlock" });
|
|
73856
74043
|
var TemplatePublicationRender = /* @__PURE__ */ Struct({
|
|
73857
74044
|
subject: String4,
|
|
@@ -73894,7 +74081,8 @@ var TemplateSummary = /* @__PURE__ */ Struct({
|
|
|
73894
74081
|
name: String4,
|
|
73895
74082
|
channel: Literal2("email"),
|
|
73896
74083
|
projectId: TemplateProjectId2,
|
|
73897
|
-
namespaceId: Union2([NamespaceId2, Null2]).annotate({ description: "The namespace the template's project belongs to; every entry in a project shares it. Null at organization level. A namespace-scoped key reaches only its own namespace's templates." }),
|
|
74084
|
+
namespaceId: Union2([NamespaceId2, Null2]).annotate({ description: "The namespace the template's project belongs to; every entry in a project shares it. Null at organization level. A namespace-scoped key reaches only its own namespace's templates and shared ones." }),
|
|
74085
|
+
shared: Boolean3.annotate({ description: "Whether the template's project is shared with every namespace. Every entry in a project shares it. A namespace-scoped key reads and sends a shared template in its own namespace but cannot change it. Only an organization-level project is shared." }),
|
|
73898
74086
|
entryPath: String4.check(isMaxCodePoints(1024).annotate({ expected: "a string with at most 1024 code points" })).check(isPattern2(/* @__PURE__ */ new RegExp("^(?!\\/)(?!.*\\\\)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)\\.git(?:\\/|$))(?:templates|emails)\\/.+\\.tsx$", "u")).annotate({ expected: "a string matching the RegExp ^(?!\\/)(?!.*\\\\)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)\\.git(?:\\/|$))(?:templates|emails)\\/.+\\.tsx$" })).check(isMaxLength(1024).annotate({ expected: "a value with a length of at most 1024" })),
|
|
73899
74087
|
status: Union2([
|
|
73900
74088
|
Literal2("draft"),
|
|
@@ -73919,7 +74107,8 @@ var Template = /* @__PURE__ */ Struct({
|
|
|
73919
74107
|
name: String4,
|
|
73920
74108
|
channel: Literal2("email"),
|
|
73921
74109
|
projectId: TemplateProjectId2,
|
|
73922
|
-
namespaceId: Union2([NamespaceId2, Null2]).annotate({ description: "The namespace the template's project belongs to; every entry in a project shares it. Null at organization level. A namespace-scoped key reaches only its own namespace's templates." }),
|
|
74110
|
+
namespaceId: Union2([NamespaceId2, Null2]).annotate({ description: "The namespace the template's project belongs to; every entry in a project shares it. Null at organization level. A namespace-scoped key reaches only its own namespace's templates and shared ones." }),
|
|
74111
|
+
shared: Boolean3.annotate({ description: "Whether the template's project is shared with every namespace. Every entry in a project shares it. A namespace-scoped key reads and sends a shared template in its own namespace but cannot change it. Only an organization-level project is shared." }),
|
|
73923
74112
|
entryPath: String4.check(isMaxCodePoints(1024).annotate({ expected: "a string with at most 1024 code points" })).check(isPattern2(/* @__PURE__ */ new RegExp("^(?!\\/)(?!.*\\\\)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)\\.git(?:\\/|$))(?:templates|emails)\\/.+\\.tsx$", "u")).annotate({ expected: "a string matching the RegExp ^(?!\\/)(?!.*\\\\)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)\\.git(?:\\/|$))(?:templates|emails)\\/.+\\.tsx$" })).check(isMaxLength(1024).annotate({ expected: "a value with a length of at most 1024" })),
|
|
73924
74113
|
status: Union2([
|
|
73925
74114
|
Literal2("draft"),
|
|
@@ -73977,6 +74166,14 @@ var TemplatePage = /* @__PURE__ */ Struct({
|
|
|
73977
74166
|
items: ArraySchema(TemplateSummary),
|
|
73978
74167
|
nextCursor: Union2([String4, Null2])
|
|
73979
74168
|
}).annotate({ identifier: "TemplatePage" });
|
|
74169
|
+
var TemplateFromWebsite = /* @__PURE__ */ Struct({
|
|
74170
|
+
template: Template,
|
|
74171
|
+
website: Struct({
|
|
74172
|
+
url: String4.annotate({ description: "The address Samva checked, normalized." }),
|
|
74173
|
+
host: String4
|
|
74174
|
+
}),
|
|
74175
|
+
ask: String4.annotate({ description: "The request to send the template assistant in the editor. The assistant reads the site, rebuilds the project's theme and blocks to match it, and proposes a brand that changes nothing until the customer applies it." })
|
|
74176
|
+
}).annotate({ identifier: "TemplateFromWebsite" });
|
|
73980
74177
|
var TemplateFamilyPage = /* @__PURE__ */ Struct({
|
|
73981
74178
|
items: ArraySchema(TemplateFamily),
|
|
73982
74179
|
nextCursor: Union2([String4, Null2])
|
|
@@ -74004,7 +74201,8 @@ var TemplatesListQuery = /* @__PURE__ */ Struct({
|
|
|
74004
74201
|
]), Null2])),
|
|
74005
74202
|
search: optionalKey2(Union2([String4, Null2])),
|
|
74006
74203
|
projectId: optionalKey2(Union2([TemplateProjectId2, Null2])),
|
|
74007
|
-
namespaceId: optionalKey2(Union2([NamespaceId2, Null2]).annotate({ description: "Only templates
|
|
74204
|
+
namespaceId: optionalKey2(Union2([NamespaceId2, Null2]).annotate({ description: "Only the templates this namespace uses: its own and those shared with every namespace (add `shared=false` for its own only). A namespace-scoped key lists its own namespace's and shared templates." })),
|
|
74205
|
+
shared: optionalKey2(Union2([Union2([Literal2("true"), Literal2("false")]), Null2]).annotate({ description: "Only templates whose project is shared with every namespace (true), or only unshared ones (false)." }))
|
|
74008
74206
|
});
|
|
74009
74207
|
var TemplatesList200 = TemplatePage;
|
|
74010
74208
|
var TemplatesList401 = UnauthorizedError2;
|
|
@@ -74030,6 +74228,7 @@ var TemplatesCreateRequestJson = /* @__PURE__ */ Struct({
|
|
|
74030
74228
|
]), Null2]), Null2])),
|
|
74031
74229
|
isActive: optionalKey2(Union2([Boolean3, Null2])),
|
|
74032
74230
|
namespaceId: optionalKey2(Union2([NamespaceId2, Null2]).annotate({ description: "The namespace the new template project belongs to; the project and every entry added to it share it. Omit for organization level. A namespace-scoped key always creates in its own namespace." })),
|
|
74231
|
+
shared: optionalKey2(Union2([Boolean3, Null2]).annotate({ description: "Share the new organization-level project with every namespace: namespace-scoped keys read and send its entries but cannot change them. Defaults to false. Not with `namespaceId`, and not from a namespace-scoped key." })),
|
|
74033
74232
|
idempotencyKey: String4.check(isMinLength(1).annotate({ expected: "a value with a length of at least 1" })).check(isMaxCodePoints(255).annotate({ expected: "a string with at most 255 code points" })).check(isMaxLength(255).annotate({ expected: "a value with a length of at most 255" }))
|
|
74034
74233
|
});
|
|
74035
74234
|
var TemplatesCreate201 = Template;
|
|
@@ -74073,7 +74272,8 @@ var TemplatesUpdateRequestJson = /* @__PURE__ */ Struct({ data: Struct({
|
|
|
74073
74272
|
]), Null2]), Null2])),
|
|
74074
74273
|
fromDefault: optionalKey2(Union2([Union2([TemplateFromDefault, Null2]), Null2])),
|
|
74075
74274
|
replyToDefault: optionalKey2(Union2([Union2([ArraySchema(String4), Null2]), Null2])),
|
|
74076
|
-
isActive: optionalKey2(Union2([Boolean3, Null2]))
|
|
74275
|
+
isActive: optionalKey2(Union2([Boolean3, Null2])),
|
|
74276
|
+
shared: optionalKey2(Union2([Boolean3, Null2]).annotate({ description: "Share the template's project with every namespace, or stop sharing it. Applies to every entry in the project. Only an organization-level project is shared." }))
|
|
74077
74277
|
}) });
|
|
74078
74278
|
var TemplatesUpdate200 = Template;
|
|
74079
74279
|
var TemplatesUpdate401 = UnauthorizedError2;
|
|
@@ -75621,6 +75821,20 @@ var TemplatesCreateFamilyFromTemplate409 = ConflictError2;
|
|
|
75621
75821
|
var TemplatesCreateFamilyFromTemplate422 = ValidationError2;
|
|
75622
75822
|
var TemplatesCreateFamilyFromTemplate500 = InternalError2;
|
|
75623
75823
|
var TemplatesCreateFamilyFromTemplate504 = GatewayTimeoutError2;
|
|
75824
|
+
var TemplatesCreateFromWebsiteRequestJson = /* @__PURE__ */ Struct({
|
|
75825
|
+
url: String4.annotate({ description: "The website whose look the template should match: a public https address, or a bare domain such as `example.com`." }).check(isMinLength(1).annotate({ expected: "a value with a length of at least 1" })).check(isMaxCodePoints(2048).annotate({ expected: "a string with at most 2048 code points" })).check(isMaxLength(2048).annotate({ expected: "a value with a length of at most 2048" })),
|
|
75826
|
+
name: optionalKey2(Union2([String4.check(isMinLength(1).annotate({ expected: "a value with a length of at least 1" })).check(isMaxCodePoints(200).annotate({ expected: "a string with at most 200 code points" })).check(isMaxLength(200).annotate({ expected: "a value with a length of at most 200" })), Null2])),
|
|
75827
|
+
namespaceId: optionalKey2(Union2([NamespaceId2, Null2]).annotate({ description: "The namespace the new template project belongs to. Omit for organization level. A namespace-scoped key always creates in its own namespace." })),
|
|
75828
|
+
idempotencyKey: String4.annotate({ description: "Repeating a request with the same key, address, name, and namespace returns the project the first one created, even if the site has since stopped answering. The same key with another address, name, or namespace is refused with a 409." }).check(isMinLength(1).annotate({ expected: "a value with a length of at least 1" })).check(isMaxCodePoints(255).annotate({ expected: "a string with at most 255 code points" })).check(isMaxLength(255).annotate({ expected: "a value with a length of at most 255" }))
|
|
75829
|
+
});
|
|
75830
|
+
var TemplatesCreateFromWebsite201 = TemplateFromWebsite;
|
|
75831
|
+
var TemplatesCreateFromWebsite401 = UnauthorizedError2;
|
|
75832
|
+
var TemplatesCreateFromWebsite403 = ForbiddenError2;
|
|
75833
|
+
var TemplatesCreateFromWebsite404 = ResourceNotFoundError2;
|
|
75834
|
+
var TemplatesCreateFromWebsite409 = ConflictError2;
|
|
75835
|
+
var TemplatesCreateFromWebsite422 = ValidationError2;
|
|
75836
|
+
var TemplatesCreateFromWebsite500 = InternalError2;
|
|
75837
|
+
var TemplatesCreateFromWebsite504 = GatewayTimeoutError2;
|
|
75624
75838
|
var TemplatesAddEntryPathParams = /* @__PURE__ */ Struct({ id: TemplateProjectId2 });
|
|
75625
75839
|
var TemplatesAddEntryRequestJson = /* @__PURE__ */ Struct({
|
|
75626
75840
|
entryPath: String4.check(isMaxCodePoints(1024).annotate({ expected: "a string with at most 1024 code points" })).check(isPattern2(/* @__PURE__ */ new RegExp("^(?!\\/)(?!.*\\\\)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)\\.git(?:\\/|$))(?:templates|emails)\\/.+\\.tsx$", "u")).annotate({ expected: "a string matching the RegExp ^(?!\\/)(?!.*\\\\)(?!.*(?:^|\\/)\\.{1,2}(?:\\/|$))(?!.*(?:^|\\/)\\.git(?:\\/|$))(?:templates|emails)\\/.+\\.tsx$" })).check(isMaxLength(1024).annotate({ expected: "a value with a length of at most 1024" })),
|
|
@@ -76033,7 +76247,19 @@ var TemplatesGroup = class extends make37("Templates").add(get5("templatesList",
|
|
|
76033
76247
|
TemplatesCreateFamilyFromTemplate500,
|
|
76034
76248
|
TemplatesCreateFamilyFromTemplate504.pipe(status(504))
|
|
76035
76249
|
]
|
|
76036
|
-
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "templates.createFamilyFromTemplate").annotate(Summary, "Create a family from a template").annotate(Description, "Creates a template family containing the specified template and returns the new family with status 201."), post("
|
|
76250
|
+
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "templates.createFamilyFromTemplate").annotate(Summary, "Create a family from a template").annotate(Description, "Creates a template family containing the specified template and returns the new family with status 201."), post("templatesCreateFromWebsite", "/v1/templates/from-website", {
|
|
76251
|
+
payload: TemplatesCreateFromWebsiteRequestJson,
|
|
76252
|
+
success: TemplatesCreateFromWebsite201.pipe(status(201)),
|
|
76253
|
+
error: [
|
|
76254
|
+
TemplatesCreateFromWebsite401.pipe(status(401)),
|
|
76255
|
+
TemplatesCreateFromWebsite403.pipe(status(403)),
|
|
76256
|
+
TemplatesCreateFromWebsite404.pipe(status(404)),
|
|
76257
|
+
TemplatesCreateFromWebsite409.pipe(status(409)),
|
|
76258
|
+
TemplatesCreateFromWebsite422.pipe(status(422)),
|
|
76259
|
+
TemplatesCreateFromWebsite500,
|
|
76260
|
+
TemplatesCreateFromWebsite504.pipe(status(504))
|
|
76261
|
+
]
|
|
76262
|
+
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "templates.createFromWebsite").annotate(Summary, "Create a template from a website").annotate(Description, "Checks that the website is a public page Samva can read, creates an email template project from Samva's canonical TSX starter, and returns the template with status 201 and the request that makes the template assistant match the site. The assistant runs when the request is sent in the editor; it rebuilds the project's theme and blocks from the site and proposes a brand that applies only after the customer approves it. A 422 names why the site cannot be matched: not a web address, not public, no answer, a missing page, refused automated visits, or not a web page."), post("templatesAddEntry", "/v1/templates/projects/:id/entries", {
|
|
76037
76263
|
params: TemplatesAddEntryPathParams,
|
|
76038
76264
|
payload: TemplatesAddEntryRequestJson,
|
|
76039
76265
|
success: TemplatesAddEntry201.pipe(status(201)),
|
|
@@ -76053,6 +76279,7 @@ var TemplatesGroup = class extends make37("Templates").add(get5("templatesList",
|
|
|
76053
76279
|
var make54 = /* @__PURE__ */ forGroup(TemplatesGroup);
|
|
76054
76280
|
var list26 = /* @__PURE__ */ make54("templatesList");
|
|
76055
76281
|
var create15 = /* @__PURE__ */ make54("templatesCreate");
|
|
76282
|
+
var createFromWebsite = /* @__PURE__ */ make54("templatesCreateFromWebsite");
|
|
76056
76283
|
var addEntry = /* @__PURE__ */ make54("templatesAddEntry");
|
|
76057
76284
|
var getById = /* @__PURE__ */ make54("templatesGetById");
|
|
76058
76285
|
var remove13 = /* @__PURE__ */ make54("templatesRemove");
|
|
@@ -76652,6 +76879,66 @@ var formatReportedDiagnostic = (diagnostic) => {
|
|
|
76652
76879
|
Upgrade: ${diagnostic.upgrade}`;
|
|
76653
76880
|
};
|
|
76654
76881
|
|
|
76882
|
+
// src/commands/templates/from-website-command.ts
|
|
76883
|
+
var webOriginForApi = (apiUrl) => {
|
|
76884
|
+
if (!URL.canParse(apiUrl))
|
|
76885
|
+
return null;
|
|
76886
|
+
const url = new URL(apiUrl);
|
|
76887
|
+
if (!url.hostname.startsWith("api."))
|
|
76888
|
+
return null;
|
|
76889
|
+
url.hostname = url.hostname.slice("api.".length);
|
|
76890
|
+
return url.origin;
|
|
76891
|
+
};
|
|
76892
|
+
var editorPath = (orgSlug, templateId, ask) => `/dashboard/${encodeURIComponent(orgSlug)}/templates/${encodeURIComponent(templateId)}/edit?ask=${encodeURIComponent(ask)}`;
|
|
76893
|
+
var urlArgument = String10("url").pipe(withDescription4("The website to match: a public https address or a bare domain"));
|
|
76894
|
+
var fromWebsiteCommand = make29("from-website", {
|
|
76895
|
+
url: urlArgument,
|
|
76896
|
+
name: String9("name").pipe(withDescription2("Name for the new template; defaults to one derived from the site"), optional4),
|
|
76897
|
+
namespace: namespaceFlag("create"),
|
|
76898
|
+
idempotencyKey: String9("idempotency-key").pipe(withDescription2("Key (1-255 characters) so a retry returns the original template; generated per run when omitted"), optional4),
|
|
76899
|
+
json: jsonFlag
|
|
76900
|
+
}).pipe(withDescription3("Create a template project from a website, then open the editor to have the assistant match its brand"), withExamples([
|
|
76901
|
+
{
|
|
76902
|
+
command: "samva templates from-website example.com",
|
|
76903
|
+
description: "Check the site, create the project, and print the editor link"
|
|
76904
|
+
},
|
|
76905
|
+
{
|
|
76906
|
+
command: 'samva templates from-website https://example.com --name "Example welcome" --json',
|
|
76907
|
+
description: "Machine-readable result with the template, website, ask, and editor link"
|
|
76908
|
+
}
|
|
76909
|
+
]), withHandler(({ url, name, namespace, idempotencyKey, json }) => gen2(function* () {
|
|
76910
|
+
const key = getOrUndefined(idempotencyKey);
|
|
76911
|
+
if (key !== undefined && (key.length < 1 || key.length > 255))
|
|
76912
|
+
return yield* new SamvaCliError({
|
|
76913
|
+
message: "--idempotency-key must contain between 1 and 255 characters."
|
|
76914
|
+
});
|
|
76915
|
+
const client = yield* getClientProvider;
|
|
76916
|
+
const context = yield* resolveExecutionContext;
|
|
76917
|
+
const orgSlug = context.apiKey ? (yield* get25().pipe(client.provide, mapError2((error) => toCliApiError("Could not look up the organization", error)))).organization.slug : context.orgSlug;
|
|
76918
|
+
const result = yield* createFromWebsite({
|
|
76919
|
+
url,
|
|
76920
|
+
...isSome2(name) ? { name: name.value } : {},
|
|
76921
|
+
...namespaceField(namespace),
|
|
76922
|
+
idempotencyKey: key ?? crypto.randomUUID()
|
|
76923
|
+
}).pipe(client.provide, mapError2((error) => toCliApiError("Could not create a template from the website", error)));
|
|
76924
|
+
const path = orgSlug === undefined ? null : editorPath(orgSlug, result.template.id, result.ask);
|
|
76925
|
+
const origin = webOriginForApi(context.apiUrl);
|
|
76926
|
+
const editorUrl = path !== null && origin !== null ? `${origin}${path}` : null;
|
|
76927
|
+
if (json)
|
|
76928
|
+
return yield* printJson({
|
|
76929
|
+
template: { id: result.template.id, name: result.template.name },
|
|
76930
|
+
website: result.website,
|
|
76931
|
+
ask: result.ask,
|
|
76932
|
+
editorUrl
|
|
76933
|
+
});
|
|
76934
|
+
yield* printFields([
|
|
76935
|
+
["Template", result.template.id],
|
|
76936
|
+
["Website", result.website.host],
|
|
76937
|
+
...editorUrl !== null ? [["Editor", editorUrl]] : [["Ask", result.ask]]
|
|
76938
|
+
]);
|
|
76939
|
+
yield* log(editorUrl !== null ? "The brand proposal applies only when you approve it in the editor." : "Open the template in the dashboard editor and send the ask above; the brand proposal applies only when you approve it there.");
|
|
76940
|
+
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
76941
|
+
|
|
76655
76942
|
// src/commands/templates/local-build.ts
|
|
76656
76943
|
import { resolve as resolve9 } from "path";
|
|
76657
76944
|
var buildDir = (dir, assetBase) => runToolchain({
|
|
@@ -78776,13 +79063,18 @@ var build3 = make29("build", {
|
|
|
78776
79063
|
yield* logInfo(`Built ${built.files.length} template file(s) with compiler ${artifact.compilerVersion} to ${target}`);
|
|
78777
79064
|
}
|
|
78778
79065
|
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
79066
|
+
var sharedFilterFlag = Literals3("shared", ["true", "false"]).pipe(withDescription2("Only templates shared with every namespace (true), or only unshared ones (false)"), optional4);
|
|
78779
79067
|
var listRemote = make29("list", {
|
|
78780
79068
|
cursor: String9("cursor").pipe(withDescription2("Resume after this nextCursor from a previous page"), optional4),
|
|
78781
79069
|
limit: Int5("limit").pipe(withDescription2("Items per page"), optional4),
|
|
78782
79070
|
namespace: namespaceFlag("list"),
|
|
79071
|
+
shared: sharedFilterFlag,
|
|
78783
79072
|
all: Boolean6("all").pipe(withDescription2("Traverse every page (requires --jsonl)"), withDefault5(false)),
|
|
78784
79073
|
json: jsonFlag
|
|
78785
|
-
}).pipe(withDescription3("List remote templates"), withExamples([
|
|
79074
|
+
}).pipe(withDescription3("List remote templates"), withExamples([
|
|
79075
|
+
{ command: "samva templates list --json" },
|
|
79076
|
+
{ command: "samva templates list --shared true --json" }
|
|
79077
|
+
]), withHandler(({ cursor, limit, namespace, shared, all, json }) => gen2(function* () {
|
|
78786
79078
|
if (all && !isJsonLinesRequested())
|
|
78787
79079
|
return yield* new CliUsageError({
|
|
78788
79080
|
code: "all_requires_jsonl",
|
|
@@ -78794,7 +79086,8 @@ var listRemote = make29("list", {
|
|
|
78794
79086
|
const query = {
|
|
78795
79087
|
cursor: cursorValue,
|
|
78796
79088
|
limit: limitValue?.toString(),
|
|
78797
|
-
...namespaceField(namespace)
|
|
79089
|
+
...namespaceField(namespace),
|
|
79090
|
+
...match(shared, { onNone: () => ({}), onSome: (value) => ({ shared: value }) })
|
|
78798
79091
|
};
|
|
78799
79092
|
if (all) {
|
|
78800
79093
|
let nextCursor = cursorValue;
|
|
@@ -78821,7 +79114,8 @@ var listRemote = make29("list", {
|
|
|
78821
79114
|
["Name", item.name],
|
|
78822
79115
|
["Slug", item.slug],
|
|
78823
79116
|
["Status", item.status],
|
|
78824
|
-
["Namespace", item.namespaceId ?? "organization"]
|
|
79117
|
+
["Namespace", item.namespaceId ?? "organization"],
|
|
79118
|
+
["Shared", item.shared ? "yes" : "no"]
|
|
78825
79119
|
]);
|
|
78826
79120
|
if (data.nextCursor !== null)
|
|
78827
79121
|
yield* printFields([["Next cursor", data.nextCursor]]);
|
|
@@ -78836,20 +79130,35 @@ var getRemote = make29("get", { id: templateIdArgument2, json: jsonFlag }).pipe(
|
|
|
78836
79130
|
var createRemote = make29("create", {
|
|
78837
79131
|
data: String9("data").pipe(withDescription2("Complete template JSON payload")),
|
|
78838
79132
|
namespace: namespaceFlag("create"),
|
|
79133
|
+
shared: Boolean6("shared").pipe(withDescription2("Share the new organization-level project with every namespace; namespace-scoped keys read and send it but cannot change it"), withDefault5(false)),
|
|
78839
79134
|
dryRun: dryRunFlag6,
|
|
78840
79135
|
json: jsonFlag
|
|
78841
79136
|
}).pipe(withDescription3("Create a remote template from JSON"), withExamples([
|
|
78842
79137
|
{
|
|
78843
79138
|
command: `samva templates create --data '{"name":"Welcome","projectName":"welcome-email","idempotencyKey":"welcome-email-v1"}' --dry-run`
|
|
78844
79139
|
}
|
|
78845
|
-
]), withHandler(({ data, namespace, dryRun, json }) => gen2(function* () {
|
|
79140
|
+
]), withHandler(({ data, namespace, shared, dryRun, json }) => gen2(function* () {
|
|
78846
79141
|
const decoded = yield* decodePayload(data, "--data", CreateTemplatePayload);
|
|
79142
|
+
if (shared && decoded.shared === false)
|
|
79143
|
+
return yield* new CliUsageError({
|
|
79144
|
+
code: "shared_conflict",
|
|
79145
|
+
message: "--shared and shared: false in --data disagree."
|
|
79146
|
+
});
|
|
79147
|
+
if (shared && (isSome2(namespace) || decoded.namespaceId != null))
|
|
79148
|
+
return yield* new CliUsageError({
|
|
79149
|
+
code: "shared_namespace_conflict",
|
|
79150
|
+
message: "--shared shares an organization-level project; it cannot have a namespace."
|
|
79151
|
+
});
|
|
78847
79152
|
if (isSome2(namespace) && decoded.namespaceId != null && decoded.namespaceId !== namespace.value)
|
|
78848
79153
|
return yield* new CliUsageError({
|
|
78849
79154
|
code: "namespace_conflict",
|
|
78850
79155
|
message: "--namespace and the namespaceId in --data name different namespaces."
|
|
78851
79156
|
});
|
|
78852
|
-
const payload = {
|
|
79157
|
+
const payload = {
|
|
79158
|
+
...decoded,
|
|
79159
|
+
...namespaceField(namespace),
|
|
79160
|
+
...shared ? { shared: true } : {}
|
|
79161
|
+
};
|
|
78853
79162
|
if (dryRun)
|
|
78854
79163
|
return yield* json ? printJson({ dryRun: true, action: "templates.create", request: payload }) : printFields([
|
|
78855
79164
|
["Action", "templates.create"],
|
|
@@ -78864,14 +79173,22 @@ var createRemote = make29("create", {
|
|
|
78864
79173
|
var updateRemote = make29("update", {
|
|
78865
79174
|
id: templateIdArgument2,
|
|
78866
79175
|
data: String9("data").pipe(withDescription2("JSON object of template fields to update")),
|
|
79176
|
+
shared: Literals3("shared", ["true", "false"]).pipe(withDescription2("Share the template's organization-level project with every namespace (true) or stop sharing it (false); applies to every entry in the project"), optional4),
|
|
78867
79177
|
dryRun: dryRunFlag6,
|
|
78868
79178
|
json: jsonFlag
|
|
78869
79179
|
}).pipe(withDescription3("Update a remote template from JSON"), withExamples([
|
|
78870
79180
|
{
|
|
78871
79181
|
command: `samva templates update tmpl_abc123 --data '{"name":"Welcome back"}' --dry-run`
|
|
78872
79182
|
}
|
|
78873
|
-
]), withHandler(({ id, data, dryRun, json }) => gen2(function* () {
|
|
78874
|
-
const
|
|
79183
|
+
]), withHandler(({ id, data, shared, dryRun, json }) => gen2(function* () {
|
|
79184
|
+
const parsed = yield* parseObject(data, "--data");
|
|
79185
|
+
const sharedValue = map(shared, (value) => value === "true");
|
|
79186
|
+
if (isSome2(sharedValue) && parsed.shared !== undefined && parsed.shared !== sharedValue.value)
|
|
79187
|
+
return yield* new CliUsageError({
|
|
79188
|
+
code: "shared_conflict",
|
|
79189
|
+
message: "--shared and shared in --data disagree."
|
|
79190
|
+
});
|
|
79191
|
+
const changes = isSome2(sharedValue) ? { ...parsed, shared: sharedValue.value } : parsed;
|
|
78875
79192
|
const payload = yield* decodePayload(JSON.stringify({ data: changes }), "--data", UpdateTemplatePayload);
|
|
78876
79193
|
if (dryRun)
|
|
78877
79194
|
return yield* json ? printJson({ dryRun: true, action: "templates.update", id, request: payload }) : printFields([
|
|
@@ -78983,6 +79300,10 @@ var templatesCommand = make29("templates").pipe(withDescription3("Create, author
|
|
|
78983
79300
|
command: "samva templates clone tmpl_abc123 --dir welcome-email",
|
|
78984
79301
|
description: "Clone an existing project with its repository credential helper"
|
|
78985
79302
|
},
|
|
79303
|
+
{
|
|
79304
|
+
command: "samva templates from-website example.com",
|
|
79305
|
+
description: "Create a project from a website and print the editor link that matches its brand"
|
|
79306
|
+
},
|
|
78986
79307
|
{
|
|
78987
79308
|
command: "samva templates dev",
|
|
78988
79309
|
description: "Start the local visual editor for templates/ and emails/"
|
|
@@ -79023,6 +79344,7 @@ var templatesCommand = make29("templates").pipe(withDescription3("Create, author
|
|
|
79023
79344
|
createRemote,
|
|
79024
79345
|
updateRemote,
|
|
79025
79346
|
removeRemote,
|
|
79347
|
+
fromWebsiteCommand,
|
|
79026
79348
|
devCommand,
|
|
79027
79349
|
check5,
|
|
79028
79350
|
renderCommand,
|
|
@@ -79060,6 +79382,11 @@ var WebhookDailyDeliveries = /* @__PURE__ */ Struct({
|
|
|
79060
79382
|
exhausted: Finite.check(isInt().annotate({ expected: "an integer" })).check(isGreaterThanOrEqualTo2(0).annotate({ expected: "a value greater than or equal to 0" })),
|
|
79061
79383
|
cancelled: Finite.check(isInt().annotate({ expected: "an integer" })).check(isGreaterThanOrEqualTo2(0).annotate({ expected: "a value greater than or equal to 0" }))
|
|
79062
79384
|
}).annotate({ identifier: "WebhookDailyDeliveries" });
|
|
79385
|
+
var WebhookSigningSecret = /* @__PURE__ */ Struct({
|
|
79386
|
+
secret: SensitiveString,
|
|
79387
|
+
createdAt: String4,
|
|
79388
|
+
previousExpiresAt: Union2([String4, Null2])
|
|
79389
|
+
}).annotate({ identifier: "WebhookSigningSecret" });
|
|
79063
79390
|
var WebhookEndpoint = /* @__PURE__ */ Struct({
|
|
79064
79391
|
id: WebhookEndpointId2,
|
|
79065
79392
|
name: String4,
|
|
@@ -79090,6 +79417,9 @@ var WebhookEndpoint = /* @__PURE__ */ Struct({
|
|
|
79090
79417
|
Literal2("webhook.test"),
|
|
79091
79418
|
Literal2("domain.updated"),
|
|
79092
79419
|
Literal2("domain.warming.updated"),
|
|
79420
|
+
Literal2("template.registration.updated"),
|
|
79421
|
+
Literal2("flow.registration.updated"),
|
|
79422
|
+
Literal2("whatsapp.number.connected"),
|
|
79093
79423
|
Literal2("mailbox.message.received"),
|
|
79094
79424
|
Literal2("mailbox.message.sent"),
|
|
79095
79425
|
Literal2("mailbox.action.pending_approval"),
|
|
@@ -79139,6 +79469,9 @@ var WebhookEndpointListItem = /* @__PURE__ */ Struct({
|
|
|
79139
79469
|
Literal2("webhook.test"),
|
|
79140
79470
|
Literal2("domain.updated"),
|
|
79141
79471
|
Literal2("domain.warming.updated"),
|
|
79472
|
+
Literal2("template.registration.updated"),
|
|
79473
|
+
Literal2("flow.registration.updated"),
|
|
79474
|
+
Literal2("whatsapp.number.connected"),
|
|
79142
79475
|
Literal2("mailbox.message.received"),
|
|
79143
79476
|
Literal2("mailbox.message.sent"),
|
|
79144
79477
|
Literal2("mailbox.action.pending_approval"),
|
|
@@ -79253,6 +79586,9 @@ var WebhooksCreateRequestJson = /* @__PURE__ */ Struct({
|
|
|
79253
79586
|
Literal2("webhook.test"),
|
|
79254
79587
|
Literal2("domain.updated"),
|
|
79255
79588
|
Literal2("domain.warming.updated"),
|
|
79589
|
+
Literal2("template.registration.updated"),
|
|
79590
|
+
Literal2("flow.registration.updated"),
|
|
79591
|
+
Literal2("whatsapp.number.connected"),
|
|
79256
79592
|
Literal2("mailbox.message.received"),
|
|
79257
79593
|
Literal2("mailbox.message.sent"),
|
|
79258
79594
|
Literal2("mailbox.action.pending_approval"),
|
|
@@ -79328,6 +79664,9 @@ var WebhooksUpdateRequestJson = /* @__PURE__ */ Struct({
|
|
|
79328
79664
|
Literal2("webhook.test"),
|
|
79329
79665
|
Literal2("domain.updated"),
|
|
79330
79666
|
Literal2("domain.warming.updated"),
|
|
79667
|
+
Literal2("template.registration.updated"),
|
|
79668
|
+
Literal2("flow.registration.updated"),
|
|
79669
|
+
Literal2("whatsapp.number.connected"),
|
|
79331
79670
|
Literal2("mailbox.message.received"),
|
|
79332
79671
|
Literal2("mailbox.message.sent"),
|
|
79333
79672
|
Literal2("mailbox.action.pending_approval"),
|
|
@@ -79388,6 +79727,14 @@ var WebhooksRegenerateSecret404 = ResourceNotFoundError2;
|
|
|
79388
79727
|
var WebhooksRegenerateSecret422 = ValidationError2;
|
|
79389
79728
|
var WebhooksRegenerateSecret500 = InternalError2;
|
|
79390
79729
|
var WebhooksRegenerateSecret504 = GatewayTimeoutError2;
|
|
79730
|
+
var WebhooksGetSecretPathParams = /* @__PURE__ */ Struct({ id: WebhookEndpointId2 });
|
|
79731
|
+
var WebhooksGetSecret200 = WebhookSigningSecret;
|
|
79732
|
+
var WebhooksGetSecret401 = UnauthorizedError2;
|
|
79733
|
+
var WebhooksGetSecret403 = ForbiddenError2;
|
|
79734
|
+
var WebhooksGetSecret404 = ResourceNotFoundError2;
|
|
79735
|
+
var WebhooksGetSecret422 = ValidationError2;
|
|
79736
|
+
var WebhooksGetSecret500 = InternalError2;
|
|
79737
|
+
var WebhooksGetSecret504 = GatewayTimeoutError2;
|
|
79391
79738
|
var WebhooksGetStatsPathParams = /* @__PURE__ */ Struct({ id: WebhookEndpointId2 });
|
|
79392
79739
|
var WebhooksGetStatsQuery = /* @__PURE__ */ Struct({
|
|
79393
79740
|
after: optionalKey2(Union2([String4, Null2])),
|
|
@@ -79437,7 +79784,7 @@ var WebhooksGroup = class extends make37("Webhooks").add(get5("webhooksList", "/
|
|
|
79437
79784
|
WebhooksCreate502.pipe(status(502)),
|
|
79438
79785
|
WebhooksCreate504.pipe(status(504))
|
|
79439
79786
|
]
|
|
79440
|
-
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "webhooks.create").annotate(Summary, "Create a webhook").annotate(Description, "Creates a signed webhook endpoint for Samva delivery and inbound events, stores subscriptions, and returns the endpoint and signing secret with a 201 response. `
|
|
79787
|
+
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "webhooks.create").annotate(Summary, "Create a webhook").annotate(Description, "Creates a signed webhook endpoint for Samva delivery and inbound events, stores subscriptions, and returns the endpoint and signing secret with a 201 response. `namespaceId` confines the endpoint to one namespace's events and `mailboxIds` to those mailboxes' events; when both are set, every mailbox must belong to the namespace."), get5("webhooksGet", "/v1/webhooks/:id", {
|
|
79441
79788
|
params: WebhooksGetPathParams,
|
|
79442
79789
|
success: WebhooksGet200,
|
|
79443
79790
|
error: [
|
|
@@ -79508,7 +79855,18 @@ var WebhooksGroup = class extends make37("Webhooks").add(get5("webhooksList", "/
|
|
|
79508
79855
|
WebhooksRegenerateSecret500,
|
|
79509
79856
|
WebhooksRegenerateSecret504.pipe(status(504))
|
|
79510
79857
|
]
|
|
79511
|
-
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "webhooks.regenerateSecret").annotate(Summary, "Rotate a webhook signing secret").annotate(Description, "Generates and returns a new signing secret. The previous secret remains valid for a 24-hour rotation overlap."), get5("
|
|
79858
|
+
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "webhooks.regenerateSecret").annotate(Summary, "Rotate a webhook signing secret").annotate(Description, "Generates and returns a new signing secret. The previous secret remains valid for a 24-hour rotation overlap."), get5("webhooksGetSecret", "/v1/webhooks/:id/secret", {
|
|
79859
|
+
params: WebhooksGetSecretPathParams,
|
|
79860
|
+
success: WebhooksGetSecret200,
|
|
79861
|
+
error: [
|
|
79862
|
+
WebhooksGetSecret401.pipe(status(401)),
|
|
79863
|
+
WebhooksGetSecret403.pipe(status(403)),
|
|
79864
|
+
WebhooksGetSecret404.pipe(status(404)),
|
|
79865
|
+
WebhooksGetSecret422.pipe(status(422)),
|
|
79866
|
+
WebhooksGetSecret500,
|
|
79867
|
+
WebhooksGetSecret504.pipe(status(504))
|
|
79868
|
+
]
|
|
79869
|
+
}).middleware(ApiKeySecurityMiddleware).annotate(Identifier, "webhooks.getSecret").annotate(Summary, "Get a webhook signing secret").annotate(Description, "Returns the endpoint's current signing secret and when a rotated-out secret stops verifying. It needs the same permission as rotating the secret."), get5("webhooksGetStats", "/v1/webhooks/:id/stats", {
|
|
79512
79870
|
params: WebhooksGetStatsPathParams,
|
|
79513
79871
|
query: WebhooksGetStatsQuery,
|
|
79514
79872
|
success: WebhooksGetStats200,
|
|
@@ -79539,6 +79897,7 @@ var WebhooksGroup = class extends make37("Webhooks").add(get5("webhooksList", "/
|
|
|
79539
79897
|
var make55 = /* @__PURE__ */ forGroup(WebhooksGroup);
|
|
79540
79898
|
var list27 = /* @__PURE__ */ make55("webhooksList");
|
|
79541
79899
|
var create16 = /* @__PURE__ */ make55("webhooksCreate");
|
|
79900
|
+
var getSecret = /* @__PURE__ */ make55("webhooksGetSecret");
|
|
79542
79901
|
var get28 = /* @__PURE__ */ make55("webhooksGet");
|
|
79543
79902
|
var remove14 = /* @__PURE__ */ make55("webhooksRemove");
|
|
79544
79903
|
var update13 = /* @__PURE__ */ make55("webhooksUpdate");
|
|
@@ -79628,6 +79987,18 @@ var get29 = make29("get", { id: idArgument4, json: jsonFlag }).pipe(withDescript
|
|
|
79628
79987
|
["URL", data.url]
|
|
79629
79988
|
]);
|
|
79630
79989
|
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
79990
|
+
var secret = make29("secret", { id: idArgument4, json: jsonFlag }).pipe(withDescription3("Read the current webhook signing secret (requires webhooks:update)"), withExamples([{ command: "samva webhooks secret wep_abc123 --json" }]), withHandler(({ id, json }) => gen2(function* () {
|
|
79991
|
+
const client = yield* getClientProvider;
|
|
79992
|
+
const data = yield* getSecret({ id }).pipe(client.provide, mapError2((error) => toCliApiError("Could not read webhook signing secret", error)));
|
|
79993
|
+
if (json)
|
|
79994
|
+
return yield* printJson({ ...data, secret: value2(data.secret) });
|
|
79995
|
+
yield* printFields([
|
|
79996
|
+
["Signing secret", value2(data.secret)],
|
|
79997
|
+
["Created at", data.createdAt]
|
|
79998
|
+
]);
|
|
79999
|
+
if (data.previousExpiresAt !== null)
|
|
80000
|
+
yield* log(`The previous secret keeps verifying until ${data.previousExpiresAt}.`);
|
|
80001
|
+
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
79631
80002
|
var logs = make29("logs", {
|
|
79632
80003
|
id: idArgument4,
|
|
79633
80004
|
limit: Int5("limit").pipe(optional4),
|
|
@@ -79785,15 +80156,15 @@ var rotate3 = make29("rotate-secret", {
|
|
|
79785
80156
|
yield* confirmDestructive({
|
|
79786
80157
|
yes,
|
|
79787
80158
|
json,
|
|
79788
|
-
message: `Rotate the signing secret for webhook ${id}? The
|
|
80159
|
+
message: `Rotate the signing secret for webhook ${id}? The previous secret keeps verifying for 24 hours.`,
|
|
79789
80160
|
nonInteractiveMessage: "Secret rotation requires --yes in non-interactive mode. Preview with --dry-run."
|
|
79790
80161
|
});
|
|
79791
80162
|
const client = yield* getClientProvider;
|
|
79792
80163
|
const data = yield* regenerateSecret({ id }).pipe(client.provide, mapError2((error) => toCliApiError("Could not rotate webhook secret", error)));
|
|
79793
|
-
const warning =
|
|
80164
|
+
const warning = `Read this signing secret again with samva webhooks secret ${id}. The previous secret remains valid for 24 hours.`;
|
|
79794
80165
|
const secret = value2(data.secret);
|
|
79795
80166
|
if (json)
|
|
79796
|
-
return yield* printJson({ secret,
|
|
80167
|
+
return yield* printJson({ secret, warning });
|
|
79797
80168
|
yield* error(`WARNING: ${warning}`);
|
|
79798
80169
|
yield* log(secret);
|
|
79799
80170
|
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
@@ -79814,10 +80185,10 @@ var create17 = make29("create", {
|
|
|
79814
80185
|
]);
|
|
79815
80186
|
const client = yield* getClientProvider;
|
|
79816
80187
|
const result = yield* create16(payload).pipe(client.provide, mapError2((error) => toCliApiError("Could not create webhook", error)));
|
|
79817
|
-
const warning =
|
|
80188
|
+
const warning = `Read this signing secret again with samva webhooks secret ${result.endpoint.id}.`;
|
|
79818
80189
|
const secret = value2(result.secret);
|
|
79819
80190
|
if (json)
|
|
79820
|
-
return yield* printJson({ endpoint: result.endpoint, secret,
|
|
80191
|
+
return yield* printJson({ endpoint: result.endpoint, secret, warning });
|
|
79821
80192
|
yield* error(`WARNING: ${warning}`);
|
|
79822
80193
|
yield* printFields([
|
|
79823
80194
|
["ID", result.endpoint.id],
|
|
@@ -79873,7 +80244,19 @@ var remove15 = make29("remove", {
|
|
|
79873
80244
|
return yield* printJson(result);
|
|
79874
80245
|
yield* log(`Removed webhook ${id}.`);
|
|
79875
80246
|
}).pipe(json ? provideService2(MinimumLogLevel2, "None") : identity)));
|
|
79876
|
-
var webhooksCommand = make29("webhooks").pipe(withDescription3("Inspect, test, and recover customer webhooks"), withExamples([{ command: "samva webhooks list --json" }]), withSubcommands([
|
|
80247
|
+
var webhooksCommand = make29("webhooks").pipe(withDescription3("Inspect, test, and recover customer webhooks"), withExamples([{ command: "samva webhooks list --json" }]), withSubcommands([
|
|
80248
|
+
list28,
|
|
80249
|
+
get29,
|
|
80250
|
+
secret,
|
|
80251
|
+
create17,
|
|
80252
|
+
update14,
|
|
80253
|
+
test2,
|
|
80254
|
+
logs,
|
|
80255
|
+
stats3,
|
|
80256
|
+
retry4,
|
|
80257
|
+
rotate3,
|
|
80258
|
+
remove15
|
|
80259
|
+
]));
|
|
79877
80260
|
|
|
79878
80261
|
// src/lib/root-commands.ts
|
|
79879
80262
|
var rootCommands = [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@samva/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Samva CLI — email and platform operations from your terminal",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -50,22 +50,22 @@
|
|
|
50
50
|
"@effect/vitest": "4.0.0",
|
|
51
51
|
"@samva/contracts": "0.0.0",
|
|
52
52
|
"@samva/core": "0.0.0",
|
|
53
|
-
"@samva/mailbox": "0.5.
|
|
54
|
-
"@samva/markup": "
|
|
55
|
-
"@samva/vite": "
|
|
53
|
+
"@samva/mailbox": "0.5.2",
|
|
54
|
+
"@samva/markup": "0.15.0",
|
|
55
|
+
"@samva/vite": "0.15.0",
|
|
56
56
|
"@types/node": "^26.6.2",
|
|
57
57
|
"better-auth": "1.7.5",
|
|
58
58
|
"effect": "4.0.0",
|
|
59
|
-
"samva": "0.
|
|
59
|
+
"samva": "0.22.0",
|
|
60
60
|
"typescript": "^7.0.2",
|
|
61
61
|
"vite": "~8.3.0",
|
|
62
62
|
"vitest": "^5.0.3"
|
|
63
63
|
},
|
|
64
64
|
"optionalDependencies": {
|
|
65
|
-
"@samva/cli-darwin-arm64": "0.
|
|
66
|
-
"@samva/cli-darwin-x64": "0.
|
|
67
|
-
"@samva/cli-linux-arm64": "0.
|
|
68
|
-
"@samva/cli-linux-x64": "0.
|
|
65
|
+
"@samva/cli-darwin-arm64": "0.19.0",
|
|
66
|
+
"@samva/cli-darwin-x64": "0.19.0",
|
|
67
|
+
"@samva/cli-linux-arm64": "0.19.0",
|
|
68
|
+
"@samva/cli-linux-x64": "0.19.0"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">=18"
|