@samva/cli 0.6.1 → 0.7.1

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.
Files changed (3) hide show
  1. package/AGENTS.md +2 -1
  2. package/dist/bin.mjs +101 -43
  3. package/package.json +7 -7
package/AGENTS.md CHANGED
@@ -16,7 +16,8 @@ The developer loop for a template project is `samva templates dev` (the local vi
16
16
  and `snapshot <template> --fixture <name>` (desktop and mobile PNGs, light and dark, in
17
17
  `.samva/snapshots`, needing `playwright` in the project or a system Chrome). With `--json`, every
18
18
  finding carries `code`, `severity`, `message`, `file`, `line`/`column`, `range`, and, where the
19
- compiler has them, `fix` and a machine-applicable `edit`. Templates live in `templates/` and
19
+ compiler has them, `fix`, a machine-applicable `edit`, and `upgrade`, the instructions for migrating
20
+ a template written in a format the compiler rejects. Templates live in `templates/` and
20
21
  `emails/`; a template is a `.tsx` file that default-exports `defineTemplate`, and the compiler reads
21
22
  it without running it.
22
23
 
package/dist/bin.mjs CHANGED
@@ -4028,6 +4028,10 @@ var ignore = /* @__PURE__ */ dual((args) => isEffect(args[0]), (self, options) =
4028
4028
  onSuccess: (_) => void_
4029
4029
  });
4030
4030
  });
4031
+ var option = (self) => match5(self, {
4032
+ onFailure: none2,
4033
+ onSuccess: some2
4034
+ });
4031
4035
  var result = (self) => matchEager(self, {
4032
4036
  onFailure: fail2,
4033
4037
  onSuccess: succeed2
@@ -5597,6 +5601,7 @@ var flatMap3 = flatMap2;
5597
5601
  var andThen2 = andThen;
5598
5602
  var tap2 = tap;
5599
5603
  var result2 = result;
5604
+ var option2 = option;
5600
5605
  var exit2 = exit;
5601
5606
  var map6 = map4;
5602
5607
  var as2 = as;
@@ -13674,7 +13679,7 @@ var layer12 = /* @__PURE__ */ layer.pipe(/* @__PURE__ */ provideMerge(/* @__PURE
13674
13679
  // package.json
13675
13680
  var package_default = {
13676
13681
  name: "@samva/cli",
13677
- version: "0.6.1",
13682
+ version: "0.7.1",
13678
13683
  description: "Samva CLI \u2014 email and platform operations from your terminal",
13679
13684
  keywords: [
13680
13685
  "cli",
@@ -13722,8 +13727,8 @@ var package_default = {
13722
13727
  devDependencies: {
13723
13728
  "@effect/platform-bun": "catalog:effect",
13724
13729
  "@effect/vitest": "catalog:effect",
13725
- "@samva/markup": "workspace:0.11.0",
13726
- "@samva/vite": "workspace:0.11.0",
13730
+ "@samva/markup": "^0.11.4",
13731
+ "@samva/vite": "^0.11.4",
13727
13732
  "@types/node": "catalog:",
13728
13733
  "better-auth": "catalog:",
13729
13734
  effect: "catalog:effect",
@@ -13733,10 +13738,10 @@ var package_default = {
13733
13738
  vitest: "catalog:"
13734
13739
  },
13735
13740
  optionalDependencies: {
13736
- "@samva/cli-darwin-arm64": "workspace:0.6.1",
13737
- "@samva/cli-darwin-x64": "workspace:0.6.1",
13738
- "@samva/cli-linux-arm64": "workspace:0.6.1",
13739
- "@samva/cli-linux-x64": "workspace:0.6.1"
13741
+ "@samva/cli-darwin-arm64": "workspace:0.7.1",
13742
+ "@samva/cli-darwin-x64": "workspace:0.7.1",
13743
+ "@samva/cli-linux-arm64": "workspace:0.7.1",
13744
+ "@samva/cli-linux-x64": "workspace:0.7.1"
13740
13745
  },
13741
13746
  engines: {
13742
13747
  node: ">=18"
@@ -42413,7 +42418,7 @@ var $ = (e) => {
42413
42418
  };
42414
42419
  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 };
42415
42420
 
42416
- // ../markup/dist/theme-css-CEUrDpqq.js
42421
+ // ../../node_modules/.bun/@samva+markup@0.11.4+a19bcd7db259aaf7/node_modules/@samva/markup/dist/theme-css-CEUrDpqq.js
42417
42422
  var BRAND_NAMESPACES = [
42418
42423
  "--color-",
42419
42424
  "--font-",
@@ -42606,7 +42611,7 @@ var parseBrandCss = (css) => {
42606
42611
  fontFaces
42607
42612
  });
42608
42613
  };
42609
- // ../markup/dist/diagnostic-model-DJtqbETd.js
42614
+ // ../../node_modules/.bun/@samva+markup@0.11.4+a19bcd7db259aaf7/node_modules/@samva/markup/dist/diagnostic-codes-BduqCv_h.js
42610
42615
  var buildLineMap = (source) => {
42611
42616
  const lineStarts = [0];
42612
42617
  for (let index = 0;index < source.length; index++) {
@@ -42631,8 +42636,6 @@ var lineColumn = (lineMap, offset) => {
42631
42636
  column: offset - lineMap[low] + 1
42632
42637
  };
42633
42638
  };
42634
-
42635
- // ../markup/dist/diagnostics.js
42636
42639
  var codes2 = (table) => table;
42637
42640
  var DIAGNOSTIC_CODES = codes2({
42638
42641
  "no-template": {
@@ -43029,7 +43032,6 @@ var DIAGNOSTIC_CODES = codes2({
43029
43032
  channelOnly: true
43030
43033
  }
43031
43034
  });
43032
-
43033
43035
  // ../sdk/dist/normalize-B79uJIeb.js
43034
43036
  function toRecipientArray(recipient) {
43035
43037
  return Array.isArray(recipient) ? recipient : [recipient];
@@ -56684,9 +56686,13 @@ var missingOrgMessage = "No active organization. Run `samva org use <slug>` afte
56684
56686
  var provider = (config) => ({
56685
56687
  provide: (effect) => provide4(effect, layerFetch(config))
56686
56688
  });
56687
- var getClientProviderForContext = (context, baseUrl) => gen2(function* () {
56689
+ var getClientConfigForContext = (context, baseUrl) => gen2(function* () {
56688
56690
  if (context.apiKey) {
56689
- return provider({ apiKey: context.apiKey, baseUrl, headers: CLI_CLIENT_HEADERS });
56691
+ return {
56692
+ apiKey: context.apiKey,
56693
+ baseUrl,
56694
+ headers: CLI_CLIENT_HEADERS
56695
+ };
56690
56696
  }
56691
56697
  if (context.credentials) {
56692
56698
  if (isCredentialExpired(context.credentials, yield* currentTimeMillis2)) {
@@ -56703,18 +56709,38 @@ var getClientProviderForContext = (context, baseUrl) => gen2(function* () {
56703
56709
  message: missingOrgMessage
56704
56710
  });
56705
56711
  }
56706
- return provider({
56712
+ return {
56707
56713
  authToken,
56708
56714
  baseUrl,
56709
56715
  headers: { ...CLI_CLIENT_HEADERS, "x-org-slug": activeOrgSlug }
56710
- });
56716
+ };
56711
56717
  }
56712
56718
  return yield* new CliAuthError({
56713
56719
  code: "missing_credentials",
56714
56720
  message: "No credentials found. Run `samva login`, or set SAMVA_API_KEY for automation."
56715
56721
  });
56716
56722
  });
56717
- var getClientProvider = flatMap3(resolveExecutionContext, (context) => getClientProviderForContext(context, context.apiUrl));
56723
+ var getClientConfig = flatMap3(resolveExecutionContext, (context) => getClientConfigForContext(context, context.apiUrl));
56724
+ var getClientProvider = map6(getClientConfig, provider);
56725
+ var getAuthenticatedJsonForContext = fn2("CliClient.getAuthenticatedJson")(function* (context, path) {
56726
+ const config = yield* getClientConfigForContext(context, context.apiUrl);
56727
+ const headers = {
56728
+ ...config.headers,
56729
+ ...config.apiKey !== undefined ? { "x-api-key": config.apiKey } : { Authorization: `Bearer ${config.authToken}` }
56730
+ };
56731
+ const url = `${config.baseUrl.replace(/\/$/, "")}${path}`;
56732
+ return yield* tryPromise2({
56733
+ try: async (signal) => {
56734
+ const response = await fetch(url, { headers, signal });
56735
+ if (!response.ok) {
56736
+ throw new Error(`HTTP ${response.status}`);
56737
+ }
56738
+ return response.json();
56739
+ },
56740
+ catch: (cause) => new SamvaCliError({ message: `Request to ${url} failed: ${String(cause)}` })
56741
+ });
56742
+ });
56743
+ var getAuthenticatedJson = (path) => flatMap3(resolveExecutionContext, (context) => getAuthenticatedJsonForContext(context, path));
56718
56744
 
56719
56745
  // src/lib/output.ts
56720
56746
  var CLI_SCHEMA_VERSION = 1;
@@ -61024,7 +61050,7 @@ var resolvePublicAuthUrl = (basePath) => {
61024
61050
  } catch {}
61025
61051
  }
61026
61052
  };
61027
- var getClientConfig = (options, loadEnv) => {
61053
+ var getClientConfig2 = (options, loadEnv) => {
61028
61054
  const isCredentialsSupported = "credentials" in Request.prototype;
61029
61055
  const baseURL = getBaseURL(options?.baseURL, options?.basePath, undefined, loadEnv) ?? resolvePublicAuthUrl(options?.basePath) ?? "/api/auth";
61030
61056
  const pluginsFetchPlugins = options?.plugins?.flatMap((plugin) => plugin.fetchPlugins).filter((pl) => pl !== undefined) || [];
@@ -61226,7 +61252,7 @@ function createDynamicPathProxy(routes, client, knownPathMethods, atoms, atomLis
61226
61252
 
61227
61253
  // ../../node_modules/.bun/better-auth@1.7.5+e459c178ac81a797/node_modules/better-auth/dist/client/vanilla.mjs
61228
61254
  function createAuthClient(options) {
61229
- const { pluginPathMethods, pluginsActions, pluginsAtoms, hydrateSession, $fetch, atomListeners, $store } = getClientConfig(options);
61255
+ const { pluginPathMethods, pluginsActions, pluginsAtoms, hydrateSession, $fetch, atomListeners, $store } = getClientConfig2(options);
61230
61256
  const resolvedHooks = {};
61231
61257
  for (const [key, value] of Object.entries(pluginsAtoms))
61232
61258
  resolvedHooks[`use${capitalizeFirstLetter(key)}`] = value;
@@ -62955,6 +62981,7 @@ var reportDiagnostic = (diagnostic, context) => {
62955
62981
  ...origin === undefined ? {} : { line: origin.lineNumber, column: origin.columnNumber },
62956
62982
  ...range === undefined ? {} : { range },
62957
62983
  ...diagnostic.fix === undefined ? {} : { fix: diagnostic.fix },
62984
+ ...diagnostic.upgrade === undefined ? {} : { upgrade: diagnostic.upgrade },
62958
62985
  ...diagnostic.edit === undefined ? {} : { edit: diagnostic.edit },
62959
62986
  ...context.fixture === undefined ? {} : { fixture: context.fixture }
62960
62987
  };
@@ -62962,8 +62989,10 @@ var reportDiagnostic = (diagnostic, context) => {
62962
62989
  var formatReportedDiagnostic = (diagnostic) => {
62963
62990
  const at = diagnostic.line === undefined ? diagnostic.file : `${diagnostic.file}:${diagnostic.line}${diagnostic.column === undefined ? "" : `:${diagnostic.column}`}`;
62964
62991
  const head = `${at} ${diagnostic.severity} ${diagnostic.code}: ${diagnostic.message}`;
62965
- return diagnostic.fix === undefined ? head : `${head}
62992
+ const fixed = diagnostic.fix === undefined ? head : `${head}
62966
62993
  Fix: ${diagnostic.fix}`;
62994
+ return diagnostic.upgrade === undefined ? fixed : `${fixed}
62995
+ Upgrade: ${diagnostic.upgrade}`;
62967
62996
  };
62968
62997
 
62969
62998
  // src/commands/templates/local-build.ts
@@ -63141,7 +63170,11 @@ var digest2 = async (value) => {
63141
63170
  const bytes = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
63142
63171
  return Array.from(new Uint8Array(bytes), (byte) => byte.toString(16).padStart(2, "0")).join("");
63143
63172
  };
63173
+ var StarterCatalog = Struct({
63174
+ items: ArraySchema(Struct({ id: String4, channel: String4 }))
63175
+ });
63144
63176
  var init = make27("init", {
63177
+ starter: String8("starter").pipe(withDescription2("Gallery starter id (see the template gallery)"), optional4),
63145
63178
  name: String8("name").pipe(withDescription2("Template name")),
63146
63179
  projectName: String8("project-name").pipe(withDescription2("Template project name"), optional4),
63147
63180
  slug: String8("slug").pipe(withDescription2("Template handle"), optional4),
@@ -63149,24 +63182,30 @@ var init = make27("init", {
63149
63182
  idempotencyKey: String8("idempotency-key").pipe(withDescription2("Stable retry key for project creation"), optional4),
63150
63183
  dryRun: Boolean6("dry-run").pipe(withDescription2("Show the project creation request without writing"), withDefault4(false)),
63151
63184
  json: jsonFlag
63152
- }).pipe(withDescription3("Create and clone the canonical Samva email starter"), withExamples([
63185
+ }).pipe(withDescription3("Create and clone a Samva email starter"), withExamples([
63153
63186
  {
63154
63187
  command: 'samva templates init --name "Welcome email"',
63155
63188
  description: "Create a remote project and clone its canonical starter"
63156
63189
  },
63190
+ {
63191
+ command: 'samva templates init --name "Product newsletter" --starter product-newsletter',
63192
+ description: "Create a project from a gallery starter"
63193
+ },
63157
63194
  {
63158
63195
  command: 'samva templates init --name "Welcome email" --slug welcome --dir welcome-email --json',
63159
63196
  description: "Create deterministically for CI or a coding agent"
63160
63197
  }
63161
- ]), withHandler(({ name, projectName, slug, dir, idempotencyKey, dryRun, json }) => gen2(function* () {
63198
+ ]), withHandler(({ name, projectName, slug, dir, starter, idempotencyKey, dryRun, json }) => gen2(function* () {
63162
63199
  const root = resolve8(getOrElse(dir, () => projectDirectory(name)));
63163
63200
  const key = getOrUndefined(idempotencyKey);
63164
63201
  const request = {
63165
63202
  name,
63166
63203
  projectName: getOrUndefined(projectName),
63204
+ galleryTemplateId: getOrUndefined(starter),
63167
63205
  slug: getOrUndefined(slug),
63168
63206
  idempotencyKey: key ?? `cli:init:${(yield* promise2(() => digest2(`${name}
63169
- ${root}`))).slice(0, 48)}`
63207
+ ${root}${isSome2(starter) ? `
63208
+ ${starter.value}` : ""}`))).slice(0, 48)}`
63170
63209
  };
63171
63210
  if (dryRun) {
63172
63211
  const result = { action: "templates.init", dryRun: true, dir: root, request };
@@ -63178,9 +63217,23 @@ ${root}`))).slice(0, 48)}`
63178
63217
  ["Name", request.name],
63179
63218
  ["Project", request.projectName ?? request.name],
63180
63219
  ["Slug", request.slug ?? "Generated by Samva"],
63220
+ ["Starter", request.galleryTemplateId ?? "Canonical email starter"],
63181
63221
  ["Idempotency key", request.idempotencyKey]
63182
63222
  ]);
63183
63223
  }
63224
+ if (request.galleryTemplateId !== undefined) {
63225
+ const response = yield* getAuthenticatedJson("/v1/starters/");
63226
+ const catalog = yield* decodeUnknownEffect2(StarterCatalog)(response).pipe(mapError2((error) => new SamvaCliError({
63227
+ message: formatApiError("Could not read starter catalog", error)
63228
+ })));
63229
+ const validIds = catalog.items.filter((item) => item.channel === "email").map((item) => item.id);
63230
+ if (!validIds.includes(request.galleryTemplateId)) {
63231
+ return yield* new CliUsageError({
63232
+ code: "unknown_starter",
63233
+ message: `Unknown email starter '${request.galleryTemplateId}'. Valid starter ids: ${validIds.join(", ") || "(none)"}.`
63234
+ });
63235
+ }
63236
+ }
63184
63237
  const client = yield* getClientProvider;
63185
63238
  const created = yield* create11(request).pipe(client.provide, mapError2((error) => new SamvaCliError({
63186
63239
  message: formatApiError("Could not create template project", error)
@@ -63192,6 +63245,7 @@ ${root}`))).slice(0, 48)}`
63192
63245
  yield* printFields([
63193
63246
  ["Template", payload.templateId],
63194
63247
  ["Project", payload.projectId],
63248
+ ["Starter", request.galleryTemplateId ?? "Canonical email starter"],
63195
63249
  ["Directory", payload.dir],
63196
63250
  ["Commit", payload.commit]
63197
63251
  ]);
@@ -64831,28 +64885,32 @@ var observeTemplateCheck = (dir, tsconfig, build) => gen2(function* () {
64831
64885
  line: diagnostic.line,
64832
64886
  column: diagnostic.column
64833
64887
  }));
64834
- if (typeProblems.length > 0) {
64835
- const message = `TypeScript check failed (${typeProblems.length} problem(s)).`;
64836
- return {
64888
+ const typeFailure = () => ({
64889
+ ok: false,
64890
+ message: `TypeScript check failed (${typeProblems.length} problem(s)).`,
64891
+ problems: typeProblems,
64892
+ payload: {
64893
+ action: "templates.check",
64837
64894
  ok: false,
64838
- message,
64839
- problems: typeProblems,
64840
- payload: {
64841
- action: "templates.check",
64842
- ok: false,
64843
- phase: "typescript",
64844
- tsconfig,
64845
- typescriptVersion: typecheck.version,
64846
- problems: typeProblems
64847
- }
64848
- };
64849
- }
64850
- const built = yield* build();
64851
- const problems = collectBuildProblems(built);
64852
- const ok = isSuccessfulBuild(built);
64895
+ phase: "typescript",
64896
+ tsconfig,
64897
+ typescriptVersion: typecheck.version,
64898
+ problems: typeProblems
64899
+ }
64900
+ });
64901
+ const attempted = typeProblems.length > 0 ? yield* option2(build()) : some2(yield* build());
64902
+ if (isNone2(attempted))
64903
+ return typeFailure();
64904
+ const built = attempted.value;
64905
+ const buildProblems = collectBuildProblems(built);
64906
+ if (typeProblems.length > 0 && built.files.length === 0 && buildProblems.length === 0) {
64907
+ return typeFailure();
64908
+ }
64909
+ const problems = [...typeProblems, ...buildProblems];
64910
+ const ok = typeProblems.length === 0 && isSuccessfulBuild(built);
64853
64911
  const compilerVersion = built.compilerVersion;
64854
64912
  const warnings = compatibilityFindings(built, "warning");
64855
- const message = ok ? `TypeScript v${typecheck.version} \xB7 compiler ${compilerVersion} \xB7 ${built.files.length} template(s) compiled \xB7 ${warnings.length} compatibility warning(s)` : built.files.length === 0 ? NO_TEMPLATES(dir) : `Template check failed (${problems.length} problem(s)).`;
64913
+ const message = ok ? `TypeScript v${typecheck.version} \xB7 compiler ${compilerVersion} \xB7 ${built.files.length} template(s) compiled \xB7 ${warnings.length} compatibility warning(s)` : typeProblems.length > 0 ? `TypeScript check failed (${typeProblems.length} problem(s)); template check found ${problems.length - typeProblems.length} more.` : built.files.length === 0 ? NO_TEMPLATES(dir) : `Template check failed (${problems.length} problem(s)).`;
64856
64914
  return {
64857
64915
  ok,
64858
64916
  message,
@@ -64860,7 +64918,7 @@ var observeTemplateCheck = (dir, tsconfig, build) => gen2(function* () {
64860
64918
  payload: {
64861
64919
  action: "templates.check",
64862
64920
  ok,
64863
- phase: ok ? "complete" : "templates",
64921
+ phase: ok ? "complete" : typeProblems.length > 0 ? "typescript" : "templates",
64864
64922
  tsconfig,
64865
64923
  typescriptVersion: typecheck.version,
64866
64924
  compilerVersion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samva/cli",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Samva CLI — email and platform operations from your terminal",
5
5
  "keywords": [
6
6
  "cli",
@@ -48,8 +48,8 @@
48
48
  "devDependencies": {
49
49
  "@effect/platform-bun": "4.0.0",
50
50
  "@effect/vitest": "4.0.0",
51
- "@samva/markup": "0.11.0",
52
- "@samva/vite": "0.11.0",
51
+ "@samva/markup": "^0.11.4",
52
+ "@samva/vite": "^0.11.4",
53
53
  "@types/node": "^26.6.2",
54
54
  "better-auth": "1.7.5",
55
55
  "effect": "4.0.0",
@@ -59,10 +59,10 @@
59
59
  "vitest": "^5.0.3"
60
60
  },
61
61
  "optionalDependencies": {
62
- "@samva/cli-darwin-arm64": "0.6.1",
63
- "@samva/cli-darwin-x64": "0.6.1",
64
- "@samva/cli-linux-arm64": "0.6.1",
65
- "@samva/cli-linux-x64": "0.6.1"
62
+ "@samva/cli-darwin-arm64": "0.7.1",
63
+ "@samva/cli-darwin-x64": "0.7.1",
64
+ "@samva/cli-linux-arm64": "0.7.1",
65
+ "@samva/cli-linux-x64": "0.7.1"
66
66
  },
67
67
  "engines": {
68
68
  "node": ">=18"